diff options
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/ic/osiop.c | 1977 | ||||
| -rw-r--r-- | sys/dev/ic/osiopreg.h | 378 | ||||
| -rw-r--r-- | sys/dev/ic/osiopvar.h | 260 | ||||
| -rw-r--r-- | sys/dev/microcode/siop/Makefile | 12 | ||||
| -rw-r--r-- | sys/dev/microcode/siop/osiop.ss | 255 |
5 files changed, 2877 insertions, 5 deletions
diff --git a/sys/dev/ic/osiop.c b/sys/dev/ic/osiop.c new file mode 100644 index 00000000000..c03b3853f51 --- /dev/null +++ b/sys/dev/ic/osiop.c @@ -0,0 +1,1977 @@ +/* $NetBSD: osiop.c,v 1.1 2001/04/30 04:47:50 tsutsui Exp $ */ + +/* + * Copyright (c) 2001 Izumi Tsutsui. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR 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. + */ + +/* + * Copyright (c) 1994 Michael L. Hitch + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)siop.c 7.5 (Berkeley) 5/4/91 + */ + +/* + * MI NCR53C710 scsi adaptor driver; based on arch/amiga/dev/siop.c: + * NetBSD: siop.c,v 1.43 1999/09/30 22:59:53 thorpej Exp + * + * bus_space/bus_dma'fied by Izumi Tsutsui <tsutsui@ceres.dti.ne.jp> + * + * The 53c710 datasheet is avaliable at: + * http://www.lsilogic.com/techlib/techdocs/storage_stand_prod/index.html + */ + +/* #define OSIOP_DEBUG */ + +#include "opt_ddb.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/kernel.h> + +#include <uvm/uvm_extern.h> + +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsiconf.h> +#include <dev/scsipi/scsi_message.h> + +#include <machine/cpu.h> +#include <machine/bus.h> + +#include <dev/ic/osiopreg.h> +#include <dev/ic/osiopvar.h> + +/* 53C710 script */ +#include <dev/microcode/siop/osiop.out> + +void osiop_attach(struct osiop_softc *); +void osiop_minphys(struct buf *); +void osiop_scsipi_request(struct scsipi_channel *, scsipi_adapter_req_t, + void *); +void osiop_poll(struct osiop_softc *, struct osiop_acb *); +void osiop_sched(struct osiop_softc *); +void osiop_scsidone(struct osiop_acb *, int); +void osiop_abort(struct osiop_softc *, const char *); +void osiop_init(struct osiop_softc *); +void osiop_reset(struct osiop_softc *); +void osiop_resetbus(struct osiop_softc *); +void osiop_start(struct osiop_softc *); +int osiop_checkintr(struct osiop_softc *, u_int8_t, u_int8_t, u_int8_t, int *); +void osiop_select(struct osiop_softc *); +void scsi_period_to_osiop(struct osiop_softc *, int); +void osiop_timeout(void *); + +int osiop_reset_delay = 250; /* delay after reset, in milleseconds */ + +#ifdef OSIOP_DEBUG_SYNC +/* + * sync period transfer lookup - only valid for 66MHz clock + */ +static struct { + u_int8_t p; /* period from sync request message */ + u_int8_t r; /* siop_period << 4 | sbcl */ +} sync_tab[] = { + { 60/4, 0<<4 | 1}, + { 76/4, 1<<4 | 1}, + { 92/4, 2<<4 | 1}, + { 92/4, 0<<4 | 2}, + {108/4, 3<<4 | 1}, + {116/4, 1<<4 | 2}, + {120/4, 4<<4 | 1}, + {120/4, 0<<4 | 3}, + {136/4, 5<<4 | 1}, + {140/4, 2<<4 | 2}, + {152/4, 6<<4 | 1}, + {152/4, 1<<4 | 3}, + {164/4, 3<<4 | 2}, + {168/4, 7<<4 | 1}, + {180/4, 2<<4 | 3}, + {184/4, 4<<4 | 2}, + {208/4, 5<<4 | 2}, + {212/4, 3<<4 | 3}, + {232/4, 6<<4 | 2}, + {240/4, 4<<4 | 3}, + {256/4, 7<<4 | 2}, + {272/4, 5<<4 | 3}, + {300/4, 6<<4 | 3}, + {332/4, 7<<4 | 3} +}; +#endif + +#ifdef OSIOP_DEBUG +#define DEBUG_DMA 0x01 +#define DEBUG_INT 0x02 +#define DEBUG_PHASE 0x04 +#define DEBUG_UNEXCEPT 0x08 +#define DEBUG_DISC 0x10 +#define DEBUG_CMD 0x20 +#define DEBUG_ALL 0xff +int osiop_debug = 0; /*DEBUG_ALL;*/ +int osiopsync_debug = 0; +int osiopdma_hits = 1; +int osiopstarts = 0; +int osiopints = 0; +int osiopphmm = 0; +int osiop_trix = 0; +#define OSIOP_TRACE_SIZE 128 +#define OSIOP_TRACE(a,b,c,d) do { \ + osiop_trbuf[osiop_trix + 0] = (a); \ + osiop_trbuf[osiop_trix + 1] = (b); \ + osiop_trbuf[osiop_trix + 2] = (c); \ + osiop_trbuf[osiop_trix + 3] = (d); \ + osiop_trix = (osiop_trix + 4) & (OSIOP_TRACE_SIZE - 1); \ +} while (0) +u_int8_t osiop_trbuf[OSIOP_TRACE_SIZE]; +void osiop_dump_trace(void); +void osiop_dump_acb(struct osiop_acb *); +void osiop_dump(struct osiop_softc *); +#else +#define OSIOP_TRACE(a,b,c,d) +#endif + +void +osiop_attach(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb; + bus_dma_segment_t seg; + int nseg; + int i, err; + + /* + * Allocate and map DMA-safe memory for the script. + */ + err = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, PAGE_SIZE, 0, + &seg, 1, &nseg, BUS_DMA_NOWAIT); + if (err) { + printf(": failed to allocate script memory, err=%d\n", err); + return; + } + err = bus_dmamem_map(sc->sc_dmat, &seg, nseg, PAGE_SIZE, + (caddr_t *)&sc->sc_script, BUS_DMA_NOWAIT | BUS_DMA_COHERENT); + if (err) { + printf(": failed to map script memory, err=%d\n", err); + return; + } + err = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0, + BUS_DMA_NOWAIT, &sc->sc_scrdma); + if (err) { + printf(": failed to create script map, err=%d\n", err); + return; + } + err = bus_dmamap_load(sc->sc_dmat, sc->sc_scrdma, + sc->sc_script, PAGE_SIZE, NULL, BUS_DMA_NOWAIT); + if (err) { + printf(": failed to load script map, err=%d\n", err); + return; + } + + /* + * Copy and sync script + */ + memcpy(sc->sc_script, osiop_script, sizeof(osiop_script)); + bus_dmamap_sync(sc->sc_dmat, sc->sc_scrdma, 0, sizeof(osiop_script), + BUS_DMASYNC_PREWRITE); + + /* + * Allocate and map DMA-safe memory for the script data structure. + */ + err = bus_dmamem_alloc(sc->sc_dmat, + sizeof(struct osiop_ds) * OSIOP_NACB, PAGE_SIZE, 0, + &seg, 1, &nseg, BUS_DMA_NOWAIT); + if (err) { + printf(": failed to allocate ds memory, err=%d\n", err); + return; + } + err = bus_dmamem_map(sc->sc_dmat, &seg, nseg, + sizeof(struct osiop_ds) * OSIOP_NACB, (caddr_t *)&sc->sc_ds, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT); + if (err) { + printf(": failed to map ds memory, err=%d\n", err); + return; + } + err = bus_dmamap_create(sc->sc_dmat, + sizeof(struct osiop_ds) * OSIOP_NACB, 1, + sizeof(struct osiop_ds) * OSIOP_NACB, 0, + BUS_DMA_NOWAIT, &sc->sc_dsdma); + if (err) { + printf(": failed to create ds map, err=%d\n", err); + return; + } + err = bus_dmamap_load(sc->sc_dmat, sc->sc_dsdma, sc->sc_ds, + sizeof(struct osiop_ds) * OSIOP_NACB, NULL, BUS_DMA_NOWAIT); + if (err) { + printf(": failed to load ds map, err=%d\n", err); + return; + } + + acb = malloc(sizeof(struct osiop_acb) * OSIOP_NACB, M_DEVBUF, M_NOWAIT); + if (acb == NULL) { + printf(": can't allocate memory for acb\n"); + return; + } + memset(acb, 0, sizeof(struct osiop_acb) * OSIOP_NACB); + sc->sc_acb = acb; + sc->sc_cfflags = sc->sc_dev.dv_cfdata->cf_flags; + sc->sc_nexus = NULL; + sc->sc_active = 0; + memset(sc->sc_tinfo, 0, sizeof(sc->sc_tinfo)); + + /* Initialize command block queue */ + TAILQ_INIT(&sc->ready_list); + TAILQ_INIT(&sc->nexus_list); + TAILQ_INIT(&sc->free_list); + + /* Initialize each command block */ + for (i = 0; i < OSIOP_NACB; i++) { + bus_addr_t dsa; + + /* XXX How much size is required for each command block? */ + err = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, + 0, BUS_DMA_NOWAIT, &acb->cmddma); + if (err) { + printf(": failed to create cmddma map, err=%d\n", err); + return; + } + err = bus_dmamap_create(sc->sc_dmat, OSIOP_MAX_XFER, OSIOP_NSG, + OSIOP_MAX_XFER, 0, BUS_DMA_NOWAIT, &acb->datadma); + if (err) { + printf(": failed to create datadma map, err=%d\n", + err); + return; + } + + acb->sc = sc; + acb->ds = &sc->sc_ds[i]; + acb->dsoffset = sizeof(struct osiop_ds) * i; + + dsa = sc->sc_dsdma->dm_segs[0].ds_addr + acb->dsoffset; + acb->ds->id.addr = dsa + OSIOP_DSIDOFF; + acb->ds->status.count = 1; + acb->ds->status.addr = dsa + OSIOP_DSSTATOFF; + acb->ds->msg.count = 1; + acb->ds->msg.addr = dsa + OSIOP_DSMSGOFF; + acb->ds->msgin.count = 1; + acb->ds->msgin.addr = dsa + OSIOP_DSMSGINOFF; + acb->ds->extmsg.count = 1; + acb->ds->extmsg.addr = dsa + OSIOP_DSEXTMSGOFF; + acb->ds->synmsg.count = 3; + acb->ds->synmsg.addr = dsa + OSIOP_DSSYNMSGOFF; + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + + acb++; + } + + printf(": NCR53C710 rev %d, SCSI ID %d\n", + osiop_read_1(sc, OSIOP_CTEST8) >> 4, sc->sc_id); + + /* + * Initialize all + */ + osiop_init(sc); + + /* + * Fill in the adapter. + */ + sc->sc_adapter.adapt_dev = &sc->sc_dev; + sc->sc_adapter.adapt_nchannels = 1; + sc->sc_adapter.adapt_openings = OSIOP_NACB; + sc->sc_adapter.adapt_max_periph = 1; + sc->sc_adapter.adapt_ioctl = NULL; + sc->sc_adapter.adapt_minphys = osiop_minphys; + sc->sc_adapter.adapt_request = osiop_scsipi_request; + + /* + * Fill in the channel. + */ + sc->sc_channel.chan_adapter = &sc->sc_adapter; + sc->sc_channel.chan_bustype = &scsi_bustype; + sc->sc_channel.chan_channel = 0; + sc->sc_channel.chan_ntargets = OSIOP_NTGT; + sc->sc_channel.chan_nluns = 8; + sc->sc_channel.chan_id = sc->sc_id; + + /* + * Now try to attach all the sub devices. + */ + config_found(&sc->sc_dev, &sc->sc_channel, scsiprint); +} + +/* + * default minphys routine for osiop based controllers + */ +void +osiop_minphys(bp) + struct buf *bp; +{ + + if (bp->b_bcount > OSIOP_MAX_XFER) + bp->b_bcount = OSIOP_MAX_XFER; + minphys(bp); +} + +/* + * used by specific osiop controller + * + */ +void +osiop_scsipi_request(chan, req, arg) + struct scsipi_channel *chan; + scsipi_adapter_req_t req; + void *arg; +{ + struct scsipi_xfer *xs; + struct scsipi_periph *periph; + struct osiop_acb *acb; + struct osiop_softc *sc; + int err, flags, s; + + sc = (struct osiop_softc *)chan->chan_adapter->adapt_dev; + + switch (req) { + case ADAPTER_REQ_RUN_XFER: + xs = arg; + periph = xs->xs_periph; + flags = xs->xs_control; + + /* XXXX ?? */ + if (flags & XS_CTL_DATA_UIO) + panic("osiop: scsi data uio requested"); + + /* XXXX ?? */ + if (sc->sc_nexus && flags & XS_CTL_POLL) +#if 0 + panic("osiop_scsicmd: busy"); +#else + printf("osiop_scsicmd: busy\n"); +#endif + + s = splbio(); + acb = TAILQ_FIRST(&sc->free_list); + if (acb != NULL) { + TAILQ_REMOVE(&sc->free_list, acb, chain); + } +#ifdef DIAGNOSTIC + else { + scsipi_printaddr(periph); + printf("unable to allocate acb\n"); + panic("osiop_scsipi_request"); + } +#endif + + acb->status = ACB_S_READY; + acb->xs = xs; + + /* Setup DMA map for SCSI command buffer */ + err = bus_dmamap_load(sc->sc_dmat, acb->cmddma, + xs->cmd, xs->cmdlen, NULL, BUS_DMA_NOWAIT); + if (err) { + printf("%s: unable to load cmd DMA map: %d", + sc->sc_dev.dv_xname, err); + xs->error = XS_DRIVER_STUFFUP; + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + scsipi_done(xs); + splx(s); + return; + } + + /* Setup DMA map for data buffer */ + if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) { + err = bus_dmamap_load(sc->sc_dmat, acb->datadma, + xs->data, xs->datalen, NULL, + BUS_DMA_NOWAIT | BUS_DMA_STREAMING); + if (err) { + printf("%s: unable to load data DMA map: %d", + sc->sc_dev.dv_xname, err); + xs->error = XS_DRIVER_STUFFUP; + scsipi_done(xs); + bus_dmamap_unload(sc->sc_dmat, acb->cmddma); + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + splx(s); + return; + } + bus_dmamap_sync(sc->sc_dmat, acb->datadma, + 0, xs->datalen, (xs->xs_control & XS_CTL_DATA_IN) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + } + bus_dmamap_sync(sc->sc_dmat, acb->cmddma, 0, xs->cmdlen, + BUS_DMASYNC_PREWRITE); + + acb->cmdlen = xs->cmdlen; + acb->datalen = xs->datalen; +#ifdef OSIOP_DEBUG + acb->data = xs->data; +#endif + + TAILQ_INSERT_TAIL(&sc->ready_list, acb, chain); + + if (sc->sc_nexus == NULL) + osiop_sched(sc); + + splx(s); + + if (flags & XS_CTL_POLL || sc->sc_flags & OSIOP_NODMA) + osiop_poll(sc, acb); + return; + + case ADAPTER_REQ_GROW_RESOURCES: + return; + + case ADAPTER_REQ_SET_XFER_MODE: + return; + } +} + +void +osiop_poll(sc, acb) + struct osiop_softc *sc; + struct osiop_acb *acb; +{ + struct scsipi_xfer *xs = acb->xs; + int status, i, s, to; + u_int8_t istat, dstat, sstat0; + + s = splbio(); + to = xs->timeout / 1000; + if (!TAILQ_EMPTY(&sc->nexus_list)) + printf("%s: osiop_poll called with disconnected device\n", + sc->sc_dev.dv_xname); + for (;;) { + i = 1000; + while (((istat = osiop_read_1(sc, OSIOP_ISTAT)) & + (OSIOP_ISTAT_SIP | OSIOP_ISTAT_DIP)) == 0) { + if (i <= 0) { +#ifdef OSIOP_DEBUG + printf("waiting: tgt %d cmd %02x sbcl %02x" + " dsp %x (+%lx) dcmd %x" + " ds %p timeout %d\n", + xs->xs_periph->periph_target, + xs->cmd->opcode, + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP), + osiop_read_4(sc, OSIOP_DSP) - + sc->sc_scrdma->dm_segs[0].ds_addr, + osiop_read_1(sc, OSIOP_DCMD), + acb->ds, acb->xs->timeout); +#endif + i = 1000; + to--; + if (to <= 0) { + osiop_reset(sc); + splx(s); + return; + } + } + delay(1000); + i--; + } + sstat0 = osiop_read_1(sc, OSIOP_SSTAT0); + dstat = osiop_read_1(sc, OSIOP_DSTAT); + if (osiop_checkintr(sc, istat, dstat, sstat0, &status)) { + if (acb != sc->sc_nexus) + printf("%s: osiop_poll disconnected device" + " completed\n", sc->sc_dev.dv_xname); + else if ((sc->sc_flags & OSIOP_INTDEFER) == 0) { + sc->sc_flags &= ~OSIOP_INTSOFF; + osiop_write_1(sc, OSIOP_SIEN, sc->sc_sien); + osiop_write_1(sc, OSIOP_DIEN, sc->sc_dien); + } + osiop_scsidone(sc->sc_nexus, status); + } + + if (xs->xs_status & XS_STS_DONE) + break; + } + + splx(s); + return; +} + +/* + * start next command that's ready + */ +void +osiop_sched(sc) + struct osiop_softc *sc; +{ + struct scsipi_periph *periph; + struct osiop_acb *acb; + int i; + +#ifdef OSIOP_DEBUG + if (sc->sc_nexus != NULL) { + printf("%s: osiop_sched- nexus %p/%d ready %p/%d\n", + sc->sc_dev.dv_xname, sc->sc_nexus, + sc->sc_nexus->xs->xs_periph->periph_target, + sc->ready_list.tqh_first, + sc->ready_list.tqh_first->xs->xs_periph->periph_target); + return; + } +#endif + TAILQ_FOREACH(acb, &sc->ready_list, chain) { + periph = acb->xs->xs_periph; + i = periph->periph_target; + if ((sc->sc_tinfo[i].lubusy & (1 << periph->periph_lun)) == 0) { + struct osiop_tinfo *ti; + + TAILQ_REMOVE(&sc->ready_list, acb, chain); + sc->sc_nexus = acb; + ti = &sc->sc_tinfo[i]; + ti->lubusy |= (1 << periph->periph_lun); + break; + } + } + + if (acb == NULL) { +#ifdef OSIOP_DEBUG + printf("%s: osiop_sched didn't find ready command\n", + sc->sc_dev.dv_xname); +#endif + return; + } + + if (acb->xs->xs_control & XS_CTL_RESET) + osiop_reset(sc); + + sc->sc_active++; + osiop_select(sc); +} + +void +osiop_scsidone(acb, status) + struct osiop_acb *acb; + int status; +{ + struct scsipi_xfer *xs; + struct scsipi_periph *periph; + struct osiop_softc *sc; + int dosched = 0; + +#ifdef DIAGNOSTIC + if (acb == NULL || acb->xs == NULL) { + printf("%s: NULL acb or scsipi_xfer in osiop_scsidone\n", + sc->sc_dev.dv_xname); +#if defined(OSIOP_DEBUG) && defined(DDB) + Debugger(); +#endif + return; + } +#endif + xs = acb->xs; + sc = acb->sc; + periph = xs->xs_periph; + +#ifdef OSIOP_DEBUG + if (acb->status != ACB_S_DONE) + printf("%s: acb not done (status %d)\n", + sc->sc_dev.dv_xname, acb->status); +#endif + + xs->status = status; + + switch (status) { + case SCSI_OK: + xs->error = XS_NOERROR; + break; + case SCSI_BUSY: + xs->error = XS_BUSY; + break; + case SCSI_CHECK: + xs->error = XS_BUSY; + break; + case SCSI_OSIOP_NOCHECK: + /* + * don't check status, xs->error is already valid + */ + break; + case SCSI_OSIOP_NOSTATUS: + /* + * the status byte was not updated, cmd was + * aborted + */ + xs->error = XS_SELTIMEOUT; + break; + default: +#ifdef OSIOP_DEBUG + printf("%s: osiop_scsidone: unknown status code (0x%02x)\n", + sc->sc_dev.dv_xname, status); +#endif + xs->error = XS_DRIVER_STUFFUP; + break; + } + + if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) { + bus_dmamap_sync(sc->sc_dmat, acb->datadma, 0, acb->datalen, + (xs->xs_control & XS_CTL_DATA_IN) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, acb->datadma); + } + + bus_dmamap_sync(sc->sc_dmat, acb->cmddma, 0, acb->cmdlen, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, acb->cmddma); + + /* + * Remove the ACB from whatever queue it's on. We have to do a bit of + * a hack to figure out which queue it's on. Note that it is *not* + * necessary to cdr down the ready queue, but we must cdr down the + * nexus queue and see if it's there, so we can mark the unit as no + * longer busy. This code is sickening, but it works. + */ + if (acb == sc->sc_nexus) { + sc->sc_nexus = NULL; + sc->sc_tinfo[periph->periph_target].lubusy &= + ~(1 << periph->periph_lun); + if (!TAILQ_EMPTY(&sc->ready_list)) + dosched = 1; /* start next command */ + sc->sc_active--; + OSIOP_TRACE('d', 'a', status, 0); + } else if (sc->ready_list.tqh_last == &acb->chain.tqe_next) { + TAILQ_REMOVE(&sc->ready_list, acb, chain); + OSIOP_TRACE('d', 'r', status, 0); + } else { + struct osiop_acb *acb2; + TAILQ_FOREACH(acb2, &sc->nexus_list, chain) { + if (acb2 == acb) { + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + sc->sc_tinfo[periph->periph_target].lubusy &= + ~(1 << periph->periph_lun); + sc->sc_active--; + break; + } + } + if (acb2 == NULL) { + if (acb->chain.tqe_next != NULL) { + TAILQ_REMOVE(&sc->ready_list, acb, chain); + sc->sc_active--; + } else { + printf("%s: can't find matching acb\n", + sc->sc_dev.dv_xname); +#ifdef DDB +#if 0 + Debugger(); +#endif +#endif + } + } + OSIOP_TRACE('d', 'n', status, 0); + } + /* Put it on the free list. */ + acb->status = ACB_S_FREE; + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + sc->sc_tinfo[periph->periph_target].cmds++; + + callout_stop(&xs->xs_callout); + xs->resid = 0; + if ((xs->xs_control & XS_CTL_POLL) == 0) + scsipi_done(xs); + + if (dosched && sc->sc_nexus == NULL) + osiop_sched(sc); +} + +void +osiop_abort(sc, where) + struct osiop_softc *sc; + const char *where; +{ + + printf("%s: abort %s: dstat %02x, sstat0 %02x sbcl %02x\n", + sc->sc_dev.dv_xname, where, + osiop_read_1(sc, OSIOP_DSTAT), + osiop_read_1(sc, OSIOP_SSTAT0), + osiop_read_1(sc, OSIOP_SBCL)); + + /* XXX XXX XXX */ + if (sc->sc_active > 0) { + sc->sc_active = 0; + } +} + +void +osiop_init(sc) + struct osiop_softc *sc; +{ + int i, inhibit_sync, inhibit_disc; + + sc->sc_tcp[1] = 1000 / sc->sc_clock_freq; + sc->sc_tcp[2] = 1500 / sc->sc_clock_freq; + sc->sc_tcp[3] = 2000 / sc->sc_clock_freq; + sc->sc_minsync = sc->sc_tcp[1]; /* in 4ns units */ + + if (sc->sc_minsync < 25) + sc->sc_minsync = 25; + + if (sc->sc_clock_freq <= 25) { + sc->sc_dcntl |= OSIOP_DCNTL_CF_1; /* SCLK/1 */ + sc->sc_tcp[0] = sc->sc_tcp[1]; + } else if (sc->sc_clock_freq <= 37) { + sc->sc_dcntl |= OSIOP_DCNTL_CF_1_5; /* SCLK/1.5 */ + sc->sc_tcp[0] = sc->sc_tcp[2]; + } else if (sc->sc_clock_freq <= 50) { + sc->sc_dcntl |= OSIOP_DCNTL_CF_2; /* SCLK/2 */ + sc->sc_tcp[0] = sc->sc_tcp[3]; + } else { + sc->sc_dcntl |= OSIOP_DCNTL_CF_3; /* SCLK/3 */ + sc->sc_tcp[0] = 3000 / sc->sc_clock_freq; + } + + if ((sc->sc_cfflags & 0x10000) != 0) { + sc->sc_flags |= OSIOP_NODMA; +#ifdef OSIOP_DEBUG + printf("%s: DMA disabled; use polling\n", + sc->sc_dev.dv_xname); +#endif + } + + inhibit_sync = (sc->sc_cfflags & 0xff00) >> 8; /* XXX */ + inhibit_disc = sc->sc_cfflags & 0x00ff; /* XXX */ +#ifdef OSIOP_DEBUG + if (inhibit_sync != 0) + printf("%s: Inhibiting synchronous transfer: 0x%02x\n", + sc->sc_dev.dv_xname, inhibit_sync); + if (inhibit_disc != 0) + printf("%s: Inhibiting disconnect: 0x%02x\n", + sc->sc_dev.dv_xname, inhibit_disc); +#endif + for (i = 0; i < OSIOP_NTGT; i++) { + if (inhibit_sync & (1 << i)) + sc->sc_tinfo[i].flags |= TI_NOSYNC; + if (inhibit_disc & (1 << i)) + sc->sc_tinfo[i].flags |= TI_NODISC; + } + + osiop_resetbus(sc); + osiop_reset(sc); +} + +void +osiop_reset(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb; + int s; + u_int8_t stat; + +#ifdef OSIOP_DEBUG + printf("%s: resetting chip\n", sc->sc_dev.dv_xname); +#endif + if (sc->sc_flags & OSIOP_ALIVE) + osiop_abort(sc, "reset"); + + s = splbio(); + + /* + * Reset the chip + * XXX - is this really needed? + */ + + /* abort current script */ + osiop_write_1(sc, OSIOP_ISTAT, + osiop_read_1(sc, OSIOP_ISTAT) | OSIOP_ISTAT_ABRT); + /* reset chip */ + osiop_write_1(sc, OSIOP_ISTAT, + osiop_read_1(sc, OSIOP_ISTAT) | OSIOP_ISTAT_RST); + delay(100); + osiop_write_1(sc, OSIOP_ISTAT, + osiop_read_1(sc, OSIOP_ISTAT) & ~OSIOP_ISTAT_RST); + delay(100); + + /* + * Set up various chip parameters + */ + osiop_write_1(sc, OSIOP_SCNTL0, + OSIOP_ARB_FULL | OSIOP_SCNTL0_EPC | OSIOP_SCNTL0_EPG); + osiop_write_1(sc, OSIOP_SCNTL1, OSIOP_SCNTL1_ESR); + osiop_write_1(sc, OSIOP_DCNTL, sc->sc_dcntl); + osiop_write_1(sc, OSIOP_DMODE, OSIOP_DMODE_BL4); + /* don't enable interrupts yet */ + osiop_write_1(sc, OSIOP_SIEN, 0x00); + osiop_write_1(sc, OSIOP_DIEN, 0x00); + osiop_write_1(sc, OSIOP_SCID, OSIOP_SCID_VALUE(sc->sc_id)); + osiop_write_1(sc, OSIOP_DWT, 0x00); + osiop_write_1(sc, OSIOP_CTEST0, osiop_read_1(sc, OSIOP_CTEST0) + | OSIOP_CTEST0_BTD | OSIOP_CTEST0_EAN); + osiop_write_1(sc, OSIOP_CTEST7, + osiop_read_1(sc, OSIOP_CTEST7) | sc->sc_ctest7); + + /* will need to re-negotiate sync xfers */ + memset(&sc->sc_sync, 0, sizeof(sc->sc_sync)); + + stat = osiop_read_1(sc, OSIOP_ISTAT); + if (stat & OSIOP_ISTAT_SIP) + osiop_read_1(sc, OSIOP_SSTAT0); + if (stat & OSIOP_ISTAT_DIP) + osiop_read_1(sc, OSIOP_DSTAT); + + splx(s); + + delay(osiop_reset_delay * 1000); + + if (sc->sc_nexus != NULL) { + sc->sc_nexus->xs->error = + (sc->sc_nexus->flags & ACB_F_TIMEOUT) ? + XS_TIMEOUT : XS_RESET; + sc->sc_nexus->status = ACB_S_DONE; + sc->sc_nexus->flags = 0; + osiop_scsidone(sc->sc_nexus, SCSI_OSIOP_NOCHECK); + } + while ((acb = TAILQ_FIRST(&sc->nexus_list)) != NULL) { + acb->xs->error = (acb->flags & ACB_F_TIMEOUT) ? + XS_TIMEOUT : XS_RESET; + acb->status = ACB_S_DONE; + acb->flags = 0; + osiop_scsidone(acb, SCSI_OSIOP_NOCHECK); + } + + sc->sc_flags &= ~(OSIOP_INTDEFER | OSIOP_INTSOFF); + /* enable SCSI and DMA interrupts */ + sc->sc_sien = OSIOP_SIEN_M_A | OSIOP_SIEN_STO | /*OSIOP_SIEN_SEL |*/ + OSIOP_SIEN_SGE | OSIOP_SIEN_UDC | OSIOP_SIEN_RST | OSIOP_SIEN_PAR; + sc->sc_dien = OSIOP_DIEN_BF | OSIOP_DIEN_ABRT | OSIOP_DIEN_SIR | + /*OSIOP_DIEN_WTD |*/ OSIOP_DIEN_IID; + osiop_write_1(sc, OSIOP_SIEN, sc->sc_sien); + osiop_write_1(sc, OSIOP_DIEN, sc->sc_dien); +} + +void +osiop_resetbus(sc) + struct osiop_softc *sc; +{ + + osiop_write_1(sc, OSIOP_SIEN, 0); + osiop_write_1(sc, OSIOP_SCNTL1, + osiop_read_1(sc, OSIOP_SCNTL1) | OSIOP_SCNTL1_RST); + delay(25); + osiop_write_1(sc, OSIOP_SCNTL1, + osiop_read_1(sc, OSIOP_SCNTL1) & ~OSIOP_SCNTL1_RST); +} + +/* + * Setup Data Storage for 53C710 and start SCRIPTS processing + */ + +void +osiop_start(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb = sc->sc_nexus; + struct osiop_ds *ds = acb->ds; + struct scsipi_xfer *xs = acb->xs; + bus_dmamap_t dsdma = sc->sc_dsdma, datadma = acb->datadma; + int target = xs->xs_periph->periph_target; + int lun = xs->xs_periph->periph_lun; + int disconnect; + int i; + +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC && + osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { + printf("ACK! osiop was busy: script %p dsa %p active %d\n", + sc->sc_script, acb->ds, sc->sc_active); + printf("istat %02x sfbr %02x lcrc %02x sien %02x dien %02x\n", + osiop_read_1(sc, OSIOP_ISTAT), + osiop_read_1(sc, OSIOP_SFBR), + osiop_read_1(sc, OSIOP_LCRC), + osiop_read_1(sc, OSIOP_SIEN), + osiop_read_1(sc, OSIOP_DIEN)); +#ifdef DDB +#if 0 + Debugger(); +#endif +#endif + } +#endif + +#ifdef OSIOP_DEBUG + if (acb->status != ACB_S_READY) + panic("osiop_start: non-ready cmd in acb"); +#endif + + acb->intstat = 0; + + ds->cmd.count = acb->cmdlen; + ds->cmd.addr = acb->cmddma->dm_segs[0].ds_addr; + + ds->scsi_addr = (1 << (16 + target)) | (sc->sc_sync[target].sxfer << 8); + + disconnect = (xs->xs_control & XS_CTL_REQSENSE) == 0 && + (sc->sc_tinfo[target].flags & TI_NODISC) == 0; + + ds->msgout[0] = MSG_IDENTIFY(lun, disconnect); + ds->id.count = 1; + ds->stat[0] = SCSI_OSIOP_NOSTATUS; /* set invalid status */ + ds->msgbuf[0] = ds->msgbuf[1] = MSG_INVALID; + memset(&ds->data, 0, sizeof(ds->data)); + + /* + * Negotiate wide is the initial negotiation state; since the 53c710 + * doesn't do wide transfers, just begin the synchronous transfer + * negotation here. + */ + if (sc->sc_sync[target].state == NEG_INIT) { + if ((sc->sc_tinfo[target].flags & TI_NOSYNC) != 0) { + sc->sc_sync[target].state = NEG_DONE; + sc->sc_sync[target].sbcl = 0; + sc->sc_sync[target].sxfer = 0; +#ifdef OSIOP_DEBUG + if (osiopsync_debug) + printf("Forcing target %d asynchronous\n", + target); +#endif + } else { + ds->msgbuf[2] = MSG_INVALID; + ds->msgout[1] = MSG_EXTENDED; + ds->msgout[2] = MSG_EXT_SDTR_LEN; + ds->msgout[3] = MSG_EXT_SDTR; + ds->msgout[4] = sc->sc_minsync; + ds->msgout[5] = OSIOP_MAX_OFFSET; + ds->id.count = 6; + sc->sc_sync[target].state = NEG_WAITS; +#ifdef OSIOP_DEBUG + if (osiopsync_debug) + printf("Sending sync request to target %d\n", + target); +#endif + } + } + + acb->curaddr = 0; + acb->curlen = 0; + + /* + * Build physical DMA addresses for scatter/gather I/O + */ + if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) { + for (i = 0; i < datadma->dm_nsegs; i++) { + ds->data[i].count = datadma->dm_segs[i].ds_len; + ds->data[i].addr = datadma->dm_segs[i].ds_addr; + } + } + + /* sync script data structure */ + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + acb->status = ACB_S_ACTIVE; + + /* handle timeout */ + if ((xs->xs_control & XS_CTL_POLL) == 0) { + int timeout = acb->xs->timeout; + /* start expire timer */ + timeout = (timeout > 100000) ? + timeout / 1000 * hz : timeout * hz / 1000; + if (timeout == 0) + timeout = 1; + callout_reset(&xs->xs_callout, timeout, + osiop_timeout, acb); + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC && + osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { + printf("ACK! osiop was busy at start: " + "script %p dsa %p active %d\n", + sc->sc_script, acb->ds, sc->sc_active); +#ifdef DDB +#if 0 + Debugger(); +#endif +#endif + } +#endif + if (TAILQ_EMPTY(&sc->nexus_list)) { + if (osiop_read_1(sc, OSIOP_ISTAT) & OSIOP_ISTAT_CON) + printf("%s: osiop_select while connected?\n", + sc->sc_dev.dv_xname); + osiop_write_4(sc, OSIOP_TEMP, 0); + osiop_write_1(sc, OSIOP_SBCL, sc->sc_sync[target].sbcl); + osiop_write_4(sc, OSIOP_DSA, + dsdma->dm_segs[0].ds_addr + acb->dsoffset); + osiop_write_4(sc, OSIOP_DSP, + sc->sc_scrdma->dm_segs[0].ds_addr + Ent_scripts); + OSIOP_TRACE('s', 1, 0, 0); + } else { + if ((osiop_read_1(sc, OSIOP_ISTAT) & OSIOP_ISTAT_CON) == 0) { + osiop_write_1(sc, OSIOP_ISTAT, OSIOP_ISTAT_SIGP); + OSIOP_TRACE('s', 2, 0, 0); + } else { + OSIOP_TRACE('s', 3, + osiop_read_1(sc, OSIOP_ISTAT), 0); + } + } +#ifdef OSIOP_DEBUG + osiopstarts++; +#endif +} + +/* + * Process a DMA or SCSI interrupt from the 53C710 SIOP + */ + +int +osiop_checkintr(sc, istat, dstat, sstat0, status) + struct osiop_softc *sc; + u_int8_t istat; + u_int8_t dstat; + u_int8_t sstat0; + int *status; +{ + struct osiop_acb *acb = sc->sc_nexus; + struct osiop_ds *ds; + bus_dmamap_t dsdma = sc->sc_dsdma; + bus_addr_t scraddr = sc->sc_scrdma->dm_segs[0].ds_addr; + int target = 0; + int dfifo, dbc, intcode, sstat1; + + dfifo = osiop_read_1(sc, OSIOP_DFIFO); + dbc = osiop_read_4(sc, OSIOP_DBC) & 0x00ffffff; + sstat1 = osiop_read_1(sc, OSIOP_SSTAT1); + osiop_write_1(sc, OSIOP_CTEST8, + osiop_read_1(sc, OSIOP_CTEST8) | OSIOP_CTEST8_CLF); + while ((osiop_read_1(sc, OSIOP_CTEST1) & OSIOP_CTEST1_FMT) != + OSIOP_CTEST1_FMT) + ; + osiop_write_1(sc, OSIOP_CTEST8, + osiop_read_1(sc, OSIOP_CTEST8) & ~OSIOP_CTEST8_CLF); + intcode = osiop_read_4(sc, OSIOP_DSPS); +#ifdef OSIOP_DEBUG + osiopints++; + if (osiop_read_4(sc, OSIOP_DSP) != 0 && + (osiop_read_4(sc, OSIOP_DSP) < scraddr || + osiop_read_4(sc, OSIOP_DSP) >= scraddr + sizeof(osiop_script))) { + printf("%s: dsp not within script dsp %x scripts %lx:%lx", + sc->sc_dev.dv_xname, + osiop_read_4(sc, OSIOP_DSP), + scraddr, scraddr + sizeof(osiop_script)); + printf(" istat %x dstat %x sstat0 %x\n", istat, dstat, sstat0); +#ifdef DDB + Debugger(); +#endif + } +#endif + OSIOP_TRACE('i', dstat, istat, (istat & OSIOP_ISTAT_DIP) ? + intcode & 0xff : sstat0); + + if (acb != NULL) { /* XXX */ + ds = acb->ds; + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); +#ifdef OSIOP_DEBUG + if (acb->status != ACB_S_ACTIVE) + printf("osiop_checkintr: acb not active (status %d)\n", + acb->status); +#endif + } + + + if (dstat & OSIOP_DSTAT_SIR && intcode == A_ok) { + /* Normal completion status, or check condition */ +#ifdef OSIOP_DEBUG + if (osiop_read_4(sc, OSIOP_DSA) != + dsdma->dm_segs[0].ds_addr + acb->dsoffset) { + printf("osiop: invalid dsa: %x %lx\n", + osiop_read_4(sc, OSIOP_DSA), + dsdma->dm_segs[0].ds_addr + acb->dsoffset); + panic("*** osiop DSA invalid ***"); + } +#endif + target = acb->xs->xs_periph->periph_target; + if (sc->sc_sync[target].state == NEG_WAITS) { + if (ds->msgbuf[1] == MSG_INVALID) + printf("%s: target %d ignored sync request\n", + sc->sc_dev.dv_xname, target); + else if (ds->msgbuf[1] == MSG_MESSAGE_REJECT) + printf("%s: target %d rejected sync request\n", + sc->sc_dev.dv_xname, target); + else +/* XXX - need to set sync transfer parameters */ + printf("%s: target %d (sync) %02x %02x %02x\n", + sc->sc_dev.dv_xname, target, ds->msgbuf[1], + ds->msgbuf[2], ds->msgbuf[3]); + sc->sc_sync[target].state = NEG_DONE; + } +#ifdef OSIOP_DEBUG + if (osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { +#if 0 + printf("ACK! osiop was busy at end: " + "script %p dsa %p\n", &osiop_script, ds); +#ifdef DDB + Debugger(); +#endif +#endif + } + if (ds->msgbuf[0] != MSG_CMDCOMPLETE) + printf("%s: message was not COMMAND COMPLETE: %02x\n", + sc->sc_dev.dv_xname, ds->msgbuf[0]); +#endif + if (!TAILQ_EMPTY(&sc->nexus_list)) + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + *status = ds->stat[0]; + acb->status = ACB_S_DONE; + return (1); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_syncmsg) { + target = acb->xs->xs_periph->periph_target; + if (ds->msgbuf[1] == MSG_EXTENDED && + ds->msgbuf[2] == MSG_EXT_SDTR_LEN && + ds->msgbuf[3] == MSG_EXT_SDTR) { +#ifdef OSIOP_DEBUG + if (osiopsync_debug) + printf("sync msg in: " + "%02x %02x %02x %02x %02x %02x\n", + ds->msgbuf[0], ds->msgbuf[1], + ds->msgbuf[2], ds->msgbuf[3], + ds->msgbuf[4], ds->msgbuf[5]); +#endif + sc->sc_sync[target].sxfer = 0; + sc->sc_sync[target].sbcl = 0; + if (ds->msgbuf[2] == MSG_EXT_SDTR_LEN && + ds->msgbuf[3] == MSG_EXT_SDTR && + ds->msgbuf[5] != 0) { + printf("%s: target %d now synchronous, " + "period=%d ns, offset=%d\n", + sc->sc_dev.dv_xname, target, + ds->msgbuf[4] * 4, ds->msgbuf[5]); + scsi_period_to_osiop(sc, target); + } + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + osiop_write_1(sc, OSIOP_SXFER, + sc->sc_sync[target].sxfer); + osiop_write_1(sc, OSIOP_SBCL, + sc->sc_sync[target].sbcl); + if (sc->sc_sync[target].state == NEG_WAITS) { + sc->sc_sync[target].state = NEG_DONE; + osiop_write_4(sc, OSIOP_DSP, + scraddr + Ent_clear_ack); + return (0); + } + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + sc->sc_sync[target].state = NEG_DONE; + return (0); + } + /* XXX - not SDTR message */ + } + if (sstat0 & OSIOP_SSTAT0_M_A) { + /* Phase mismatch */ +#ifdef OSIOP_DEBUG + osiopphmm++; + if (acb == NULL) + printf("%s: Phase mismatch with no active command?\n", + sc->sc_dev.dv_xname); +#endif + if (acb->datalen > 0) { + int adjust = (dfifo - (dbc & 0x7f)) & 0x7f; + if (sstat1 & OSIOP_SSTAT1_ORF) + adjust++; + if (sstat1 & OSIOP_SSTAT1_OLF) + adjust++; + acb->curaddr = osiop_read_4(sc, OSIOP_DNAD) - adjust; + acb->curlen = dbc + adjust; +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) { + printf("Phase mismatch: curaddr %lx " + "curlen %lx dfifo %x dbc %x sstat1 %x " + "adjust %x sbcl %x starts %d acb %p\n", + acb->curaddr, acb->curlen, dfifo, + dbc, sstat1, adjust, + osiop_read_1(sc, OSIOP_SBCL), + osiopstarts, acb); + if (ds->data[1].count != 0) { + int i; + for (i = 0; ds->data[i].count != 0; i++) + printf("chain[%d] " + "addr %x len %x\n", i, + ds->data[i].addr, + ds->data[i].count); + } + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | + BUS_DMASYNC_PREWRITE); + } +#endif + } +#ifdef OSIOP_DEBUG + OSIOP_TRACE('m', osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP) >> 8, + osiop_read_4(sc, OSIOP_DSP)); + if (osiop_debug & DEBUG_PHASE) + printf("Phase mismatch: %x dsp +%lx dcmd %x\n", + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP) - scraddr, + osiop_read_4(sc, OSIOP_DBC)); +#endif + if ((osiop_read_1(sc, OSIOP_SBCL) & OSIOP_REQ) == 0) { + printf("Phase mismatch: " + "REQ not asserted! %02x dsp %x\n", + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP)); +#if defined(OSIOP_DEBUG) && defined(DDB) + /*Debugger(); XXX is*/ +#endif + } + switch (OSIOP_PHASE(osiop_read_1(sc, OSIOP_SBCL))) { + case DATA_OUT_PHASE: + case DATA_IN_PHASE: + case STATUS_PHASE: + case COMMAND_PHASE: + case MSG_IN_PHASE: + case MSG_OUT_PHASE: + osiop_write_4(sc, OSIOP_DSP, scraddr + Ent_switch); + break; + default: + printf("%s: invalid phase\n", sc->sc_dev.dv_xname); + goto bad_phase; + } + return (0); + } + if (sstat0 & OSIOP_SSTAT0_STO) { + /* Select timed out */ +#ifdef OSIOP_DEBUG + if (acb == NULL) + printf("%s: Select timeout with no active command?\n", + sc->sc_dev.dv_xname); + if (osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { + printf("ACK! osiop was busy at timeout: " + "script %p dsa %lx\n", sc->sc_script, + dsdma->dm_segs[0].ds_addr + acb->dsoffset); + printf(" sbcl %x sdid %x " + "istat %x dstat %x sstat0 %x\n", + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_1(sc, OSIOP_SDID), + istat, dstat, sstat0); + if ((osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) == 0) { + printf("Yikes, it's not busy now!\n"); +#if 0 + *status = SCSI_OSIOP_NOSTATUS; + if (!TAILQ_EMPTY(&sc->nexus_list)) + osiop_write_4(sc, OSIOP_DSP, + scraddr + Ent_wait_reselect); + return (1); +#endif + } +#if 0 + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); +#endif +#ifdef DDB + Debugger(); +#endif + return (0); + } +#endif + acb->status = ACB_S_DONE; + *status = SCSI_OSIOP_NOSTATUS; + acb->xs->error = XS_SELTIMEOUT; + if (!TAILQ_EMPTY(&sc->nexus_list)) + osiop_write_4(sc, OSIOP_DSP, + scraddr + Ent_wait_reselect); + return (1); + } + if (acb != NULL) + target = acb->xs->xs_periph->periph_target; + else + target = sc->sc_id; + if (sstat0 & OSIOP_SSTAT0_UDC) { +#ifdef OSIOP_DEBUG + if (acb == NULL) + printf("%s: Unexpected disconnect " + "with no active command?\n", sc->sc_dev.dv_xname); + printf("%s: target %d disconnected unexpectedly\n", + sc->sc_dev.dv_xname, target); +#endif +#if 0 + osiop_abort(sc, "osiop_chkintr"); +#endif + *status = SCSI_CHECK; + if (!TAILQ_EMPTY(&sc->nexus_list)) + osiop_write_4(sc, OSIOP_DSP, + scraddr + Ent_wait_reselect); + return (acb != NULL); + } + if (dstat & OSIOP_DSTAT_SIR && + (intcode == A_int_disc || intcode == A_int_disc_wodp)) { + /* Disconnect */ + if (acb == NULL) { + printf("%s: Disconnect with no active command?\n", + sc->sc_dev.dv_xname); + return (0); + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) { + printf("%s: ID %02x disconnected TEMP %x (+%lx) " + "curaddr %lx curlen %lx buf %x len %x dfifo %x " + "dbc %x sstat1 %x starts %d acb %p\n", + sc->sc_dev.dv_xname, 1 << target, + osiop_read_4(sc, OSIOP_TEMP), + (osiop_read_4(sc, OSIOP_TEMP) != 0) ? + osiop_read_4(sc, OSIOP_TEMP) - scraddr : 0, + acb->curaddr, acb->curlen, + ds->data[0].addr, ds->data[0].count, + dfifo, dbc, sstat1, osiopstarts, acb); + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } +#endif + /* + * XXXX need to update curaddr/curlen to reflect + * current data transferred. If device disconnected in + * the middle of a DMA block, they should already be set + * by the phase change interrupt. If the disconnect + * occurs on a DMA block boundary, we have to figure out + * which DMA block it was. + */ + if (acb->datalen > 0 && + osiop_read_4(sc, OSIOP_TEMP) != 0) { + long n = osiop_read_4(sc, OSIOP_TEMP) - scraddr; + + if (acb->curlen != 0 && + acb->curlen != ds->data[0].count) + printf("%s: curaddr/curlen already set? " + "n %lx iob %lx/%lx chain[0] %x/%x\n", + sc->sc_dev.dv_xname, n, + acb->curaddr, acb->curlen, + ds->data[0].addr, ds->data[0].count); + if (n < Ent_datain) + n = (n - Ent_dataout) / 16; + else + n = (n - Ent_datain) / 16; + if (n <= 0 && n > OSIOP_NSG) + printf("TEMP invalid %ld\n", n); + else { + acb->curaddr = ds->data[n].addr; + acb->curlen = ds->data[n].count; + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) { + printf("%s: TEMP offset %ld", + sc->sc_dev.dv_xname, n); + printf(" curaddr %lx curlen %lx\n", + acb->curaddr, acb->curlen); + } +#endif + } + /* + * If data transfer was interrupted by disconnect, curaddr + * and curlen should reflect the point of interruption. + * Adjust the DMA chain so that the data transfer begins + * at the appropriate place upon reselection. + * XXX This should only be done on save data pointer message? + */ + if (acb->curlen > 0) { + int i, j; + +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf("%s: adjusting DMA chain\n", + sc->sc_dev.dv_xname); + if (intcode == A_int_disc_wodp) + printf("%s: ID %02x disconnected " + "without Save Data Pointers\n", + sc->sc_dev.dv_xname, 1 << target); +#endif + for (i = 0; i < OSIOP_NSG; i++) { + if (ds->data[i].count == 0) + break; + if (acb->curaddr >= ds->data[i].addr && + acb->curaddr < + (ds->data[i].addr + ds->data[i].count)) + break; + } + if (i >= OSIOP_NSG || ds->data[i].count == 0) { + printf("couldn't find saved data pointer: " + "curaddr %lx curlen %lx i %d\n", + acb->curaddr, acb->curlen, i); +#ifdef DDB + Debugger(); +#endif + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf(" chain[0]: %x/%x -> %lx/%lx\n", + ds->data[0].addr, ds->data[0].count, + acb->curaddr, acb->curlen); +#endif + ds->data[0].addr = acb->curaddr; + ds->data[0].count = acb->curlen; + for (j = 1, i = i + 1; + i < OSIOP_NSG && ds->data[i].count > 0; + i++, j++) { +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf(" chain[%d]: %x/%x -> %x/%x\n", j, + ds->data[j].addr, ds->data[j].count, + ds->data[i].addr, ds->data[i].count); +#endif + ds->data[j].addr = ds->data[i].addr; + ds->data[j].count = ds->data[i].count; + } + if (j < OSIOP_NSG) { + ds->data[j].addr = 0; + ds->data[j].count = 0; + } + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } + sc->sc_tinfo[target].dconns++; + /* + * add nexus to waiting list + * clear nexus + * try to start another command for another target/lun + */ + acb->intstat = sc->sc_flags & OSIOP_INTSOFF; + TAILQ_INSERT_TAIL(&sc->nexus_list, acb, chain); + sc->sc_nexus = NULL; /* no current device */ + osiop_write_4(sc, OSIOP_DSP, scraddr + Ent_wait_reselect); + /* XXXX start another command ? */ + if (!TAILQ_EMPTY(&sc->ready_list)) + osiop_sched(sc); + return (0); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_reconnect) { + int reselid = ffs(osiop_read_4(sc, OSIOP_SCRATCH) & 0xff) - 1; + int reselun = osiop_read_1(sc, OSIOP_SFBR) & 0x07; +#ifdef OSIOP_DEBUG + u_int8_t resmsg; +#endif + + /* Reconnect */ + /* XXXX save current SBCL */ + sc->sc_sstat1 = osiop_read_1(sc, OSIOP_SBCL); +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf("%s: target ID %02x reselected dsps %x\n", + sc->sc_dev.dv_xname, reselid, intcode); + resmsg = osiop_read_1(sc, OSIOP_SFBR); + if (!MSG_ISIDENTIFY(resmsg)) + printf("%s: Reselect message in was not identify: " + "%02x\n", sc->sc_dev.dv_xname, resmsg); +#endif + if (sc->sc_nexus != NULL) { + struct scsipi_periph *periph = + sc->sc_nexus->xs->xs_periph; +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DISC) + printf("%s: reselect ID %02x w/active\n", + sc->sc_dev.dv_xname, reselid); +#endif + TAILQ_INSERT_HEAD(&sc->ready_list, + sc->sc_nexus, chain); + sc->sc_tinfo[periph->periph_target].lubusy + &= ~(1 << periph->periph_lun); + sc->sc_active--; + } + /* + * locate acb of reselecting device + * set sc->sc_nexus to acb + */ + TAILQ_FOREACH(acb, &sc->nexus_list, chain) { + struct scsipi_periph *periph = acb->xs->xs_periph; + if (reselid != periph->periph_target || + reselun != periph->periph_lun) { + continue; + } + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + sc->sc_nexus = acb; + sc->sc_flags |= acb->intstat; + acb->intstat = 0; + osiop_write_4(sc, OSIOP_DSA, + dsdma->dm_segs[0].ds_addr + acb->dsoffset); + osiop_write_1(sc, OSIOP_SXFER, + sc->sc_sync[reselid].sxfer); + osiop_write_1(sc, OSIOP_SBCL, + sc->sc_sync[reselid].sbcl); + break; + } + if (acb == NULL) { + printf("%s: target ID %02x reselect nexus_list %p\n", + sc->sc_dev.dv_xname, reselid, + TAILQ_FIRST(&sc->nexus_list)); + panic("unable to find reselecting device"); + } + + osiop_write_4(sc, OSIOP_TEMP, 0); + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + return (0); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_connect) { +#ifdef OSIOP_DEBUG + u_int8_t ctest2 = osiop_read_1(sc, OSIOP_CTEST2); + + /* reselect was interrupted (by Sig_P or select) */ + if (osiop_debug & DEBUG_DISC || + (ctest2 & OSIOP_CTEST2_SIGP) == 0) + printf("%s: reselect interrupted (Sig_P?) " + "scntl1 %x ctest2 %x sfbr %x istat %x/%x\n", + sc->sc_dev.dv_xname, + osiop_read_1(sc, OSIOP_SCNTL1), ctest2, + osiop_read_1(sc, OSIOP_SFBR), istat, + osiop_read_1(sc, OSIOP_ISTAT)); +#endif + /* XXX assumes it was not select */ + if (sc->sc_nexus == NULL) { +#ifdef OSIOP_DEBUG + printf("%s: reselect interrupted, sc_nexus == NULL\n", + sc->sc_dev.dv_xname); +#if 0 + osiop_dump(sc); +#ifdef DDB + Debugger(); +#endif +#endif +#endif + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + return (0); + } + target = sc->sc_nexus->xs->xs_periph->periph_target; + osiop_write_4(sc, OSIOP_TEMP, 0); + osiop_write_4(sc, OSIOP_DSA, + dsdma->dm_segs[0].ds_addr + sc->sc_nexus->dsoffset); + osiop_write_1(sc, OSIOP_SXFER, sc->sc_sync[target].sxfer); + osiop_write_1(sc, OSIOP_SBCL, sc->sc_sync[target].sbcl); + osiop_write_4(sc, OSIOP_DSP, scraddr + Ent_scripts); + return (0); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_msgin) { + /* Unrecognized message in byte */ + if (acb == NULL) + printf("%s: Bad message-in with no active command?\n", + sc->sc_dev.dv_xname); + printf("%s: Unrecognized message in data " + "sfbr %x msg %x sbcl %x\n", sc->sc_dev.dv_xname, + osiop_read_1(sc, OSIOP_SFBR), ds->msgbuf[1], + osiop_read_1(sc, OSIOP_SBCL)); + /* what should be done here? */ + osiop_write_4(sc, OSIOP_DSP, scraddr + Ent_switch); + bus_dmamap_sync(sc->sc_dmat, dsdma, + acb->dsoffset, sizeof(struct osiop_ds), + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + return (0); + } + if (dstat & OSIOP_DSTAT_SIR && intcode == A_int_status) { + /* Status phase wasn't followed by message in phase? */ + printf("%s: Status phase not followed by message in phase? " + "sbcl %x sbdl %x\n", sc->sc_dev.dv_xname, + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_1(sc, OSIOP_SBDL)); + if (osiop_read_1(sc, OSIOP_SBCL) == 0xa7) { + /* It is now, just continue the script? */ + osiop_write_1(sc, OSIOP_DCNTL, + osiop_read_1(sc, OSIOP_DCNTL) | OSIOP_DCNTL_STD); + return (0); + } + } + if (dstat & OSIOP_DSTAT_SIR && sstat0 == 0) { + printf("OSIOP interrupt: %x sts %x msg %x %x sbcl %x\n", + intcode, ds->stat[0], ds->msgbuf[0], ds->msgbuf[1], + osiop_read_1(sc, OSIOP_SBCL)); + osiop_reset(sc); + *status = SCSI_OSIOP_NOSTATUS; + return (0); /* osiop_reset has cleaned up */ + } + if (sstat0 & OSIOP_SSTAT0_SGE) + printf("%s: SCSI Gross Error\n", sc->sc_dev.dv_xname); + if (sstat0 & OSIOP_SSTAT0_PAR) + printf("%s: Parity Error\n", sc->sc_dev.dv_xname); + if (dstat & OSIOP_DSTAT_IID) + printf("%s: Invalid instruction detected\n", + sc->sc_dev.dv_xname); + bad_phase: + /* + * temporary panic for unhandled conditions + * displays various things about the 53C710 status and registers + * then panics. + * XXXX need to clean this up to print out the info, reset, and continue + */ + printf("osiop_chkintr: target %x ds %p\n", target, ds); + printf("scripts %lx ds %lx dsp %x dcmd %x\n", scraddr, + sc->sc_dsdma->dm_segs[0].ds_addr + acb->dsoffset, + osiop_read_4(sc, OSIOP_DSP), + osiop_read_4(sc, OSIOP_DBC)); + printf("osiop_chkintr: istat %x dstat %x sstat0 %x " + "dsps %x dsa %x sbcl %x sts %x msg %x %x sfbr %x\n", + istat, dstat, sstat0, intcode, + osiop_read_4(sc, OSIOP_DSA), + osiop_read_1(sc, OSIOP_SBCL), + ds->stat[0], ds->msgbuf[0], ds->msgbuf[1], + osiop_read_1(sc, OSIOP_SFBR)); +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_DMA) + panic("osiop_chkintr: **** temp ****"); +#endif +#ifdef DDB + Debugger(); +#endif + osiop_reset(sc); /* hard reset */ + *status = SCSI_OSIOP_NOSTATUS; + acb->status = ACB_S_DONE; + return (0); /* osiop_reset cleaned up */ +} + +void +osiop_select(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb = sc->sc_nexus; + +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_CMD) + printf("%s: select ", sc->sc_dev.dv_xname); +#endif + + if (acb->xs->xs_control & XS_CTL_POLL || sc->sc_flags & OSIOP_NODMA) { + sc->sc_flags |= OSIOP_INTSOFF; + sc->sc_flags &= ~OSIOP_INTDEFER; + if ((osiop_read_1(sc, OSIOP_ISTAT) & OSIOP_ISTAT_CON) == 0) { + osiop_write_1(sc, OSIOP_SIEN, 0); + osiop_write_1(sc, OSIOP_DIEN, 0); + } +#if 0 + } else if ((sc->sc_flags & OSIOP_INTDEFER) == 0) { + sc->sc_flags &= ~OSIOP_INTSOFF; + if ((osiop_read_1(sc, OSIOP_ISTAT) & OSIOP_ISTAT_CON) == 0) { + osiop_write_1(sc, OSIOP_SIEN, sc->sc_sien); + osiop_write_1(sc, OSIOP_DIEN, sc->sc_dien); + } +#endif + } +#ifdef OSIOP_DEBUG + if (osiop_debug & DEBUG_CMD) + printf("osiop_select: target %x cmd %02x ds %p\n", + acb->xs->xs_periph->periph_target, + acb->xs->cmd->opcode, sc->sc_nexus->ds); +#endif + + osiop_start(sc); + + return; +} + +/* + * 53C710 interrupt handler + */ + +void +osiop_intr(sc) + struct osiop_softc *sc; +{ + int status, s; + u_int8_t istat, dstat, sstat0; + + s = splbio(); + + istat = sc->sc_istat; + if ((istat & (OSIOP_ISTAT_SIP | OSIOP_ISTAT_DIP)) == 0) { + splx(s); + return; + } + + /* Got a valid interrupt on this device; set by MD handler */ + dstat = sc->sc_dstat; + sstat0 = sc->sc_sstat0; + sc->sc_istat = 0; +#ifdef OSIOP_DEBUG + if (!sc->sc_active) { + /* XXX needs sync */ + printf("%s: spurious interrupt? " + "istat %x dstat %x sstat0 %x nexus %p status %x\n", + sc->sc_dev.dv_xname, istat, dstat, sstat0, sc->sc_nexus, + (sc->sc_nexus != NULL) ? sc->sc_nexus->ds->stat[0] : 0); + } +#endif + +#ifdef OSIOP_DEBUG + if (osiop_debug & (DEBUG_INT|DEBUG_CMD)) { + /* XXX needs sync */ + printf("%s: intr istat %x dstat %x sstat0 %x dsps %x " + "sbcl %x dsp %x dcmd %x sts %x msg %x\n", + sc->sc_dev.dv_xname, + istat, dstat, sstat0, + osiop_read_4(sc, OSIOP_DSPS), + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_4(sc, OSIOP_DSP), + osiop_read_4(sc, OSIOP_DBC), + (sc->sc_nexus != NULL) ? sc->sc_nexus->ds->stat[0] : 0, + (sc->sc_nexus != NULL) ? sc->sc_nexus->ds->msgbuf[0] : 0); + } +#endif + if (sc->sc_flags & OSIOP_INTDEFER) { + sc->sc_flags &= ~(OSIOP_INTDEFER | OSIOP_INTSOFF); + osiop_write_1(sc, OSIOP_SIEN, sc->sc_sien); + osiop_write_1(sc, OSIOP_DIEN, sc->sc_dien); + } + if (osiop_checkintr(sc, istat, dstat, sstat0, &status)) { +#if 0 + if (status == SCSI_OSIOP_NOSTATUS) + printf("osiop_intr: no valid status \n"); +#endif + if ((sc->sc_flags & (OSIOP_INTSOFF | OSIOP_INTDEFER)) != + OSIOP_INTSOFF) { +#if 0 + if (osiop_read_1(sc, OSIOP_SBCL) & OSIOP_BSY) { + struct scsipi_periph *periph; + + periph = sc->sc_nexus->xs->xs_periph; + printf("%s: SCSI bus busy at completion" + " targ %d sbcl %02x sfbr %x lcrc " + "%02x dsp +%x\n", sc->sc_dev.dv_xname, + periph->periphtarget, + osiop_read_1(sc, OSIOP_SBCL), + osiop_read_1(sc, OSIOP_SFBR), + osiop_read_1(sc, OSIOP_LCRC), + osiop_read_4(sc, OSIOP_DSP) - + sc->sc_scrdma->dm_segs[0].ds_addr); + } +#endif + osiop_scsidone(sc->sc_nexus, status); + } + } + splx(s); +} + +/* + * This is based on the Progressive Peripherals 33Mhz Zeus driver and will + * not be correct for other 53c710 boards. + * + */ +void +scsi_period_to_osiop(sc, target) + struct osiop_softc *sc; + int target; +{ + int period, offset, sxfer, sbcl = 0; +#ifdef DEBUG_SYNC + int i; +#endif + + period = sc->sc_nexus->ds->msgbuf[4]; + offset = sc->sc_nexus->ds->msgbuf[5]; +#ifdef DEBUG_SYNC + sxfer = 0; + if (offset <= OSIOP_MAX_OFFSET) + sxfer = offset; + for (i = 0; i < sizeof(sync_tab) / sizeof(sync_tab[0]); i++) { + if (period <= sync_tab[i].p) { + sxfer |= sync_tab[i].r & 0x70; + sbcl = sync_tab[i].r & 0x03; + break; + } + } + printf("osiop sync old: osiop_sxfr %02x, osiop_sbcl %02x\n", + sxfer, sbcl); +#endif + for (sbcl = 1; sbcl < 4; sbcl++) { + sxfer = (period * 4 - 1) / sc->sc_tcp[sbcl] - 3; + if (sxfer >= 0 && sxfer <= 7) + break; + } + if (sbcl > 3) { + printf("osiop sync: unable to compute sync params " + "for period %d ns\n", period * 4); + /* + * XXX need to pick a value we can do and renegotiate + */ + sxfer = sbcl = 0; + } else { + sxfer = (sxfer << 4) | ((offset <= OSIOP_MAX_OFFSET) ? + offset : OSIOP_MAX_OFFSET); +#ifdef DEBUG_SYNC + printf("osiop sync: params for period %dns: sxfer %x sbcl %x", + period * 4, sxfer, sbcl); + printf(" actual period %dns\n", + sc->sc_tcp[sbcl] * ((sxfer >> 4) + 4)); +#endif + } + sc->sc_sync[target].sxfer = sxfer; + sc->sc_sync[target].sbcl = sbcl; +#ifdef DEBUG_SYNC + printf("osiop sync: osiop_sxfr %02x, osiop_sbcl %02x\n", sxfer, sbcl); +#endif +} + +void +osiop_timeout(arg) + void *arg; +{ + struct osiop_acb *acb = arg; + struct scsipi_xfer *xs = acb->xs; + struct osiop_softc *sc = acb->sc; + int s; + + scsipi_printaddr(xs->xs_periph); + printf("command timeout\n"); + + s = splbio(); + /* reset the scsi bus */ + osiop_resetbus(sc); + + /* deactivate callout */ + callout_stop(&xs->xs_callout); + acb->flags |= ACB_F_TIMEOUT; + osiop_reset(sc); + splx(s); + return; +} + +#ifdef OSIOP_DEBUG + +#if OSIOP_TRACE_SIZE +void +osiop_dump_trace() +{ + int i; + + printf("osiop trace: next index %d\n", osiop_trix); + i = osiop_trix; + do { + printf("%3d: '%c' %02x %02x %02x\n", i, + osiop_trbuf[i], osiop_trbuf[i + 1], + osiop_trbuf[i + 2], osiop_trbuf[i + 3]); + i = (i + 4) & (OSIOP_TRACE_SIZE - 1); + } while (i != osiop_trix); +} +#endif + +void +osiop_dump_acb(acb) + struct osiop_acb *acb; +{ + u_int8_t *b; + int i; + + printf("acb@%p ", acb); + if (acb->xs == NULL) { + printf("<unused>\n"); + return; + } + + b = (u_int8_t *)&acb->xs->cmd; + printf("(%d:%d) status %2x cmdlen %2ld cmd ", + acb->xs->xs_periph->periph_target, + acb->xs->xs_periph->periph_lun, acb->status, acb->cmdlen); + for (i = acb->cmdlen; i > 0; i--) + printf(" %02x", *b++); + printf("\n"); + printf(" xs: %p data %p:%04x ", acb->xs, acb->xs->data, + acb->xs->datalen); + printf("va %p:%lx ", acb->data, acb->datalen); + printf("cur %lx:%lx\n", acb->curaddr, acb->curlen); +} + +void +osiop_dump(sc) + struct osiop_softc *sc; +{ + struct osiop_acb *acb; + int i, s; + + s = splbio(); +#if OSIOP_TRACE_SIZE + osiop_dump_trace(); +#endif + printf("%s@%p istat %02x\n", + sc->sc_dev.dv_xname, sc, osiop_read_1(sc, OSIOP_ISTAT)); + if ((acb = TAILQ_FIRST(&sc->free_list)) != NULL) { + printf("Free list:\n"); + while (acb) { + osiop_dump_acb(acb); + acb = TAILQ_NEXT(acb, chain); + } + } + if ((acb = TAILQ_FIRST(&sc->ready_list)) != NULL) { + printf("Ready list:\n"); + while (acb) { + osiop_dump_acb(acb); + acb = TAILQ_NEXT(acb, chain); + } + } + if ((acb = TAILQ_FIRST(&sc->nexus_list)) != NULL) { + printf("Nexus list:\n"); + while (acb) { + osiop_dump_acb(acb); + acb = TAILQ_NEXT(acb, chain); + } + } + if (sc->sc_nexus) { + printf("Nexus:\n"); + osiop_dump_acb(sc->sc_nexus); + } + for (i = 0; i < OSIOP_NTGT; i++) { + if (sc->sc_tinfo[i].cmds > 2) { + printf("tgt %d: cmds %d disc %d lubusy %x\n", + i, sc->sc_tinfo[i].cmds, + sc->sc_tinfo[i].dconns, + sc->sc_tinfo[i].lubusy); + } + } + splx(s); +} +#endif diff --git a/sys/dev/ic/osiopreg.h b/sys/dev/ic/osiopreg.h new file mode 100644 index 00000000000..3dd078d1f0b --- /dev/null +++ b/sys/dev/ic/osiopreg.h @@ -0,0 +1,378 @@ +/* $NetBSD: osiopreg.h,v 1.1 2001/04/30 04:47:51 tsutsui Exp $ */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)siopreg.h 7.3 (Berkeley) 2/5/91 + */ + +/* + * NCR 53C710 SCSI interface hardware description. + * + * From the Mach scsi driver for the 53C710 and amiga siop driver + */ + +/* byte lane definitions */ +#if BYTE_ORDER == LITTLE_ENDIAN +#define BL0 0 +#define BL1 1 +#define BL2 2 +#define BL3 3 +#else +#define BL0 3 +#define BL1 2 +#define BL2 1 +#define BL3 0 +#endif + +#define OSIOP_SCNTL0 (0x00+BL0) /* rw: SCSI control reg 0 */ +#define OSIOP_SCNTL1 (0x00+BL1) /* rw: SCSI control reg 1 */ +#define OSIOP_SDID (0x00+BL2) /* rw: SCSI destination ID */ +#define OSIOP_SIEN (0x00+BL3) /* rw: SCSI interrupt enable */ + +#define OSIOP_SCID (0x04+BL0) /* rw: SCSI Chip ID reg */ +#define OSIOP_SXFER (0x04+BL1) /* rw: SCSI Transfer reg */ +#define OSIOP_SODL (0x04+BL2) /* rw: SCSI Output Data Latch */ +#define OSIOP_SOCL (0x04+BL3) /* rw: SCSI Output Control Latch */ + +#define OSIOP_SFBR (0x08+BL0) /* ro: SCSI First Byte Received */ +#define OSIOP_SIDL (0x08+BL1) /* ro: SCSI Input Data Latch */ +#define OSIOP_SBDL (0x08+BL2) /* ro: SCSI Bus Data Lines */ +#define OSIOP_SBCL (0x08+BL3) /* rw: SCSI Bus Control Lines */ + +#define OSIOP_DSTAT (0x0c+BL0) /* ro: DMA status */ +#define OSIOP_SSTAT0 (0x0c+BL1) /* ro: SCSI status reg 0 */ +#define OSIOP_SSTAT1 (0x0c+BL2) /* ro: SCSI status reg 1 */ +#define OSIOP_SSTAT2 (0x0c+BL3) /* ro: SCSI status reg 2 */ + +#define OSIOP_DSA 0x10 /* rw: Data Structure Address */ + +#define OSIOP_CTEST0 (0x14+BL0) /* ro: Chip test register 0 */ +#define OSIOP_CTEST1 (0x14+BL1) /* ro: Chip test register 1 */ +#define OSIOP_CTEST2 (0x14+BL2) /* ro: Chip test register 2 */ +#define OSIOP_CTEST3 (0x14+BL3) /* ro: Chip test register 3 */ + +#define OSIOP_CTEST4 (0x18+BL0) /* rw: Chip test register 4 */ +#define OSIOP_CTEST5 (0x18+BL1) /* rw: Chip test register 5 */ +#define OSIOP_CTEST6 (0x18+BL2) /* rw: Chip test register 6 */ +#define OSIOP_CTEST7 (0x18+BL3) /* rw: Chip test register 7 */ + +#define OSIOP_TEMP 0x1c /* rw: Temporary Stack reg */ + +#define OSIOP_DFIFO (0x20+BL0) /* rw: DMA FIFO */ +#define OSIOP_ISTAT (0x20+BL1) /* rw: Interrupt Status reg */ +#define OSIOP_CTEST8 (0x20+BL2) /* rw: Chip test register 8 */ +#define OSIOP_LCRC (0x20+BL3) /* rw: LCRC value */ + +#define OSIOP_DBC 0x24 /* rw: DMA Counter reg (longword) */ +#define OSIOP_DBC0 (0x24+BL0) /* rw: DMA Byte Counter reg 0 */ +#define OSIOP_DBC1 (0x24+BL1) /* rw: DMA Byte Counter reg 1 */ +#define OSIOP_DBC2 (0x24+BL2) /* rw: DMA Byte Counter reg 2 */ +#define OSIOP_DCMD (0x24+BL3) /* rw: DMA Command Register */ + +#define OSIOP_DNAD 0x28 /* rw: DMA Next Data Address */ + +#define OSIOP_DSP 0x2c /* rw: DMA SCRIPTS Pointer reg */ + +#define OSIOP_DSPS 0x30 /* rw: DMA SCRIPTS Pointer Save reg */ + +#define OSIOP_SCRATCH 0x34 /* rw: Scratch register */ + +#define OSIOP_DMODE (0x38+BL0) /* rw: DMA Mode reg */ +#define OSIOP_DIEN (0x38+BL1) /* rw: DMA Interrupt Enable */ +#define OSIOP_DWT (0x38+BL2) /* rw: DMA Watchdog Timer */ +#define OSIOP_DCNTL (0x38+BL3) /* rw: DMA Control reg */ + +#define OSIOP_ADDER 0x3c /* ro: Adder Sum Output */ + +#define OSIOP_NREGS 0x40 + + +/* + * Register defines + */ + +/* Scsi control register 0 (scntl0) */ + +#define OSIOP_SCNTL0_ARB 0xc0 /* Arbitration mode */ +#define OSIOP_ARB_SIMPLE 0x00 +#define OSIOP_ARB_FULL 0xc0 +#define OSIOP_SCNTL0_START 0x20 /* Start Sequence */ +#define OSIOP_SCNTL0_WATN 0x10 /* (Select) With ATN */ +#define OSIOP_SCNTL0_EPC 0x08 /* Enable Parity Checking */ +#define OSIOP_SCNTL0_EPG 0x04 /* Enable Parity Generation */ +#define OSIOP_SCNTL0_AAP 0x02 /* Assert ATN on Parity Error */ +#define OSIOP_SCNTL0_TRG 0x01 /* Target Mode */ + +/* Scsi control register 1 (scntl1) */ + +#define OSIOP_SCNTL1_EXC 0x80 /* Extra Clock Cycle of data setup */ +#define OSIOP_SCNTL1_ADB 0x40 /* Assert Data Bus */ +#define OSIOP_SCNTL1_ESR 0x20 /* Enable Selection/Reselection */ +#define OSIOP_SCNTL1_CON 0x10 /* Connected */ +#define OSIOP_SCNTL1_RST 0x08 /* Assert RST */ +#define OSIOP_SCNTL1_AESP 0x04 /* Assert even SCSI parity */ +#define OSIOP_SCNTL1_PAR 0x04 /* Force bad Parity */ +#define OSIOP_SCNTL1_RES0 0x02 /* Reserved */ +#define OSIOP_SCNTL1_RES1 0x01 /* Reserved */ + +/* Scsi interrupt enable register (sien) */ + +#define OSIOP_SIEN_M_A 0x80 /* Phase Mismatch or ATN active */ +#define OSIOP_SIEN_FCMP 0x40 /* Function Complete */ +#define OSIOP_SIEN_STO 0x20 /* (Re)Selection timeout */ +#define OSIOP_SIEN_SEL 0x10 /* (Re)Selected */ +#define OSIOP_SIEN_SGE 0x08 /* SCSI Gross Error */ +#define OSIOP_SIEN_UDC 0x04 /* Unexpected Disconnect */ +#define OSIOP_SIEN_RST 0x02 /* RST asserted */ +#define OSIOP_SIEN_PAR 0x01 /* Parity Error */ + +/* Scsi chip ID (scid) */ + +#define OSIOP_SCID_VALUE(i) (1 << (i)) + +/* Scsi transfer register (sxfer) */ + +#define OSIOP_SXFER_DHP 0x80 /* Disable Halt on Parity error/ + ATN asserted */ +#define OSIOP_SXFER_TP 0x70 /* Synch Transfer Period */ + /* see specs for formulas: + Period = TCP * (4 + XFERP ) + TCP = 1 + CLK + 1..2; + */ +#define OSIOP_SXFER_MO 0x0f /* Synch Max Offset */ +#define OSIOP_MAX_OFFSET 8 + +/* Scsi output data latch register (sodl) */ + +/* Scsi output control latch register (socl) */ + +#define OSIOP_REQ 0x80 /* SCSI signal <x> asserted */ +#define OSIOP_ACK 0x40 +#define OSIOP_BSY 0x20 +#define OSIOP_SEL 0x10 +#define OSIOP_ATN 0x08 +#define OSIOP_MSG 0x04 +#define OSIOP_CD 0x02 +#define OSIOP_IO 0x01 + +#define OSIOP_PHASE(x) ((x) & (OSIOP_MSG|OSIOP_CD|OSIOP_IO)) +#define DATA_OUT_PHASE 0x00 +#define DATA_IN_PHASE OSIOP_IO +#define COMMAND_PHASE OSIOP_CD +#define STATUS_PHASE (OSIOP_CD|OSIOP_IO) +#define MSG_OUT_PHASE (OSIOP_MSG|OSIOP_CD) +#define MSG_IN_PHASE (OSIOP_MSG|OSIOP_CD|OSIOP_IO) + +/* Scsi first byte received register (sfbr) */ + +/* Scsi input data latch register (sidl) */ + +/* Scsi bus data lines register (sbdl) */ + +/* Scsi bus control lines register (sbcl). Same as socl */ + +#define OSIOP_SBCL_SSCF1 0x02 /* wo */ +#define OSIOP_SBCL_SSCF0 0x01 /* wo */ + +/* DMA status register (dstat) */ + +#define OSIOP_DSTAT_DFE 0x80 /* DMA FIFO empty */ +#define OSIOP_DSTAT_RES 0x40 +#define OSIOP_DSTAT_BF 0x20 /* Bus fault */ +#define OSIOP_DSTAT_ABRT 0x10 /* Aborted */ +#define OSIOP_DSTAT_SSI 0x08 /* SCRIPT Single Step */ +#define OSIOP_DSTAT_SIR 0x04 /* SCRIPT Interrupt Instruction */ +#define OSIOP_DSTAT_WTD 0x02 /* Watchdog Timeout Detected */ +#define OSIOP_DSTAT_IID 0x01 /* Invalid Instruction Detected */ + +/* Scsi status register 0 (sstat0) */ + +#define OSIOP_SSTAT0_M_A 0x80 /* Phase Mismatch or ATN active */ +#define OSIOP_SSTAT0_FCMP 0x40 /* Function Complete */ +#define OSIOP_SSTAT0_STO 0x20 /* (Re)Selection timeout */ +#define OSIOP_SSTAT0_SEL 0x10 /* (Re)Selected */ +#define OSIOP_SSTAT0_SGE 0x08 /* SCSI Gross Error */ +#define OSIOP_SSTAT0_UDC 0x04 /* Unexpected Disconnect */ +#define OSIOP_SSTAT0_RST 0x02 /* RST asserted */ +#define OSIOP_SSTAT0_PAR 0x01 /* Parity Error */ + +/* Scsi status register 1 (sstat1) */ + +#define OSIOP_SSTAT1_ILF 0x80 /* Input latch (sidl) full */ +#define OSIOP_SSTAT1_ORF 0x40 /* output reg (sodr) full */ +#define OSIOP_SSTAT1_OLF 0x20 /* output latch (sodl) full */ +#define OSIOP_SSTAT1_AIP 0x10 /* Arbitration in progress */ +#define OSIOP_SSTAT1_LOA 0x08 /* Lost arbitration */ +#define OSIOP_SSTAT1_WOA 0x04 /* Won arbitration */ +#define OSIOP_SSTAT1_RST 0x02 /* SCSI RST current value */ +#define OSIOP_SSTAT1_SDP 0x01 /* SCSI SDP current value */ + +/* Scsi status register 2 (sstat2) */ + +#define OSIOP_SSTAT2_FF 0xf0 /* SCSI FIFO flags (bytecount) */ +#define OSIOP_SCSI_FIFO_DEEP 8 +#define OSIOP_SSTAT2_SDP 0x08 /* Latched (on REQ) SCSI SDP */ +#define OSIOP_SSTAT2_MSG 0x04 /* Latched SCSI phase */ +#define OSIOP_SSTAT2_CD 0x02 +#define OSIOP_SSTAT2_IO 0x01 + +/* Chip test register 0 (ctest0) */ + +#define OSIOP_CTEST0_RES0 0x80 +#define OSIOP_CTEST0_BTD 0x40 /* Byte-to-byte Timer Disable */ +#define OSIOP_CTEST0_GRP 0x20 /* Generate Receive Parity */ +#define OSIOP_CTEST0_EAN 0x10 /* Enable Active Negation */ +#define OSIOP_CTEST0_HSC 0x08 /* Halt SCSI clock */ +#define OSIOP_CTEST0_ERF 0x04 /* Extend REQ/ACK Filtering */ +#define OSIOP_CTEST0_RES1 0x02 +#define OSIOP_CTEST0_DDIR 0x01 /* Xfer direction (1-> from SCSI bus) */ + + +/* Chip test register 1 (ctest1) */ + +#define OSIOP_CTEST1_FMT 0xf0 /* Byte empty in DMA FIFO bottom + (high->byte3) */ +#define OSIOP_CTEST1_FFL 0x0f /* Byte full in DMA FIFO top, same */ + +/* Chip test register 2 (ctest2) */ + +#define OSIOP_CTEST2_RES 0x80 +#define OSIOP_CTEST2_SIGP 0x40 /* Signal process */ +#define OSIOP_CTEST2_SOFF 0x20 /* Synch Offset compare + (1-> zero Init, max Tgt */ +#define OSIOP_CTEST2_SFP 0x10 /* SCSI FIFO Parity */ +#define OSIOP_CTEST2_DFP 0x08 /* DMA FIFO Parity */ +#define OSIOP_CTEST2_TEOP 0x04 /* True EOP (a-la 5380) */ +#define OSIOP_CTEST2_DREQ 0x02 /* DREQ status */ +#define OSIOP_CTEST2_DACK 0x01 /* DACK status */ + +/* Chip test register 3 (ctest3) read-only, top of SCSI FIFO */ + +/* Chip test register 4 (ctest4) */ + +#define OSIOP_CTEST4_MUX 0x80 /* Host bus multiplex mode */ +#define OSIOP_CTEST4_ZMOD 0x40 /* High-impedance outputs */ +#define OSIOP_CTEST4_SZM 0x20 /* ditto, SCSI "outputs" */ +#define OSIOP_CTEST4_SLBE 0x10 /* SCSI loobpack enable */ +#define OSIOP_CTEST4_SFWR 0x08 /* SCSI FIFO write enable (from sodl) */ +#define OSIOP_CTEST4_FBL 0x07 /* DMA FIFO Byte Lane select + (from ctest6) 4->0, .. 7->3 */ + +/* Chip test register 5 (ctest5) */ + +#define OSIOP_CTEST5_ADCK 0x80 /* Clock Address Incrementor */ +#define OSIOP_CTEST5_BBCK 0x40 /* Clock Byte counter */ +#define OSIOP_CTEST5_ROFF 0x20 /* Reset SCSI offset */ +#define OSIOP_CTEST5_MASR 0x10 /* Master set/reset pulses + (of bits 3-0) */ +#define OSIOP_CTEST5_DDIR 0x08 /* (re)set internal DMA direction */ +#define OSIOP_CTEST5_EOP 0x04 /* (re)set internal EOP */ +#define OSIOP_CTEST5_DREQ 0x02 /* (re)set internal REQ */ +#define OSIOP_CTEST5_DACK 0x01 /* (re)set internal ACK */ + +/* Chip test register 6 (ctest6) DMA FIFO access */ + +/* Chip test register 7 (ctest7) */ + +#define OSIOP_CTEST7_CDIS 0x80 /* Cache burst disable */ +#define OSIOP_CTEST7_SC1 0x40 /* Snoop control 1 */ +#define OSIOP_CTEST7_SC0 0x20 /* Snoop contorl 0 */ +#define OSIOP_CTEST7_STD 0x10 /* Selection timeout disable */ +#define OSIOP_CTEST7_DFP 0x08 /* DMA FIFO parity bit */ +#define OSIOP_CTEST7_EVP 0x04 /* Even parity (to host bus) */ +#define OSIOP_CTEST7_TT1 0x02 /* Transfer type bit */ +#define OSIOP_CTEST7_DIFF 0x01 /* Differential mode */ + +/* DMA FIFO register (dfifo) */ + +#define OSIOP_DFIFO_FLF 0x80 /* Flush (spill) DMA FIFO */ +#define OSIOP_DFIFO_BO 0x7f /* FIFO byte offset counter */ + +/* Interrupt status register (istat) */ + +#define OSIOP_ISTAT_ABRT 0x80 /* Abort operation */ +#define OSIOP_ISTAT_RST 0x40 /* Software reset */ +#define OSIOP_ISTAT_SIGP 0x20 /* Signal process */ +#define OSIOP_ISTAT_RES 0x10 +#define OSIOP_ISTAT_CON 0x08 /* Connected */ +#define OSIOP_ISTAT_RES1 0x04 +#define OSIOP_ISTAT_SIP 0x02 /* SCSI Interrupt pending */ +#define OSIOP_ISTAT_DIP 0x01 /* DMA Interrupt pending */ + +/* Chip test register 8 (ctest8) */ + +#define OSIOP_CTEST8_V 0xf0 /* Chip revision level */ +#define OSIOP_CTEST8_FLF 0x08 /* Flush DMA FIFO */ +#define OSIOP_CTEST8_CLF 0x04 /* Clear DMA and SCSI FIFOs */ +#define OSIOP_CTEST8_FM 0x02 /* Fetch pin mode */ +#define OSIOP_CTEST8_SM 0x01 /* Snoop pins mode */ + +/* DMA Mode register (dmode) */ + +#define OSIOP_DMODE_BL_MASK 0xc0 /* DMA burst length */ +#define OSIOP_DMODE_BL8 0xc0 /* 8 bytes */ +#define OSIOP_DMODE_BL4 0x80 /* 4 bytes */ +#define OSIOP_DMODE_BL2 0x40 /* 2 bytes */ +#define OSIOP_DMODE_BL1 0x00 /* 1 byte */ +#define OSIOP_DMODE_FC 0x30 /* Function code */ +#define OSIOP_DMODE_PD 0x08 /* Program/data */ +#define OSIOP_DMODE_FAM 0x04 /* fixed address mode */ +#define OSIOP_DMODE_U0 0x02 /* User programmable transfer type */ +#define OSIOP_DMODE_MAN 0x01 /* SCRIPTS in Manual start mode */ + +/* DMA interrupt enable register (dien) */ + +#define OSIOP_DIEN_RES 0xc0 +#define OSIOP_DIEN_BF 0x20 /* On Bus Fault */ +#define OSIOP_DIEN_ABRT 0x10 /* On Abort */ +#define OSIOP_DIEN_SSI 0x08 /* On SCRIPTS sstep */ +#define OSIOP_DIEN_SIR 0x04 /* On SCRIPTS intr instruction */ +#define OSIOP_DIEN_WTD 0x02 /* On watchdog timeout */ +#define OSIOP_DIEN_IID 0x01 /* On illegal instruction detected */ + +/* DMA control register (dcntl) */ + +#define OSIOP_DCNTL_CF_MASK 0xc0 /* Clock frequency dividers: */ +#define OSIOP_DCNTL_CF_2 0x00 /* 0 --> 37.51..50.00 Mhz, div=2 */ +#define OSIOP_DCNTL_CF_1_5 0x40 /* 1 --> 25.01..37.50 Mhz, div=1.5 */ +#define OSIOP_DCNTL_CF_1 0x80 /* 2 --> 16.67..25.00 Mhz, div=1 */ +#define OSIOP_DCNTL_CF_3 0xc0 /* 3 --> 50.01..66.67 Mhz, div=3 */ +#define OSIOP_DCNTL_EA 0x20 /* Enable ACK */ +#define OSIOP_DCNTL_SSM 0x10 /* Single step mode */ +#define OSIOP_DCNTL_LLM 0x08 /* Enable SCSI Low-level mode */ +#define OSIOP_DCNTL_STD 0x04 /* Start DMA operation */ +#define OSIOP_DCNTL_FA 0x02 /* Fast arbitration */ +#define OSIOP_DCNTL_COM 0x01 /* 53C700 Compatibility */ diff --git a/sys/dev/ic/osiopvar.h b/sys/dev/ic/osiopvar.h new file mode 100644 index 00000000000..bbabd04ac70 --- /dev/null +++ b/sys/dev/ic/osiopvar.h @@ -0,0 +1,260 @@ +/* $NetBSD: osiopvar.h,v 1.1 2001/04/30 04:47:51 tsutsui Exp $ */ + +/* + * Copyright (c) 2001 Izumi Tsutsui. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR 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. + */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * 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 University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)siopvar.h 7.1 (Berkeley) 5/8/90 + */ + +#define osiop_read_1(sc, reg) \ + bus_space_read_1((sc)->sc_bst, (sc)->sc_reg, reg) +#define osiop_write_1(sc, reg, val) \ + bus_space_write_1((sc)->sc_bst, (sc)->sc_reg, reg, val) + +#define osiop_read_4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_reg, reg) +#define osiop_write_4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_reg, reg, val) + +/* + * The largest single request will be MAXPHYS bytes which will require + * at most MAXPHYS/NBPG+1 chain elements to describe, i.e. if none of + * the buffer pages are physically contiguous (MAXPHYS/NBPG) and the + * buffer is not page aligned (+1). + */ +/* XXX This should be (MAXPHYS / NBPG + 1), but hardcoded in script */ +#define OSIOP_NSG (16 + 1) +#if MAXPHYS > (PAGE_SIZE * (OSIOP_NSG - 1)) +#define OSIOP_MAX_XFER (PAGE_SIZE * (OSIOP_NSG - 1)) +#else +#define OSIOP_MAX_XFER MAXPHYS +#endif + +#define OSIOP_NTGT 8 +#define OSIOP_NACB 32 /* XXX (PAGE_SIZE / sizeof(osiop_ds)) is better? */ + +/* + * Data Structure for SCRIPTS program + */ +typedef struct buf_table { + u_int32_t count; + u_int32_t addr; +} buf_table_t; + +struct osiop_ds { + u_int32_t scsi_addr; /* SCSI ID & sync */ + u_int32_t pad1; + buf_table_t id; /* Identify message */ + buf_table_t cmd; /* SCSI command */ + buf_table_t status; /* Status */ + buf_table_t msg; /* Message */ + buf_table_t msgin; /* Message in */ + buf_table_t extmsg; /* Extended message in */ + buf_table_t synmsg; /* Sync transfer request */ + buf_table_t data[OSIOP_NSG]; /* DMA S/G buffers */ + + u_int8_t msgout[8]; + u_int8_t msgbuf[8]; + u_int8_t stat[8]; +} __attribute__((__packed__)); + +/* status can hold the SCSI_* status values, and 2 additionnal values: */ +#define SCSI_OSIOP_NOCHECK 0xfe /* don't check the scsi status */ +#define SCSI_OSIOP_NOSTATUS 0xff /* device didn't report status */ + +#define MSG_INVALID 0xff /* dummy value for message buffer */ + +#define OSIOP_DSOFF(x) offsetof(struct osiop_ds, x) +#define OSIOP_DSIDOFF OSIOP_DSOFF(msgout[0]) +#define OSIOP_DSMSGOFF OSIOP_DSOFF(msgbuf[0]) +#define OSIOP_DSMSGINOFF OSIOP_DSOFF(msgbuf[1]) +#define OSIOP_DSEXTMSGOFF OSIOP_DSOFF(msgbuf[2]) +#define OSIOP_DSSYNMSGOFF OSIOP_DSOFF(msgbuf[3]) +#define OSIOP_DSSTATOFF OSIOP_DSOFF(stat[0]) + +/* + * ACB. Holds additional information for each SCSI command Comments: + * Basicly, we refrain from fiddling with the scsi_xfer struct + * (except do the expected updating of return values). + * We'll generally update: xs->{flags,resid,error,status} and + * occasionally xs->retries. + */ +struct osiop_acb { + TAILQ_ENTRY(osiop_acb) chain; + struct scsipi_xfer *xs; /* SCSI xfer ctrl block from upper layer */ + struct osiop_softc *sc; /* points back to our adapter */ + + bus_dmamap_t cmddma; /* DMA map for SCSI command */ + bus_dmamap_t datadma; /* DMA map for data transfer */ + + struct osiop_ds *ds; /* data structure for this acb */ + bus_size_t dsoffset; /* offset of data structure for this acb */ + + bus_size_t cmdlen; /* command length */ + bus_size_t datalen; /* transfer data length */ +#ifdef OSIOP_DEBUG + void *data; /* transfer data buffer ptr */ +#endif + + bus_addr_t curaddr; /* current transfer data buffer */ + bus_size_t curlen; /* current transfer data length */ + + int status; /* status of this acb */ +/* status defs */ +#define ACB_S_FREE 0 /* cmd slot is free */ +#define ACB_S_READY 1 /* cmd slot is waiting for processing */ +#define ACB_S_ACTIVE 2 /* cmd slot is being processed */ +#define ACB_S_DONE 3 /* cmd slot has been processed */ + + int flags; /* cmd slot flags */ +#define ACB_F_TIMEOUT 0x01 /* command timeout */ + + u_int8_t intstat; /* buffer to save sc_flags on disconnect */ +}; + +/* + * Some info about each (possible) target on the SCSI bus. This should + * probably have been a "per target+lunit" structure, but we'll leave it at + * this for now. Is there a way to reliably hook it up to sc->fordriver?? + */ +struct osiop_tinfo { + int cmds; /* number of commands processed */ + int dconns; /* number of disconnects */ + int touts; /* number of timeouts */ + int perrs; /* number of parity errors */ + u_int8_t lubusy; /* What local units/subr. are busy? */ + u_int8_t period; /* Period suggestion */ + u_int8_t offset; /* Offset suggestion */ + u_int8_t flags; /* misc flags per each target */ +#define TI_NOSYNC 0x01 /* disable sync xfer on this target */ +#define TI_NODISC 0x02 /* disable disconnect on this target */ + +} tinfo_t; + +struct osiop_softc { + struct device sc_dev; + + bus_space_tag_t sc_bst; /* bus space tag */ + bus_space_handle_t sc_reg; /* register I/O handle */ + + bus_dma_tag_t sc_dmat; /* bus dma tag */ + bus_dmamap_t sc_scrdma; /* script dma map */ + bus_dmamap_t sc_dsdma; /* script data dma map */ + + u_int32_t *sc_script; /* ptr to script memory */ + struct osiop_ds *sc_ds; /* ptr to data structure memory */ + + int sc_id; /* adapter SCSI id */ + int sc_active; /* number of active I/O's */ + + struct osiop_acb *sc_nexus; /* current command */ + struct osiop_acb *sc_acb; /* the real command blocks */ + + /* Lists of command blocks */ + TAILQ_HEAD(acb_list, osiop_acb) free_list, + ready_list, + nexus_list; + + struct scsipi_adapter sc_adapter; + struct scsipi_channel sc_channel; + + struct osiop_tinfo sc_tinfo[OSIOP_NTGT]; + + int sc_clock_freq; + int sc_tcp[4]; + int sc_flags; +#define OSIOP_INTSOFF 0x80 /* Interrupts turned off */ +#define OSIOP_INTDEFER 0x40 /* MD interrupt has been deferred */ +#define OSIOP_NODMA 0x02 /* No DMA transfer */ +#define OSIOP_ALIVE 0x01 /* controller initialized */ + + int sc_cfflags; /* copy of config flags */ + + /* one for each target */ + struct syncpar { + u_int8_t state; + u_int8_t sxfer; + u_int8_t sbcl; + u_int8_t pad; + } sc_sync[OSIOP_NTGT]; + + u_int8_t sc_minsync; + + u_int8_t sc_dstat; + u_int8_t sc_sstat0; + u_int8_t sc_sstat1; + u_int8_t sc_istat; + u_int8_t sc_dcntl; + u_int8_t sc_ctest7; + u_int8_t sc_sien; + u_int8_t sc_dien; +}; + +/* negotiation states */ +#define NEG_INIT 0 /* Initial negotiate state */ +#define NEG_WIDE 0 /* Negotiate wide transfers */ +#define NEG_WAITW 1 /* Waiting for wide negotation response */ +#define NEG_SYNC 2 /* Negotiate synch transfers */ +#define NEG_WAITS 3 /* Waiting for synch negoation response */ +#define NEG_DONE 4 /* Wide and/or sync negotation done */ + +void osiop_attach(struct osiop_softc *); +void osiop_intr(struct osiop_softc *); diff --git a/sys/dev/microcode/siop/Makefile b/sys/dev/microcode/siop/Makefile index 01461bfff26..56a9a344e45 100644 --- a/sys/dev/microcode/siop/Makefile +++ b/sys/dev/microcode/siop/Makefile @@ -1,15 +1,17 @@ -# $NetBSD: Makefile,v 1.1 2000/04/21 17:57:01 bouyer Exp $ +# $NetBSD: Makefile,v 1.2 2001/04/30 04:47:51 tsutsui Exp $ -all: siop.out +all: siop.out osiop.out PROG= ncr53cxxx MKSHARE=no .include <bsd.prog.mk> -regen: siop.out -headers: siop.out +regen: siop.out osiop.out +headers: siop.out osiop.out siop.out: siop.ss ${PROG} ./${PROG} siop.ss -o siop.out - + +osiop.out: osiop.ss ${PROG} + ./${PROG} osiop.ss -p osiop.out diff --git a/sys/dev/microcode/siop/osiop.ss b/sys/dev/microcode/siop/osiop.ss new file mode 100644 index 00000000000..a78e1a815ed --- /dev/null +++ b/sys/dev/microcode/siop/osiop.ss @@ -0,0 +1,255 @@ +; $NetBSD: osiop.ss,v 1.1 2001/04/30 04:47:51 tsutsui Exp $ + +; +; Copyright (c) 1995 Michael L. Hitch +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions +; are met: +; 1. Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; 3. All advertising materials mentioning features or use of this software +; must display the following acknowledgement: +; This product includes software developed by Michael L. Hitch. +; 4. The name of the author may not be used to endorse or promote products +; derived from this software without specific prior written permission +; +; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +; IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +; IN NO EVENT SHALL THE AUTHOR 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. +; + +; NCR 53c710 script +; +ARCH 710 +; +ABSOLUTE ds_Device = 0 +ABSOLUTE ds_MsgOut = ds_Device + 8 +ABSOLUTE ds_Cmd = ds_MsgOut + 8 +ABSOLUTE ds_Status = ds_Cmd + 8 +ABSOLUTE ds_Msg = ds_Status + 8 +ABSOLUTE ds_MsgIn = ds_Msg + 8 +ABSOLUTE ds_ExtMsg = ds_MsgIn + 8 +ABSOLUTE ds_SyncMsg = ds_ExtMsg + 8 +ABSOLUTE ds_Data1 = ds_SyncMsg + 8 +ABSOLUTE ds_Data2 = ds_Data1 + 8 +ABSOLUTE ds_Data3 = ds_Data2 + 8 +ABSOLUTE ds_Data4 = ds_Data3 + 8 +ABSOLUTE ds_Data5 = ds_Data4 + 8 +ABSOLUTE ds_Data6 = ds_Data5 + 8 +ABSOLUTE ds_Data7 = ds_Data6 + 8 +ABSOLUTE ds_Data8 = ds_Data7 + 8 +ABSOLUTE ds_Data9 = ds_Data8 + 8 +ABSOLUTE ds_Data10 = ds_Data9 + 8 +ABSOLUTE ds_Data11 = ds_Data10 + 8 +ABSOLUTE ds_Data12 = ds_Data11 + 8 +ABSOLUTE ds_Data13 = ds_Data12 + 8 +ABSOLUTE ds_Data14 = ds_Data13 + 8 +ABSOLUTE ds_Data15 = ds_Data14 + 8 +ABSOLUTE ds_Data16 = ds_Data15 + 8 +ABSOLUTE ds_Data17 = ds_Data16 + 8 + + +ABSOLUTE ok = 0xff00 +ABSOLUTE int_disc = 0xff01 +ABSOLUTE int_disc_wodp = 0xff02 +ABSOLUTE int_reconnect = 0xff03 +ABSOLUTE int_connect = 0xff04 +ABSOLUTE int_phase = 0xff05 +ABSOLUTE int_msgin = 0xff06 +ABSOLUTE int_extmsg = 0xff07 +ABSOLUTE int_msgsdp = 0xff08 +ABSOLUTE int_identify = 0xff09 +ABSOLUTE int_status = 0xff0a +ABSOLUTE int_syncmsg = 0xff0b + +ENTRY scripts +ENTRY switch +ENTRY wait_reselect +ENTRY dataout +ENTRY datain +ENTRY clear_ack + +PROC osiop_script: + +scripts: + + SELECT ATN FROM ds_Device, REL(reselect) +; +switch: + JUMP REL(msgin), WHEN MSG_IN + JUMP REL(msgout), IF MSG_OUT + JUMP REL(command_phase), IF CMD + JUMP REL(dataout), IF DATA_OUT + JUMP REL(datain), IF DATA_IN + JUMP REL(end), IF STATUS + + INT int_phase ; Unrecognized phase + +msgin: + MOVE FROM ds_MsgIn, WHEN MSG_IN + JUMP REL(ext_msg), IF 0x01 ; extended message + JUMP REL(disc), IF 0x04 ; disconnect message + JUMP REL(msg_sdp), IF 0x02 ; save data pointers + JUMP REL(msg_rej), IF 0x07 ; message reject + JUMP REL(msg_rdp), IF 0x03 ; restore data pointers + INT int_msgin ; unrecognized message + +msg_rej: +; Do we need to interrupt host here to let it handle the reject? +msg_rdp: +clear_ack: + CLEAR ACK + CLEAR ATN + JUMP REL(switch) + +ext_msg: + CLEAR ACK + MOVE FROM ds_ExtMsg, WHEN MSG_IN + JUMP REL(sync_msg), IF 0x03 + int int_extmsg ; extended message not SDTR + +sync_msg: + CLEAR ACK + MOVE FROM ds_SyncMsg, WHEN MSG_IN + int int_syncmsg ; Let host handle the message +; If we continue from the interrupt, the host has set up a response +; message to be sent. Set ATN, clear ACK, and continue. + SET ATN + CLEAR ACK + JUMP REL(switch) + +disc: + CLEAR ACK + WAIT DISCONNECT + + int int_disc_wodp ; signal disconnect w/o save DP + +msg_sdp: + CLEAR ACK ; acknowledge message + JUMP REL(switch), WHEN NOT MSG_IN + MOVE FROM ds_ExtMsg, WHEN MSG_IN + INT int_msgsdp, IF NOT 0x04 ; interrupt if not disconnect + CLEAR ACK + WAIT DISCONNECT + + INT int_disc ; signal disconnect + +reselect: +wait_reselect: + WAIT RESELECT REL(select_adr) + MOVE LCRC to SFBR ; Save reselect ID + MOVE SFBR to SCRATCH0 + + INT int_identify, WHEN NOT MSG_IN + MOVE FROM ds_Msg, WHEN MSG_IN + INT int_reconnect ; let host know about reconnect + CLEAR ACK ; acknowlege the message + JUMP REL(switch) + +select_adr: + MOVE SCNTL1 & 0x10 to SFBR ; get connected status + INT int_connect, IF 0x00 ; tell host if not connected + MOVE CTEST2 & 0x40 to SFBR ; clear Sig_P + JUMP REL(wait_reselect) ; and try reselect again + +msgout: + MOVE FROM ds_MsgOut, WHEN MSG_OUT + JUMP REL(switch) + +command_phase: + CLEAR ATN + MOVE FROM ds_Cmd, WHEN CMD + JUMP REL(switch) + +dataout: + MOVE FROM ds_Data1, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data2, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data3, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data4, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data5, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data6, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data7, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data8, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data9, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data10, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data11, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data12, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data13, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data14, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data15, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data16, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data17, WHEN DATA_OUT + CALL REL(switch) + +datain: + MOVE FROM ds_Data1, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data2, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data3, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data4, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data5, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data6, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data7, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data8, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data9, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data10, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data11, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data12, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data13, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data14, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data15, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data16, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data17, WHEN DATA_IN + CALL REL(switch) + +end: + MOVE FROM ds_Status, WHEN STATUS + int int_status, WHEN NOT MSG_IN ; status not followed by msg + MOVE FROM ds_Msg, WHEN MSG_IN + CLEAR ACK + WAIT DISCONNECT + INT ok ; signal completion + JUMP REL(wait_reselect) |
