/* $NetBSD: wd33c93.c,v 1.26 2017/01/11 07:16:48 skrll 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 __KERNEL_RCSID(0, "$NetBSD: wd33c93.c,v 1.26 2017/01/11 07:16:48 skrll Exp $"); #include "opt_ddb.h" #include #include #include #include /* For hz */ #include #include #include #include #include #include #include #include #include /* * SCSI delays * In u-seconds, primarily for state changes on the SPC. */ #define SBIC_CMD_WAIT 50000 /* wait per step of 'immediate' cmds */ #define SBIC_DATA_WAIT 50000 /* wait per data in/out step */ #define SBIC_INIT_WAIT 50000 /* 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 scsipi_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 *); void wd33c93_update_xfer_mode (struct wd33c93_softc *, int); static struct pool wd33c93_pool; /* Adapter Control Blocks */ static 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 DEBUG #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 scsipi_adapter *adapt = &sc->sc_adapter; struct scsipi_channel *chan = &sc->sc_channel; adapt->adapt_dev = sc->sc_dev; adapt->adapt_nchannels = 1; adapt->adapt_openings = 256; adapt->adapt_max_periph = 256; /* Max tags per device */ adapt->adapt_ioctl = NULL; /* adapt_request initialized by MD interface */ /* adapt_minphys initialized by MD interface */ memset(chan, 0, sizeof(*chan)); chan->chan_adapter = &sc->sc_adapter; chan->chan_bustype = &scsi_bustype; chan->chan_channel = 0; chan->chan_ntargets = SBIC_NTARG; chan->chan_nluns = SBIC_NLUN; chan->chan_id = sc->sc_id; callout_init(&sc->sc_watchdog, 0); /* * Add reference to adapter so that we drop the reference after * config_found() to make sure the adapter is disabled. */ if (scsipi_adapter_addref(&sc->sc_adapter) != 0) { aprint_error_dev(sc->sc_dev, "unable to enable controller\n"); return; } sc->sc_cfflags = device_cfdata(sc->sc_dev)->cf_flags; wd33c93_init(sc); aprint_normal(": %s (%d.%d MHz clock, %s, SCSI ID %d)\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", sc->sc_channel.chan_id); if (sc->sc_chip == SBIC_CHIP_WD33C93B) { aprint_normal_dev(sc->sc_dev, "microcode revision 0x%02x", sc->sc_rev); if (sc->sc_minsyncperiod < 50) aprint_normal(", Fast SCSI"); aprint_normal("\n"); } sc->sc_child = config_found(sc->sc_dev, &sc->sc_channel, scsiprint); scsipi_adapter_delref(&sc->sc_adapter); } /* * Initialize driver-private structures */ void wd33c93_init(struct wd33c93_softc *sc) { u_int i; if (!wd33c93_pool_initialized) { /* All instances share the same pool */ pool_init(&wd33c93_pool, sizeof(struct wd33c93_acb), 0, 0, 0, "wd33c93_acb", NULL, IPL_BIO); ++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)); callout_reset(&sc->sc_watchdog, 60 * hz, wd33c93_watchdog, sc); } else panic("wd33c93: reinitializing driver!"); sc->sc_flags = 0; sc->sc_state = SBIC_IDLE; wd33c93_reset(sc); for (i = 0; i < 8; 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_channel.chan_id & 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 scsipi_xfer *xs = acb->xs; KASSERT(xs); if (xs->xs_control & XS_CTL_SILENT) return; scsipi_printaddr(xs->xs_periph); 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 scsipi_xfer *xs) { if (wd33c93_nodma || sc->sc_dmamode == SBIC_CTL_NO_DMA || (xs->xs_control & XS_CTL_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 scsipi layer */ void wd33c93_scsi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg) { struct wd33c93_softc *sc = device_private(chan->chan_adapter->adapt_dev); struct scsipi_xfer *xs; struct scsipi_periph *periph; struct wd33c93_acb *acb; int flags, s; SBIC_DEBUG(MISC, ("wd33c93_scsi_request: req 0x%x\n", (int)req)); switch (req) { case ADAPTER_REQ_RUN_XFER: xs = arg; periph = xs->xs_periph; flags = xs->xs_control; if (flags & XS_CTL_DATA_UIO) panic("wd33c93: scsi data uio requested"); if (sc->sc_nexus && (flags & XS_CTL_POLL)) panic("wd33c93_scsicmd: busy"); s = splbio(); acb = (struct wd33c93_acb *)pool_get(&wd33c93_pool, PR_NOWAIT); splx(s); if (acb == NULL) { scsipi_printaddr(periph); printf("cannot allocate acb\n"); xs->error = XS_RESOURCE_SHORTAGE; scsipi_done(xs); return; } acb->flags = ACB_ACTIVE; acb->xs = xs; acb->clen = xs->cmdlen; acb->daddr = xs->data; acb->dleft = xs->datalen; acb->timeout = xs->timeout; memcpy(&acb->cmd, xs->cmd, xs->cmdlen); if (flags & XS_CTL_POLL) { /* * Complete currently active command(s) before * issuing an immediate command */ while (sc->sc_nexus) wd33c93_poll(sc, sc->sc_nexus); } 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 & XS_CTL_POLL) == 0) return; if (wd33c93_poll(sc, acb)) { wd33c93_timeout(acb); if (wd33c93_poll(sc, acb)) /* 2nd retry for ABORT */ wd33c93_timeout(acb); } return; case ADAPTER_REQ_GROW_RESOURCES: /* XXX Not supported. */ return; case ADAPTER_REQ_SET_XFER_MODE: { struct wd33c93_tinfo *ti; struct scsipi_xfer_mode *xm = arg; ti = &sc->sc_tinfo[xm->xm_target]; ti->flags &= ~T_WANTSYNC; if ((CFFLAGS_NOTAGS(sc->sc_cfflags, xm->xm_target) == 0) && (xm->xm_mode & PERIPH_CAP_TQING) && !wd33c93_notags) ti->flags |= T_TAG; else ti->flags &= ~T_TAG; SBIC_DEBUG(SYNC, ("wd33c93_scsi_request: " "target %d: scsipi requested %s\n", xm->xm_target, (xm->xm_mode & PERIPH_CAP_SYNC) ? "sync" : "async")); if ((xm->xm_mode & PERIPH_CAP_SYNC) != 0 && (ti->flags & T_NOSYNC) == 0) ti->flags |= T_WANTSYNC; /* * If we're not going to negotiate, send the notification * now, since it won't happen later. */ if (!(ti->flags & T_WANTSYNC) == !(ti->flags & T_SYNCMODE)) wd33c93_update_xfer_mode(sc, xm->xm_target); else ti->flags |= T_NEGOTIATE; return; } } } /* * attempt to start the next available command */ void wd33c93_sched(struct wd33c93_softc *sc) { struct scsipi_periph *periph = NULL; /* Gag the compiler */ struct wd33c93_acb *acb; struct wd33c93_tinfo *ti; struct wd33c93_linfo *li; int lun, tag, flags; 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) { periph = acb->xs->xs_periph; lun = periph->periph_lun; ti = &sc->sc_tinfo[periph->periph_target]; li = TINFO_LUN(ti, lun); 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->xs_control & XS_CTL_POLL) tag = 0; /* No tags for polled commands */ else tag = acb->xs->xs_tag_type; if (li == NULL) { /* Initialize LUN info and add to list. */ li = malloc(sizeof(*li), M_DEVBUF, M_NOWAIT); if (li == NULL) continue; memset(li, 0, sizeof(*li)); 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; periph = acb->xs->xs_periph; } else /* Not ready yet */ continue; } acb->tag_type = tag; if (tag != 0) { if (li->queued[acb->xs->xs_tag_id]) printf("queueing to active tag\n"); li->queued[acb->xs->xs_tag_id] = acb; acb->tag_id = acb->xs->xs_tag_id; li->used++; break; } 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", periph->periph_target, periph->periph_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", periph->periph_target, periph->periph_lun)); TAILQ_REMOVE(&sc->ready_list, acb, chain); acb->flags &= ~ACB_READY; flags = acb->xs->xs_control; if (flags & XS_CTL_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; } return; } void wd33c93_scsidone(struct wd33c93_softc *sc, struct wd33c93_acb *acb, int status) { struct scsipi_xfer *xs = acb->xs; struct wd33c93_tinfo *ti; struct wd33c93_linfo *li; int s; #ifdef DIAGNOSTIC KASSERT(sc->target == xs->xs_periph->periph_target); KASSERT(sc->lun == xs->xs_periph->periph_lun); if (acb == NULL || xs == NULL) { panic("wd33c93_scsidone -- (%d,%d) no scsipi_xfer", sc->target, sc->lun); } KASSERT(acb->flags != ACB_FREE); #endif SBIC_DEBUG(ACBS, ("scsidone: (%d,%d)->(%d,%d)%02x\n", xs->xs_periph->periph_target, xs->xs_periph->periph_lun, sc->target, sc->lun, status)); callout_stop(&xs->xs_callout); xs->status = status & SCSI_STATUS_MASK; xs->resid = acb->dleft; if (xs->error == XS_NOERROR) { 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->target]; li = TINFO_LUN(ti, sc->lun); ti->cmds++; if (xs->error == XS_SELTIMEOUT) { /* Selection timeout -- discard this LUN if empty */ if (li->untagged == NULL && li->used == 0) { if (sc->lun < SBIC_NLUN) ti->lun[sc->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. */ s = splbio(); acb->flags = ACB_FREE; pool_put(&wd33c93_pool, acb); splx(s); scsipi_done(xs); } void wd33c93_dequeue(struct wd33c93_softc *sc, struct wd33c93_acb *acb) { struct wd33c93_tinfo *ti = &sc->sc_tinfo[acb->xs->xs_periph->periph_target]; struct wd33c93_linfo *li; int lun = acb->xs->xs_periph->periph_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); printf("wd33c93_wait: TIMEO @%d with asr=x%x csr=x%x\n", line, val, csr); #if defined(DDB) && defined(DEBUG) Debugger(); #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); scsipi_printaddr(acb->xs->xs_periph); 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. */ callout_reset(&acb->xs->xs_callout, mstohz(acb->timeout), wd33c93_timeout, acb); 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); } scsipi_printaddr(acb->xs->xs_periph); printf("sending ABORT command\n"); WAIT_CIP(sc); SET_SBIC_cmd(sc, SBIC_CMD_ABORT); WAIT_CIP(sc); GET_SBIC_asr(sc, asr); scsipi_printaddr(acb->xs->xs_periph); 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 scsipi_xfer *xs = acb->xs; struct wd33c93_tinfo *ti; u_char target, lun, asr, csr, id; KASSERT(sc->sc_state == SBIC_IDLE); target = xs->xs_periph->periph_target; lun = xs->xs_periph->periph_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->xs_control & XS_CTL_POLL) == 0) callout_reset(&xs->xs_callout, mstohz(acb->timeout), wd33c93_timeout, acb); /* * 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) { /* Inititae 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, ("