diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2008-08-18 23:19:30 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2008-08-18 23:19:30 +0000 |
commit | 87d00f400eaf86a770dc09268649713d4a3d50f4 (patch) | |
tree | 9303df1e5e118503c8e8801c63811ceb1e56e53a /sys/arch/vax/dec | |
parent | fdbef39f25299be7dfee8fd1e022a3c078802917 (diff) |
Add support for the ``Firefox'' VAXstation 3520/3540/3820/3840 workstations,
currently limited to serial console and a single processor working.
All ``on-board'' devices, including the Q-bus adapter, but except for
the frame buffer, are supported. The machine will boot over the network
or from SCSI devices.
Lots of thanks to Al Kossow for www.bitsavers.org, on which I found the
technical documentation allowing me to complete this port (which was
lacking at the time I got that machine...).
Diffstat (limited to 'sys/arch/vax/dec')
-rw-r--r-- | sys/arch/vax/dec/files.dec | 6 | ||||
-rw-r--r-- | sys/arch/vax/dec/sii.c | 1798 | ||||
-rw-r--r-- | sys/arch/vax/dec/siireg.h | 235 | ||||
-rw-r--r-- | sys/arch/vax/dec/siivar.h | 71 |
4 files changed, 2109 insertions, 1 deletions
diff --git a/sys/arch/vax/dec/files.dec b/sys/arch/vax/dec/files.dec index fe2848d1163..1f81a9f177a 100644 --- a/sys/arch/vax/dec/files.dec +++ b/sys/arch/vax/dec/files.dec @@ -1,4 +1,4 @@ -# $OpenBSD: files.dec,v 1.5 2008/08/18 23:04:28 miod Exp $ +# $OpenBSD: files.dec,v 1.6 2008/08/18 23:19:22 miod Exp $ # $NetBSD: files.dec,v 1.4 1999/08/04 07:17:51 nisimura Exp $ # # Config file and device description for machine-independent @@ -14,3 +14,7 @@ file arch/vax/dec/wskbdmap_lk201.c lkkbd # VSxxx mouse file arch/vax/dec/vsms_ws.c lkms + +# DC7061 SII DSSI/SCSI controller +device sii: scsi +file arch/vax/dec/sii.c sii diff --git a/sys/arch/vax/dec/sii.c b/sys/arch/vax/dec/sii.c new file mode 100644 index 00000000000..0a677fa557e --- /dev/null +++ b/sys/arch/vax/dec/sii.c @@ -0,0 +1,1798 @@ +/* $OpenBSD: sii.c,v 1.1 2008/08/18 23:19:22 miod Exp $ */ +/* $NetBSD: sii.c,v 1.42 2000/06/02 20:20:29 mhitch Exp $ */ +/* + * Copyright (c) 2008 Miodrag Vallat. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell and Rick Macklem. + * + * 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. 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. + * + * @(#)sii.c 8.2 (Berkeley) 11/30/93 + * + * from: Header: /sprite/src/kernel/dev/ds3100.md/RCS/devSII.c, + * v 9.2 89/09/14 13:37:41 jhh Exp $ SPRITE (DECWRL)"; + */ + +/* + * SCSI interface driver + */ +#include <sys/param.h> +#include <sys/buf.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/systm.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <scsi/scsi_message.h> +#ifdef DEBUG +#include <scsi/scsi_disk.h> +#endif + +#include <vax/dec/siireg.h> /* device registers */ +#include <vax/dec/siivar.h> /* softc and prototypes */ + +/* XXX not in scsi/scsi_message.h */ +#define MSG_EXT_MODIFY_DATA_PTR 0x00 + +struct cfdriver sii_cd = { + NULL, "sii", DV_DULL +}; + +/* + * MACROS for timing out spin loops. + * + * Wait until expression is true. + * + * Control register bits can change at any time so when the CPU + * reads a register, the bits might change and + * invalidate the setup and hold times for the CPU. + * This macro reads the register twice to be sure the value is stable. + * + * args: var - variable to save control register contents + * reg - control register to read + * expr - expression to spin on + * spincount - maximum number of times through the loop + * cntr - variable for number of tries + */ +#define SII_WAIT_UNTIL(var, reg, expr, spincount, cntr) { \ + u_int tmp = reg; \ + for (cntr = 0; cntr < spincount; cntr++) { \ + while (tmp != (var = reg)) \ + tmp = var; \ + if (expr) \ + break; \ + if (cntr >= 100) \ + DELAY(100); \ + } \ + } + +#ifdef DEBUG +int sii_debug = 1; +int sii_debug_cmd; +int sii_debug_bn; +int sii_debug_sz; +#define NLOG 16 +struct sii_log { + u_short cstat; + u_short dstat; + u_short comm; + u_short msg; + int rlen; + int dlen; + int target; +} sii_log[NLOG], *sii_logp = sii_log; +#endif + +#define NORESET 0 +#define RESET 1 +#define NOWAIT 0 +#define WAIT 1 + +#if 0 +/* + * XXX That hardcoded 7 below implies SII_NCMD <= 7, which is not the case... + * XXX Thankfully all uses of SII_BUF_ADDR are in #if 0 sections (and this + * XXX should be reasonably safe as long as we use 7 as the host id). + */ +/* + * Define a safe address in the SCSI buffer for doing status & message DMA + * XXX why not add another field to softc? + */ +#define SII_BUF_ADDR(sc) (SII_MAX_DMA_XFER_LENGTH * 7 * 2) +#endif + +/* + * Forward references + */ + +void sii_Reset(struct sii_softc *sc, int resetbus); +void sii_StartCmd(struct sii_softc *sc, int target); +void sii_CmdDone(struct sii_softc *sc, int target, int error); +void sii_DoIntr(struct sii_softc *sc, u_int dstat); +void sii_StateChg(struct sii_softc *sc, u_int cstat); +int sii_GetByte(SIIRegs *regs, int phase, int ack); +void sii_DoSync(struct sii_softc *, State *); +void sii_StartDMA(SIIRegs *regs, int phase, u_int dmaAddr, int size); +int sii_scsi_cmd(struct scsi_xfer *); +#ifdef DEBUG +void sii_DumpLog(void); +#endif + +struct scsi_adapter sii_scsiswitch = { + sii_scsi_cmd, + minphys, + NULL, + NULL, + NULL +}; + +struct scsi_device sii_scsidev = { + NULL, + NULL, + NULL, + NULL +}; + +void +sii_attach(sc) + struct sii_softc *sc; +{ + struct scsibus_attach_args saa; + int i; + + sc->sc_target = -1; /* no command active */ + + /* + * Give each target its own DMA buffer region. + * Make it big enough for 2 max transfers so we can ping pong buffers + * while we copy the data. + */ + for (i = 0; i < SII_NCMD; i++) { + sc->sc_st[i].dmaAddr[0] = + SII_MAX_DMA_XFER_LENGTH * 2 * i; + sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] + + SII_MAX_DMA_XFER_LENGTH; + } + + sii_Reset(sc, RESET); + printf("\n"); + + /* + * fill in the prototype scsi_link. + */ + sc->sc_link.adapter = &sii_scsiswitch; + sc->sc_link.adapter_buswidth = 8; + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = sc->sc_regs->id & SII_IDMSK; + sc->sc_link.device = &sii_scsidev; + sc->sc_link.openings = 1; /* driver can't queue requests yet */ + + /* + * Now try to attach all the sub-devices + */ + bzero(&saa, sizeof(saa)); + saa.saa_sc_link = &sc->sc_link; + config_found(&sc->sc_dev, &saa, scsiprint); +} + +/* + * Start activity on a SCSI device. + * We maintain information on each device separately since devices can + * connect/disconnect during an operation. + */ + +int +sii_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + int target = xs->sc_link->target; + struct sii_softc *sc = xs->sc_link->adapter_softc; + int s; + int count; + + s = splbio(); + if (sc->sc_cmd[target]) { + splx(s); +#ifdef DEBUG + printf("[busy at start]\n"); +#endif + return (TRY_AGAIN_LATER); + } + /* + * Build a ScsiCmd for this command and start it. + */ + sc->sc_xs[target] = xs; + sc->sc_cmd[target] = &sc->sc_cmd_fake[target]; /* XXX */ + sc->sc_cmd[target]->unit = 0; + sc->sc_cmd[target]->flags = 0; + sc->sc_cmd[target]->buflen = xs->datalen; + sc->sc_cmd[target]->buf = xs->data; + sc->sc_cmd[target]->cmdlen = xs->cmdlen; + sc->sc_cmd[target]->cmd = (u_char *)xs->cmd; + sc->sc_cmd[target]->lun = xs->sc_link->lun; + sii_StartCmd(sc, target); + splx(s); + + if ((xs->flags & ITSDONE) != 0) + return (COMPLETE); + if ((xs->flags & SCSI_POLL) == 0) + return (SUCCESSFULLY_QUEUED); + count = xs->timeout; + while (count) { + s = splbio(); +#if 0 + (void)sii_intr(sc); +#else +{ + u_int dstat; + + /* + * Find which controller caused the interrupt. + */ + dstat = sc->sc_regs->dstat; + if (dstat & (SII_CI | SII_DI)) + sii_DoIntr(sc, dstat); +} +#endif + splx(s); + if ((xs->flags & ITSDONE) != 0) + break; + /* XXX schedule another command? */ + DELAY(1000); + --count; + } + if ((xs->flags & ITSDONE) != 0) + return (COMPLETE); + xs->error = XS_TIMEOUT; + xs->flags |= ITSDONE; + s = splbio(); + scsi_done(xs); + splx(s); + return (COMPLETE); +} + +/* + * Check to see if any SII chips have pending interrupts + * and process as appropriate. + */ +int +sii_intr(xxxsc) + void *xxxsc; +{ + struct sii_softc *sc = xxxsc; + u_int dstat; + + /* + * Find which controller caused the interrupt. + */ + dstat = sc->sc_regs->dstat; + if (dstat & (SII_CI | SII_DI)) { + sii_DoIntr(sc, dstat); + return (1); + } + + return (0); +} + +/* + * Reset the SII chip and do a SCSI reset if 'reset' is true. + * NOTE: if !cold && reset, should probably probe for devices + * since a SCSI bus reset will set UNIT_ATTENTION. + */ +void +sii_Reset(sc, reset) + struct sii_softc* sc; + int reset; /* TRUE => reset SCSI bus */ +{ + SIIRegs *regs = sc->sc_regs; + +#ifdef DEBUG + if (sii_debug > 1) + printf("sii: RESET\n"); +#endif + /* + * Reset the SII chip. + */ + regs->comm = SII_CHRESET; + /* + * Set arbitrated bus mode. + */ + regs->csr = SII_HPM; + /* + * Set host adapter ID. + */ + regs->id = SII_ID_IO | sc->sc_hostid; + /* + * Enable SII to drive the SCSI bus. + */ + regs->dictrl = SII_PRE; + regs->dmctrl = 0; + + if (reset) { + int i; + + /* + * Assert SCSI bus reset for at least 25 Usec to clear the + * world. SII_DO_RST is self clearing. + * Delay 250 ms before doing any commands. + */ + regs->comm = SII_DO_RST; + DELAY(250000); + + /* rearbitrate synchronous offset */ + for (i = 0; i < SII_NCMD; i++) + sc->sc_st[i].dmaReqAck = 0; + } + + /* + * Clear any pending interrupts from the reset. + */ + regs->cstat = regs->cstat; + regs->dstat = regs->dstat; + /* + * Set up SII for arbitrated bus mode, SCSI parity checking, + * Reselect Enable, and Interrupt Enable. + */ + regs->csr = SII_HPM | SII_RSE | SII_PCE | SII_IE; +} + +/* + * Start a SCSI command by sending the cmd data + * to a SCSI controller via the SII. + * Call the device done procedure if it can't be started. + * NOTE: we should be called with interrupts disabled. + */ +void +sii_StartCmd(sc, target) + struct sii_softc *sc; /* which SII to use */ + int target; /* which command to start */ +{ + SIIRegs *regs; + ScsiCmd *scsicmd; + State *state; + u_int status; + int error, retval; + + splassert(IPL_BIO); + + /* if another command is currently in progress, just wait */ + if (sc->sc_target >= 0) + return; + + /* initialize state information for this command */ + scsicmd = sc->sc_cmd[target]; + state = &sc->sc_st[target]; + state->flags = FIRST_DMA; + state->prevComm = 0; + state->dmalen = 0; + state->dmaCurPhase = -1; + state->dmaPrevPhase = -1; + state->dmaBufIndex = 0; + state->cmd = scsicmd->cmd; + state->cmdlen = scsicmd->cmdlen; + if ((state->buflen = scsicmd->buflen) == 0) { + state->dmaDataPhase = -1; /* illegal phase. shouldn't happen */ + state->buf = (char *)0; + } else { + state->buf = scsicmd->buf; + } + +#ifdef DEBUG + if (sii_debug > 1) { + printf("sii_StartCmd: %s target %d cmd 0x%x addr %p size %d DMA %d\n", + sc->sc_dev.dv_xname, + target, scsicmd->cmd[0], scsicmd->buf, scsicmd->buflen, + state->dmaDataPhase); + } + sii_debug_cmd = scsicmd->cmd[0]; + if (scsicmd->cmd[0] == READ_BIG || + scsicmd->cmd[0] == WRITE_BIG) { + sii_debug_bn = (scsicmd->cmd[2] << 24) | + (scsicmd->cmd[3] << 16) | + (scsicmd->cmd[4] << 8) | + scsicmd->cmd[5]; + sii_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8]; + } else { + sii_debug_bn = 0; + sii_debug_sz = 0; + } +#endif + + /* try to select the target */ + regs = sc->sc_regs; + + /* + * Another device may have selected us; in which case, + * this command will be restarted later. + */ + if ((status = regs->dstat) & (SII_CI | SII_DI)) { + sii_DoIntr(sc, status); + return; + } + + sc->sc_target = target; +#if 0 + /* seem to have problems with synchronous transfers */ + if (scsicmd->flags & SCSICMD_USE_SYNC) { + printf("sii_StartCmd: doing extended msg\n"); /* XXX */ + /* + * Setup to send both the identify message and the synchronous + * data transfer request. + */ + sc->sc_buf[0] = MSG_IDENTIFYFLAG | MSG_IDENTIFY_DISCFLAG; + sc->sc_buf[1] = MSG_EXTENDED; + sc->sc_buf[2] = MSG_EXT_SDTR_LEN; + sc->sc_buf[3] = MSG_EXT_SDTR; + sc->sc_buf[4] = 0; + sc->sc_buf[5] = 3; /* maximum SII chip supports */ + + state->dmaCurPhase = SII_MSG_OUT_PHASE, + state->dmalen = 6; + sc->sii_copytobuf(sc, sc->sc_buf, SII_BUF_ADDR(sc), 6); + regs->slcsr = target; + regs->dmctrl = state->dmaReqAck; + regs->dmaddrl = SII_BUF_ADDR(sc); + regs->dmaddrh = SII_BUF_ADDR(sc) >> 16; + regs->dmlotc = 6; + regs->comm = SII_DMA | SII_INXFER | SII_SELECT | SII_ATN | + SII_CON | SII_MSG_OUT_PHASE; + } else +#endif + { + /* do a chained, select with ATN and programmed I/O command */ + regs->data = MSG_IDENTIFYFLAG | MSG_IDENTIFY_DISCFLAG | + scsicmd->lun; + regs->slcsr = target; + regs->dmctrl = state->dmaReqAck; + regs->comm = SII_INXFER | SII_SELECT | SII_ATN | SII_CON | + SII_MSG_OUT_PHASE; + } + + /* + * Wait for something to happen + * (should happen soon or we would use interrupts). + */ + SII_WAIT_UNTIL(status, regs->cstat, status & (SII_CI | SII_DI), + SII_WAIT_COUNT/4, retval); + + /* check to see if we are connected OK */ + if ((status & (SII_RST | SII_SCH | SII_STATE_MSK)) == + (SII_SCH | SII_CON)) { + regs->cstat = status; + +#ifdef DEBUG + sii_logp->target = target; + sii_logp->cstat = status; + sii_logp->dstat = 0; + sii_logp->comm = regs->comm; + sii_logp->msg = -1; + sii_logp->rlen = state->buflen; + sii_logp->dlen = state->dmalen; + if (++sii_logp >= &sii_log[NLOG]) + sii_logp = sii_log; +#endif + + /* wait a short time for command phase */ + SII_WAIT_UNTIL(status, regs->dstat, status & SII_MIS, + SII_WAIT_COUNT, retval); +#ifdef DEBUG + if (sii_debug > 2) + printf("sii_StartCmd: ds %x cnt %d\n", status, retval); +#endif + if ((status & (SII_CI | SII_MIS | SII_PHASE_MSK)) != + (SII_MIS | SII_CMD_PHASE)) { + printf("sii_StartCmd: timeout cs %x ds %x cnt %d\n", + regs->cstat, status, retval); /* XXX */ + /* process interrupt or continue until it happens */ + if (status & (SII_CI | SII_DI)) + sii_DoIntr(sc, status); + return; + } + regs->dstat = SII_DNE; /* clear Msg Out DMA done */ + + /* send command data */ + sc->sii_copytobuf(sc, state->cmd, state->dmaAddr[0], + state->cmdlen); + sii_StartDMA(regs, state->dmaCurPhase = SII_CMD_PHASE, + state->dmaAddr[0], state->dmalen = scsicmd->cmdlen); + + /* wait a little while for DMA to finish */ + SII_WAIT_UNTIL(status, regs->dstat, status & (SII_CI | SII_DI), + SII_WAIT_COUNT, retval); +#ifdef DEBUG + if (sii_debug > 2) + printf("sii_StartCmd: ds %x, cnt %d\n", status, retval); +#endif + if (status & (SII_CI | SII_DI)) + sii_DoIntr(sc, status); +#ifdef DEBUG + if (sii_debug > 2) + printf("sii_StartCmd: DONE ds %x\n", regs->dstat); +#endif + return; + } + + /* + * Another device may have selected us; in which case, + * this command will be restarted later. + */ + if (status & (SII_CI | SII_DI)) { + sii_DoIntr(sc, regs->dstat); + return; + } + + /* + * Disconnect if selection command still in progress. + */ + if (status & SII_SIP) { + error = XS_SELTIMEOUT; /* device didn't respond */ + regs->comm = SII_DISCON; + SII_WAIT_UNTIL(status, regs->cstat, + !(status & (SII_CON | SII_SIP)), + SII_WAIT_COUNT, retval); + } else + error = XS_BUSY; /* couldn't get the bus */ +#ifdef DEBUG + if (sii_debug > 1) + printf("sii_StartCmd: Couldn't select target %d error %d\n", + target, error); +#endif + sc->sc_target = -1; + regs->cstat = 0xffff; + regs->dstat = 0xffff; + regs->comm = 0; + sii_CmdDone(sc, target, error); +} + +/* + * Process interrupt conditions. + */ +void +sii_DoIntr(sc, dstat) + struct sii_softc *sc; + u_int dstat; +{ + SIIRegs *regs = sc->sc_regs; + State *state; + u_int cstat; + int i, msg; + u_int comm; + +again: + comm = regs->comm; + +#ifdef DEBUG + if (sii_debug > 3) + printf("sii_DoIntr: cs %x, ds %x cm %x ", + regs->cstat, dstat, comm); + sii_logp->target = sc->sc_target; + sii_logp->cstat = regs->cstat; + sii_logp->dstat = dstat; + sii_logp->comm = comm; + sii_logp->msg = -1; + if (sc->sc_target >= 0) { + sii_logp->rlen = sc->sc_st[sc->sc_target].buflen; + sii_logp->dlen = sc->sc_st[sc->sc_target].dmalen; + } else { + sii_logp->rlen = 0; + sii_logp->dlen = 0; + } + if (++sii_logp >= &sii_log[NLOG]) + sii_logp = sii_log; +#endif + + regs->dstat = dstat; /* acknowledge everything */ + + if (dstat & SII_CI) { + /* deglitch cstat register */ + msg = regs->cstat; + while (msg != (cstat = regs->cstat)) + msg = cstat; + regs->cstat = cstat; /* acknowledge everything */ +#ifdef DEBUG + if (sii_logp > sii_log) + sii_logp[-1].cstat = cstat; + else + sii_log[NLOG - 1].cstat = cstat; +#endif + + /* check for a BUS RESET */ + if (cstat & SII_RST) { + printf("%s: SCSI bus reset\n", sc->sc_dev.dv_xname); + /* need to flush disconnected commands */ + for (i = 0; i < SII_NCMD; i++) { + if (!sc->sc_cmd[i]) + continue; + sii_CmdDone(sc, i, XS_DRIVER_STUFFUP /* EIO */); + } + /* rearbitrate synchronous offset */ + for (i = 0; i < SII_NCMD; i++) + sc->sc_st[i].dmaReqAck = 0; + sc->sc_target = -1; + return; + } + +#ifdef notdef + /* + * Check for a BUS ERROR. + * According to DEC, this feature doesn't really work + * and to just clear the bit if it's set. + */ + if (cstat & SII_BER) { + regs->cstat = SII_BER; + } +#endif + + /* check for state change */ + if (cstat & SII_SCH) { + sii_StateChg(sc, cstat); + comm = regs->comm; + } + } + + /* check for DMA completion */ + if (dstat & SII_DNE) { + u_int dma; + char *buf; + + /* + * There is a race condition with SII_SCH. There is a short + * window between the time a SII_SCH is seen after a disconnect + * and when the SII_SCH is cleared. A reselect can happen + * in this window and we will clear the SII_SCH without + * processing the reconnect. + */ + if (sc->sc_target < 0) { + cstat = regs->cstat; + printf("%s: target %d DNE?? dev %d,%d cs %x\n", + sc->sc_dev.dv_xname, sc->sc_target, + regs->slcsr, regs->destat, + cstat); /* XXX */ + if (cstat & SII_DST) { + sc->sc_target = regs->destat; + state = &sc->sc_st[sc->sc_target]; + state->prevComm = 0; + } else + panic("sc_target 1"); + } + state = &sc->sc_st[sc->sc_target]; + /* check for a PARITY ERROR */ + if (dstat & SII_IPE) { + state->flags |= PARITY_ERR; + printf("%s: Parity error\n", sc->sc_dev.dv_xname); + goto abort; + } + /* dmalen = amount left to transfer, i = amount transfered */ + i = state->dmalen; + state->dmalen = 0; + state->dmaCurPhase = -1; +#ifdef DEBUG + if (sii_debug > 4) { + printf("DNE: amt %d ", i); + if (!(dstat & SII_TCZ)) + printf("no TCZ?? (%d) ", regs->dmlotc); + } else if (!(dstat & SII_TCZ)) { + printf("%s: device %d: no TCZ?? (%d)\n", + sc->sc_dev.dv_xname, sc->sc_target, regs->dmlotc); + sii_DumpLog(); /* XXX */ + } +#endif + switch (comm & SII_PHASE_MSK) { + case SII_CMD_PHASE: + state->cmdlen -= i; + break; + + case SII_DATA_IN_PHASE: + /* check for more data for the same phase */ + dma = state->dmaAddr[state->dmaBufIndex]; + buf = state->buf; + state->buf += i; + state->buflen -= i; + if (state->buflen > 0 && !(dstat & SII_MIS)) { + int len; + + /* start reading next chunk */ + len = state->buflen; + if (len > SII_MAX_DMA_XFER_LENGTH) + len = SII_MAX_DMA_XFER_LENGTH; + state->dmaBufIndex = !state->dmaBufIndex; + sii_StartDMA(regs, + state->dmaCurPhase = SII_DATA_IN_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = len); + dstat &= ~(SII_IBF | SII_TBE); + } + /* copy in the data */ + sc->sii_copyfrombuf(sc, dma, buf, i); + break; + + case SII_DATA_OUT_PHASE: + state->dmaBufIndex = !state->dmaBufIndex; + state->buf += i; + state->buflen -= i; + + /* check for more data for the same phase */ + if (state->buflen <= 0 || (dstat & SII_MIS)) + break; + + /* start next chunk */ + i = state->buflen; + if (i > SII_MAX_DMA_XFER_LENGTH) { + sii_StartDMA(regs, state->dmaCurPhase = + SII_DATA_OUT_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = + SII_MAX_DMA_XFER_LENGTH); + /* prepare for next chunk */ + i -= SII_MAX_DMA_XFER_LENGTH; + if (i > SII_MAX_DMA_XFER_LENGTH) + i = SII_MAX_DMA_XFER_LENGTH; + sc->sii_copytobuf(sc, (u_char *)(state->buf + + SII_MAX_DMA_XFER_LENGTH), + state->dmaAddr[!state->dmaBufIndex], i); + } else { + sii_StartDMA(regs, state->dmaCurPhase = + SII_DATA_OUT_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = i); + } + dstat &= ~(SII_IBF | SII_TBE); + } + } + + /* check for phase change or another MsgIn/Out */ + if (dstat & (SII_MIS | SII_IBF | SII_TBE)) { + /* + * There is a race condition with SII_SCH. There is a short + * window between the time a SII_SCH is seen after a disconnect + * and when the SII_SCH is cleared. A reselect can happen + * in this window and we will clear the SII_SCH without + * processing the reconnect. + */ + if (sc->sc_target < 0) { + cstat = regs->cstat; + printf("%s: target %d MIS?? dev %d,%d cs %x ds %x\n", + sc->sc_dev.dv_xname, sc->sc_target, + regs->slcsr, regs->destat, + cstat, dstat); /* XXX */ + if (cstat & SII_DST) { + sc->sc_target = regs->destat; + state = &sc->sc_st[sc->sc_target]; + state->prevComm = 0; + } else { +#ifdef DEBUG + sii_DumpLog(); +#endif + panic("sc_target 2"); + } + } + state = &sc->sc_st[sc->sc_target]; + switch (dstat & SII_PHASE_MSK) { + case SII_CMD_PHASE: + if (state->dmaPrevPhase >= 0) { + /* restart DMA after disconnect/reconnect */ + if (state->dmaPrevPhase != SII_CMD_PHASE) { + printf("%s: device %d: DMA reselect phase doesn't match\n", + sc->sc_dev.dv_xname, sc->sc_target); + goto abort; + } + state->dmaCurPhase = SII_CMD_PHASE; + state->dmaPrevPhase = -1; + regs->dmaddrl = state->dmaAddrL; + regs->dmaddrh = state->dmaAddrH; + regs->dmlotc = state->dmaCnt; + if (state->dmaCnt & 1) + regs->dmabyte = state->dmaByte; + regs->comm = SII_DMA | SII_INXFER | + (comm & SII_STATE_MSK) | SII_CMD_PHASE; +#ifdef DEBUG + if (sii_debug > 4) + printf("Cmd dcnt %d dadr %x ", + state->dmaCnt, + (state->dmaAddrH << 16) | + state->dmaAddrL); +#endif + } else { + /* send command data */ + i = state->cmdlen; + if (i == 0) { + printf("%s: device %d: cmd count exceeded\n", + sc->sc_dev.dv_xname, sc->sc_target); + goto abort; + } + sc->sii_copytobuf(sc, state->cmd, + state->dmaAddr[0], i); + sii_StartDMA(regs, state->dmaCurPhase = + SII_CMD_PHASE, state->dmaAddr[0], + state->dmaCnt = state->dmalen = i); + } + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i); + if (dstat & (SII_CI | SII_DI)) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt %d\n", i); + else if (sii_debug > 0) + printf("sii_DoIntr: cmd wait ds %x cnt %d\n", + dstat, i); +#endif + goto again; + } + break; + + case SII_DATA_IN_PHASE: + case SII_DATA_OUT_PHASE: + if (state->cmdlen > 0) { + printf("%s: device %d: cmd 0x%x: command data not all sent (%d) 1\n", + sc->sc_dev.dv_xname, sc->sc_target, + sc->sc_cmd[sc->sc_target]->cmd[0], + state->cmdlen); + state->cmdlen = 0; +#ifdef DEBUG + sii_DumpLog(); +#endif + } + if (state->dmaPrevPhase >= 0) { + /* restart DMA after disconnect/reconnect */ + if (state->dmaPrevPhase != + (dstat & SII_PHASE_MSK)) { + printf("%s: device %d: DMA reselect phase doesn't match\n", + sc->sc_dev.dv_xname, sc->sc_target); + goto abort; + } + state->dmaCurPhase = state->dmaPrevPhase; + state->dmaPrevPhase = -1; + regs->dmaddrl = state->dmaAddrL; + regs->dmaddrh = state->dmaAddrH; + regs->dmlotc = state->dmaCnt; + if (state->dmaCnt & 1) + regs->dmabyte = state->dmaByte; + regs->comm = SII_DMA | SII_INXFER | + (comm & SII_STATE_MSK) | + state->dmaCurPhase; +#ifdef DEBUG + if (sii_debug > 4) + printf("Data %d dcnt %d dadr %x ", + state->dmaDataPhase, + state->dmaCnt, + (state->dmaAddrH << 16) | + state->dmaAddrL); +#endif + break; + } +#ifdef DEBUG + if (sii_debug > 4) { + printf("Data %d ", state->dmaDataPhase); + if (sii_debug > 5) + printf("\n"); + } +#endif + i = state->buflen; + if (i == 0) { + printf("%s: device %d: data count exceeded\n", + sc->sc_dev.dv_xname, sc->sc_target); + goto abort; + } + if (i > SII_MAX_DMA_XFER_LENGTH) + i = SII_MAX_DMA_XFER_LENGTH; + if ((dstat & SII_PHASE_MSK) == SII_DATA_IN_PHASE) { + sii_StartDMA(regs, + state->dmaCurPhase = SII_DATA_IN_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = i); + break; + } + /* start first chunk */ + if (state->flags & FIRST_DMA) { + state->flags &= ~FIRST_DMA; + sc->sii_copytobuf(sc, (u_char *)state->buf, + state->dmaAddr[state->dmaBufIndex], i); + } + sii_StartDMA(regs, + state->dmaCurPhase = SII_DATA_OUT_PHASE, + state->dmaAddr[state->dmaBufIndex], + state->dmaCnt = state->dmalen = i); + i = state->buflen - SII_MAX_DMA_XFER_LENGTH; + if (i > 0) { + /* prepare for next chunk */ + if (i > SII_MAX_DMA_XFER_LENGTH) + i = SII_MAX_DMA_XFER_LENGTH; + sc->sii_copytobuf(sc, (u_char *)(state->buf + + SII_MAX_DMA_XFER_LENGTH), + state->dmaAddr[!state->dmaBufIndex], i); + } + break; + + case SII_STATUS_PHASE: + if (state->cmdlen > 0) { + printf("%s: device %d: cmd 0x%x: command data not all sent (%d) 2\n", + sc->sc_dev.dv_xname, sc->sc_target, + sc->sc_cmd[sc->sc_target]->cmd[0], + state->cmdlen); + state->cmdlen = 0; +#ifdef DEBUG + sii_DumpLog(); +#endif + } + + /* read amount transfered if DMA didn't finish */ + if (state->dmalen > 0) { + i = state->dmalen - regs->dmlotc; + state->dmalen = 0; + state->dmaCurPhase = -1; + regs->dmlotc = 0; + regs->comm = comm & + (SII_STATE_MSK | SII_PHASE_MSK); + regs->dstat = SII_DNE; +#ifdef DEBUG + if (sii_debug > 4) + printf("DMA amt %d ", i); +#endif + switch (comm & SII_PHASE_MSK) { + case SII_DATA_IN_PHASE: + /* copy in the data */ + sc->sii_copyfrombuf(sc, + state->dmaAddr[state->dmaBufIndex], + state->buf, i); + + case SII_CMD_PHASE: + case SII_DATA_OUT_PHASE: + state->buflen -= i; + } + } + + /* read a one byte status message */ + state->statusByte = msg = + sii_GetByte(regs, SII_STATUS_PHASE, 1); + if (msg < 0) { + dstat = regs->dstat; + goto again; + } +#ifdef DEBUG + if (sii_debug > 4) + printf("Status %x ", msg); + if (sii_logp > sii_log) + sii_logp[-1].msg = msg; + else + sii_log[NLOG - 1].msg = msg; +#endif + + /* do a quick wait for COMMAND_COMPLETE */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i); + if (dstat & (SII_CI | SII_DI)) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt2 %d\n", i); +#endif + goto again; + } + break; + + case SII_MSG_IN_PHASE: + /* + * Save DMA state if DMA didn't finish. + * Be careful not to save state again after reconnect + * and see RESTORE_POINTER message. + * Note that the SII DMA address is not incremented + * as DMA proceeds. + */ + if (state->dmaCurPhase >= 0) { + /* save DMA registers */ + state->dmaPrevPhase = state->dmaCurPhase; + state->dmaCurPhase = -1; + if (dstat & SII_OBB) + state->dmaByte = regs->dmabyte; + i = regs->dmlotc; + if (i != 0) + i = state->dmaCnt - i; + /* note: no carry from dmaddrl to dmaddrh */ + state->dmaAddrL = regs->dmaddrl + i; + state->dmaAddrH = regs->dmaddrh; + state->dmaCnt = regs->dmlotc; + if (state->dmaCnt == 0) + state->dmaCnt = SII_MAX_DMA_XFER_LENGTH; + regs->comm = comm & + (SII_STATE_MSK | SII_PHASE_MSK); + regs->dstat = SII_DNE; +#ifdef DEBUG + if (sii_debug > 4) { + printf("SavP dcnt %d dadr %x ", + state->dmaCnt, + (state->dmaAddrH << 16) | + state->dmaAddrL); + if (((dstat & SII_OBB) != 0) ^ + (state->dmaCnt & 1)) + printf("OBB??? "); + } else if (sii_debug > 0) { + if (((dstat & SII_OBB) != 0) ^ + (state->dmaCnt & 1)) { + printf("sii_DoIntr: OBB??? ds %x cnt %d\n", + dstat, state->dmaCnt); + sii_DumpLog(); + } + } +#endif + } + + /* read a one byte message */ + msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 0); + if (msg < 0) { + dstat = regs->dstat; + goto again; + } +#ifdef DEBUG + if (sii_debug > 4) + printf("MsgIn %x ", msg); + if (sii_logp > sii_log) + sii_logp[-1].msg = msg; + else + sii_log[NLOG - 1].msg = msg; +#endif + + /* process message */ + switch (msg) { + case MSG_CMDCOMPLETE: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + msg = sc->sc_target; + sc->sc_target = -1; + /* + * Wait a short time for disconnect. + * Don't be fooled if SII_BER happens first. + * Note: a reselect may happen here. + */ + SII_WAIT_UNTIL(cstat, regs->cstat, + cstat & (SII_RST | SII_SCH), + SII_WAIT_COUNT, i); + if ((cstat & (SII_RST | SII_SCH | + SII_STATE_MSK)) == SII_SCH) { + regs->cstat = SII_SCH | SII_BER; + regs->comm = 0; + /* + * Double check that we didn't miss a + * state change between seeing it and + * clearing the SII_SCH bit. + */ + i = regs->cstat; + if (!(i & SII_SCH) && + (i & SII_STATE_MSK) != + (cstat & SII_STATE_MSK)) + sii_StateChg(sc, i); + } +#ifdef DEBUG + if (sii_debug > 4) + printf("cs %x\n", cstat); +#endif + sii_CmdDone(sc, msg, XS_NOERROR); + break; + + case MSG_EXTENDED: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + /* read the message length */ + msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 1); + if (msg < 0) { + dstat = regs->dstat; + goto again; + } + sc->sc_buf[1] = msg; /* message length */ + if (msg == 0) + msg = 256; + /* + * We read and acknowlege all the bytes + * except the last so we can assert ATN + * if needed before acknowledging the last. + */ + for (i = 0; i < msg; i++) { + dstat = sii_GetByte(regs, + SII_MSG_IN_PHASE, i < msg - 1); + if ((int)dstat < 0) { + dstat = regs->dstat; + goto again; + } + sc->sc_buf[i + 2] = dstat; + } + + switch (sc->sc_buf[2]) { + case MSG_EXT_MODIFY_DATA_PTR: + /* acknowledge last byte */ + regs->comm = SII_INXFER | + SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, + SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + i = (sc->sc_buf[3] << 24) | + (sc->sc_buf[4] << 16) | + (sc->sc_buf[5] << 8) | + sc->sc_buf[6]; + if (state->dmaPrevPhase >= 0) { + state->dmaAddrL += i; + state->dmaCnt -= i; + } + break; + + case MSG_EXT_SDTR_LEN: + /* + * Acknowledge last byte and + * signal a request for MSG_OUT. + */ + regs->comm = SII_INXFER | SII_ATN | + SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, + SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + sii_DoSync(sc, state); + break; + + default: + reject: + /* + * Acknowledge last byte and + * signal a request for MSG_OUT. + */ + regs->comm = SII_INXFER | SII_ATN | + SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, + SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + + /* wait for MSG_OUT phase */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_TBE, + SII_WAIT_COUNT, i); + + /* send a reject message */ + regs->data = MSG_MESSAGE_REJECT; + regs->comm = SII_INXFER | + (regs->cstat & SII_STATE_MSK) | + SII_MSG_OUT_PHASE; + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, + SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + } + break; + + case MSG_SAVEDATAPOINTER: + case MSG_RESTOREPOINTERS: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + /* wait a short time for another msg */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & (SII_CI | SII_DI), + SII_WAIT_COUNT, i); + if (dstat & (SII_CI | SII_DI)) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt %d\n", i); +#endif + goto again; + } + break; + + case MSG_DISCONNECT: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + state->prevComm = comm; +#ifdef DEBUG + if (sii_debug > 4) + printf("disconn %d ", sc->sc_target); +#endif + /* + * Wait a short time for disconnect. + * Don't be fooled if SII_BER happens first. + * Note: a reselect may happen here. + */ + SII_WAIT_UNTIL(cstat, regs->cstat, + cstat & (SII_RST | SII_SCH), + SII_WAIT_COUNT, i); + if ((cstat & (SII_RST | SII_SCH | + SII_STATE_MSK)) != SII_SCH) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt %d\n", i); +#endif + dstat = regs->dstat; + goto again; + } + regs->cstat = SII_SCH | SII_BER; + regs->comm = 0; + sc->sc_target = -1; + /* + * Double check that we didn't miss a state + * change between seeing it and clearing + * the SII_SCH bit. + */ + i = regs->cstat; + if (!(i & SII_SCH) && (i & SII_STATE_MSK) != + (cstat & SII_STATE_MSK)) + sii_StateChg(sc, i); + break; + + case MSG_MESSAGE_REJECT: + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + printf("%s: device %d: message reject.\n", + sc->sc_dev.dv_xname, sc->sc_target); + break; + + default: + if (!(msg & MSG_IDENTIFYFLAG)) { + printf("%s: device %d: couldn't handle " + "message 0x%x... rejecting.\n", + sc->sc_dev.dv_xname, sc->sc_target, + msg); +#ifdef DEBUG + sii_DumpLog(); +#endif + goto reject; + } + /* acknowledge last byte */ + regs->comm = SII_INXFER | SII_MSG_IN_PHASE | + (comm & SII_STATE_MSK); + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & SII_DNE, SII_WAIT_COUNT, i); + regs->dstat = SII_DNE; + /* may want to check LUN some day */ + /* wait a short time for another msg */ + SII_WAIT_UNTIL(dstat, regs->dstat, + dstat & (SII_CI | SII_DI), + SII_WAIT_COUNT, i); + if (dstat & (SII_CI | SII_DI)) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt %d\n", i); +#endif + goto again; + } + } + break; + + case SII_MSG_OUT_PHASE: +#ifdef DEBUG + if (sii_debug > 4) + printf("MsgOut\n"); +#endif + printf("MsgOut %x\n", state->flags); /* XXX */ + + /* + * Check for parity error. + * Hardware will automatically set ATN + * to request the device for a MSG_OUT phase. + */ + if (state->flags & PARITY_ERR) { + state->flags &= ~PARITY_ERR; + regs->data = MSG_PARITY_ERROR; + } else + regs->data = MSG_NOOP; + regs->comm = SII_INXFER | (comm & SII_STATE_MSK) | + SII_MSG_OUT_PHASE; + + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE, + SII_WAIT_COUNT, i); +#ifdef DEBUG + if (sii_debug > 4) + printf("ds %x i %d\n", dstat, i); +#endif + /* just clear the DNE bit and check errors later */ + if (dstat & SII_DNE) { + regs->dstat = SII_DNE; + } + break; + + default: + printf("%s: Couldn't handle phase %d... ignoring.\n", + sc->sc_dev.dv_xname, dstat & SII_PHASE_MSK); + } + } + +#ifdef DEBUG + if (sii_debug > 3) + printf("\n"); +#endif + /* + * Check to make sure we won't be interrupted again. + * Deglitch dstat register. + */ + msg = regs->dstat; + while (msg != (dstat = regs->dstat)) + msg = dstat; + if (dstat & (SII_CI | SII_DI)) + goto again; + + if (sc->sc_target < 0) { + /* look for another device that is ready */ + for (i = 0; i < SII_NCMD; i++) { + /* don't restart a disconnected command */ + if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm) + continue; + sii_StartCmd(sc, i); + break; + } + } + return; + +abort: + /* jump here to abort the current command */ + printf("%s: device %d: current command terminated\n", + sc->sc_dev.dv_xname, sc->sc_target); +#ifdef DEBUG + sii_DumpLog(); +#endif + + if ((cstat = regs->cstat) & SII_CON) { + /* try to send an abort msg for awhile */ + regs->dstat = SII_DNE; + regs->data = MSG_ABORT; + regs->comm = SII_INXFER | SII_ATN | (cstat & SII_STATE_MSK) | + SII_MSG_OUT_PHASE; + SII_WAIT_UNTIL(dstat, regs->dstat, + (dstat & (SII_DNE | SII_PHASE_MSK)) == + (SII_DNE | SII_MSG_OUT_PHASE), + 2 * SII_WAIT_COUNT, i); +#ifdef DEBUG + if (sii_debug > 0) + printf("Abort: cs %x ds %x i %d\n", cstat, dstat, i); +#endif + if ((dstat & (SII_DNE | SII_PHASE_MSK)) == + (SII_DNE | SII_MSG_OUT_PHASE)) { + /* disconnect if command in progress */ + regs->comm = SII_DISCON; + SII_WAIT_UNTIL(cstat, regs->cstat, + !(cstat & SII_CON), SII_WAIT_COUNT, i); + } + } else { +#ifdef DEBUG + if (sii_debug > 0) + printf("Abort: cs %x\n", cstat); +#endif + } + regs->cstat = 0xffff; + regs->dstat = 0xffff; + regs->comm = 0; + + i = sc->sc_target; + sc->sc_target = -1; + sii_CmdDone(sc, i, XS_DRIVER_STUFFUP /* EIO */); +#ifdef DEBUG + if (sii_debug > 4) + printf("sii_DoIntr: after CmdDone target %d\n", sc->sc_target); +#endif +} + +void +sii_StateChg(sc, cstat) + struct sii_softc *sc; + u_int cstat; +{ + SIIRegs *regs = sc->sc_regs; + State *state; + int i; + +#ifdef DEBUG + if (sii_debug > 4) + printf("SCH: "); +#endif + + switch (cstat & SII_STATE_MSK) { + case 0: + /* disconnect */ + i = sc->sc_target; + sc->sc_target = -1; +#ifdef DEBUG + if (sii_debug > 4) + printf("disconn %d ", i); +#endif + if (i >= 0 && !sc->sc_st[i].prevComm) { + printf("%s: device %d: spurrious disconnect (%d)\n", + sc->sc_dev.dv_xname, i, regs->slcsr); + sc->sc_st[i].prevComm = 0; + } + break; + + case SII_CON: + /* connected as initiator */ + i = regs->slcsr; + if (sc->sc_target == i) + break; + printf("%s: device %d: connect to device %d??\n", + sc->sc_dev.dv_xname, sc->sc_target, i); + sc->sc_target = i; + break; + + case SII_DST: + /* + * Wait for CON to become valid, + * chip is slow sometimes. + */ + SII_WAIT_UNTIL(cstat, regs->cstat, + cstat & SII_CON, SII_WAIT_COUNT, i); + if (!(cstat & SII_CON)) + panic("sii resel"); + /* FALLTHROUGH */ + + case SII_CON | SII_DST: + /* + * Its a reselection. Save the ID and wait for + * interrupts to tell us what to do next + * (should be MSG_IN of IDENTIFY). + * NOTE: sc_target may be >= 0 if we were in + * the process of trying to start a command + * and were reselected before the select + * command finished. + */ + sc->sc_target = i = regs->destat; + state = &sc->sc_st[i]; + regs->comm = SII_CON | SII_DST | SII_MSG_IN_PHASE; + regs->dmctrl = state->dmaReqAck; + if (!state->prevComm) { + printf("%s: device %d: spurious reselection\n", + sc->sc_dev.dv_xname, i); + break; + } + state->prevComm = 0; +#ifdef DEBUG + if (sii_debug > 4) + printf("resel %d ", sc->sc_target); +#endif + break; + +#ifdef notyet + case SII_DST | SII_TGT: + case SII_CON | SII_DST | SII_TGT: + /* connected as target */ + printf("%s: Selected by device %d as target!!\n", + sc->sc_dev.dv_xname, regs->destat); + regs->comm = SII_DISCON; + SII_WAIT_UNTIL(!(regs->cstat & SII_CON), + SII_WAIT_COUNT, i); + regs->cstat = 0xffff; + regs->dstat = 0xffff; + regs->comm = 0; + break; +#endif + + default: + printf("%s: Unknown state change (cs %x)!!\n", + sc->sc_dev.dv_xname, cstat); +#ifdef DEBUG + sii_DumpLog(); +#endif + } +} + +/* + * Read one byte of data. + * If 'ack' is true, acknowledge the byte. + */ +int +sii_GetByte(regs, phase, ack) + SIIRegs *regs; + int phase, ack; +{ + u_int dstat; + u_int state; + int i; + int data; + + dstat = regs->dstat; + state = regs->cstat & SII_STATE_MSK; + i = -1; + if (!(dstat & SII_IBF) || (dstat & SII_MIS)) { + regs->comm = state | phase; + /* wait a short time for IBF */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_IBF, + SII_WAIT_COUNT, i); +#ifdef DEBUG + if (!(dstat & SII_IBF)) + printf("status no IBF\n"); +#endif + } + if (dstat & SII_DNE) { /* XXX */ + printf("sii_GetByte: DNE set 5\n"); +#ifdef DEBUG + sii_DumpLog(); +#endif + regs->dstat = SII_DNE; + } + data = regs->data; + /* check for parity error */ + if (dstat & SII_IPE) { +#ifdef DEBUG + if (sii_debug > 4) + printf("cnt0 %d\n", i); +#endif + printf("sii_GetByte: data %x ?? ds %x cm %x i %d\n", + data, dstat, regs->comm, i); /* XXX */ + data = -1; + ack = 1; + } + + if (ack) { + regs->comm = SII_INXFER | state | phase; + + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE, + SII_WAIT_COUNT, i); + + /* clear the DNE */ + if (dstat & SII_DNE) { + regs->dstat = SII_DNE; + } + } + + return (data); +} + +/* + * Exchange messages to initiate synchronous data transfers. + */ +void +sii_DoSync(sc, state) + struct sii_softc *sc; + State *state; +{ + SIIRegs *regs = sc->sc_regs; + u_int dstat, comm; + int i, j; + u_int len; + +#ifdef DEBUG + if (sii_debug) + printf("sii_DoSync: len %d per %d req/ack %d\n", + sc->sc_buf[1], sc->sc_buf[3], sc->sc_buf[4]); +#endif + + /* SII chip can only handle a minimum transfer period of ??? */ + if (sc->sc_buf[3] < 64) + sc->sc_buf[3] = 64; + /* SII chip can only handle a maximum REQ/ACK offset of 3 */ + len = sc->sc_buf[4]; + if (len > 3) + len = 3; + + sc->sc_buf[0] = MSG_EXTENDED; + sc->sc_buf[1] = MSG_EXT_SDTR_LEN; + sc->sc_buf[2] = MSG_EXT_SDTR; + sc->sc_buf[4] = len; +#if 1 + comm = SII_INXFER | SII_ATN | SII_MSG_OUT_PHASE | + (regs->cstat & SII_STATE_MSK); + regs->comm = comm & ~SII_INXFER; + for (j = 0; j < 5; j++) { + /* wait for target to request the next byte */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_TBE, + SII_WAIT_COUNT, i); + if (!(dstat & SII_TBE) || + (dstat & SII_PHASE_MSK) != SII_MSG_OUT_PHASE) { + printf("sii_DoSync: TBE? ds %x cm %x i %d\n", + dstat, comm, i); /* XXX */ + return; + } + + /* the last message byte should have ATN off */ + if (j == 4) + comm &= ~SII_ATN; + + regs->data = sc->sc_buf[j]; + regs->comm = comm; + + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE, + SII_WAIT_COUNT, i); + + if (!(dstat & SII_DNE)) { + printf("sii_DoSync: DNE? ds %x cm %x i %d\n", + dstat, comm, i); /* XXX */ + return; + } + + /* clear the DNE, other errors handled later */ + regs->dstat = SII_DNE; + } +#else /* 0 */ + sc->sii_copytobuf(sc, sc->sc_buf, SII_BUF_ADDR(sc), 5); + printf("sii_DoSync: %x %x %x ds %x\n", + ((u_short *)sc->sc_buf)[0], ((u_short *)sc->sc_buf)[1], + ((u_short *)sc->sc_buf)[2], regs->dstat); /* XXX */ + regs->dmaddrl = SII_BUF_ADDR(sc); + regs->dmaddrh = SII_BUF_ADDR(sc) >> 16; + regs->dmlotc = 5; + regs->comm = SII_DMA | SII_INXFER | SII_ATN | + (regs->cstat & SII_STATE_MSK) | SII_MSG_OUT_PHASE; + + /* wait a short time for XFER complete */ + SII_WAIT_UNTIL(dstat, regs->dstat, + (dstat & (SII_DNE | SII_TCZ)) == (SII_DNE | SII_TCZ), + SII_WAIT_COUNT, i); + + if ((dstat & (SII_DNE | SII_TCZ)) != (SII_DNE | SII_TCZ)) { + printf("sii_DoSync: ds %x cm %x i %d lotc %d\n", + dstat, regs->comm, i, regs->dmlotc); /* XXX */ + sii_DumpLog(); /* XXX */ + return; + } + /* clear the DNE, other errors handled later */ + regs->dstat = SII_DNE; +#endif /* 0 */ + +#if 0 + SII_WAIT_UNTIL(dstat, regs->dstat, dstat & (SII_CI | SII_DI), + SII_WAIT_COUNT, i); + printf("sii_DoSync: ds %x cm %x i %d lotc %d\n", + dstat, regs->comm, i, regs->dmlotc); /* XXX */ +#endif + + state->dmaReqAck = len; +} + +/* + * Issue the sequence of commands to the controller to start DMA. + * NOTE: the data buffer should be word-aligned for DMA out. + */ +void +sii_StartDMA(regs, phase, dmaAddr, size) + SIIRegs *regs; /* which SII to use */ + int phase; /* phase to send/receive data */ + u_int dmaAddr; /* DMA buffer offset */ + int size; /* # of bytes to transfer */ +{ + + if (regs->dstat & SII_DNE) { /* XXX */ + regs->dstat = SII_DNE; + printf("sii_StartDMA: DNE set\n"); +#ifdef DEBUG + sii_DumpLog(); +#endif + } + regs->dmaddrl = dmaAddr; + regs->dmaddrh = dmaAddr >> 16; + regs->dmlotc = size; + regs->comm = SII_DMA | SII_INXFER | (regs->cstat & SII_STATE_MSK) | + phase; + +#ifdef DEBUG + if (sii_debug > 5) { + printf("sii_StartDMA: cs 0x%x, ds 0x%x, cm 0x%x, size %d\n", + regs->cstat, regs->dstat, regs->comm, size); + } +#endif +} + +/* + * Call the device driver's 'done' routine to let it know the command is done. + * The 'done' routine may try to start another command. + * To be fair, we should start pending commands for other devices + * before allowing the same device to start another command. + */ +void +sii_CmdDone(sc, target, error) + struct sii_softc *sc; /* which SII to use */ + int target; /* which device is done */ + int error; /* error code if any errors */ +{ + ScsiCmd *scsicmd; + struct scsi_xfer *xs; + int i; + + splassert(IPL_BIO); + + scsicmd = sc->sc_cmd[target]; +#ifdef DIAGNOSTIC + if (target < 0 || !scsicmd) + panic("sii_CmdDone"); +#endif + sc->sc_cmd[target] = NULL; +#ifdef DEBUG + if (sii_debug > 1) { + printf("sii_CmdDone: %s target %d cmd 0x%x err %d resid %d\n", + sc->sc_dev.dv_xname, + target, scsicmd->cmd[0], error, sc->sc_st[target].buflen); + } +#endif + + /* look for another device that is ready */ + for (i = 0; i < SII_NCMD; i++) { + /* don't restart a disconnected command */ + if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm) + continue; + sii_StartCmd(sc, i); + break; + } + + xs = sc->sc_xs[target]; + xs->status = sc->sc_st[target].statusByte; + xs->error = error; + xs->resid = sc->sc_st[target].buflen; + xs->flags |= ITSDONE; + scsi_done(xs); +} + +#ifdef DEBUG +void +sii_DumpLog() +{ + struct sii_log *lp; + + printf("sii: cmd 0x%x bn %d cnt %d\n", sii_debug_cmd, sii_debug_bn, + sii_debug_sz); + lp = sii_logp; + do { + printf("target %d cs %x ds %x cm %x msg %x rlen %x dlen %x\n", + lp->target, lp->cstat, lp->dstat, lp->comm, lp->msg, + lp->rlen, lp->dlen); + if (++lp >= &sii_log[NLOG]) + lp = sii_log; + } while (lp != sii_logp); +} +#endif diff --git a/sys/arch/vax/dec/siireg.h b/sys/arch/vax/dec/siireg.h new file mode 100644 index 00000000000..c83f0df9e37 --- /dev/null +++ b/sys/arch/vax/dec/siireg.h @@ -0,0 +1,235 @@ +/* $OpenBSD: siireg.h,v 1.1 2008/08/18 23:19:22 miod Exp $ */ +/* $NetBSD: siireg.h,v 1.2 2006/07/29 19:10:57 ad Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ralph Campbell. + * + * 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. 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. + * + * @(#)siireg.h 8.1 (Berkeley) 6/10/93 + * + * sii.h -- + * + * SII registers. + * + * Copyright (C) 1989 Digital Equipment Corporation. + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appears in all copies. + * Digital Equipment Corporation makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * from: Header: /sprite/src/kernel/dev/ds3100.md/RCS/sii.h, + * v 1.2 89/08/15 19:53:04 rab Exp SPRITE (DECWRL) + */ + +#ifndef _SII +#define _SII + +/* + * SII hardware registers + */ +typedef volatile struct { + u_short sdb; /* SCSI Data Bus and Parity */ + u_short pad0; + u_short sc1; /* SCSI Control Signals One */ + u_short pad1; + u_short sc2; /* SCSI Control Signals Two */ + u_short pad2; + u_short csr; /* Control/Status register */ + u_short pad3; + u_short id; /* Bus ID register */ + u_short pad4; + u_short slcsr; /* Select Control and Status Register */ + u_short pad5; + u_short destat; /* Selection Detector Status Register */ + u_short pad6; + u_short dstmo; /* DSSI Timeout Register */ + u_short pad7; + u_short data; /* Data Register */ + u_short pad8; + u_short dmctrl; /* DMA Control Register */ + u_short pad9; + u_short dmlotc; /* DMA Length of Transfer Counter */ + u_short pad10; + u_short dmaddrl; /* DMA Address Register Low */ + u_short pad11; + u_short dmaddrh; /* DMA Address Register High */ + u_short pad12; + u_short dmabyte; /* DMA Initial Byte Register */ + u_short pad13; + u_short stlp; /* DSSI Short Target List Pointer */ + u_short pad14; + u_short ltlp; /* DSSI Long Target List Pointer */ + u_short pad15; + u_short ilp; /* DSSI Initiator List Pointer */ + u_short pad16; + u_short dsctrl; /* DSSI Control Register */ + u_short pad17; + u_short cstat; /* Connection Status Register */ + u_short pad18; + u_short dstat; /* Data Transfer Status Register */ + u_short pad19; + u_short comm; /* Command Register */ + u_short pad20; + u_short dictrl; /* Diagnostic Control Register */ + u_short pad21; + u_short clock; /* Diagnostic Clock Register */ + u_short pad22; + u_short bhdiag; /* Bus Handler Diagnostic Register */ + u_short pad23; + u_short sidiag; /* SCSI IO Diagnostic Register */ + u_short pad24; + u_short dmdiag; /* Data Mover Diagnostic Register */ + u_short pad25; + u_short mcdiag; /* Main Control Diagnostic Register */ + u_short pad26; +} SIIRegs; + +/* + * SC1 - SCSI Control Signals One + */ +#define SII_SC1_MSK 0x1ff /* All possible signals on the bus */ +#define SII_SC1_SEL 0x80 /* SCSI SEL signal active on bus */ +#define SII_SC1_ATN 0x08 /* SCSI ATN signal active on bus */ + +/* + * SC2 - SCSI Control Signals Two + */ +#define SII_SC2_IGS 0x8 /* SCSI drivers for initiator mode */ + +/* + * CSR - Control/Status Register + */ +#define SII_HPM 0x10 /* SII in on an arbitrated SCSI bus */ +#define SII_RSE 0x08 /* 1 = respond to reselections */ +#define SII_SLE 0x04 /* 1 = respond to selections */ +#define SII_PCE 0x02 /* 1 = report parity errors */ +#define SII_IE 0x01 /* 1 = enable interrupts */ + +/* + * ID - Bus ID Register + */ +#define SII_ID_IO 0x8000 /* I/O */ + +/* + * DESTAT - Selection Detector Status Register + */ +#define SII_IDMSK 0x7 /* ID of target reselected the SII */ + +/* + * DMCTRL - DMA Control Register + */ +#define SII_ASYNC 0x00 /* REQ/ACK Offset for async mode */ +#define SII_SYNC 0x03 /* REQ/ACK Offset for sync mode */ + +/* + * DMLOTC - DMA Length Of Transfer Counter + */ +#define SII_TCMSK 0x1fff /* transfer count mask */ + +/* + * CSTAT - Connection Status Register + */ +#define SII_CI 0x8000 /* composite interrupt bit for CSTAT */ +#define SII_DI 0x4000 /* composite interrupt bit for DSTAT */ +#define SII_RST 0x2000 /* 1 if reset is asserted on SCSI bus */ +#define SII_BER 0x1000 /* Bus error */ +#define SII_OBC 0x0800 /* Out_en Bit Cleared (DSSI mode) */ +#define SII_TZ 0x0400 /* Target pointer Zero (STLP or LTLP is zero) */ +#define SII_BUF 0x0200 /* Buffer service - outbound pkt to non-DSSI */ +#define SII_LDN 0x0100 /* List element Done */ +#define SII_SCH 0x0080 /* State Change */ +#define SII_CON 0x0040 /* SII is Connected to another device */ +#define SII_DST 0x0020 /* SII was Destination of current transfer */ +#define SII_TGT 0x0010 /* SII is operating as a Target */ +#define SII_STATE_MSK 0x0070 /* State Mask */ +#define SII_SWA 0x0008 /* Selected With Attention */ +#define SII_SIP 0x0004 /* Selection In Progress */ +#define SII_LST 0x0002 /* Lost arbitration */ + +/* + * DSTAT - Data Transfer Status Register + */ +#define SII_DNE 0x2000 /* DMA transfer Done */ +#define SII_TCZ 0x1000 /* Transfer Count register is Zero */ +#define SII_TBE 0x0800 /* Transmit Buffer Empty */ +#define SII_IBF 0x0400 /* Input Buffer Full */ +#define SII_IPE 0x0200 /* Incoming Parity Error */ +#define SII_OBB 0x0100 /* Odd Byte Boundry */ +#define SII_MIS 0x0010 /* Phase Mismatch */ +#define SII_ATN 0x0008 /* ATN set by initiator if in Target mode */ +#define SII_MSG 0x0004 /* current bus state of MSG */ +#define SII_CD 0x0002 /* current bus state of C/D */ +#define SII_IO 0x0001 /* current bus state of I/O */ +#define SII_PHASE_MSK 0x0007 /* Phase Mask */ + +/* + * The different phases. + */ +#define SII_MSG_IN_PHASE 0x7 +#define SII_MSG_OUT_PHASE 0x6 +#define SII_STATUS_PHASE 0x3 +#define SII_CMD_PHASE 0x2 +#define SII_DATA_IN_PHASE 0x1 +#define SII_DATA_OUT_PHASE 0x0 + +/* + * COMM - Command Register + */ +#define SII_DMA 0x8000 /* DMA mode */ +#define SII_DO_RST 0x4000 /* Assert reset on SCSI bus for 25 usecs */ +#define SII_RSL 0x1000 /* 0 = select, 1 = reselect desired device */ + +/* Commands: I - Initiator, T - Target, D - Disconnected */ +#define SII_INXFER 0x0800 /* Information Transfer command (I,T) */ +#define SII_SELECT 0x0400 /* Select command (D) */ +#define SII_REQDATA 0x0200 /* Request Data command (T) */ +#define SII_DISCON 0x0100 /* Disconnect command (I,T,D) */ +#define SII_CHRESET 0x0080 /* Chip Reset command (I,T,D) */ + +/* Command state bits same as connection status register */ +/* Command phase bits same as data transfer status register */ + +/* + * DICTRL - Diagnostic Control Register + */ +#define SII_PRE 0x4 /* Enable the SII to drive the SCSI bus */ + +#define SII_WAIT_COUNT 10000 /* Delay count used for the SII chip */ +/* + * Max DMA transfer length for SII + * The SII chip only has a 13 bit counter. If 8192 is used as the max count, + * you can't tell the difference between a count of zero and 8192. + * 8190 is used instead of 8191 so the count is even. + */ +#define SII_MAX_DMA_XFER_LENGTH 8192 + +#endif /* _SII */ diff --git a/sys/arch/vax/dec/siivar.h b/sys/arch/vax/dec/siivar.h new file mode 100644 index 00000000000..82a92518d19 --- /dev/null +++ b/sys/arch/vax/dec/siivar.h @@ -0,0 +1,71 @@ +/* $OpenBSD: siivar.h,v 1.1 2008/08/18 23:19:22 miod Exp $ */ +/* $NetBSD: siivar.h,v 1.6 2000/06/02 20:16:51 mhitch Exp $ */ + +#ifndef _SIIVAR_H +#define _SIIVAR_H + +/* + * This structure contains information that a SCSI interface controller + * needs to execute a SCSI command. + */ +typedef struct ScsiCmd { + int unit; /* unit number passed to device done routine */ + int flags; /* control flags for this command (see below) */ + int buflen; /* length of the data buffer in bytes */ + char *buf; /* pointer to data buffer for this command */ + int cmdlen; /* length of data in cmdbuf */ + u_char *cmd; /* buffer for the SCSI command */ + int error; /* compatibility hack for new scsi */ + int lun; /* LUN for MI SCSI */ +} ScsiCmd; + +typedef struct scsi_state { + int statusByte; /* status byte returned during STATUS_PHASE */ + int dmaDataPhase; /* which data phase to expect */ + int dmaCurPhase; /* SCSI phase if DMA is in progress */ + int dmaPrevPhase; /* SCSI phase of DMA suspended by disconnect */ + u_int dmaAddr[2]; /* DMA buffer memory offsets */ + int dmaBufIndex; /* which of the above is currently in use */ + int dmalen; /* amount to transfer in this chunk */ + int cmdlen; /* total remaining amount of cmd to transfer */ + u_char *cmd; /* current pointer within scsicmd->cmd */ + int buflen; /* total remaining amount of data to transfer */ + char *buf; /* current pointer within scsicmd->buf */ + u_short flags; /* see below */ + u_short prevComm; /* command reg before disconnect */ + u_short dmaCtrl; /* DMA control register if disconnect */ + u_short dmaAddrL; /* DMA address register if disconnect */ + u_short dmaAddrH; /* DMA address register if disconnect */ + u_short dmaCnt; /* DMA count if disconnect */ + u_short dmaByte; /* DMA byte if disconnect on odd boundary */ + u_short dmaReqAck; /* DMA synchronous xfer offset or 0 if async */ +} State; + +/* state flags */ +#define FIRST_DMA 0x01 /* true if no data DMA started yet */ +#define PARITY_ERR 0x02 /* true if parity error seen */ + +#define SII_NCMD 8 +struct sii_softc { + struct device sc_dev; /* us as a device */ + struct scsi_link sc_link; /* scsi link struct */ + ScsiCmd sc_cmd_fake[SII_NCMD]; /* XXX - hack!!! */ + struct scsi_xfer *sc_xs[SII_NCMD]; /* XXX - hack!!! */ + SIIRegs *sc_regs; /* HW address of SII controller chip */ + int sc_flags; + int sc_target; /* target SCSI ID if connected */ + int sc_hostid; + ScsiCmd *sc_cmd[SII_NCMD]; /* active command indexed by ID */ + void (*sii_copytobuf)(void *, u_char *, u_int, int); + void (*sii_copyfrombuf)(void *, u_int, u_char *, int); + + State sc_st[SII_NCMD]; /* state info for each active command */ + + u_char sc_buf[258]; /* used for extended messages */ +}; + +/* Machine-independent back-end attach entry point */ +void sii_attach(struct sii_softc *sc); +int sii_intr(void *sc); + +#endif /* _SIIVAR_H */ |