summaryrefslogtreecommitdiff
path: root/sys/arch/pmax/dev/sii.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/arch/pmax/dev/sii.c
initial import of NetBSD tree
Diffstat (limited to 'sys/arch/pmax/dev/sii.c')
-rw-r--r--sys/arch/pmax/dev/sii.c1851
1 files changed, 1851 insertions, 0 deletions
diff --git a/sys/arch/pmax/dev/sii.c b/sys/arch/pmax/dev/sii.c
new file mode 100644
index 00000000000..4cd2086dd7b
--- /dev/null
+++ b/sys/arch/pmax/dev/sii.c
@@ -0,0 +1,1851 @@
+/* $NetBSD: sii.c,v 1.8 1995/09/13 19:35:58 jonathan 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 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. 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.
+ *
+ * @(#)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)";
+ */
+
+#include "sii.h"
+#if NSII > 0
+/*
+ * SCSI interface driver
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/dkstat.h>
+#include <sys/buf.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+
+#ifdef notyet
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#endif
+
+#include <machine/autoconf.h>
+#include <machine/machConst.h>
+#include <pmax/dev/device.h>
+#include <pmax/dev/scsi.h>
+#include <pmax/dev/siireg.h>
+
+#include <pmax/pmax/kn01.h>
+
+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_short *dmaAddr[2]; /* DMA buffer memory address */
+ 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 7
+struct siisoftc {
+ struct device sc_dev; /* us as a device */
+ SIIRegs *sc_regs; /* HW address of SII controller chip */
+ int sc_flags;
+ int sc_target; /* target SCSI ID if connected */
+ ScsiCmd *sc_cmd[SII_NCMD]; /* active command indexed by ID */
+ State sc_st[SII_NCMD]; /* state info for each active command */
+#ifdef NEW_SCSI
+ struct scsi_link sc_link; /* scsi lint struct */
+#endif
+};
+
+/*
+ * Device definition for autoconfiguration.
+ *
+ */
+int siimatch __P((struct device * parent, void *cfdata, void *aux));
+void siiattach __P((struct device *parent, struct device *self, void *aux));
+int siiprint(void*, char*);
+
+int sii_doprobe __P((void *addr, int unit, int flags, int pri,
+ struct device *self));
+int siiintr __P((void *sc));
+
+extern struct cfdriver siicd;
+struct cfdriver siicd = {
+ NULL, "sii", siimatch, siiattach, DV_DULL, sizeof(struct siisoftc)
+};
+
+#ifdef USE_NEW_SCSI
+/* Glue to the machine-independent scsi */
+struct scsi_adapter asc_switch = {
+ NULL, /* XXX - asc_scsi_cmd */
+#if 0
+/*XXX*/ minphys, /* no max transfer size; DMA engine deals */
+#else
+ SII_MAX_DMA_XFER_LENGTH,
+#endif
+ NULL,
+ NULL,
+};
+
+struct scsi_device sii_dev = {
+/*XXX*/ NULL, /* Use default error handler */
+/*XXX*/ NULL, /* have a queue, served by this */
+/*XXX*/ NULL, /* have no async handler */
+/*XXX*/ NULL, /* Use default 'done' routine */
+};
+#endif
+
+/*
+ * Definition of the controller for the old auto-configuration program.
+ */
+void siistart();
+struct pmax_driver siidriver = {
+ "sii", NULL, siistart, 0,
+};
+
+/*
+ * 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) { \
+ register 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
+
+u_char sii_buf[256]; /* used for extended messages */
+
+#define NORESET 0
+#define RESET 1
+#define NOWAIT 0
+#define WAIT 1
+
+/* define a safe address in the SCSI buffer for doing status & message DMA */
+#define SII_BUF_ADDR (MACH_PHYS_TO_UNCACHED(KN01_SYS_SII_B_START) \
+ + SII_MAX_DMA_XFER_LENGTH * 14)
+
+static void sii_Reset();
+static void sii_StartCmd();
+static void sii_CmdDone();
+static void sii_DoIntr();
+static void sii_StateChg();
+static void sii_DoSync();
+static void sii_StartDMA();
+static int sii_GetByte();
+
+
+/*
+ * Match driver based on name
+ */
+int
+siimatch(parent, match, aux)
+ struct device *parent;
+ void *match;
+ void *aux;
+{
+ struct cfdata *cf = match;
+ struct confargs *ca = aux;
+
+ if (!BUS_MATCHNAME(ca, "sii") && !BUS_MATCHNAME(ca, "PMAZ-AA "))
+ return (0);
+
+ /* XXX check for bad address */
+ /* XXX kn01s have exactly one SII. Does any other machine use them? */
+ return (1);
+}
+
+void
+siiattach(parent, self, aux)
+ struct device *parent;
+ struct device *self;
+ void *aux;
+{
+ register struct confargs *ca = aux;
+ register struct siisoftc *sc = (struct siisoftc *) self;
+ register void *siiaddr;
+ register int i;
+
+ siiaddr = (void*)MACH_PHYS_TO_UNCACHED(BUS_CVTADDR(ca));
+
+ sc->sc_regs = (SIIRegs *)siiaddr;
+ sc->sc_flags = sc->sc_dev.dv_cfdata->cf_flags;
+ 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] = (u_short *)
+ MACH_PHYS_TO_UNCACHED(KN01_SYS_SII_B_START) +
+ 2 * SII_MAX_DMA_XFER_LENGTH * i;
+ sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] +
+ SII_MAX_DMA_XFER_LENGTH;
+ }
+
+ /* Hack for old-sytle SCSI-device probe */
+ (void) pmax_add_scsi(&siidriver, sc->sc_dev.dv_unit);
+
+ sii_Reset(sc, RESET);
+
+ /*priority = ca->ca_slot;*/
+ /* tie pseudo-slot to device */
+ BUS_INTR_ESTABLISH(ca, siiintr, sc);
+ printf("\n");
+}
+
+/*
+ * Start activity on a SCSI device.
+ * We maintain information on each device separately since devices can
+ * connect/disconnect during an operation.
+ */
+void
+siistart(scsicmd)
+ register ScsiCmd *scsicmd; /* command to start */
+{
+ register struct pmax_scsi_device *sdp = scsicmd->sd;
+ register struct siisoftc *sc = siicd.cd_devs[sdp->sd_ctlr];
+ int s;
+
+ s = splbio();
+ /*
+ * Check if another command is already in progress.
+ * We may have to change this if we allow SCSI devices with
+ * separate LUNs.
+ */
+ if (sc->sc_cmd[sdp->sd_drive]) {
+ printf("%s: device %s busy at start\n", sc->sc_dev.dv_xname,
+ sdp->sd_driver->d_name);
+ (*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY,
+ scsicmd->buflen, 0);
+ splx(s);
+ }
+ sc->sc_cmd[sdp->sd_drive] = scsicmd;
+ sii_StartCmd(sc, sdp->sd_drive);
+ splx(s);
+}
+
+/*
+ * Check to see if any SII chips have pending interrupts
+ * and process as appropriate.
+ */
+int
+siiintr(xxxsc)
+ void *xxxsc;
+{
+ register struct siisoftc *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);
+}
+
+/*
+ * 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.
+ */
+static void
+sii_Reset(sc, reset)
+ register struct siisoftc* sc;
+ int reset; /* TRUE => reset SCSI bus */
+{
+ register 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;
+ /*
+ * SII is always ID 7.
+ */
+ regs->id = SII_ID_IO | 7;
+ /*
+ * Enable SII to drive the SCSI bus.
+ */
+ regs->dictrl = SII_PRE;
+ regs->dmctrl = 0;
+
+ if (reset) {
+ register 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;
+ MachEmptyWriteBuffer();
+ 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;
+ MachEmptyWriteBuffer();
+}
+
+/*
+ * Start a SCSI command by sending the cmd data
+ * to a SCSI controller via the SII.
+ * Call the device done proceedure if it can't be started.
+ * NOTE: we should be called with interrupts disabled.
+ */
+static void
+sii_StartCmd(sc, target)
+ register struct siisoftc *sc; /* which SII to use */
+ register int target; /* which command to start */
+{
+ register SIIRegs *regs;
+ register ScsiCmd *scsicmd;
+ register State *state;
+ register u_int status;
+ int error, retval;
+
+ /* 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->dmaDataPhase =
+ (scsicmd->flags & SCSICMD_DATA_TO_DEVICE) ?
+ SII_DATA_OUT_PHASE : SII_DATA_IN_PHASE;
+ state->buf = scsicmd->buf;
+ }
+
+#ifdef DEBUG
+ if (sii_debug > 1) {
+ printf("sii_StartCmd: %s target %d cmd 0x%x addr %x size %d dma %d\n",
+ scsicmd->sd->sd_driver->d_name, target,
+ scsicmd->cmd[0], scsicmd->buf, scsicmd->buflen,
+ state->dmaDataPhase);
+ }
+ sii_debug_cmd = scsicmd->cmd[0];
+ if (scsicmd->cmd[0] == SCSI_READ_EXT ||
+ scsicmd->cmd[0] == SCSI_WRITE_EXT) {
+ 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.
+ */
+ sii_buf[0] = SCSI_DIS_REC_IDENTIFY;
+ sii_buf[1] = SCSI_EXTENDED_MSG;
+ sii_buf[2] = 3; /* message length */
+ sii_buf[3] = SCSI_SYNCHRONOUS_XFER;
+ sii_buf[4] = 0;
+ sii_buf[5] = 3; /* maximum SII chip supports */
+
+ state->dmaCurPhase = SII_MSG_OUT_PHASE,
+ state->dmalen = 6;
+ CopyToBuffer((u_short *)sii_buf,
+ (volatile u_short *)SII_BUF_ADDR, 6);
+ regs->slcsr = target;
+ regs->dmctrl = state->dmaReqAck;
+ regs->dmaddrl = (u_short)(SII_BUF_ADDR >> 1);
+ regs->dmaddrh = (u_short)(SII_BUF_ADDR >> 17) & 03;
+ 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 = SCSI_DIS_REC_IDENTIFY;
+ regs->slcsr = target;
+ regs->dmctrl = state->dmaReqAck;
+ regs->comm = SII_INXFER | SII_SELECT | SII_ATN | SII_CON |
+ SII_MSG_OUT_PHASE;
+ }
+ MachEmptyWriteBuffer();
+
+ /*
+ * 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;
+ MachEmptyWriteBuffer();
+
+#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 */
+ CopyToBuffer((u_short *)state->cmd,
+ (volatile u_short *)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 = ENXIO; /* device didn't respond */
+ regs->comm = SII_DISCON;
+ MachEmptyWriteBuffer();
+ SII_WAIT_UNTIL(status, regs->cstat,
+ !(status & (SII_CON | SII_SIP)),
+ SII_WAIT_COUNT, retval);
+ } else
+ error = EBUSY; /* 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;
+ MachEmptyWriteBuffer();
+ sii_CmdDone(sc, target, error);
+}
+
+/*
+ * Process interrupt conditions.
+ */
+static void
+sii_DoIntr(sc, dstat)
+ register struct siisoftc *sc;
+ register u_int dstat;
+{
+ register SIIRegs *regs = sc->sc_regs;
+ register State *state;
+ register 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 */
+ MachEmptyWriteBuffer();
+
+ if (dstat & SII_CI) {
+ /* deglitch cstat register */
+ msg = regs->cstat;
+ while (msg != (cstat = regs->cstat))
+ msg = cstat;
+ regs->cstat = cstat; /* acknowledge everything */
+ MachEmptyWriteBuffer();
+#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, 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) {
+ }
+#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_short *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->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->dmalen = len);
+ dstat &= ~(SII_IBF | SII_TBE);
+ }
+ /* copy in the data */
+ CopyFromBuffer((volatile u_short *)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->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;
+ CopyToBuffer((u_short *)(state->buf +
+ SII_MAX_DMA_XFER_LENGTH),
+ (volatile u_short *)
+ state->dmaAddr[!state->dmaBufIndex], i);
+ } else {
+ sii_StartDMA(regs, state->dmaCurPhase =
+ SII_DATA_OUT_PHASE,
+ state->dmaAddr[state->dmaBufIndex],
+ 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->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;
+ MachEmptyWriteBuffer();
+#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;
+ }
+ CopyToBuffer((u_short *)state->cmd,
+ (volatile u_short *)state->dmaAddr[0],
+ i);
+ sii_StartDMA(regs, state->dmaCurPhase =
+ SII_CMD_PHASE, state->dmaAddr[0],
+ 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 %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;
+ MachEmptyWriteBuffer();
+#ifdef DEBUG
+ if (sii_debug > 4)
+ printf("Data %d dcnt %d dadr %x ",
+ state->dmaDataPhase,
+ state->dmaCnt,
+ (state->dmaAddrH << 16) |
+ state->dmaAddrL);
+#endif
+ break;
+ }
+ if (state->dmaDataPhase != (dstat & SII_PHASE_MSK)) {
+ printf("%s: device %d: cmd %x: dma phase doesn't match\n",
+ sc->sc_dev.dv_xname, sc->sc_target,
+ sc->sc_cmd[sc->sc_target]->cmd[0]);
+ goto abort;
+ }
+#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->dmalen = i);
+ break;
+ }
+ /* start first chunk */
+ if (state->flags & FIRST_DMA) {
+ state->flags &= ~FIRST_DMA;
+ CopyToBuffer((u_short *)state->buf,
+ (volatile u_short *)
+ state->dmaAddr[state->dmaBufIndex], i);
+ }
+ sii_StartDMA(regs,
+ state->dmaCurPhase = SII_DATA_OUT_PHASE,
+ state->dmaAddr[state->dmaBufIndex],
+ 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;
+ CopyToBuffer((u_short *)(state->buf +
+ SII_MAX_DMA_XFER_LENGTH),
+ (volatile u_short *)
+ state->dmaAddr[!state->dmaBufIndex], i);
+ }
+ break;
+
+ case SII_STATUS_PHASE:
+ if (state->cmdlen > 0) {
+ printf("%s: device %d: cmd %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);
+ MachEmptyWriteBuffer();
+ regs->dstat = SII_DNE;
+ MachEmptyWriteBuffer();
+#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 */
+ CopyFromBuffer((volatile u_short *)
+ 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;
+ state->dmaCnt = i = regs->dmlotc;
+ if (dstat & SII_OBB)
+ state->dmaByte = regs->dmabyte;
+ if (i == 0)
+ i = SII_MAX_DMA_XFER_LENGTH;
+ i = state->dmalen - i;
+ /* note: no carry from dmaddrl to dmaddrh */
+ state->dmaAddrL = regs->dmaddrl + i;
+ state->dmaAddrH = regs->dmaddrh;
+ regs->comm = comm &
+ (SII_STATE_MSK | SII_PHASE_MSK);
+ MachEmptyWriteBuffer();
+ regs->dstat = SII_DNE;
+ MachEmptyWriteBuffer();
+#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 SCSI_COMMAND_COMPLETE:
+ /* 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;
+ MachEmptyWriteBuffer();
+ 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;
+ MachEmptyWriteBuffer();
+ /*
+ * 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, 0);
+ break;
+
+ case SCSI_EXTENDED_MSG:
+ /* 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;
+ MachEmptyWriteBuffer();
+ /* read the message length */
+ msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 1);
+ if (msg < 0) {
+ dstat = regs->dstat;
+ goto again;
+ }
+ sii_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;
+ }
+ sii_buf[i + 2] = dstat;
+ }
+
+ switch (sii_buf[2]) {
+ case SCSI_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;
+ MachEmptyWriteBuffer();
+ i = (sii_buf[3] << 24) |
+ (sii_buf[4] << 16) |
+ (sii_buf[5] << 8) |
+ sii_buf[6];
+ if (state->dmaPrevPhase >= 0) {
+ state->dmaAddrL += i;
+ state->dmaCnt -= i;
+ }
+ break;
+
+ case SCSI_SYNCHRONOUS_XFER:
+ /*
+ * 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;
+ MachEmptyWriteBuffer();
+ sii_DoSync(regs, 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;
+ MachEmptyWriteBuffer();
+
+ /* wait for MSG_OUT phase */
+ SII_WAIT_UNTIL(dstat, regs->dstat,
+ dstat & SII_TBE,
+ SII_WAIT_COUNT, i);
+
+ /* send a reject message */
+ regs->data = SCSI_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;
+ MachEmptyWriteBuffer();
+ }
+ break;
+
+ case SCSI_SAVE_DATA_POINTER:
+ case SCSI_RESTORE_POINTERS:
+ /* 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;
+ MachEmptyWriteBuffer();
+ /* 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 SCSI_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;
+ MachEmptyWriteBuffer();
+ 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;
+ MachEmptyWriteBuffer();
+ 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 SCSI_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;
+ MachEmptyWriteBuffer();
+ printf("%s: device %d: message reject.\n",
+ sc->sc_dev.dv_xname, sc->sc_target);
+ break;
+
+ default:
+ if (!(msg & SCSI_IDENTIFY)) {
+ 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;
+ MachEmptyWriteBuffer();
+ /* 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 = SCSI_MESSAGE_PARITY_ERROR;
+ } else
+ regs->data = SCSI_NO_OP;
+ regs->comm = SII_INXFER | (comm & SII_STATE_MSK) |
+ SII_MSG_OUT_PHASE;
+ MachEmptyWriteBuffer();
+
+ /* 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;
+ MachEmptyWriteBuffer();
+ }
+ 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 = SCSI_ABORT;
+ regs->comm = SII_INXFER | SII_ATN | (cstat & SII_STATE_MSK) |
+ SII_MSG_OUT_PHASE;
+ MachEmptyWriteBuffer();
+ 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;
+ MachEmptyWriteBuffer();
+ 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;
+ MachEmptyWriteBuffer();
+
+ i = sc->sc_target;
+ sc->sc_target = -1;
+ sii_CmdDone(sc, i, EIO);
+#ifdef DEBUG
+ if (sii_debug > 4)
+ printf("sii_DoIntr: after CmdDone target %d\n", sc->sc_target);
+#endif
+}
+
+static void
+sii_StateChg(sc, cstat)
+ register struct siisoftc *sc;
+ register u_int cstat;
+{
+ register SIIRegs *regs = sc->sc_regs;
+ register State *state;
+ register 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;
+ MachEmptyWriteBuffer();
+ 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;
+ MachEmptyWriteBuffer();
+ 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.
+ */
+static int
+sii_GetByte(regs, phase, ack)
+ register SIIRegs *regs;
+ int phase, ack;
+{
+ register u_int dstat;
+ register u_int state;
+ register int i;
+ register int data;
+
+ dstat = regs->dstat;
+ state = regs->cstat & SII_STATE_MSK;
+ if (!(dstat & SII_IBF) || (dstat & SII_MIS)) {
+ regs->comm = state | phase;
+ MachEmptyWriteBuffer();
+ /* 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;
+ MachEmptyWriteBuffer();
+
+ /* 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;
+ MachEmptyWriteBuffer();
+ }
+ }
+
+ return (data);
+}
+
+/*
+ * Exchange messages to initiate synchronous data transfers.
+ */
+static void
+sii_DoSync(regs, state)
+ register SIIRegs *regs;
+ register State *state;
+{
+ register u_int dstat, comm;
+ register int i, j;
+ u_int len;
+
+#ifdef DEBUG
+ if (sii_debug)
+ printf("sii_DoSync: len %d per %d req/ack %d\n",
+ sii_buf[1], sii_buf[3], sii_buf[4]);
+#endif
+
+ /* SII chip can only handle a minimum transfer period of ??? */
+ if (sii_buf[3] < 64)
+ sii_buf[3] = 64;
+ /* SII chip can only handle a maximum REQ/ACK offset of 3 */
+ len = sii_buf[4];
+ if (len > 3)
+ len = 3;
+
+ sii_buf[0] = SCSI_EXTENDED_MSG;
+ sii_buf[1] = 3; /* message length */
+ sii_buf[2] = SCSI_SYNCHRONOUS_XFER;
+ sii_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 = sii_buf[j];
+ regs->comm = comm;
+ MachEmptyWriteBuffer();
+
+ /* 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;
+ MachEmptyWriteBuffer();
+ }
+#else
+ CopyToBuffer((u_short *)sii_buf, (volatile u_short *)SII_BUF_ADDR, 5);
+ printf("sii_DoSync: %x %x %x ds %x\n",
+ ((volatile u_short *)SII_BUF_ADDR)[0],
+ ((volatile u_short *)SII_BUF_ADDR)[2],
+ ((volatile u_short *)SII_BUF_ADDR)[4],
+ regs->dstat); /* XXX */
+ regs->dmaddrl = (u_short)(SII_BUF_ADDR >> 1);
+ regs->dmaddrh = (u_short)(SII_BUF_ADDR >> 17) & 03;
+ regs->dmlotc = 5;
+ regs->comm = SII_DMA | SII_INXFER | SII_ATN |
+ (regs->cstat & SII_STATE_MSK) | SII_MSG_OUT_PHASE;
+ MachEmptyWriteBuffer();
+
+ /* 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;
+ MachEmptyWriteBuffer();
+#endif
+
+#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.
+ */
+static void
+sii_StartDMA(regs, phase, dmaAddr, size)
+ register SIIRegs *regs; /* which SII to use */
+ int phase; /* phase to send/receive data */
+ u_short *dmaAddr; /* DMA buffer address */
+ 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 = ((u_long)dmaAddr >> 1);
+ regs->dmaddrh = ((u_long)dmaAddr >> 17) & 03;
+ regs->dmlotc = size;
+ regs->comm = SII_DMA | SII_INXFER | (regs->cstat & SII_STATE_MSK) |
+ phase;
+ MachEmptyWriteBuffer();
+
+#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.
+ */
+static void
+sii_CmdDone(sc, target, error)
+ register struct siisoftc *sc; /* which SII to use */
+ int target; /* which device is done */
+ int error; /* error code if any errors */
+{
+ register ScsiCmd *scsicmd;
+ register int i;
+
+ scsicmd = sc->sc_cmd[target];
+#ifdef DIAGNOSTIC
+ if (target < 0 || !scsicmd)
+ panic("sii_CmdDone");
+#endif
+ sc->sc_cmd[target] = (ScsiCmd *)0;
+#ifdef DEBUG
+ if (sii_debug > 1) {
+ printf("sii_CmdDone: %s target %d cmd %x err %d resid %d\n",
+ scsicmd->sd->sd_driver->d_name, 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;
+ }
+
+ (*scsicmd->sd->sd_driver->d_done)(scsicmd->unit, error,
+ sc->sc_st[target].buflen, sc->sc_st[target].statusByte);
+}
+
+#ifdef DEBUG
+sii_DumpLog()
+{
+ register struct sii_log *lp;
+
+ printf("sii: cmd %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
+#endif
+