summaryrefslogtreecommitdiffstats
path: root/sys/dev/ic/wd33c93.c
diff options
context:
space:
mode:
authormiod <miod@openbsd.org>2012-03-28 20:44:23 +0000
committermiod <miod@openbsd.org>2012-03-28 20:44:23 +0000
commit89e78ff68dbb60985d19f72ea5384db19f165e9c (patch)
treece4bbedba0e946ef806252ab74d9ca08167b58a3 /sys/dev/ic/wd33c93.c
parentAllow dma map boundary smaller than the kernel page size to work in (diff)
downloadwireguard-openbsd-89e78ff68dbb60985d19f72ea5384db19f165e9c.tar.xz
wireguard-openbsd-89e78ff68dbb60985d19f72ea5384db19f165e9c.zip
Work in progress support for the SGI Indigo, Indigo 2 and Indy systems
(IP20, IP22, IP24) in 64-bit mode, adapated from NetBSD. Currently limited to headless operation, input and video drivers will get ported soon. Should work on all R4000, R4440 and R5000 based systems. L2 cache on R5000SC Indy not supported yet (coming soon), R4600 not supported yet either (coming soon as well). Tested to boot multiuser on: Indigo2 R4000SC, Indy R4000PC, Indy R4000SC, Indy R5000SC, Indigo2 R4400SC. There are still glitches in the Ethernet driver which are being looked at. Expansion support is limited to the GIO E++ board; GIO boards with PCI-GIO bridges not ported yet due to the lack of hardware, and this kind of driver does not port blindly. Most of this work comes from NetBSD, polishing and integration work, as well as putting as many ``R4x00 in 64-bit mode'' erratas as necessary, by yours truly. More work is coming, as well as trying to get some easy way to boot install kernels (as older PROM can only boot ECOFF binaries, which won't do for the kernel).
Diffstat (limited to 'sys/dev/ic/wd33c93.c')
-rw-r--r--sys/dev/ic/wd33c93.c2337
1 files changed, 2337 insertions, 0 deletions
diff --git a/sys/dev/ic/wd33c93.c b/sys/dev/ic/wd33c93.c
new file mode 100644
index 00000000000..2c6fabc6660
--- /dev/null
+++ b/sys/dev/ic/wd33c93.c
@@ -0,0 +1,2337 @@
+/* $OpenBSD: wd33c93.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */
+/* $NetBSD: wd33c93.c,v 1.24 2010/11/13 13:52:02 uebayasi Exp $ */
+
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ *
+ * @(#)scsi.c 7.5 (Berkeley) 5/4/91
+ */
+
+/*
+ * Changes Copyright (c) 2001 Wayne Knowles
+ * Changes Copyright (c) 1996 Steve Woodford
+ * Original Copyright (c) 1994 Christian E. Hopps
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson of Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)scsi.c 7.5 (Berkeley) 5/4/91
+ */
+
+/*
+ * This version of the driver is pretty well generic, so should work with
+ * any flavour of WD33C93 chip.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h> /* For hz */
+#include <sys/malloc.h>
+#include <sys/pool.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+#include <scsi/scsi_message.h>
+
+#include <machine/bus.h>
+
+#include <dev/ic/wd33c93reg.h>
+#include <dev/ic/wd33c93var.h>
+
+/*
+ * SCSI delays
+ * In u-seconds, primarily for state changes on the SPC.
+ */
+#define SBIC_CMD_WAIT 200000 /* wait per step of 'immediate' cmds */
+#define SBIC_DATA_WAIT 200000 /* wait per data in/out step */
+#define SBIC_INIT_WAIT 200000 /* wait per step (both) during init */
+
+#define STATUS_UNKNOWN 0xff /* uninitialized status */
+
+/*
+ * Convenience macro for waiting for a particular wd33c93 event
+ */
+#define SBIC_WAIT(regs, until, timeo) wd33c93_wait(regs, until, timeo, __LINE__)
+
+void wd33c93_init(struct wd33c93_softc *);
+void wd33c93_reset(struct wd33c93_softc *);
+int wd33c93_go(struct wd33c93_softc *, struct wd33c93_acb *);
+int wd33c93_dmaok(struct wd33c93_softc *, struct scsi_xfer *);
+int wd33c93_wait(struct wd33c93_softc *, u_char, int , int);
+u_char wd33c93_selectbus(struct wd33c93_softc *, struct wd33c93_acb *);
+int wd33c93_xfout(struct wd33c93_softc *, int, void *);
+int wd33c93_xfin(struct wd33c93_softc *, int, void *);
+int wd33c93_poll(struct wd33c93_softc *, struct wd33c93_acb *);
+int wd33c93_nextstate(struct wd33c93_softc *, struct wd33c93_acb *,
+ u_char, u_char);
+int wd33c93_abort(struct wd33c93_softc *, struct wd33c93_acb *,
+ const char *);
+void wd33c93_xferdone(struct wd33c93_softc *);
+void wd33c93_error(struct wd33c93_softc *, struct wd33c93_acb *);
+void wd33c93_scsidone(struct wd33c93_softc *, struct wd33c93_acb *, int);
+void wd33c93_sched(struct wd33c93_softc *);
+void wd33c93_dequeue(struct wd33c93_softc *, struct wd33c93_acb *);
+void wd33c93_dma_stop(struct wd33c93_softc *);
+void wd33c93_dma_setup(struct wd33c93_softc *, int);
+int wd33c93_msgin_phase(struct wd33c93_softc *, int);
+void wd33c93_msgin(struct wd33c93_softc *, u_char *, int);
+void wd33c93_reselect(struct wd33c93_softc *, int, int, int, int);
+void wd33c93_sched_msgout(struct wd33c93_softc *, u_short);
+void wd33c93_msgout(struct wd33c93_softc *);
+void wd33c93_timeout(void *arg);
+void wd33c93_watchdog(void *arg);
+u_char wd33c93_stp2syn(struct wd33c93_softc *, struct wd33c93_tinfo *);
+void wd33c93_setsync(struct wd33c93_softc *, struct wd33c93_tinfo *);
+
+struct pool wd33c93_pool; /* Adapter Control Blocks */
+int wd33c93_pool_initialized = 0;
+
+/*
+ * Timeouts
+ */
+int wd33c93_cmd_wait = SBIC_CMD_WAIT;
+int wd33c93_data_wait = SBIC_DATA_WAIT;
+int wd33c93_init_wait = SBIC_INIT_WAIT;
+
+int wd33c93_nodma = 0; /* Use polled IO transfers */
+int wd33c93_nodisc = 0; /* Allow command queues */
+int wd33c93_notags = 0; /* No Tags */
+
+/*
+ * Some useful stuff for debugging purposes
+ */
+#ifdef SBICDEBUG
+
+#define QPRINTF(a) SBIC_DEBUG(MISC, a)
+
+int wd33c93_debug = 0; /* Debug flags */
+
+void wd33c93_print_csr (u_char);
+void wd33c93_hexdump (u_char *, int);
+
+#else
+#define QPRINTF(a) /* */
+#endif
+
+static const char *wd33c93_chip_names[] = SBIC_CHIP_LIST;
+
+/*
+ * Attach instance of driver and probe for sub devices
+ */
+void
+wd33c93_attach(struct wd33c93_softc *sc, struct scsi_adapter *adapter)
+{
+ struct scsibus_attach_args saa;
+
+ sc->sc_cfflags = sc->sc_dev.dv_cfdata->cf_flags;
+ timeout_set(&sc->sc_watchdog, wd33c93_watchdog, sc);
+ wd33c93_init(sc);
+
+ printf(": %s, %d.%d MHz, %s\n",
+ wd33c93_chip_names[sc->sc_chip],
+ sc->sc_clkfreq / 10, sc->sc_clkfreq % 10,
+ (sc->sc_dmamode == SBIC_CTL_DMA) ? "DMA" :
+ (sc->sc_dmamode == SBIC_CTL_DBA_DMA) ? "DBA" :
+ (sc->sc_dmamode == SBIC_CTL_BURST_DMA) ? "burst DMA" : "PIO");
+ if (sc->sc_chip == SBIC_CHIP_WD33C93B) {
+ printf("%s: microcode revision 0x%02x",
+ sc->sc_dev.dv_xname, sc->sc_rev);
+ if (sc->sc_minsyncperiod < 50)
+ printf(", fast SCSI");
+ printf("\n");
+ }
+
+ sc->sc_link.adapter_softc = sc;
+ sc->sc_link.adapter_target = sc->sc_id;
+ sc->sc_link.adapter_buswidth = SBIC_NTARG;
+ sc->sc_link.adapter = adapter;
+ sc->sc_link.openings = 2;
+ sc->sc_link.luns = SBIC_NLUN;
+
+ bzero(&saa, sizeof(saa));
+ saa.saa_sc_link = &sc->sc_link;
+
+ config_found(&sc->sc_dev, &saa, scsiprint);
+ timeout_add_sec(&sc->sc_watchdog, 60);
+}
+
+/*
+ * Initialize driver-private structures
+ */
+void
+wd33c93_init(struct wd33c93_softc *sc)
+{
+ u_int i;
+
+ timeout_del(&sc->sc_watchdog);
+
+ if (!wd33c93_pool_initialized) {
+ /* All instances share the same pool */
+ pool_init(&wd33c93_pool, sizeof(struct wd33c93_acb), 0, 0, 0,
+ "wd33c93_acb", NULL);
+ ++wd33c93_pool_initialized;
+ }
+
+ if (sc->sc_state == 0) {
+ TAILQ_INIT(&sc->ready_list);
+
+ sc->sc_nexus = NULL;
+ sc->sc_disc = 0;
+ memset(sc->sc_tinfo, 0, sizeof(sc->sc_tinfo));
+ } else {
+ /* XXX cancel all active commands */
+ panic("wd33c93: reinitializing driver!");
+ }
+
+ sc->sc_flags = 0;
+ sc->sc_state = SBIC_IDLE;
+ wd33c93_reset(sc);
+
+ for (i = 0; i < SBIC_NTARG; i++) {
+ struct wd33c93_tinfo *ti = &sc->sc_tinfo[i];
+ /*
+ * cf_flags = 0xTTSSRR
+ *
+ * TT = Bitmask to disable Tagged Queues
+ * SS = Bitmask to disable Sync negotiation
+ * RR = Bitmask to disable disconnect/reselect
+ */
+ ti->flags = T_NEED_RESET;
+ if (CFFLAGS_NOSYNC(sc->sc_cfflags, i))
+ ti->flags |= T_NOSYNC;
+ if (CFFLAGS_NODISC(sc->sc_cfflags, i) || wd33c93_nodisc)
+ ti->flags |= T_NODISC;
+ ti->period = sc->sc_minsyncperiod;
+ ti->offset = 0;
+ }
+}
+
+void
+wd33c93_reset(struct wd33c93_softc *sc)
+{
+ u_int my_id, s, div, i;
+ u_char csr, reg;
+
+ SET_SBIC_cmd(sc, SBIC_CMD_ABORT);
+ WAIT_CIP(sc);
+
+ s = splbio();
+
+ if (sc->sc_reset != NULL)
+ (*sc->sc_reset)(sc);
+
+ my_id = sc->sc_link.adapter_target & SBIC_ID_MASK;
+
+ /* Enable advanced features and really(!) advanced features */
+#if 1
+ my_id |= (SBIC_ID_EAF | SBIC_ID_RAF); /* XXX - MD Layer */
+#endif
+
+ SET_SBIC_myid(sc, my_id);
+
+ /* Reset the chip */
+ SET_SBIC_cmd(sc, SBIC_CMD_RESET);
+ DELAY(25);
+ SBIC_WAIT(sc, SBIC_ASR_INT, 0);
+
+ /* Set up various chip parameters */
+ SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+ GET_SBIC_csr(sc, csr); /* clears interrupt also */
+ GET_SBIC_cdb1(sc, sc->sc_rev); /* valid with RAF on wd33c93b */
+
+ switch (csr) {
+ case SBIC_CSR_RESET:
+ sc->sc_chip = SBIC_CHIP_WD33C93;
+ break;
+ case SBIC_CSR_RESET_AM:
+ SET_SBIC_queue_tag(sc, 0x55);
+ GET_SBIC_queue_tag(sc, reg);
+ sc->sc_chip = (reg == 0x55) ?
+ SBIC_CHIP_WD33C93B : SBIC_CHIP_WD33C93A;
+ SET_SBIC_queue_tag(sc, 0x0);
+ break;
+ default:
+ sc->sc_chip = SBIC_CHIP_UNKNOWN;
+ }
+
+ /*
+ * Choose a suitable clock divisor and work out the resulting
+ * sync transfer periods in 4ns units.
+ */
+ if (sc->sc_clkfreq < 110) {
+ my_id |= SBIC_ID_FS_8_10;
+ div = 2;
+ } else if (sc->sc_clkfreq < 160) {
+ my_id |= SBIC_ID_FS_12_15;
+ div = 3;
+ } else if (sc->sc_clkfreq < 210) {
+ my_id |= SBIC_ID_FS_16_20;
+ div = 4;
+ } else
+ panic("wd33c93: invalid clock speed %d", sc->sc_clkfreq);
+
+ for (i = 0; i < 7; i++)
+ sc->sc_syncperiods[i] =
+ (i + 2) * div * 1250 / sc->sc_clkfreq;
+ sc->sc_minsyncperiod = sc->sc_syncperiods[0];
+ SBIC_DEBUG(SYNC, ("available sync periods: %d %d %d %d %d %d %d\n",
+ sc->sc_syncperiods[0], sc->sc_syncperiods[1],
+ sc->sc_syncperiods[2], sc->sc_syncperiods[3],
+ sc->sc_syncperiods[4], sc->sc_syncperiods[5],
+ sc->sc_syncperiods[6]));
+
+ if (sc->sc_clkfreq >= 160 && sc->sc_chip == SBIC_CHIP_WD33C93B) {
+ for (i = 0; i < 3; i++)
+ sc->sc_fsyncperiods[i] =
+ (i + 2) * 2 * 1250 / sc->sc_clkfreq;
+ SBIC_DEBUG(SYNC, ("available fast sync periods: %d %d %d\n",
+ sc->sc_fsyncperiods[0], sc->sc_fsyncperiods[1],
+ sc->sc_fsyncperiods[2]));
+ sc->sc_minsyncperiod = sc->sc_fsyncperiods[0];
+ }
+
+ /* Max Sync Offset */
+ if (sc->sc_chip == SBIC_CHIP_WD33C93A ||
+ sc->sc_chip == SBIC_CHIP_WD33C93B)
+ sc->sc_maxoffset = SBIC_SYN_93AB_MAX_OFFSET;
+ else
+ sc->sc_maxoffset = SBIC_SYN_93_MAX_OFFSET;
+
+ /*
+ * don't allow Selection (SBIC_RID_ES)
+ * until we can handle target mode!!
+ */
+ SET_SBIC_rselid(sc, SBIC_RID_ER);
+
+ /* Asynchronous for now */
+ SET_SBIC_syn(sc, 0);
+
+ sc->sc_flags = 0;
+ sc->sc_state = SBIC_IDLE;
+
+ splx(s);
+}
+
+void
+wd33c93_error(struct wd33c93_softc *sc, struct wd33c93_acb *acb)
+{
+ struct scsi_xfer *xs = acb->xs;
+
+ KASSERT(xs);
+
+ if (xs->flags & SCSI_SILENT)
+ return;
+
+ sc_print_addr(xs->sc_link);
+ printf("SCSI Error\n");
+}
+
+/*
+ * Determine an appropriate value for the synchronous transfer register
+ * given the period and offset values in *ti.
+ */
+u_char
+wd33c93_stp2syn(struct wd33c93_softc *sc, struct wd33c93_tinfo *ti)
+{
+ unsigned i;
+
+ /* see if we can handle fast scsi (100-200ns) first */
+ if (ti->period < 50 && sc->sc_minsyncperiod < 50) {
+ for (i = 0; i < 3; i++)
+ if (sc->sc_fsyncperiods[i] >= ti->period)
+ return (SBIC_SYN(ti->offset, i + 2, 1));
+ }
+
+ for (i = 0; i < 7; i++) {
+ if (sc->sc_syncperiods[i] >= ti->period) {
+ if (i == 6)
+ return (SBIC_SYN(0, 0, 0));
+ else
+ return (SBIC_SYN(ti->offset, i + 2, 0));
+ }
+ }
+
+ /* XXX - can't handle it; do async */
+ return (SBIC_SYN(0, 0, 0));
+}
+
+/*
+ * Setup sync mode for given target
+ */
+void
+wd33c93_setsync(struct wd33c93_softc *sc, struct wd33c93_tinfo *ti)
+{
+ u_char syncreg;
+
+ if (ti->flags & T_SYNCMODE)
+ syncreg = wd33c93_stp2syn(sc, ti);
+ else
+ syncreg = SBIC_SYN(0, 0, 0);
+
+ SBIC_DEBUG(SYNC, ("wd33c93_setsync: sync reg = 0x%02x\n", syncreg));
+ SET_SBIC_syn(sc, syncreg);
+}
+
+/*
+ * Check if current operation can be done using DMA
+ *
+ * returns 1 if DMA OK, 0 for polled I/O transfer
+ */
+int
+wd33c93_dmaok(struct wd33c93_softc *sc, struct scsi_xfer *xs)
+{
+ if (wd33c93_nodma || sc->sc_dmamode == SBIC_CTL_NO_DMA ||
+ (xs->flags & SCSI_POLL) || xs->datalen == 0)
+ return (0);
+ return(1);
+}
+
+/*
+ * Setup for DMA transfer
+ */
+void
+wd33c93_dma_setup(struct wd33c93_softc *sc, int datain)
+{
+ struct wd33c93_acb *acb = sc->sc_nexus;
+ int s;
+
+ sc->sc_daddr = acb->daddr;
+ sc->sc_dleft = acb->dleft;
+
+ s = splbio();
+ /* Indicate that we're in DMA mode */
+ if (sc->sc_dleft) {
+ sc->sc_dmasetup(sc, &sc->sc_daddr, &sc->sc_dleft,
+ datain, &sc->sc_dleft);
+ }
+ splx(s);
+ return;
+}
+
+
+/*
+ * Save DMA pointers. Take into account partial transfer. Shut down DMA.
+ */
+void
+wd33c93_dma_stop(struct wd33c93_softc *sc)
+{
+ size_t count;
+ int asr;
+
+ /* Wait until WD chip is idle */
+ do {
+ GET_SBIC_asr(sc, asr); /* XXX */
+ if (asr & SBIC_ASR_DBR) {
+ printf("wd33c93_dma_stop: asr %02x canceled!\n", asr);
+ break;
+ }
+ } while (asr & (SBIC_ASR_BSY|SBIC_ASR_CIP));
+
+ /* Only need to save pointers if DMA was active */
+ if (sc->sc_flags & SBICF_INDMA) {
+ int s = splbio();
+
+ /* Shut down DMA and flush FIFO's */
+ sc->sc_dmastop(sc);
+
+ /* Fetch the residual count */
+ SBIC_TC_GET(sc, count);
+
+ /* Work out how many bytes were actually transferred */
+ count = sc->sc_tcnt - count;
+
+ if (sc->sc_dleft < count)
+ printf("xfer too large: dleft=%zu resid=%zu\n",
+ sc->sc_dleft, count);
+
+ /* Fixup partial xfers */
+ sc->sc_daddr = (char *)sc->sc_daddr + count;
+ sc->sc_dleft -= count;
+ sc->sc_tcnt = 0;
+ sc->sc_flags &= ~SBICF_INDMA;
+ splx(s);
+ SBIC_DEBUG(DMA, ("dma_stop\n"));
+ }
+ /*
+ * Ensure the WD chip is back in polled I/O mode, with nothing to
+ * transfer.
+ */
+ SBIC_TC_PUT(sc, 0);
+ SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
+}
+
+
+/*
+ * Handle new request from scsi layer
+ */
+void
+wd33c93_scsi_cmd(struct scsi_xfer *xs)
+{
+ struct scsi_link *sc_link = xs->sc_link;
+ struct wd33c93_softc *sc = sc_link->adapter_softc;
+ struct wd33c93_acb *acb;
+ int flags, s;
+
+ SBIC_DEBUG(MISC, ("wd33c93_scsi_cmd\n"));
+
+ flags = xs->flags;
+
+ if (sc->sc_nexus && (flags & SCSI_POLL))
+ panic("wd33c93_scsicmd: busy");
+
+ s = splbio();
+ acb = (struct wd33c93_acb *)pool_get(&wd33c93_pool,
+ PR_NOWAIT | PR_ZERO);
+ splx(s);
+
+ if (acb == NULL) {
+ xs->error = XS_NO_CCB;
+ scsi_done(xs);
+ return;
+ }
+
+ acb->flags = ACB_ACTIVE;
+ acb->xs = xs;
+ acb->timeout = xs->timeout;
+ timeout_set(&acb->to, wd33c93_timeout, acb);
+
+ memcpy(&acb->cmd, xs->cmd, xs->cmdlen);
+ acb->clen = xs->cmdlen;
+ acb->daddr = xs->data;
+ acb->dleft = xs->datalen;
+
+#if 0
+ if (flags & SCSI_POLL) {
+ /*
+ * Complete currently active command(s) before
+ * issuing an immediate command
+ */
+ while (sc->sc_nexus)
+ wd33c93_poll(sc, sc->sc_nexus);
+ }
+#endif
+
+ s = splbio();
+ TAILQ_INSERT_TAIL(&sc->ready_list, acb, chain);
+ acb->flags |= ACB_READY;
+
+ /* If nothing is active, try to start it now. */
+ if (sc->sc_state == SBIC_IDLE)
+ wd33c93_sched(sc);
+ splx(s);
+
+ if ((flags & SCSI_POLL) == 0)
+ return;
+
+ if (wd33c93_poll(sc, acb)) {
+ wd33c93_timeout(acb);
+ if (wd33c93_poll(sc, acb)) /* 2nd retry for ABORT */
+ wd33c93_timeout(acb);
+ }
+}
+
+/*
+ * attempt to start the next available command
+ */
+void
+wd33c93_sched(struct wd33c93_softc *sc)
+{
+ struct scsi_link *sc_link;
+ struct wd33c93_acb *acb;
+ struct wd33c93_tinfo *ti;
+ struct wd33c93_linfo *li;
+ int lun, tag, flags;
+ int s;
+
+ if (sc->sc_state != SBIC_IDLE)
+ return;
+
+ KASSERT(sc->sc_nexus == NULL);
+
+ /* Loop through the ready list looking for work to do... */
+ TAILQ_FOREACH(acb, &sc->ready_list, chain) {
+ sc_link = acb->xs->sc_link;
+ lun = sc_link->lun;
+ ti = &sc->sc_tinfo[sc_link->target];
+
+ KASSERT(acb->flags & ACB_READY);
+
+ /* Select type of tag for this command */
+ if ((ti->flags & T_NODISC) != 0)
+ tag = 0;
+ else if ((ti->flags & T_TAG) == 0)
+ tag = 0;
+ else if ((acb->flags & ACB_SENSE) != 0)
+ tag = 0;
+ else if (acb->xs->flags & SCSI_POLL)
+ tag = 0; /* No tags for polled commands */
+ else
+ tag = MSG_SIMPLE_Q_TAG;
+
+ s = splbio();
+ li = TINFO_LUN(ti, lun);
+ if (li == NULL) {
+ /* Initialize LUN info and add to list. */
+ li = malloc(sizeof(*li), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (li == NULL) {
+ splx(s);
+ continue;
+ }
+ li->lun = lun;
+ if (lun < SBIC_NLUN)
+ ti->lun[lun] = li;
+ }
+ li->last_used = time_second;
+
+ /*
+ * We've found a potential command, but is the target/lun busy?
+ */
+
+ if (tag == 0 && li->untagged == NULL)
+ li->untagged = acb; /* Issue untagged */
+
+ if (li->untagged != NULL) {
+ tag = 0;
+ if ((li->state != L_STATE_BUSY) && li->used == 0) {
+ /* Issue this untagged command now */
+ acb = li->untagged;
+ sc_link = acb->xs->sc_link;
+ } else{
+ /* Not ready yet */
+ splx(s);
+ continue;
+ }
+ }
+
+ acb->tag_type = tag;
+ if (tag != 0) {
+ int i;
+
+ /* Allocate a tag */
+ if (li->used == 255) {
+ /* no free tags */
+ splx(s);
+ continue;
+ }
+ /* Start from the last used location */
+ for (i = li->avail; i < 256; i++) {
+ if (li->queued[i] == NULL)
+ break;
+ }
+ /* Couldn't find one, start again from the beginning */
+ if (i == 256) {
+ for (i = 0; i < 256; i++) {
+ if (li->queued[i] == NULL)
+ break;
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (i == 256)
+ panic("%s: tag alloc failure", __func__);
+#endif
+
+ /* Save where to start next time. */
+ li->avail = i + 1;
+ li->used++;
+ li->queued[i] = acb;
+ acb->tag_id = i;
+ }
+ splx(s);
+ if (li->untagged != NULL && (li->state != L_STATE_BUSY)) {
+ li->state = L_STATE_BUSY;
+ break;
+ }
+ if (li->untagged == NULL && tag != 0) {
+ break;
+ } else
+ printf("%d:%d busy\n", sc_link->target, sc_link->lun);
+ }
+
+ if (acb == NULL) {
+ SBIC_DEBUG(ACBS, ("wd33c93sched: no work\n"));
+ return; /* did not find an available command */
+ }
+
+ SBIC_DEBUG(ACBS, ("wd33c93_sched(%d,%d)\n", sc_link->target,
+ sc_link->lun));
+
+ TAILQ_REMOVE(&sc->ready_list, acb, chain);
+ acb->flags &= ~ACB_READY;
+
+ flags = acb->xs->flags;
+ if (flags & SCSI_RESET)
+ wd33c93_reset(sc);
+
+ /* XXX - Implicitly call scsidone on select timeout */
+ if (wd33c93_go(sc, acb) != 0 || acb->xs->error == XS_SELTIMEOUT) {
+ acb->dleft = sc->sc_dleft;
+ wd33c93_scsidone(sc, acb, sc->sc_status);
+ return;
+ }
+}
+
+void
+wd33c93_scsidone(struct wd33c93_softc *sc, struct wd33c93_acb *acb, int status)
+{
+ struct scsi_xfer *xs = acb->xs;
+ struct scsi_link *sc_link = xs->sc_link;
+ struct wd33c93_tinfo *ti;
+ struct wd33c93_linfo *li;
+ int s;
+
+#ifdef DIAGNOSTIC
+ KASSERT(sc->target == sc_link->target);
+ KASSERT(sc->lun == sc_link->lun);
+ KASSERT(acb->flags != ACB_FREE);
+#endif
+
+ SBIC_DEBUG(ACBS, ("scsidone: (%d,%d)->(%d,%d)%02x\n",
+ sc_link->target, sc_link->lun, sc->target, sc->lun, status));
+
+ timeout_del(&acb->to);
+
+ if (xs->error == XS_NOERROR) {
+ xs->status = status & SCSI_STATUS_MASK;
+ xs->resid = acb->dleft;
+
+ switch (xs->status) {
+ case SCSI_CHECK:
+ case SCSI_TERMINATED:
+ /* XXX Need to read sense - return busy for now */
+ /*FALLTHROUGH*/
+ case SCSI_QUEUE_FULL:
+ case SCSI_BUSY:
+ xs->error = XS_BUSY;
+ break;
+ }
+ }
+
+ ti = &sc->sc_tinfo[sc_link->target];
+ li = TINFO_LUN(ti, sc_link->lun);
+ ti->cmds++;
+ if (xs->error == XS_SELTIMEOUT) {
+ /* Selection timeout -- discard this LUN if empty */
+ if (li->untagged == NULL && li->used == 0) {
+ if (sc_link->lun < SBIC_NLUN)
+ ti->lun[sc_link->lun] = NULL;
+ free(li, M_DEVBUF);
+ }
+ }
+
+ wd33c93_dequeue(sc, acb);
+ if (sc->sc_nexus == acb) {
+ sc->sc_state = SBIC_IDLE;
+ sc->sc_nexus = NULL;
+ sc->sc_flags = 0;
+
+ if (!TAILQ_EMPTY(&sc->ready_list))
+ wd33c93_sched(sc);
+ }
+
+ /* place control block back on free list. */
+ if ((xs->flags & SCSI_POLL) == 0) {
+ s = splbio();
+ acb->flags = ACB_FREE;
+ pool_put(&wd33c93_pool, acb);
+ splx(s);
+ }
+
+ scsi_done(xs);
+}
+
+void
+wd33c93_dequeue(struct wd33c93_softc *sc, struct wd33c93_acb *acb)
+{
+ struct wd33c93_tinfo *ti = &sc->sc_tinfo[acb->xs->sc_link->target];
+ struct wd33c93_linfo *li;
+ int lun = acb->xs->sc_link->lun;
+
+ li = TINFO_LUN(ti, lun);
+#ifdef DIAGNOSTIC
+ if (li == NULL || li->lun != lun)
+ panic("wd33c93_dequeue: lun %d for ecb %p does not exist",
+ lun, acb);
+#endif
+ if (li->untagged == acb) {
+ li->state = L_STATE_IDLE;
+ li->untagged = NULL;
+ }
+ if (acb->tag_type && li->queued[acb->tag_id] != NULL) {
+#ifdef DIAGNOSTIC
+ if (li->queued[acb->tag_id] != NULL &&
+ (li->queued[acb->tag_id] != acb))
+ panic("wd33c93_dequeue: slot %d for lun %d has %p "
+ "instead of acb %p\n", acb->tag_id,
+ lun, li->queued[acb->tag_id], acb);
+#endif
+ li->queued[acb->tag_id] = NULL;
+ li->used--;
+ }
+}
+
+
+int
+wd33c93_wait(struct wd33c93_softc *sc, u_char until, int timeo, int line)
+{
+ u_char val;
+
+ if (timeo == 0)
+ timeo = 1000000; /* some large value.. */
+ GET_SBIC_asr(sc, val);
+ while ((val & until) == 0) {
+ if (timeo-- == 0) {
+ int csr;
+ GET_SBIC_csr(sc, csr);
+#ifdef SBICDEBUG
+ printf("wd33c93_wait: TIMEO @%d with asr=0x%x csr=0x%x\n",
+ line, val, csr);
+#ifdef DDB
+ Debugger();
+#endif
+#endif
+ return(val); /* Maybe I should abort */
+ break;
+ }
+ DELAY(1);
+ GET_SBIC_asr(sc, val);
+ }
+ return(val);
+}
+
+int
+wd33c93_abort(struct wd33c93_softc *sc, struct wd33c93_acb *acb,
+ const char *where)
+{
+ u_char csr, asr;
+
+ GET_SBIC_asr(sc, asr);
+ GET_SBIC_csr(sc, csr);
+
+ sc_print_addr(acb->xs->sc_link);
+ printf("ABORT in %s: csr=0x%02x, asr=0x%02x\n", where, csr, asr);
+
+ acb->timeout = SBIC_ABORT_TIMEOUT;
+ acb->flags |= ACB_ABORT;
+
+ /*
+ * Clean up chip itself
+ */
+ if (sc->sc_nexus == acb) {
+ /* Reschedule timeout. */
+ timeout_add_msec(&acb->to, acb->timeout);
+
+ while (asr & SBIC_ASR_DBR) {
+ /*
+ * wd33c93 is jammed w/data. need to clear it
+ * But we don't know what direction it needs to go
+ */
+ GET_SBIC_data(sc, asr);
+ printf("abort %s: clearing data buffer 0x%02x\n",
+ where, asr);
+ GET_SBIC_asr(sc, asr);
+ if (asr & SBIC_ASR_DBR) /* Not the read direction */
+ SET_SBIC_data(sc, asr);
+ GET_SBIC_asr(sc, asr);
+ }
+
+ sc_print_addr(acb->xs->sc_link);
+ printf("sending ABORT command\n");
+
+ WAIT_CIP(sc);
+ SET_SBIC_cmd(sc, SBIC_CMD_ABORT);
+ WAIT_CIP(sc);
+
+ GET_SBIC_asr(sc, asr);
+
+ sc_print_addr(acb->xs->sc_link);
+ if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) {
+ /*
+ * ok, get more drastic..
+ */
+ printf("Resetting bus\n");
+ wd33c93_reset(sc);
+ } else {
+ printf("sending DISCONNECT to target\n");
+ SET_SBIC_cmd(sc, SBIC_CMD_DISC);
+ WAIT_CIP(sc);
+
+ do {
+ SBIC_WAIT (sc, SBIC_ASR_INT, 0);
+ GET_SBIC_asr(sc, asr);
+ GET_SBIC_csr(sc, csr);
+ SBIC_DEBUG(MISC, ("csr: 0x%02x, asr: 0x%02x\n",
+ csr, asr));
+ } while ((csr != SBIC_CSR_DISC) &&
+ (csr != SBIC_CSR_DISC_1) &&
+ (csr != SBIC_CSR_CMD_INVALID));
+ }
+ sc->sc_state = SBIC_ERROR;
+ sc->sc_flags = 0;
+ }
+ return SBIC_STATE_ERROR;
+}
+
+
+/*
+ * select the bus, return when selected or error.
+ *
+ * Returns the current CSR following selection and optionally MSG out phase.
+ * i.e. the returned CSR *should* indicate CMD phase...
+ * If the return value is 0, some error happened.
+ */
+u_char
+wd33c93_selectbus(struct wd33c93_softc *sc, struct wd33c93_acb *acb)
+{
+ struct scsi_xfer *xs = acb->xs;
+ struct scsi_link *sc_link = xs->sc_link;
+ struct wd33c93_tinfo *ti;
+ u_char target, lun, asr, csr, id;
+
+ KASSERT(sc->sc_state == SBIC_IDLE);
+
+ target = sc_link->target;
+ lun = sc_link->lun;
+ ti = &sc->sc_tinfo[target];
+
+ sc->sc_state = SBIC_SELECTING;
+ sc->target = target;
+ sc->lun = lun;
+
+ SBIC_DEBUG(PHASE, ("wd33c93_selectbus %d: ", target));
+
+ if ((xs->flags & SCSI_POLL) == 0)
+ timeout_add_msec(&acb->to, acb->timeout);
+
+ /*
+ * issue select
+ */
+ SBIC_TC_PUT(sc, 0);
+ SET_SBIC_selid(sc, target);
+ SET_SBIC_timeo(sc, SBIC_TIMEOUT(250, sc->sc_clkfreq));
+
+ GET_SBIC_asr(sc, asr);
+ if (asr & (SBIC_ASR_INT|SBIC_ASR_BSY)) {
+ /* This means we got ourselves reselected upon */
+ SBIC_DEBUG(PHASE, ("WD busy (reselect?) ASR=%02x\n", asr));
+ return 0;
+ }
+
+ SET_SBIC_cmd(sc, SBIC_CMD_SEL_ATN);
+ WAIT_CIP(sc);
+
+ /*
+ * wait for select (merged from separate function may need
+ * cleanup)
+ */
+ do {
+ asr = SBIC_WAIT(sc, SBIC_ASR_INT | SBIC_ASR_LCI, 0);
+ if (asr & SBIC_ASR_LCI) {
+ QPRINTF(("late LCI: asr %02x\n", asr));
+ return 0;
+ }
+
+ /* Clear interrupt */
+ GET_SBIC_csr (sc, csr);
+
+ /* Reselected from under our feet? */
+ if (csr == SBIC_CSR_RSLT_NI || csr == SBIC_CSR_RSLT_IFY) {
+ SBIC_DEBUG(PHASE, ("got reselected, asr %02x\n", asr));
+ /*
+ * We need to handle this now so we don't lock up later
+ */
+ wd33c93_nextstate(sc, acb, csr, asr);
+ return 0;
+ }
+
+ /* Whoops! */
+ if (csr == SBIC_CSR_SLT || csr == SBIC_CSR_SLT_ATN) {
+ panic("wd33c93_selectbus: target issued select!");
+ return 0;
+ }
+
+ } while (csr != (SBIC_CSR_MIS_2 | MESG_OUT_PHASE) &&
+ csr != (SBIC_CSR_MIS_2 | CMD_PHASE) &&
+ csr != SBIC_CSR_SEL_TIMEO);
+
+ /* Anyone at home? */
+ if (csr == SBIC_CSR_SEL_TIMEO) {
+ xs->error = XS_SELTIMEOUT;
+ SBIC_DEBUG(PHASE, ("-- Selection Timeout\n"));
+ return 0;
+ }
+
+ SBIC_DEBUG(PHASE, ("Selection Complete\n"));
+
+ /* Assume we're now selected */
+ GET_SBIC_selid(sc, id);
+ if (id != target) {
+ /* Something went wrong - wrong target was select */
+ printf("wd33c93_selectbus: wrong target selected;"
+ " WANTED %d GOT %d", target, id);
+ return 0; /* XXX: Need to call nexstate to handle? */
+ }
+
+ sc->sc_flags |= SBICF_SELECTED;
+ sc->sc_state = SBIC_CONNECTED;
+
+ /* setup correct sync mode for this target */
+ wd33c93_setsync(sc, ti);
+
+ if (ti->flags & T_NODISC && sc->sc_disc == 0)
+ SET_SBIC_rselid (sc, 0); /* Not expecting a reselect */
+ else
+ SET_SBIC_rselid (sc, SBIC_RID_ER);
+
+ /*
+ * We only really need to do anything when the target goes to MSG out
+ * If the device ignored ATN, it's probably old and brain-dead,
+ * but we'll try to support it anyhow.
+ * If it doesn't support message out, it definately doesn't
+ * support synchronous transfers, so no point in even asking...
+ */
+ if (csr == (SBIC_CSR_MIS_2 | MESG_OUT_PHASE)) {
+ if (ti->flags & T_NEGOTIATE) {
+ /* Initiate a SDTR message */
+ SBIC_DEBUG(SYNC, ("Sending SDTR to target %d\n", id));
+ if (ti->flags & T_WANTSYNC) {
+ ti->period = sc->sc_minsyncperiod;
+ ti->offset = sc->sc_maxoffset;
+ } else {
+ ti->period = 0;
+ ti->offset = 0;
+ }
+ /* Send Sync negotiation message */
+ sc->sc_omsg[0] = MSG_IDENTIFY(lun, 0); /* No Disc */
+ sc->sc_omsg[1] = MSG_EXTENDED;
+ sc->sc_omsg[2] = MSG_EXT_SDTR_LEN;
+ sc->sc_omsg[3] = MSG_EXT_SDTR;
+ if (ti->flags & T_WANTSYNC) {
+ sc->sc_omsg[4] = sc->sc_minsyncperiod;
+ sc->sc_omsg[5] = sc->sc_maxoffset;
+ } else {
+ sc->sc_omsg[4] = 0;
+ sc->sc_omsg[5] = 0;
+ }
+ wd33c93_xfout(sc, 6, sc->sc_omsg);
+ sc->sc_msgout |= SEND_SDTR; /* may be rejected */
+ sc->sc_flags |= SBICF_SYNCNEGO;
+ } else {
+ if (sc->sc_nexus->tag_type != 0) {
+ /* Use TAGS */
+ SBIC_DEBUG(TAGS, ("<select %d:%d TAG=%x>\n",
+ sc->target, sc->lun,
+ sc->sc_nexus->tag_id));
+ sc->sc_omsg[0] = MSG_IDENTIFY(lun, 1);
+ sc->sc_omsg[1] = sc->sc_nexus->tag_type;
+ sc->sc_omsg[2] = sc->sc_nexus->tag_id;
+ wd33c93_xfout(sc, 3, sc->sc_omsg);
+ sc->sc_msgout |= SEND_TAG;
+ } else {
+ int no_disc;
+
+ /* Setup LUN nexus and disconnect privilege */
+ no_disc = xs->flags & SCSI_POLL ||
+ ti->flags & T_NODISC;
+ SEND_BYTE(sc, MSG_IDENTIFY(lun, !no_disc));
+ }
+ }
+ /*
+ * There's one interrupt still to come:
+ * the change to CMD phase...
+ */
+ SBIC_WAIT(sc, SBIC_ASR_INT , 0);
+ GET_SBIC_csr(sc, csr);
+ }
+
+ return csr;
+}
+
+/*
+ * Information Transfer *to* a SCSI Target.
+ *
+ * Note: Don't expect there to be an interrupt immediately after all
+ * the data is transferred out. The WD spec sheet says that the Transfer-
+ * Info command for non-MSG_IN phases only completes when the target
+ * next asserts 'REQ'. That is, when the SCSI bus changes to a new state.
+ *
+ * This can have a nasty effect on commands which take a relatively long
+ * time to complete, for example a START/STOP unit command may remain in
+ * CMD phase until the disk has spun up. Only then will the target change
+ * to STATUS phase. This is really only a problem for immediate commands
+ * since we don't allow disconnection for them (yet).
+ */
+int
+wd33c93_xfout(struct wd33c93_softc *sc, int len, void *bp)
+{
+ int wait = wd33c93_data_wait;
+ u_char asr, *buf = bp;
+
+ QPRINTF(("wd33c93_xfout {%d} %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x\n", len, buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9]));
+
+ /*
+ * sigh.. WD-PROTO strikes again.. sending the command in one go
+ * causes the chip to lock up if talking to certain (misbehaving?)
+ * targets. Anyway, this procedure should work for all targets, but
+ * it's slightly slower due to the overhead
+ */
+
+ SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
+ SBIC_TC_PUT (sc, (unsigned)len);
+
+ WAIT_CIP (sc);
+ SET_SBIC_cmd (sc, SBIC_CMD_XFER_INFO);
+
+ /*
+ * Loop for each byte transferred
+ */
+ do {
+ GET_SBIC_asr (sc, asr);
+
+ if (asr & SBIC_ASR_DBR) {
+ if (len) {
+ SET_SBIC_data (sc, *buf);
+ buf++;
+ len--;
+ } else {
+ SET_SBIC_data (sc, 0);
+ }
+ wait = wd33c93_data_wait;
+ }
+ } while (len && (asr & SBIC_ASR_INT) == 0 && wait-- > 0);
+
+ QPRINTF(("wd33c93_xfout done: %d bytes remaining (wait:%d)\n", len, wait));
+
+ /*
+ * Normally, an interrupt will be pending when this routing returns.
+ */
+ return(len);
+}
+
+/*
+ * Information Transfer *from* a Scsi Target
+ * returns # bytes left to read
+ */
+int
+wd33c93_xfin(struct wd33c93_softc *sc, int len, void *bp)
+{
+ int wait = wd33c93_data_wait;
+ u_char *buf = bp;
+ u_char asr;
+#ifdef SBICDEBUG
+ u_char *obp = bp;
+#endif
+ SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
+ SBIC_TC_PUT (sc, (unsigned)len);
+
+ WAIT_CIP (sc);
+ SET_SBIC_cmd (sc, SBIC_CMD_XFER_INFO);
+
+ /*
+ * Loop for each byte transferred
+ */
+ do {
+ GET_SBIC_asr (sc, asr);
+
+ if (asr & SBIC_ASR_DBR) {
+ if (len) {
+ GET_SBIC_data (sc, *buf);
+ buf++;
+ len--;
+ } else {
+ u_char foo;
+ GET_SBIC_data (sc, foo);
+ }
+ wait = wd33c93_data_wait;
+ }
+
+ } while ((asr & SBIC_ASR_INT) == 0 && wait-- > 0);
+
+ QPRINTF(("wd33c93_xfin {%d} %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2],
+ obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9]));
+
+ SBIC_TC_PUT (sc, 0);
+
+ /*
+ * this leaves with one csr to be read
+ */
+ return len;
+}
+
+
+/*
+ * Finish SCSI xfer command: After the completion interrupt from
+ * a read/write operation, sequence through the final phases in
+ * programmed i/o.
+ */
+void
+wd33c93_xferdone(struct wd33c93_softc *sc)
+{
+ u_char phase, csr;
+ int s;
+
+ QPRINTF(("{"));
+ s = splbio();
+
+ /*
+ * have the wd33c93 complete on its own
+ */
+ SBIC_TC_PUT(sc, 0);
+ SET_SBIC_cmd_phase(sc, 0x46);
+ SET_SBIC_cmd(sc, SBIC_CMD_SEL_ATN_XFER);
+
+ do {
+ SBIC_WAIT (sc, SBIC_ASR_INT, 0);
+ GET_SBIC_csr (sc, csr);
+ QPRINTF(("%02x:", csr));
+ } while ((csr != SBIC_CSR_DISC) &&
+ (csr != SBIC_CSR_DISC_1) &&
+ (csr != SBIC_CSR_S_XFERRED));
+
+ sc->sc_flags &= ~SBICF_SELECTED;
+ sc->sc_state = SBIC_DISCONNECT;
+
+ GET_SBIC_cmd_phase (sc, phase);
+ QPRINTF(("}%02x", phase));
+
+ if (phase == 0x60)
+ GET_SBIC_tlun(sc, sc->sc_status);
+ else
+ wd33c93_error(sc, sc->sc_nexus);
+
+ QPRINTF(("=STS:%02x=\n", sc->sc_status));
+ splx(s);
+}
+
+
+int
+wd33c93_go(struct wd33c93_softc *sc, struct wd33c93_acb *acb)
+{
+ struct scsi_xfer *xs = acb->xs;
+ struct scsi_link *sc_link = xs->sc_link;
+ int i, dmaok;
+ u_char csr, asr;
+
+ SBIC_DEBUG(ACBS, ("wd33c93_go(%d:%d)\n", sc_link->target, sc_link->lun));
+
+ sc->sc_nexus = acb;
+
+ sc->target = sc_link->target;
+ sc->lun = sc_link->lun;
+
+ sc->sc_status = STATUS_UNKNOWN;
+ sc->sc_daddr = acb->daddr;
+ sc->sc_dleft = acb->dleft;
+
+ sc->sc_msgpriq = sc->sc_msgout = sc->sc_msgoutq = 0;
+ sc->sc_flags = 0;
+
+ dmaok = wd33c93_dmaok(sc, xs);
+
+ if (dmaok == 0)
+ sc->sc_flags |= SBICF_NODMA;
+
+ SBIC_DEBUG(DMA, ("wd33c93_go dmago:%d(tcnt=%zx) dmaok=%d\n",
+ sc->target, sc->sc_tcnt, dmaok));
+
+ /* select the SCSI bus (it's an error if bus isn't free) */
+ if ((csr = wd33c93_selectbus(sc, acb)) == 0)
+ return(0); /* Not done: needs to be rescheduled */
+
+ /*
+ * Lets cycle a while then let the interrupt handler take over.
+ */
+ GET_SBIC_asr(sc, asr);
+ do {
+ QPRINTF(("go[0x%x] ", csr));
+
+ /* Handle the new phase */
+ i = wd33c93_nextstate(sc, acb, csr, asr);
+ WAIT_CIP(sc); /* XXX */
+ if (sc->sc_state == SBIC_CONNECTED) {
+
+ GET_SBIC_asr(sc, asr);
+
+ if (asr & SBIC_ASR_LCI)
+ printf("wd33c93_go: LCI asr:%02x csr:%02x\n", asr, csr);
+
+ if (asr & SBIC_ASR_INT)
+ GET_SBIC_csr(sc, csr);
+ }
+
+ } while (sc->sc_state == SBIC_CONNECTED &&
+ asr & (SBIC_ASR_INT|SBIC_ASR_LCI));
+
+ QPRINTF(("> done i=%d stat=%02x\n", i, sc->sc_status));
+
+ if (i == SBIC_STATE_DONE) {
+ if (sc->sc_status == STATUS_UNKNOWN) {
+ printf("wd33c93_go: done & stat == UNKNOWN\n");
+ return 1; /* Did we really finish that fast? */
+ }
+ }
+ return 0;
+}
+
+
+int
+wd33c93_intr(void *v)
+{
+ struct wd33c93_softc *sc = v;
+ u_char asr, csr;
+ int i;
+
+ /*
+ * pending interrupt?
+ */
+ GET_SBIC_asr (sc, asr);
+ if ((asr & SBIC_ASR_INT) == 0)
+ return(0);
+
+ GET_SBIC_csr(sc, csr);
+
+ do {
+ SBIC_DEBUG(INTS, ("intr[csr=0x%x]", csr));
+
+ i = wd33c93_nextstate(sc, sc->sc_nexus, csr, asr);
+ WAIT_CIP(sc); /* XXX */
+ if (sc->sc_state == SBIC_CONNECTED) {
+ GET_SBIC_asr(sc, asr);
+
+ if (asr & SBIC_ASR_LCI)
+ printf("wd33c93_intr: LCI asr:%02x csr:%02x\n",
+ asr, csr);
+
+ if (asr & SBIC_ASR_INT)
+ GET_SBIC_csr(sc, csr);
+ }
+ } while (sc->sc_state == SBIC_CONNECTED &&
+ asr & (SBIC_ASR_INT|SBIC_ASR_LCI));
+
+ SBIC_DEBUG(INTS, ("intr done. state=%d, asr=0x%02x\n", i, asr));
+
+ return(1);
+}
+
+/*
+ * Complete current command using polled I/O. Used when interrupt driven
+ * I/O is not allowed (ie. during boot and shutdown)
+ *
+ * Polled I/O is very processor intensive
+ */
+int
+wd33c93_poll(struct wd33c93_softc *sc, struct wd33c93_acb *acb)
+{
+ u_char asr, csr=0;
+ int i, count;
+ struct scsi_xfer *xs = acb->xs;
+ int s;
+
+ SBIC_WAIT(sc, SBIC_ASR_INT, wd33c93_cmd_wait);
+ for (count = acb->timeout; count;) {
+ GET_SBIC_asr(sc, asr);
+ if (asr & SBIC_ASR_LCI)
+ printf("wd33c93_poll: LCI; asr:%02x csr:%02x\n",
+ asr, csr);
+ if (asr & SBIC_ASR_INT) {
+ GET_SBIC_csr(sc, csr);
+ sc->sc_flags |= SBICF_NODMA;
+ i = wd33c93_nextstate(sc, sc->sc_nexus, csr, asr);
+ WAIT_CIP(sc); /* XXX */
+ } else {
+ DELAY(1000);
+ count--;
+ }
+
+ if ((xs->flags & ITSDONE) != 0) {
+ s = splbio();
+ acb->flags = ACB_FREE;
+ pool_put(&wd33c93_pool, acb);
+ splx(s);
+
+ return (0);
+ }
+
+ if (sc->sc_state == SBIC_IDLE) {
+ SBIC_DEBUG(ACBS, ("[poll: rescheduling] "));
+ wd33c93_sched(sc);
+ }
+ }
+ return (1);
+}
+
+static inline int
+__verify_msg_format(u_char *p, int len)
+{
+ if (len == 1 && IS1BYTEMSG(p[0]))
+ return 1;
+ if (len == 2 && IS2BYTEMSG(p[0]))
+ return 1;
+ if (len >= 3 && ISEXTMSG(p[0]) &&
+ len == p[1] + 2)
+ return 1;
+ return 0;
+}
+
+/*
+ * Handle message_in phase
+ */
+int
+wd33c93_msgin_phase(struct wd33c93_softc *sc, int reselect)
+{
+ int len;
+ u_char asr, csr, *msg;
+
+ GET_SBIC_asr(sc, asr);
+
+ SBIC_DEBUG(MSGS, ("wd33c93msgin asr=%02x\n", asr));
+
+ GET_SBIC_selid (sc, csr);
+ SET_SBIC_selid (sc, csr | SBIC_SID_FROM_SCSI);
+
+ SBIC_TC_PUT(sc, 0);
+
+ SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+ msg = sc->sc_imsg;
+ len = 0;
+
+ do {
+ /* Fetch the next byte of the message */
+ RECV_BYTE(sc, *msg++);
+ len++;
+
+ /*
+ * get the command completion interrupt, or we
+ * can't send a new command (LCI)
+ */
+ SBIC_WAIT(sc, SBIC_ASR_INT, 0);
+ GET_SBIC_csr(sc, csr);
+
+ if (__verify_msg_format(sc->sc_imsg, len))
+ break; /* Complete message received */
+
+ /*
+ * Clear ACK, and wait for the interrupt
+ * for the next byte or phase change
+ */
+ SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK);
+ SBIC_WAIT(sc, SBIC_ASR_INT, 0);
+
+ GET_SBIC_csr(sc, csr);
+ } while (len < SBIC_MAX_MSGLEN);
+
+ if (__verify_msg_format(sc->sc_imsg, len))
+ wd33c93_msgin(sc, sc->sc_imsg, len);
+
+ /*
+ * Clear ACK, and wait for the interrupt
+ * for the phase change
+ */
+ SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK);
+ SBIC_WAIT(sc, SBIC_ASR_INT, 0);
+
+ /* Should still have one CSR to read */
+ return SBIC_STATE_RUNNING;
+}
+
+
+void wd33c93_msgin(struct wd33c93_softc *sc, u_char *msgaddr, int msglen)
+{
+ struct wd33c93_acb *acb = sc->sc_nexus;
+ struct wd33c93_tinfo *ti = &sc->sc_tinfo[sc->target];
+ struct wd33c93_linfo *li;
+ u_char asr;
+
+ switch (sc->sc_state) {
+ case SBIC_CONNECTED:
+ switch (msgaddr[0]) {
+ case MSG_MESSAGE_REJECT:
+ SBIC_DEBUG(MSGS, ("msgin: MSG_REJECT, "
+ "last msgout=%x\n", sc->sc_msgout));
+ switch (sc->sc_msgout) {
+ case SEND_TAG:
+ printf("%s: tagged queuing rejected: "
+ "target %d\n",
+ sc->sc_dev.dv_xname, sc->target);
+ ti->flags &= ~T_TAG;
+ li = TINFO_LUN(ti, sc->lun);
+ if (acb->tag_type &&
+ li->queued[acb->tag_id] != NULL) {
+ li->queued[acb->tag_id] = NULL;
+ li->used--;
+ }
+ acb->tag_type = acb->tag_id = 0;
+ li->untagged = acb;
+ li->state = L_STATE_BUSY;
+ break;
+
+ case SEND_SDTR:
+ printf("%s: sync transfer rejected: target %d\n",
+ sc->sc_dev.dv_xname, sc->target);
+
+ sc->sc_flags &= ~SBICF_SYNCNEGO;
+ ti->flags &= ~(T_NEGOTIATE | T_SYNCMODE);
+ wd33c93_setsync(sc, ti);
+
+ case SEND_INIT_DET_ERR:
+ goto abort;
+
+ default:
+ SBIC_DEBUG(MSGS, ("Unexpected MSG_REJECT\n"));
+ break;
+ }
+ sc->sc_msgout = 0;
+ break;
+
+ case MSG_HEAD_OF_Q_TAG:
+ case MSG_ORDERED_Q_TAG:
+ case MSG_SIMPLE_Q_TAG:
+ printf("-- Out of phase TAG;"
+ "Nexus=%d:%d Tag=%02x/%02x\n",
+ sc->target, sc->lun, msgaddr[0], msgaddr[1]);
+ break;
+
+ case MSG_DISCONNECT:
+ SBIC_DEBUG(MSGS, ("msgin: DISCONNECT"));
+ /*
+ * Mark the fact that all bytes have moved. The
+ * target may not bother to do a SAVE POINTERS
+ * at this stage. This flag will set the residual
+ * count to zero on MSG COMPLETE.
+ */
+ if (sc->sc_dleft == 0)
+ acb->flags |= ACB_COMPLETE;
+
+ if (acb->xs->flags & SCSI_POLL)
+ /* Don't allow disconnect in immediate mode */
+ goto reject;
+ else { /* Allow disconnect */
+ sc->sc_flags &= ~SBICF_SELECTED;
+ sc->sc_state = SBIC_DISCONNECT;
+ }
+ if ((acb->xs->sc_link->quirks & SDEV_AUTOSAVE) == 0)
+ break;
+ /*FALLTHROUGH*/
+
+ case MSG_SAVEDATAPOINTER:
+ SBIC_DEBUG(MSGS, ("msgin: SAVEDATAPTR"));
+ acb->daddr = sc->sc_daddr;
+ acb->dleft = sc->sc_dleft;
+ break;
+
+ case MSG_RESTOREPOINTERS:
+ SBIC_DEBUG(MSGS, ("msgin: RESTOREPTR"));
+ sc->sc_daddr = acb->daddr;
+ sc->sc_dleft = acb->dleft;
+ break;
+
+ case MSG_CMDCOMPLETE:
+ /*
+ * !! KLUDGE ALERT !! quite a few drives don't seem to
+ * really like the current way of sending the
+ * sync-handshake together with the ident-message, and
+ * they react by sending command-complete and
+ * disconnecting right after returning the valid sync
+ * handshake. So, all I can do is reselect the drive,
+ * and hope it won't disconnect again. I don't think
+ * this is valid behavior, but I can't help fixing a
+ * problem that apparently exists.
+ *
+ * Note: we should not get here on `normal' command
+ * completion, as that condition is handled by the
+ * high-level sel&xfer resume command used to walk
+ * thru status/cc-phase.
+ */
+ SBIC_DEBUG(MSGS, ("msgin: CMD_COMPLETE"));
+ SBIC_DEBUG(SYNC, ("GOT MSG %d! target %d"
+ " acting weird.."
+ " waiting for disconnect...\n",
+ msgaddr[0], sc->target));
+
+ /* Check to see if wd33c93 is handling this */
+ GET_SBIC_asr(sc, asr);
+ if (asr & SBIC_ASR_BSY)
+ break;
+
+ /* XXX: Assume it works and set status to 00 */
+ sc->sc_status = 0;
+ sc->sc_state = SBIC_CMDCOMPLETE;
+ break;
+
+ case MSG_EXTENDED:
+ switch(msgaddr[2]) {
+ case MSG_EXT_SDTR: /* Sync negotiation */
+ SBIC_DEBUG(MSGS, ("msgin: EXT_SDTR; "
+ "period %d, offset %d",
+ msgaddr[3], msgaddr[4]));
+ if (msgaddr[1] != 3)
+ goto reject;
+
+ ti->period =
+ MAX(msgaddr[3], sc->sc_minsyncperiod);
+ ti->offset = MIN(msgaddr[4], sc->sc_maxoffset);
+
+ /*
+ * <SGI, IBM DORS-32160, WA6A> will do nothing
+ * but attempt sync negotiation until it gets
+ * what it wants. To avoid an infinite loop set
+ * off by the identify request, oblige them.
+ */
+ if ((sc->sc_flags&SBICF_SYNCNEGO) == 0 &&
+ msgaddr[3] != 0)
+ ti->flags |= T_WANTSYNC;
+
+ if (!(ti->flags & T_WANTSYNC))
+ ti->period = ti->offset = 0;
+
+ ti->flags &= ~T_NEGOTIATE;
+
+ if (ti->offset == 0)
+ ti->flags &= ~T_SYNCMODE; /* Async */
+ else
+ ti->flags |= T_SYNCMODE; /* Sync */
+
+ /* target initiated negotiation */
+ if ((sc->sc_flags&SBICF_SYNCNEGO) == 0)
+ wd33c93_sched_msgout(sc, SEND_SDTR);
+ sc->sc_flags &= ~SBICF_SYNCNEGO;
+
+ SBIC_DEBUG(SYNC, ("msgin(%d): SDTR(o=%d,p=%d)",
+ sc->target, ti->offset,
+ ti->period));
+ wd33c93_setsync(sc, ti);
+ break;
+
+ case MSG_EXT_WDTR:
+ SBIC_DEBUG(MSGS, ("msgin: EXT_WDTR rejected"));
+ goto reject;
+
+ default:
+ sc_print_addr(acb->xs->sc_link);
+ printf("unrecognized MESSAGE EXTENDED;"
+ " sending REJECT\n");
+ goto reject;
+ }
+ break;
+
+ default:
+ sc_print_addr(acb->xs->sc_link);
+ printf("unrecognized MESSAGE; sending REJECT\n");
+
+ reject:
+ /* We don't support whatever this message is... */
+ wd33c93_sched_msgout(sc, SEND_REJECT);
+ break;
+ }
+ break;
+
+ case SBIC_IDENTIFIED:
+ /*
+ * IDENTIFY message was received and queue tag is expected now
+ */
+ if ((msgaddr[0]!=MSG_SIMPLE_Q_TAG) || (sc->sc_msgify==0)) {
+ printf("%s: TAG reselect without IDENTIFY;"
+ " MSG %x; sending DEVICE RESET\n",
+ sc->sc_dev.dv_xname, msgaddr[0]);
+ goto reset;
+ }
+ SBIC_DEBUG(TAGS, ("TAG %x/%x\n", msgaddr[0], msgaddr[1]));
+ if (sc->sc_nexus)
+ printf("*TAG Recv with active nexus!!\n");
+ wd33c93_reselect(sc, sc->target, sc->lun,
+ msgaddr[0], msgaddr[1]);
+ break;
+
+ case SBIC_RESELECTED:
+ /*
+ * IDENTIFY message with target
+ */
+ if (MSG_ISIDENTIFY(msgaddr[0])) {
+ SBIC_DEBUG(PHASE, ("IFFY[%x] ", msgaddr[0]));
+ sc->sc_msgify = msgaddr[0];
+ } else {
+ printf("%s: reselect without IDENTIFY;"
+ " MSG %x;"
+ " sending DEVICE RESET\n",
+ sc->sc_dev.dv_xname, msgaddr[0]);
+ goto reset;
+ }
+ break;
+
+ default:
+ printf("%s: unexpected MESSAGE IN. State=%d - Sending RESET\n",
+ sc->sc_dev.dv_xname, sc->sc_state);
+ reset:
+ wd33c93_sched_msgout(sc, SEND_DEV_RESET);
+ break;
+ abort:
+ wd33c93_sched_msgout(sc, SEND_ABORT);
+ break;
+ }
+}
+
+void
+wd33c93_sched_msgout(struct wd33c93_softc *sc, u_short msg)
+{
+ u_char asr;
+
+ SBIC_DEBUG(SYNC,("sched_msgout: %04x\n", msg));
+ sc->sc_msgpriq |= msg;
+
+ /* Schedule MSGOUT Phase to send message */
+
+ WAIT_CIP(sc);
+ SET_SBIC_cmd(sc, SBIC_CMD_SET_ATN);
+ WAIT_CIP(sc);
+ GET_SBIC_asr(sc, asr);
+ if (asr & SBIC_ASR_LCI) {
+ printf("MSGOUT Failed!\n");
+ }
+ SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK);
+ WAIT_CIP(sc);
+}
+
+/*
+ * Send the highest priority, scheduled message
+ */
+void
+wd33c93_msgout(struct wd33c93_softc *sc)
+{
+ struct wd33c93_tinfo *ti;
+ struct wd33c93_acb *acb = sc->sc_nexus;
+
+ if (acb == NULL)
+ panic("MSGOUT with no nexus");
+
+ if (sc->sc_omsglen == 0) {
+ /* Pick up highest priority message */
+ sc->sc_msgout = sc->sc_msgpriq & -sc->sc_msgpriq;
+ sc->sc_msgoutq |= sc->sc_msgout;
+ sc->sc_msgpriq &= ~sc->sc_msgout;
+ sc->sc_omsglen = 1; /* "Default" message len */
+ switch (sc->sc_msgout) {
+ case SEND_SDTR:
+ ti = &sc->sc_tinfo[acb->xs->sc_link->target];
+ sc->sc_omsg[0] = MSG_EXTENDED;
+ sc->sc_omsg[1] = MSG_EXT_SDTR_LEN;
+ sc->sc_omsg[2] = MSG_EXT_SDTR;
+ if (ti->flags & T_WANTSYNC) {
+ sc->sc_omsg[3] = ti->period;
+ sc->sc_omsg[4] = ti->offset;
+ } else {
+ sc->sc_omsg[3] = 0;
+ sc->sc_omsg[4] = 0;
+ }
+ sc->sc_omsglen = 5;
+ if ((sc->sc_flags & SBICF_SYNCNEGO) == 0) {
+ if (ti->flags & T_WANTSYNC)
+ ti->flags |= T_SYNCMODE;
+ else
+ ti->flags &= ~T_SYNCMODE;
+ wd33c93_setsync(sc, ti);
+ }
+ break;
+ case SEND_IDENTIFY:
+ if (sc->sc_state != SBIC_CONNECTED) {
+ printf("%s at line %d: no nexus\n",
+ sc->sc_dev.dv_xname, __LINE__);
+ }
+ sc->sc_omsg[0] =
+ MSG_IDENTIFY(acb->xs->sc_link->lun, 0);
+ break;
+ case SEND_TAG:
+ if (sc->sc_state != SBIC_CONNECTED) {
+ printf("%s at line %d: no nexus\n",
+ sc->sc_dev.dv_xname, __LINE__);
+ }
+ sc->sc_omsg[0] = acb->tag_type;
+ sc->sc_omsg[1] = acb->tag_id;
+ sc->sc_omsglen = 2;
+ break;
+ case SEND_DEV_RESET:
+ sc->sc_omsg[0] = MSG_BUS_DEV_RESET;
+ ti = &sc->sc_tinfo[sc->target];
+ ti->flags &= ~T_SYNCMODE;
+ if ((ti->flags & T_NOSYNC) == 0)
+ /* We can re-start sync negotiation */
+ ti->flags |= T_NEGOTIATE;
+ break;
+ case SEND_PARITY_ERROR:
+ sc->sc_omsg[0] = MSG_PARITY_ERROR;
+ break;
+ case SEND_ABORT:
+ sc->sc_flags |= SBICF_ABORTING;
+ sc->sc_omsg[0] = MSG_ABORT;
+ break;
+ case SEND_INIT_DET_ERR:
+ sc->sc_omsg[0] = MSG_INITIATOR_DET_ERR;
+ break;
+ case SEND_REJECT:
+ sc->sc_omsg[0] = MSG_MESSAGE_REJECT;
+ break;
+ default:
+ /* Wasn't expecting MSGOUT Phase */
+ sc->sc_omsg[0] = MSG_NOOP;
+ break;
+ }
+ }
+
+ wd33c93_xfout(sc, sc->sc_omsglen, sc->sc_omsg);
+}
+
+
+/*
+ * wd33c93_nextstate()
+ * return:
+ * SBIC_STATE_DONE == done
+ * SBIC_STATE_RUNNING == working
+ * SBIC_STATE_DISCONNECT == disconnected
+ * SBIC_STATE_ERROR == error
+ */
+int
+wd33c93_nextstate(struct wd33c93_softc *sc, struct wd33c93_acb *acb, u_char csr, u_char asr)
+{
+ SBIC_DEBUG(PHASE, ("next[a=%02x,c=%02x]: ",asr,csr));
+
+ switch (csr) {
+
+ case SBIC_CSR_XFERRED | CMD_PHASE:
+ case SBIC_CSR_MIS | CMD_PHASE:
+ case SBIC_CSR_MIS_1 | CMD_PHASE:
+ case SBIC_CSR_MIS_2 | CMD_PHASE:
+
+ if (wd33c93_xfout(sc, acb->clen, &acb->cmd))
+ goto abort;
+ break;
+
+ case SBIC_CSR_XFERRED | STATUS_PHASE:
+ case SBIC_CSR_MIS | STATUS_PHASE:
+ case SBIC_CSR_MIS_1 | STATUS_PHASE:
+ case SBIC_CSR_MIS_2 | STATUS_PHASE:
+
+ SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
+
+ /*
+ * this should be the normal i/o completion case.
+ * get the status & cmd complete msg then let the
+ * device driver look at what happened.
+ */
+ wd33c93_xferdone(sc);
+
+ wd33c93_dma_stop(sc);
+
+ /* Fixup byte count to be passed to higher layer */
+ acb->dleft = (acb->flags & ACB_COMPLETE) ? 0 :
+ sc->sc_dleft;
+
+ /*
+ * Indicate to the upper layers that the command is done
+ */
+ wd33c93_scsidone(sc, acb, sc->sc_status);
+
+ return SBIC_STATE_DONE;
+
+
+ case SBIC_CSR_XFERRED | DATA_IN_PHASE:
+ case SBIC_CSR_MIS | DATA_IN_PHASE:
+ case SBIC_CSR_MIS_1 | DATA_IN_PHASE:
+ case SBIC_CSR_MIS_2 | DATA_IN_PHASE:
+ case SBIC_CSR_XFERRED | DATA_OUT_PHASE:
+ case SBIC_CSR_MIS | DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_1 | DATA_OUT_PHASE:
+ case SBIC_CSR_MIS_2 | DATA_OUT_PHASE:
+ /*
+ * Verify that we expected to transfer data...
+ */
+ if (acb->dleft <= 0) {
+ printf("next: DATA phase with xfer count == %zd, asr:0x%02x csr:0x%02x\n",
+ acb->dleft, asr, csr);
+ goto abort;
+ }
+
+ /*
+ * Should we transfer using PIO or DMA ?
+ */
+ if (acb->xs->flags & SCSI_POLL ||
+ sc->sc_flags & SBICF_NODMA) {
+ /* Perfrom transfer using PIO */
+ int resid;
+
+ SBIC_DEBUG(DMA, ("PIO xfer: %d(%p:%zx)\n", sc->target,
+ sc->sc_daddr, sc->sc_dleft));
+
+ if (SBIC_PHASE(csr) == DATA_IN_PHASE)
+ /* data in */
+ resid = wd33c93_xfin(sc, sc->sc_dleft,
+ sc->sc_daddr);
+ else /* data out */
+ resid = wd33c93_xfout(sc, sc->sc_dleft,
+ sc->sc_daddr);
+
+ sc->sc_daddr = (char *)sc->sc_daddr +
+ (acb->dleft - resid);
+ sc->sc_dleft = resid;
+ } else {
+ int datain = SBIC_PHASE(csr) == DATA_IN_PHASE;
+
+ /* Perform transfer using DMA */
+ wd33c93_dma_setup(sc, datain);
+
+ SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI |
+ sc->sc_dmamode);
+
+ SBIC_DEBUG(DMA, ("DMA xfer: %d(%p:%zx)\n", sc->target,
+ sc->sc_daddr, sc->sc_dleft));
+
+ /* Setup byte count for transfer */
+ SBIC_TC_PUT(sc, (unsigned)sc->sc_dleft);
+
+ /* Start the transfer */
+ SET_SBIC_cmd(sc, SBIC_CMD_XFER_INFO);
+
+ /* Start the DMA chip going */
+ sc->sc_tcnt = sc->sc_dmago(sc);
+
+ /* Indicate that we're in DMA mode */
+ sc->sc_flags |= SBICF_INDMA;
+ }
+ break;
+
+ case SBIC_CSR_XFERRED | MESG_IN_PHASE:
+ case SBIC_CSR_MIS | MESG_IN_PHASE:
+ case SBIC_CSR_MIS_1 | MESG_IN_PHASE:
+ case SBIC_CSR_MIS_2 | MESG_IN_PHASE:
+
+ wd33c93_dma_stop(sc);
+
+ /* Handle a single message in... */
+ return wd33c93_msgin_phase(sc, 0);
+
+ case SBIC_CSR_MSGIN_W_ACK:
+
+ /*
+ * We should never see this since it's handled in
+ * 'wd33c93_msgin_phase()' but just for the sake of paranoia...
+ */
+ SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK);
+
+ printf("Acking unknown msgin CSR:%02x",csr);
+ break;
+
+ case SBIC_CSR_XFERRED | MESG_OUT_PHASE:
+ case SBIC_CSR_MIS | MESG_OUT_PHASE:
+ case SBIC_CSR_MIS_1 | MESG_OUT_PHASE:
+ case SBIC_CSR_MIS_2 | MESG_OUT_PHASE:
+
+ /*
+ * Message out phase. ATN signal has been asserted
+ */
+ wd33c93_dma_stop(sc);
+ wd33c93_msgout(sc);
+ return SBIC_STATE_RUNNING;
+
+ case SBIC_CSR_DISC:
+ case SBIC_CSR_DISC_1:
+ SBIC_DEBUG(RSEL, ("wd33c93next target %d disconnected\n",
+ sc->target));
+ wd33c93_dma_stop(sc);
+
+ sc->sc_nexus = NULL;
+ sc->sc_state = SBIC_IDLE;
+ sc->sc_flags = 0;
+
+ ++sc->sc_tinfo[sc->target].dconns;
+ ++sc->sc_disc;
+
+ if (acb->xs->flags & SCSI_POLL || wd33c93_nodisc)
+ return SBIC_STATE_DISCONNECT;
+
+ /* Try to schedule another target */
+ wd33c93_sched(sc);
+
+ return SBIC_STATE_DISCONNECT;
+
+ case SBIC_CSR_RSLT_NI:
+ case SBIC_CSR_RSLT_IFY:
+ {
+ /*
+ * A reselection.
+ * Note that since we don't enable Advanced Features (assuming
+ * the WD chip is at least the 'A' revision), we're only ever
+ * likely to see the 'SBIC_CSR_RSLT_NI' status. But for the
+ * hell of it, we'll handle it anyway, for all the extra code
+ * it needs...
+ */
+ u_char newtarget, newlun;
+
+ if (sc->sc_flags & SBICF_INDMA) {
+ printf("**** RESELECT WHILE DMA ACTIVE!!! ***\n");
+ wd33c93_dma_stop(sc);
+ }
+
+ sc->sc_state = SBIC_RESELECTED;
+ GET_SBIC_rselid(sc, newtarget);
+
+ /* check SBIC_RID_SIV? */
+ newtarget &= SBIC_RID_MASK;
+
+ if (csr == SBIC_CSR_RSLT_IFY) {
+ /* Read Identify msg to avoid lockup */
+ GET_SBIC_data(sc, newlun);
+ WAIT_CIP(sc);
+ newlun &= SBIC_TLUN_MASK;
+ sc->sc_msgify = MSG_IDENTIFY(newlun, 0);
+ } else {
+ /*
+ * Need to read Identify message the hard way, assuming
+ * the target even sends us one...
+ */
+ for (newlun = 255; newlun; --newlun) {
+ GET_SBIC_asr(sc, asr);
+ if (asr & SBIC_ASR_INT)
+ break;
+ DELAY(10);
+ }
+
+ /* If we didn't get an interrupt, somethink's up */
+ if ((asr & SBIC_ASR_INT) == 0) {
+ printf("%s: Reselect without identify? asr %x\n",
+ sc->sc_dev.dv_xname, asr);
+ newlun = 0; /* XXXX */
+ } else {
+ /*
+ * We got an interrupt, verify that it's a
+ * change to message in phase, and if so
+ * read the message.
+ */
+ GET_SBIC_csr(sc,csr);
+
+ if (csr == (SBIC_CSR_MIS | MESG_IN_PHASE) ||
+ csr == (SBIC_CSR_MIS_1 | MESG_IN_PHASE) ||
+ csr == (SBIC_CSR_MIS_2 | MESG_IN_PHASE)) {
+ /*
+ * Yup, gone to message in.
+ * Fetch the target LUN
+ */
+ sc->sc_msgify = 0;
+ wd33c93_msgin_phase(sc, 1);
+ newlun = sc->sc_msgify & SBIC_TLUN_MASK;
+ } else {
+ /*
+ * Whoops! Target didn't go to msg_in
+ * phase!!
+ */
+ printf("RSLT_NI - not MESG_IN_PHASE %x\n", csr);
+ newlun = 0; /* XXXSCW */
+ }
+ }
+ }
+
+ /* Ok, we have the identity of the reselecting target. */
+ SBIC_DEBUG(RSEL, ("wd33c93next: reselect from targ %d lun %d",
+ newtarget, newlun));
+ wd33c93_reselect(sc, newtarget, newlun, 0, 0);
+ sc->sc_disc--;
+
+ if (csr == SBIC_CSR_RSLT_IFY)
+ SET_SBIC_cmd(sc, SBIC_CMD_CLR_ACK);
+ break;
+ }
+
+ default:
+ abort:
+ /* Something unexpected happend -- deal with it. */
+ printf("next: aborting asr 0x%02x csr 0x%02x\n", asr, csr);
+
+#ifdef DDB
+ Debugger();
+#endif
+
+ SET_SBIC_control(sc, SBIC_CTL_EDI | SBIC_CTL_IDI);
+ if (acb->xs)
+ wd33c93_error(sc, acb);
+ wd33c93_abort(sc, acb, "next");
+
+ if (sc->sc_flags & SBICF_INDMA) {
+ wd33c93_dma_stop(sc);
+ wd33c93_scsidone(sc, acb, STATUS_UNKNOWN);
+ }
+ return SBIC_STATE_ERROR;
+ }
+ return SBIC_STATE_RUNNING;
+}
+
+
+void
+wd33c93_reselect(struct wd33c93_softc *sc, int target, int lun, int tag_type, int tag_id)
+{
+
+ struct wd33c93_tinfo *ti;
+ struct wd33c93_linfo *li;
+ struct wd33c93_acb *acb;
+
+ if (sc->sc_nexus) {
+ /*
+ * Whoops! We've been reselected with a
+ * command in progress!
+ * The best we can do is to put the current
+ * command back on the ready list and hope
+ * for the best.
+ */
+ SBIC_DEBUG(RSEL, ("%s: reselect with active command\n",
+ sc->sc_dev.dv_xname));
+ ti = &sc->sc_tinfo[sc->target];
+ li = TINFO_LUN(ti, sc->lun);
+ li->state = L_STATE_IDLE;
+
+ wd33c93_dequeue(sc, sc->sc_nexus);
+ TAILQ_INSERT_HEAD(&sc->ready_list, sc->sc_nexus, chain);
+ sc->sc_nexus->flags |= ACB_READY;
+
+ sc->sc_nexus = NULL;
+ }
+
+ /* Setup state for new nexus */
+ acb = NULL;
+ sc->sc_flags = SBICF_SELECTED;
+ sc->sc_msgpriq = sc->sc_msgout = sc->sc_msgoutq = 0;
+
+ ti = &sc->sc_tinfo[target];
+ li = TINFO_LUN(ti, lun);
+
+ if (li != NULL) {
+ if (li->untagged != NULL && li->state)
+ acb = li->untagged;
+ else if (tag_type != MSG_SIMPLE_Q_TAG) {
+ /* Wait for tag to come by during MESG_IN Phase */
+ sc->target = target; /* setup I_T_L nexus */
+ sc->lun = lun;
+ sc->sc_state = SBIC_IDENTIFIED;
+ return;
+ } else if (tag_type)
+ acb = li->queued[tag_id];
+ }
+
+ if (acb == NULL) {
+ printf("%s: reselect from target %d lun %d tag %x:%x "
+ "with no nexus; sending ABORT\n",
+ sc->sc_dev.dv_xname, target, lun, tag_type, tag_id);
+ goto abort;
+ }
+
+ sc->target = target;
+ sc->lun = lun;
+ sc->sc_nexus = acb;
+ sc->sc_state = SBIC_CONNECTED;
+
+ if (!wd33c93_dmaok(sc, acb->xs))
+ sc->sc_flags |= SBICF_NODMA;
+
+ /* Do an implicit RESTORE POINTERS. */
+ sc->sc_daddr = acb->daddr;
+ sc->sc_dleft = acb->dleft;
+
+ /* Set sync modes for new target */
+ wd33c93_setsync(sc, ti);
+
+ if (acb->flags & ACB_RESET)
+ wd33c93_sched_msgout(sc, SEND_DEV_RESET);
+ else if (acb->flags & ACB_ABORT)
+ wd33c93_sched_msgout(sc, SEND_ABORT);
+ return;
+
+abort:
+ wd33c93_sched_msgout(sc, SEND_ABORT);
+ return;
+
+}
+
+void
+wd33c93_timeout(void *arg)
+{
+ struct wd33c93_acb *acb = arg;
+ struct scsi_xfer *xs = acb->xs;
+ struct scsi_link *sc_link = xs->sc_link;
+ struct wd33c93_softc *sc = sc_link->adapter_softc;
+ int s, asr;
+
+ s = splbio();
+
+ GET_SBIC_asr(sc, asr);
+
+ sc_print_addr(sc_link);
+ printf("%s: timed out; asr=0x%02x [acb %p (flags 0x%x, dleft %zx)], "
+ "<state %d, nexus %p, resid %lx, msg(q %x,o %x)>",
+ sc->sc_dev.dv_xname, asr, acb, acb->flags, acb->dleft,
+ sc->sc_state, sc->sc_nexus, (long)sc->sc_dleft,
+ sc->sc_msgpriq, sc->sc_msgout);
+
+ if (asr & SBIC_ASR_INT) {
+ /* We need to service a missed IRQ */
+ wd33c93_intr(sc);
+ } else {
+ (void) wd33c93_abort(sc, sc->sc_nexus, "timeout");
+ }
+ splx(s);
+}
+
+
+void
+wd33c93_watchdog(void *arg)
+{
+ struct wd33c93_softc *sc = arg;
+ struct wd33c93_tinfo *ti;
+ struct wd33c93_linfo *li;
+ int t, s, l;
+ /* scrub LUN's that have not been used in the last 10min. */
+ time_t old = time_second - (10 * 60);
+
+ for (t = 0; t < SBIC_NTARG; t++) {
+ ti = &sc->sc_tinfo[t];
+ for (l = 0; l < SBIC_NLUN; l++) {
+ s = splbio();
+ li = TINFO_LUN(ti, l);
+ if (li && li->last_used < old &&
+ li->untagged == NULL && li->used == 0) {
+ ti->lun[li->lun] = NULL;
+ free(li, M_DEVBUF);
+ }
+ splx(s);
+ }
+ }
+ timeout_add_sec(&sc->sc_watchdog, 60);
+}
+
+
+#ifdef SBICDEBUG
+void
+wd33c93_hexdump(u_char *buf, int len)
+{
+ printf("{%d}:", len);
+ while (len--)
+ printf(" %02x", *buf++);
+ printf("\n");
+}
+
+
+void
+wd33c93_print_csr(u_char csr)
+{
+ switch (SCSI_PHASE(csr)) {
+ case CMD_PHASE:
+ printf("CMD_PHASE\n");
+ break;
+
+ case STATUS_PHASE:
+ printf("STATUS_PHASE\n");
+ break;
+
+ case DATA_IN_PHASE:
+ printf("DATAIN_PHASE\n");
+ break;
+
+ case DATA_OUT_PHASE:
+ printf("DATAOUT_PHASE\n");
+ break;
+
+ case MESG_IN_PHASE:
+ printf("MESG_IN_PHASE\n");
+ break;
+
+ case MESG_OUT_PHASE:
+ printf("MESG_OUT_PHASE\n");
+ break;
+
+ default:
+ switch (csr) {
+ case SBIC_CSR_DISC_1:
+ printf("DISC_1\n");
+ break;
+
+ case SBIC_CSR_RSLT_NI:
+ printf("RESELECT_NO_IFY\n");
+ break;
+
+ case SBIC_CSR_RSLT_IFY:
+ printf("RESELECT_IFY\n");
+ break;
+
+ case SBIC_CSR_SLT:
+ printf("SELECT\n");
+ break;
+
+ case SBIC_CSR_SLT_ATN:
+ printf("SELECT, ATN\n");
+ break;
+
+ case SBIC_CSR_UNK_GROUP:
+ printf("UNK_GROUP\n");
+ break;
+
+ default:
+ printf("UNKNOWN csr=%02x\n", csr);
+ }
+ }
+}
+#endif