diff options
Diffstat (limited to 'sys/arch/hp300/dev/scsi.c')
-rw-r--r-- | sys/arch/hp300/dev/scsi.c | 1541 |
1 files changed, 0 insertions, 1541 deletions
diff --git a/sys/arch/hp300/dev/scsi.c b/sys/arch/hp300/dev/scsi.c deleted file mode 100644 index 2492ba758d0..00000000000 --- a/sys/arch/hp300/dev/scsi.c +++ /dev/null @@ -1,1541 +0,0 @@ -/* $OpenBSD: scsi.c,v 1.16 2003/10/26 15:07:25 jmc Exp $ */ -/* $NetBSD: scsi.c,v 1.21 1997/05/05 21:08:26 thorpej Exp $ */ - -/* - * Copyright (c) 1996, 1997 Jason R. Thorpe. All rights reserved. - * Copyright (c) 1990, 1993 - * 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 8.2 (Berkeley) 1/12/94 - */ - -/* - * HP 9000/3xx 98658 SCSI host adaptor driver. - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/buf.h> -#include <sys/device.h> - -#include <machine/autoconf.h> -#include <machine/cpu.h> -#include <machine/intr.h> -#include <machine/hp300spu.h> - -#include <hp300/dev/dioreg.h> -#include <hp300/dev/diovar.h> -#include <hp300/dev/diodevs.h> - -#include <hp300/dev/dmavar.h> - -#include <hp300/dev/scsireg.h> -#include <hp300/dev/scsivar.h> - -struct scsi_softc { - struct device sc_dev; /* generic device glue */ - volatile struct scsidevice *sc_regs; /* card registers */ - struct dmaqueue sc_dq; /* our entry in DMA job queue */ - TAILQ_HEAD(, scsiqueue) sc_queue; /* job queue */ - u_char sc_flags; - u_char sc_sync; - u_char sc_scsi_addr; - u_char sc_scsiid; /* XXX unencoded copy of sc_scsi_addr */ - u_char sc_stat[2]; - u_char sc_msg[7]; -}; - -/* sc_flags */ -#define SCSI_IO 0x80 /* DMA I/O in progress */ -#define SCSI_DMA32 0x40 /* 32-bit DMA should be used */ -#define SCSI_HAVEDMA 0x04 /* controller has DMA channel */ -#ifdef DEBUG -#define SCSI_PAD 0x02 /* 'padded' transfer in progress */ -#endif -#define SCSI_ALIVE 0x01 /* controller initialized */ - -/* - * SCSI delays - * In u-seconds, primarily for state changes on the SPC. - */ -#define SCSI_CMD_WAIT 10000 /* wait per step of 'immediate' cmds */ -#define SCSI_DATA_WAIT 10000 /* wait per data in/out step */ -#define SCSI_INIT_WAIT 50000 /* wait per step (both) during init */ - -static void scsiabort(int, struct scsi_softc *, - volatile struct scsidevice *, char *); -static void scsierror(struct scsi_softc *, - volatile struct scsidevice *, u_char); -static int issue_select(volatile struct scsidevice *, - u_char, u_char); -static int wait_for_select(volatile struct scsidevice *); -static int ixfer_start(volatile struct scsidevice *, - int, u_char, int); -static int ixfer_out(volatile struct scsidevice *, int, u_char *); -static void ixfer_in(volatile struct scsidevice *, int, u_char *); -static int mxfer_in(volatile struct scsidevice *, - int, u_char *, u_char); -static int scsiicmd(struct scsi_softc *, int, u_char *, int, - u_char *, int, u_char); -static void finishxfer(struct scsi_softc *, - volatile struct scsidevice *, int); - -int scsimatch(struct device *, void *, void *); -void scsiattach(struct device *, struct device *, void *); -void scsi_attach_children(struct scsi_softc *); -int scsisubmatch(struct device *, void *, void *); - -struct cfattach oscsi_ca = { - sizeof(struct scsi_softc), scsimatch, scsiattach -}; - -struct cfdriver oscsi_cd = { - NULL, "oscsi", DV_DULL -}; - -int scsi_cmd_wait = SCSI_CMD_WAIT; -int scsi_data_wait = SCSI_DATA_WAIT; -int scsi_init_wait = SCSI_INIT_WAIT; - -int scsi_nosync = 1; /* inhibit sync xfers if 1 */ -int scsi_pridma = 0; /* use "priority" dma */ - -#ifdef DEBUG -int scsi_debug = 0; -#define WAITHIST -#endif - -#ifdef WAITHIST -#define MAXWAIT 1022 -u_int ixstart_wait[MAXWAIT+2]; -u_int ixin_wait[MAXWAIT+2]; -u_int ixout_wait[MAXWAIT+2]; -u_int mxin_wait[MAXWAIT+2]; -u_int mxin2_wait[MAXWAIT+2]; -u_int cxin_wait[MAXWAIT+2]; -u_int fxfr_wait[MAXWAIT+2]; -u_int sgo_wait[MAXWAIT+2]; -#define HIST(h,w) (++h[((w)>MAXWAIT? MAXWAIT : ((w) < 0 ? -1 : (w))) + 1]); -#else -#define HIST(h,w) -#endif - -#define b_cylin b_resid - -static void -scsiabort(target, hs, hd, where) - int target; - struct scsi_softc *hs; - volatile struct scsidevice *hd; - char *where; -{ - int len; - int maxtries; /* XXX - kludge till I understand whats *supposed* to happen */ - int startlen; /* XXX - kludge till I understand whats *supposed* to happen */ - u_char junk; - - printf("%s: ", hs->sc_dev.dv_xname); - if (target != -1) - printf("targ %d ", target); - printf("abort from %s: phase=0x%x, ssts=0x%x, ints=0x%x\n", - where, hd->scsi_psns, hd->scsi_ssts, hd->scsi_ints); - - hd->scsi_ints = hd->scsi_ints; - hd->scsi_csr = 0; - if (hd->scsi_psns == 0 || (hd->scsi_ssts & SSTS_INITIATOR) == 0) - /* no longer connected to scsi target */ - return; - - /* get the number of bytes remaining in current xfer + fudge */ - len = (hd->scsi_tch << 16) | (hd->scsi_tcm << 8) | hd->scsi_tcl; - - /* for that many bus cycles, try to send an abort msg */ - for (startlen = (len += 1024); (hd->scsi_ssts & SSTS_INITIATOR) && --len >= 0; ) { - hd->scsi_scmd = SCMD_SET_ATN; - maxtries = 1000; - while ((hd->scsi_psns & PSNS_REQ) == 0) { - if (! (hd->scsi_ssts & SSTS_INITIATOR)) - goto out; - DELAY(1); - if (--maxtries == 0) { - printf("-- scsiabort gave up after 1000 tries (startlen = %d len = %d)\n", - startlen, len); - goto out2; - } - - } -out2: - if ((hd->scsi_psns & PHASE) == MESG_OUT_PHASE) - hd->scsi_scmd = SCMD_RST_ATN; - hd->scsi_pctl = hd->scsi_psns & PHASE; - if (hd->scsi_psns & PHASE_IO) { - /* one of the input phases - read & discard a byte */ - hd->scsi_scmd = SCMD_SET_ACK; - if (hd->scsi_tmod == 0) - while (hd->scsi_psns & PSNS_REQ) - DELAY(1); - junk = hd->scsi_temp; - } else { - /* one of the output phases - send an abort msg */ - hd->scsi_temp = MSG_ABORT; - hd->scsi_scmd = SCMD_SET_ACK; - if (hd->scsi_tmod == 0) - while (hd->scsi_psns & PSNS_REQ) - DELAY(1); - } - hd->scsi_scmd = SCMD_RST_ACK; - } -out: - /* - * Either the abort was successful & the bus is disconnected or - * the device didn't listen. If the latter, announce the problem. - * Either way, reset the card & the SPC. - */ - if (len < 0 && hs) - printf("%s: abort failed. phase=0x%x, ssts=0x%x\n", - hs->sc_dev.dv_xname, hd->scsi_psns, hd->scsi_ssts); - - if (! ((junk = hd->scsi_ints) & INTS_RESEL)) { - hd->scsi_sctl |= SCTL_CTRLRST; - DELAY(2); - hd->scsi_sctl &=~ SCTL_CTRLRST; - hd->scsi_hconf = 0; - hd->scsi_ints = hd->scsi_ints; - } -} - -/* - * XXX Set/reset long delays. - * - * if delay == 0, reset default delays - * if delay < 0, set both delays to default long initialization values - * if delay > 0, set both delays to this value - * - * Used when a devices is expected to respond slowly (e.g. during - * initialization). - */ -void -scsi_delay(delay) - int delay; -{ - static int saved_cmd_wait, saved_data_wait; - - if (delay) { - saved_cmd_wait = scsi_cmd_wait; - saved_data_wait = scsi_data_wait; - if (delay > 0) - scsi_cmd_wait = scsi_data_wait = delay; - else - scsi_cmd_wait = scsi_data_wait = scsi_init_wait; - } else { - scsi_cmd_wait = saved_cmd_wait; - scsi_data_wait = saved_data_wait; - } -} - -int -scsimatch(parent, match, aux) - struct device *parent; - void *match, *aux; -{ - struct dio_attach_args *da = aux; - - switch (da->da_id) { - case DIO_DEVICE_ID_SCSI0: - case DIO_DEVICE_ID_SCSI1: - case DIO_DEVICE_ID_SCSI2: - case DIO_DEVICE_ID_SCSI3: - return (1); - } - - return (0); -} - -void -scsiattach(parent, self, aux) - struct device *parent, *self; - void *aux; -{ - struct scsi_softc *hs = (struct scsi_softc *)self; - struct dio_attach_args *da = aux; - struct scsidevice *hd; - int ipl, unit = self->dv_unit; - - /* - * Set up DMA job queue entry. - */ - hs->sc_dq.dq_softc = hs; - hs->sc_dq.dq_start = scsistart; - hs->sc_dq.dq_done = scsidone; - - /* Initialize request queue. */ - TAILQ_INIT(&hs->sc_queue); - - /* Map the device. */ - hd = (struct scsidevice *)iomap(dio_scodetopa(da->da_scode), - da->da_size); - if (hd == NULL) { - printf("\n%s: can't map registers\n", self->dv_xname); - return; - } - ipl = DIO_IPL(hd); - - printf(" ipl %d", ipl); - - hs->sc_regs = hd; - - /* Establish the interrupt handler. */ - (void) dio_intr_establish(scsiintr, hs, ipl, IPL_BIO); - - /* Reset the controller. */ - scsireset(unit); - - /* - * Print information about what we've found. - */ - printf(":"); - if (hs->sc_flags & SCSI_DMA32) - printf(" 32 bit dma, "); - - switch (hs->sc_sync) { - case 0: - printf("async"); - break; - - case (TMOD_SYNC | 0x3e): - printf("250ns sync"); - break; - - case (TMOD_SYNC | 0x5e): - printf("375ns sync"); - break; - - case (TMOD_SYNC | 0x7d): - printf("500ns sync"); - break; - - default: - panic("scsiattach: unknown sync param 0x%x", hs->sc_sync); - } - - if ((hd->scsi_hconf & HCONF_PARITY) == 0) - printf(", no parity"); - - printf(", scsi id %d\n", hs->sc_scsiid); - - /* - * XXX scale initialization wait according to CPU speed. - * Should we do this for all wait? Should we do this at all? - */ - scsi_init_wait *= (cpuspeed / 8); - - /* - * Find and attach devices on the SCSI bus. - */ - scsi_attach_children(hs); -} - -void -scsi_attach_children(sc) - struct scsi_softc *sc; -{ - struct oscsi_attach_args osa; - struct scsi_inquiry inqbuf; - int target, lun; - - /* - * Look for devices on the SCSI bus. - */ - - for (target = 0; target < 8; target++) { - /* Skip target used by controller. */ - if (target == sc->sc_scsiid) - continue; - - for (lun = 0; lun < 1 /* XXX */; lun++) { - bzero(&inqbuf, sizeof(inqbuf)); - if (scsi_probe_device(sc->sc_dev.dv_unit, - target, lun, &inqbuf, sizeof(inqbuf))) { - /* - * XXX First command on some tapes - * XXX always fails. (Or, at least, - * XXX that's what the old Utah "st" - * XXX driver claimed.) - */ - bzero(&inqbuf, sizeof(inqbuf)); - if (scsi_probe_device(sc->sc_dev.dv_unit, - target, lun, &inqbuf, sizeof(inqbuf))) - continue; - } - - /* - * There is a device here; find a driver - * to match it. - */ - osa.osa_target = target; - osa.osa_lun = lun; - osa.osa_inqbuf = &inqbuf; - (void)config_found_sm(&sc->sc_dev, &osa, - scsi_print, scsisubmatch); - } - } -} - -int -scsisubmatch(parent, match, aux) - struct device *parent; - void *match, *aux; -{ - struct cfdata *cf = match; - struct oscsi_attach_args *osa = aux; - - if (cf->cf_loc[0] != -1 && - cf->cf_loc[0] != osa->osa_target) - return (0); - - if (cf->cf_loc[1] != -1 && - cf->cf_loc[1] != osa->osa_lun) - return (0); - - return ((*cf->cf_attach->ca_match)(parent, match, aux)); -} - -int -scsi_print(aux, pnp) - void *aux; - const char *pnp; -{ - struct oscsi_attach_args *osa = aux; - struct scsi_inquiry *inqbuf = osa->osa_inqbuf; - char vendor[9], product[17], revision[5]; - - if (pnp == NULL) - printf(" targ %d lun %d: ", osa->osa_target, osa->osa_lun); - - bzero(vendor, sizeof(vendor)); - bzero(product, sizeof(product)); - bzero(revision, sizeof(revision)); - switch (inqbuf->version) { - case 1: - case 2: - scsi_str(inqbuf->vendor_id, vendor, sizeof(inqbuf->vendor_id)); - scsi_str(inqbuf->product_id, product, - sizeof(inqbuf->product_id)); - scsi_str(inqbuf->rev, revision, sizeof(inqbuf->rev)); - printf("<%s, %s, %s>", vendor, product, revision); - if (inqbuf->version == 2) - printf(" (SCSI-2)"); - break; - default: - printf("type 0x%x, qual 0x%x, ver %d", - inqbuf->type, inqbuf->qual, inqbuf->version); - } - if (pnp != NULL) - printf(" at %s targ %d lun %d", - pnp, osa->osa_target, osa->osa_lun); - - return (UNCONF); -} - -void -scsireset(unit) - int unit; -{ - struct scsi_softc *hs = oscsi_cd.cd_devs[unit]; - volatile struct scsidevice *hd = hs->sc_regs; - u_int i; - - if (hs->sc_flags & SCSI_ALIVE) - scsiabort(-1, hs, hd, "reset"); - - hd->scsi_id = 0xFF; - DELAY(100); - /* - * Disable interrupts then reset the FUJI chip. - */ - hd->scsi_csr = 0; - hd->scsi_sctl = SCTL_DISABLE | SCTL_CTRLRST; - hd->scsi_scmd = 0; - hd->scsi_tmod = 0; - hd->scsi_pctl = 0; - hd->scsi_temp = 0; - hd->scsi_tch = 0; - hd->scsi_tcm = 0; - hd->scsi_tcl = 0; - hd->scsi_ints = 0; - - if ((hd->scsi_id & ID_WORD_DMA) == 0) - hs->sc_flags |= SCSI_DMA32; - - /* Determine Max Synchronous Transfer Rate */ - if (scsi_nosync) - i = 3; - else - i = SCSI_SYNC_XFER(hd->scsi_hconf); - switch (i) { - case 0: - hs->sc_sync = TMOD_SYNC | 0x3e; /* 250 nsecs */ - break; - case 1: - hs->sc_sync = TMOD_SYNC | 0x5e; /* 375 nsecs */ - break; - case 2: - hs->sc_sync = TMOD_SYNC | 0x7d; /* 500 nsecs */ - break; - case 3: - hs->sc_sync = 0; - break; - } - - /* - * Configure the FUJI chip with its SCSI address, all - * interrupts enabled & appropriate parity. - */ - i = (~hd->scsi_hconf) & 0x7; - hs->sc_scsi_addr = 1 << i; - hd->scsi_bdid = i; - hs->sc_scsiid = i; - if (hd->scsi_hconf & HCONF_PARITY) - hd->scsi_sctl = SCTL_DISABLE | SCTL_ABRT_ENAB | - SCTL_SEL_ENAB | SCTL_RESEL_ENAB | - SCTL_INTR_ENAB | SCTL_PARITY_ENAB; - else - hd->scsi_sctl = SCTL_DISABLE | SCTL_ABRT_ENAB | - SCTL_SEL_ENAB | SCTL_RESEL_ENAB | - SCTL_INTR_ENAB; - - hd->scsi_sctl &=~ SCTL_DISABLE; - hs->sc_flags |= SCSI_ALIVE; -} - -static void -scsierror(hs, hd, ints) - struct scsi_softc *hs; - volatile struct scsidevice *hd; - u_char ints; -{ - char *sep = ""; - - printf("%s: ", hs->sc_dev.dv_xname); - if (ints & INTS_RST) { - DELAY(100); - if (hd->scsi_hconf & HCONF_SD) - printf("spurious RST interrupt"); - else - printf("hardware error - check fuse"); - sep = ", "; - } - if ((ints & INTS_HARD_ERR) || hd->scsi_serr) { - if (hd->scsi_serr & SERR_SCSI_PAR) { - printf("%sparity err", sep); - sep = ", "; - } - if (hd->scsi_serr & SERR_SPC_PAR) { - printf("%sSPC parity err", sep); - sep = ", "; - } - if (hd->scsi_serr & SERR_TC_PAR) { - printf("%sTC parity err", sep); - sep = ", "; - } - if (hd->scsi_serr & SERR_PHASE_ERR) { - printf("%sphase err", sep); - sep = ", "; - } - if (hd->scsi_serr & SERR_SHORT_XFR) { - printf("%ssync short transfer err", sep); - sep = ", "; - } - if (hd->scsi_serr & SERR_OFFSET) { - printf("%ssync offset error", sep); - sep = ", "; - } - } - if (ints & INTS_TIMEOUT) - printf("%sSPC select timeout error", sep); - if (ints & INTS_SRV_REQ) - printf("%sspurious SRV_REQ interrupt", sep); - if (ints & INTS_CMD_DONE) - printf("%sspurious CMD_DONE interrupt", sep); - if (ints & INTS_DISCON) - printf("%sspurious disconnect interrupt", sep); - if (ints & INTS_RESEL) - printf("%sspurious reselect interrupt", sep); - if (ints & INTS_SEL) - printf("%sspurious select interrupt", sep); - printf("\n"); -} - -static int -issue_select(hd, target, our_addr) - volatile struct scsidevice *hd; - u_char target, our_addr; -{ - if (hd->scsi_ssts & (SSTS_INITIATOR|SSTS_TARGET|SSTS_BUSY)) - return (1); - - if (hd->scsi_ints & INTS_DISCON) - hd->scsi_ints = INTS_DISCON; - - hd->scsi_pctl = 0; - hd->scsi_temp = (1 << target) | our_addr; - /* select timeout is hardcoded to 2ms */ - hd->scsi_tch = 15; - hd->scsi_tcm = 32; - hd->scsi_tcl = 4; - - hd->scsi_scmd = SCMD_SELECT; - return (0); -} - -static int -wait_for_select(hd) - volatile struct scsidevice *hd; -{ - u_char ints; - - while ((ints = hd->scsi_ints) == 0) - DELAY(1); - hd->scsi_ints = ints; - return (!(hd->scsi_ssts & SSTS_INITIATOR)); -} - -static int -ixfer_start(hd, len, phase, wait) - volatile struct scsidevice *hd; - int len; - u_char phase; - int wait; -{ - - hd->scsi_tch = len >> 16; - hd->scsi_tcm = len >> 8; - hd->scsi_tcl = len; - hd->scsi_pctl = phase; - hd->scsi_tmod = 0; /*XXX*/ - hd->scsi_scmd = SCMD_XFR | SCMD_PROG_XFR; - - /* wait for xfer to start or svc_req interrupt */ - while ((hd->scsi_ssts & SSTS_BUSY) == 0) { - if (hd->scsi_ints || --wait < 0) { -#ifdef DEBUG - if (scsi_debug) - printf("ixfer_start fail: i%x, w%d\n", - hd->scsi_ints, wait); -#endif - HIST(ixstart_wait, wait) - return (0); - } - DELAY(1); - } - HIST(ixstart_wait, wait) - return (1); -} - -static int -ixfer_out(hd, len, buf) - volatile struct scsidevice *hd; - int len; - u_char *buf; -{ - int wait = scsi_data_wait; - - for (; len > 0; --len) { - while (hd->scsi_ssts & SSTS_DREG_FULL) { - if (hd->scsi_ints || --wait < 0) { -#ifdef DEBUG - if (scsi_debug) - printf("ixfer_out fail: l%d i%x w%d\n", - len, hd->scsi_ints, wait); -#endif - HIST(ixout_wait, wait) - return (len); - } - DELAY(1); - } - hd->scsi_dreg = *buf++; - } - HIST(ixout_wait, wait) - return (0); -} - -static void -ixfer_in(hd, len, buf) - volatile struct scsidevice *hd; - int len; - u_char *buf; -{ - int wait = scsi_data_wait; - - for (; len > 0; --len) { - while (hd->scsi_ssts & SSTS_DREG_EMPTY) { - if (hd->scsi_ints || --wait < 0) { - while (! (hd->scsi_ssts & SSTS_DREG_EMPTY)) { - *buf++ = hd->scsi_dreg; - --len; - } -#ifdef DEBUG - if (scsi_debug) - printf("ixfer_in fail: l%d i%x w%d\n", - len, hd->scsi_ints, wait); -#endif - HIST(ixin_wait, wait) - return; - } - DELAY(1); - } - *buf++ = hd->scsi_dreg; - } - HIST(ixin_wait, wait) -} - -static int -mxfer_in(hd, len, buf, phase) - volatile struct scsidevice *hd; - int len; - u_char *buf; - u_char phase; -{ - int wait = scsi_cmd_wait; - int i; - - hd->scsi_tmod = 0; - for (i = 0; i < len; ++i) { - /* - * manual says: reset ATN before ACK is sent. - */ - if (hd->scsi_psns & PSNS_ATN) - hd->scsi_scmd = SCMD_RST_ATN; - /* - * wait for the request line (which says the target - * wants to give us data). If the phase changes while - * we're waiting, we're done. - */ - while ((hd->scsi_psns & PSNS_REQ) == 0) { - if (--wait < 0) { - HIST(mxin_wait, wait) - return (-1); - } - if ((hd->scsi_psns & PHASE) != phase || - (hd->scsi_ssts & SSTS_INITIATOR) == 0) - goto out; - - DELAY(1); - } - /* - * set ack (which says we're ready for the data, wait for - * req to go away (target says data is available), grab the - * data, then reset ack (say we've got the data). - */ - hd->scsi_pctl = phase; - hd->scsi_scmd = SCMD_SET_ACK; - while (hd->scsi_psns & PSNS_REQ) { - if (--wait < 0) { - HIST(mxin_wait, wait) - return (-2); - } - DELAY(1); - } - *buf++ = hd->scsi_temp; - hd->scsi_scmd = SCMD_RST_ACK; - } -out: - HIST(mxin_wait, wait) - /* - * Wait for manual transfer to finish. - * Avoids occasional "unexpected phase" errors in finishxfer - * formerly addressed by per-slave delays. - */ - wait = scsi_cmd_wait; - while ((hd->scsi_ssts & SSTS_ACTIVE) == SSTS_INITIATOR) { - if (--wait < 0) - break; - DELAY(1); - } - HIST(mxin2_wait, wait) - return (i); -} - -/* - * SCSI 'immediate' command: issue a command to some SCSI device - * and get back an 'immediate' response (i.e., do programmed xfer - * to get the response data). 'cbuf' is a buffer containing a scsi - * command of length clen bytes. 'buf' is a buffer of length 'len' - * bytes for data. The transfer direction is determined by the device - * (i.e., by the scsi bus data xfer phase). If 'len' is zero, the - * command must supply no data. 'xferphase' is the bus phase the - * caller expects to happen after the command is issued. It should - * be one of DATA_IN_PHASE, DATA_OUT_PHASE or STATUS_PHASE. - */ -static int -scsiicmd(hs, target, cbuf, clen, buf, len, xferphase) - struct scsi_softc *hs; - int target; - u_char *cbuf; - int clen; - u_char *buf; - int len; - u_char xferphase; -{ - volatile struct scsidevice *hd = hs->sc_regs; - u_char phase, ints; - int wait; - - /* select the SCSI bus (it's an error if bus isn't free) */ - if (issue_select(hd, target, hs->sc_scsi_addr)) - return (-1); - if (wait_for_select(hd)) - return (-1); - /* - * Wait for a phase change (or error) then let the device - * sequence us through the various SCSI phases. - */ - hs->sc_stat[0] = 0xff; - hs->sc_msg[0] = 0xff; - phase = CMD_PHASE; - while (1) { - wait = scsi_cmd_wait; - switch (phase) { - - case CMD_PHASE: - if (ixfer_start(hd, clen, phase, wait)) - if (ixfer_out(hd, clen, cbuf)) - goto abort; - phase = xferphase; - break; - - case DATA_IN_PHASE: - if (len <= 0) - goto abort; - wait = scsi_data_wait; - if (ixfer_start(hd, len, phase, wait) || - !(hd->scsi_ssts & SSTS_DREG_EMPTY)) - ixfer_in(hd, len, buf); - phase = STATUS_PHASE; - break; - - case DATA_OUT_PHASE: - if (len <= 0) - goto abort; - wait = scsi_data_wait; - if (ixfer_start(hd, len, phase, wait)) { - if (ixfer_out(hd, len, buf)) - goto abort; - } - phase = STATUS_PHASE; - break; - - case STATUS_PHASE: - wait = scsi_data_wait; - if (ixfer_start(hd, sizeof(hs->sc_stat), phase, wait) || - !(hd->scsi_ssts & SSTS_DREG_EMPTY)) - ixfer_in(hd, sizeof(hs->sc_stat), hs->sc_stat); - phase = MESG_IN_PHASE; - break; - - case MESG_IN_PHASE: - if (ixfer_start(hd, sizeof(hs->sc_msg), phase, wait) || - !(hd->scsi_ssts & SSTS_DREG_EMPTY)) { - ixfer_in(hd, sizeof(hs->sc_msg), hs->sc_msg); - hd->scsi_scmd = SCMD_RST_ACK; - } - phase = BUS_FREE_PHASE; - break; - - case BUS_FREE_PHASE: - goto out; - - default: - printf("%s: unexpected phase %d in icmd from %d\n", - hs->sc_dev.dv_xname, phase, target); - goto abort; - } - /* wait for last command to complete */ - while ((ints = hd->scsi_ints) == 0) { - if (--wait < 0) { - HIST(cxin_wait, wait) - goto abort; - } - DELAY(1); - } - HIST(cxin_wait, wait) - hd->scsi_ints = ints; - if (ints & INTS_SRV_REQ) - phase = hd->scsi_psns & PHASE; - else if (ints & INTS_DISCON) - goto out; - else if ((ints & INTS_CMD_DONE) == 0) { - scsierror(hs, hd, ints); - goto abort; - } - } -abort: - scsiabort(target, hs, hd, "icmd"); -out: - return (hs->sc_stat[0]); -} - -/* - * Finish SCSI xfer command: After the completion interrupt from - * a read/write operation, sequence through the final phases in - * programmed i/o. This routine is a lot like scsiicmd except we - * skip (and don't allow) the select, cmd out and data in/out phases. - */ -static void -finishxfer(hs, hd, target) - struct scsi_softc *hs; - volatile struct scsidevice *hd; - int target; -{ - u_char phase, ints; - - /* - * We specified padding xfer so we ended with either a phase - * change interrupt (normal case) or an error interrupt (handled - * elsewhere). Reset the board dma logic then try to get the - * completion status & command done msg. The reset confuses - * the SPC REQ/ACK logic so we have to do any status/msg input - * operations via 'manual xfer'. - */ - if (hd->scsi_ssts & SSTS_BUSY) { - int wait = scsi_cmd_wait; - - /* wait for dma operation to finish */ - while (hd->scsi_ssts & SSTS_BUSY) { - if (--wait < 0) { -#ifdef DEBUG - if (scsi_debug) - printf("finishxfer fail: ssts %x\n", - hd->scsi_ssts); -#endif - HIST(fxfr_wait, wait) - goto abort; - } - } - HIST(fxfr_wait, wait) - } - hd->scsi_scmd |= SCMD_PROG_XFR; - hd->scsi_sctl |= SCTL_CTRLRST; - DELAY(2); - hd->scsi_sctl &=~ SCTL_CTRLRST; - hd->scsi_hconf = 0; - /* - * The following delay is definitely needed when trying to - * write on a write protected disk (in the optical jukebox anyways), - * but we shall see if other unexplained machine freezeups - * also stop occurring... A value of 5 seems to work but - * 10 seems safer considering the potential consequences. - */ - DELAY(10); - hs->sc_stat[0] = 0xff; - hs->sc_msg[0] = 0xff; - hd->scsi_csr = 0; - hd->scsi_ints = ints = hd->scsi_ints; - while (1) { - phase = hd->scsi_psns & PHASE; - switch (phase) { - - case STATUS_PHASE: - if (mxfer_in(hd, sizeof(hs->sc_stat), - (u_char *)hs->sc_stat, phase) <= 0) - goto abort; - break; - - case MESG_IN_PHASE: - if (mxfer_in(hd, sizeof(hs->sc_msg), - (u_char *)hs->sc_msg, phase) < 0) - goto abort; - break; - - case BUS_FREE_PHASE: - return; - - default: - printf("%s: unexpected phase %d in finishxfer from %d\n", - hs->sc_dev.dv_xname, phase, target); - goto abort; - } - if ((ints = hd->scsi_ints)) { - hd->scsi_ints = ints; - if (ints & INTS_DISCON) - return; - else if (ints & ~(INTS_SRV_REQ|INTS_CMD_DONE)) { - scsierror(hs, hd, ints); - break; - } - } - if ((hd->scsi_ssts & SSTS_INITIATOR) == 0) - return; - } -abort: - scsiabort(target, hs, hd, "finishxfer"); - hs->sc_stat[0] = 0xfe; -} - -int -scsi_test_unit_rdy(ctlr, slave, unit) - int ctlr, slave, unit; -{ - struct scsi_softc *hs = oscsi_cd.cd_devs[ctlr]; - static struct scsi_cdb6 cdb = { CMD_TEST_UNIT_READY }; - - cdb.lun = unit; - return (scsiicmd(hs, slave, (u_char *)&cdb, sizeof(cdb), - (u_char *)0, 0, STATUS_PHASE)); -} - -int -scsi_request_sense(ctlr, slave, unit, buf, len) - int ctlr, slave, unit; - u_char *buf; - u_int len; -{ - struct scsi_softc *hs = oscsi_cd.cd_devs[ctlr]; - static struct scsi_cdb6 cdb = { CMD_REQUEST_SENSE }; - - cdb.lun = unit; - cdb.len = len; - return (scsiicmd(hs, slave, (u_char *)&cdb, sizeof(cdb), - buf, len, DATA_IN_PHASE)); -} - -int -scsi_immed_command(ctlr, slave, unit, cdb, buf, len, rd) - int ctlr, slave, unit, rd; - struct scsi_fmt_cdb *cdb; - u_char *buf; - u_int len; -{ - struct scsi_softc *hs = oscsi_cd.cd_devs[ctlr]; - - cdb->cdb[1] |= unit << 5; - return (scsiicmd(hs, slave, cdb->cdb, cdb->len, buf, len, - rd != 0? DATA_IN_PHASE : DATA_OUT_PHASE)); -} - -/* - * The following routines are test-and-transfer i/o versions of read/write - * for things like reading disk labels and writing core dumps. The - * routine scsigo should be used for normal data transfers, NOT these - * routines. - */ -int -scsi_tt_read(ctlr, slave, unit, buf, len, blk, bshift) - int ctlr, slave, unit; - u_char *buf; - u_int len; - daddr_t blk; - int bshift; -{ - struct scsi_softc *hs = oscsi_cd.cd_devs[ctlr]; - struct scsi_cdb10 cdb; - int stat; - int old_wait = scsi_data_wait; - - scsi_data_wait = 300000; - bzero(&cdb, sizeof(cdb)); - cdb.cmd = CMD_READ_EXT; - cdb.lun = unit; - blk >>= bshift; - cdb.lbah = blk >> 24; - cdb.lbahm = blk >> 16; - cdb.lbalm = blk >> 8; - cdb.lbal = blk; - cdb.lenh = len >> (8 + DEV_BSHIFT + bshift); - cdb.lenl = len >> (DEV_BSHIFT + bshift); - stat = scsiicmd(hs, slave, (u_char *)&cdb, sizeof(cdb), - buf, len, DATA_IN_PHASE); - scsi_data_wait = old_wait; - return (stat); -} - -int -scsi_tt_write(ctlr, slave, unit, buf, len, blk, bshift) - int ctlr, slave, unit; - u_char *buf; - u_int len; - daddr_t blk; - int bshift; -{ - struct scsi_softc *hs = oscsi_cd.cd_devs[ctlr]; - struct scsi_cdb10 cdb; - int stat; - int old_wait = scsi_data_wait; - - scsi_data_wait = 300000; - - bzero(&cdb, sizeof(cdb)); - cdb.cmd = CMD_WRITE_EXT; - cdb.lun = unit; - blk >>= bshift; - cdb.lbah = blk >> 24; - cdb.lbahm = blk >> 16; - cdb.lbalm = blk >> 8; - cdb.lbal = blk; - cdb.lenh = len >> (8 + DEV_BSHIFT + bshift); - cdb.lenl = len >> (DEV_BSHIFT + bshift); - stat = scsiicmd(hs, slave, (u_char *)&cdb, sizeof(cdb), - buf, len, DATA_OUT_PHASE); - scsi_data_wait = old_wait; - return (stat); -} - -int -scsireq(pdev, sq) - struct device *pdev; - struct scsiqueue *sq; -{ - struct scsi_softc *hs = (struct scsi_softc *)pdev; - int s; - - s = splhigh(); /* XXXthorpej */ - TAILQ_INSERT_TAIL(&hs->sc_queue, sq, sq_list); - splx(s); - - if (hs->sc_queue.tqh_first == sq) - return (1); - - return (0); -} - -int -scsiustart(unit) - int unit; -{ - struct scsi_softc *hs = oscsi_cd.cd_devs[unit]; - - hs->sc_dq.dq_chan = DMA0 | DMA1; - hs->sc_flags |= SCSI_HAVEDMA; - if (dmareq(&hs->sc_dq)) - return(1); - return(0); -} - -void -scsistart(arg) - void *arg; -{ - struct scsi_softc *hs = arg; - struct scsiqueue *sq; - - sq = hs->sc_queue.tqh_first; - (sq->sq_go)(sq->sq_softc); -} - -int -scsigo(ctlr, slave, unit, bp, cdb, pad) - int ctlr, slave, unit; - struct buf *bp; - struct scsi_fmt_cdb *cdb; - int pad; -{ - struct scsi_softc *hs = oscsi_cd.cd_devs[ctlr]; - volatile struct scsidevice *hd = hs->sc_regs; - int i, dmaflags; - u_char phase, ints, cmd; - - cdb->cdb[1] |= unit << 5; - - /* select the SCSI bus (it's an error if bus isn't free) */ - if (issue_select(hd, slave, hs->sc_scsi_addr) || wait_for_select(hd)) { - if (hs->sc_flags & SCSI_HAVEDMA) { - hs->sc_flags &=~ SCSI_HAVEDMA; - dmafree(&hs->sc_dq); - } - return (1); - } - /* - * Wait for a phase change (or error) then let the device - * sequence us through command phase (we may have to take - * a msg in/out before doing the command). If the disk has - * to do a seek, it may be a long time until we get a change - * to data phase so, in the absense of an explicit phase - * change, we assume data phase will be coming up and tell - * the SPC to start a transfer whenever it does. We'll get - * a service required interrupt later if this assumption is - * wrong. Otherwise we'll get a service required int when - * the transfer changes to status phase. - */ - phase = CMD_PHASE; - while (1) { - int wait = scsi_cmd_wait; - - switch (phase) { - - case CMD_PHASE: - if (ixfer_start(hd, cdb->len, phase, wait)) - if (ixfer_out(hd, cdb->len, cdb->cdb)) - goto abort; - break; - - case MESG_IN_PHASE: - if (ixfer_start(hd, sizeof(hs->sc_msg), phase, wait)|| - !(hd->scsi_ssts & SSTS_DREG_EMPTY)) { - ixfer_in(hd, sizeof(hs->sc_msg), hs->sc_msg); - hd->scsi_scmd = SCMD_RST_ACK; - } - phase = BUS_FREE_PHASE; - break; - - case DATA_IN_PHASE: - case DATA_OUT_PHASE: - goto out; - - default: - printf("%s: unexpected phase %d in go from %d\n", - hs->sc_dev.dv_xname, phase, slave); - goto abort; - } - while ((ints = hd->scsi_ints) == 0) { - if (--wait < 0) { - HIST(sgo_wait, wait) - goto abort; - } - DELAY(1); - } - HIST(sgo_wait, wait) - hd->scsi_ints = ints; - if (ints & INTS_SRV_REQ) - phase = hd->scsi_psns & PHASE; - else if (ints & INTS_CMD_DONE) - goto out; - else { - scsierror(hs, hd, ints); - goto abort; - } - } -out: - /* - * Reset the card dma logic, setup the dma channel then - * get the dio part of the card set for a dma xfer. - */ - hd->scsi_hconf = 0; - cmd = CSR_IE; - dmaflags = DMAGO_NOINT; - if (scsi_pridma) - dmaflags |= DMAGO_PRI; - if (bp->b_flags & B_READ) - dmaflags |= DMAGO_READ; - if ((hs->sc_flags & SCSI_DMA32) && - ((int)bp->b_un.b_addr & 3) == 0 && (bp->b_bcount & 3) == 0) { - cmd |= CSR_DMA32; - dmaflags |= DMAGO_LWORD; - } else - dmaflags |= DMAGO_WORD; - dmago(hs->sc_dq.dq_chan, bp->b_un.b_addr, bp->b_bcount, dmaflags); - - if (bp->b_flags & B_READ) { - cmd |= CSR_DMAIN; - phase = DATA_IN_PHASE; - } else - phase = DATA_OUT_PHASE; - /* - * DMA enable bits must be set after size and direction bits. - */ - hd->scsi_csr = cmd; - hd->scsi_csr |= (CSR_DE0 << hs->sc_dq.dq_chan); - /* - * Setup the SPC for the transfer. We don't want to take - * first a command complete then a service required interrupt - * at the end of the transfer so we try to disable the cmd - * complete by setting the transfer counter to more bytes - * than we expect. (XXX - This strategy may have to be - * modified to deal with devices that return variable length - * blocks, e.g., some tape drives.) - */ - cmd = SCMD_XFR; - i = (unsigned)bp->b_bcount; - if (pad) { - cmd |= SCMD_PAD; - /* - * XXX - If we don't do this, the last 2 or 4 bytes - * (depending on word/lword DMA) of a read get trashed. - * It looks like it is necessary for the DMA to complete - * before the SPC goes into "pad mode"??? Note: if we - * also do this on a write, the request never completes. - */ - if (bp->b_flags & B_READ) - i += 2; -#ifdef DEBUG - hs->sc_flags |= SCSI_PAD; - if (i & 1) - printf("%s: odd byte count: %d bytes @ %ld\n", - hs->sc_dev.dv_xname, i, bp->b_cylin); -#endif - } else - i += 4; - hd->scsi_tch = i >> 16; - hd->scsi_tcm = i >> 8; - hd->scsi_tcl = i; - hd->scsi_pctl = phase; - hd->scsi_tmod = 0; - hd->scsi_scmd = cmd; - hs->sc_flags |= SCSI_IO; - return (0); -abort: - scsiabort(slave, hs, hd, "go"); - hs->sc_flags &=~ SCSI_HAVEDMA; - dmafree(&hs->sc_dq); - return (1); -} - -void -scsidone(arg) - void *arg; -{ - struct scsi_softc *hs = arg; - volatile struct scsidevice *hd = hs->sc_regs; - -#ifdef DEBUG - if (scsi_debug) - printf("%s: done called!\n", hs->sc_dev.dv_xname); -#endif - /* dma operation is done -- turn off card dma */ - hd->scsi_csr &=~ (CSR_DE1|CSR_DE0); -} - -int -scsiintr(arg) - void *arg; -{ - struct scsi_softc *hs = arg; - volatile struct scsidevice *hd = hs->sc_regs; - u_char ints; - struct scsiqueue *sq; - - if ((hd->scsi_csr & (CSR_IE|CSR_IR)) != (CSR_IE|CSR_IR)) - return (0); - - sq = hs->sc_queue.tqh_first; - - ints = hd->scsi_ints; - if ((ints & INTS_SRV_REQ) && (hs->sc_flags & SCSI_IO)) { - /* - * this should be the normal i/o completion case. - * get the status & cmd complete msg then let the - * device driver look at what happened. - */ -#ifdef DEBUG - int len = (hd->scsi_tch << 16) | (hd->scsi_tcm << 8) | - hd->scsi_tcl; - if (!(hs->sc_flags & SCSI_PAD)) - len -= 4; - hs->sc_flags &=~ SCSI_PAD; -#endif - finishxfer(hs, hd, sq->sq_target); - hs->sc_flags &=~ (SCSI_IO|SCSI_HAVEDMA); - dmafree(&hs->sc_dq); - (sq->sq_intr)(sq->sq_softc, hs->sc_stat[0]); - } else { - /* Something unexpected happened -- deal with it. */ - hd->scsi_ints = ints; - hd->scsi_csr = 0; - scsierror(hs, hd, ints); - scsiabort(sq->sq_target, hs, hd, "intr"); - if (hs->sc_flags & SCSI_IO) { - hs->sc_flags &=~ (SCSI_IO|SCSI_HAVEDMA); - dmafree(&hs->sc_dq); - (sq->sq_intr)(sq->sq_softc, -1); - } - } - return(1); -} - -void -scsifree(pdev, sq) - struct device *pdev; - struct scsiqueue *sq; -{ - struct scsi_softc *hs = (struct scsi_softc *)pdev; - int s; - - s = splhigh(); /* XXXthorpej */ - TAILQ_REMOVE(&hs->sc_queue, sq, sq_list); - splx(s); - - if ((sq = hs->sc_queue.tqh_first) != NULL) - (*sq->sq_start)(sq->sq_softc); -} - -/* - * (XXX) The following routine is needed for the SCSI tape driver - * to read odd-size records. - */ - -#include "st.h" -#if NST > 0 -int -scsi_tt_oddio(ctlr, slave, unit, buf, len, b_flags, freedma) - int ctlr, slave, unit, b_flags, freedma; - u_char *buf; - u_int len; -{ - struct scsi_softc *hs = oscsi_cd.cd_devs[ctlr]; - struct scsi_cdb6 cdb; - u_char iphase; - int stat; - -#ifdef DEBUG - if ((freedma && (hs->sc_flags & SCSI_HAVEDMA) == 0) || - (!freedma && (hs->sc_flags & SCSI_HAVEDMA))) - printf("oddio: freedma (%d) inconsistency (flags=%x)\n", - freedma, hs->sc_flags); -#endif - /* - * First free any DMA channel that was allocated. - * We can't use DMA to do this transfer. - */ - if (freedma) { - hs->sc_flags &=~ SCSI_HAVEDMA; - dmafree(&hs->sc_dq); - } - /* - * Initialize command block - */ - bzero(&cdb, sizeof(cdb)); - cdb.lun = unit; - cdb.lbam = (len >> 16) & 0xff; - cdb.lbal = (len >> 8) & 0xff; - cdb.len = len & 0xff; - if (buf == 0) { - cdb.cmd = CMD_SPACE; - cdb.lun |= 0x00; - len = 0; - iphase = MESG_IN_PHASE; - } else if (b_flags & B_READ) { - cdb.cmd = CMD_READ; - iphase = DATA_IN_PHASE; - } else { - cdb.cmd = CMD_WRITE; - iphase = DATA_OUT_PHASE; - } - /* - * Perform command (with very long delays) - */ - scsi_delay(30000000); - stat = scsiicmd(hs, slave, (u_char *)&cdb, sizeof(cdb), - buf, len, iphase); - scsi_delay(0); - return (stat); -} -#endif - -/* - * Copy a counted string, trimming the trailing space, and turn - * the result into a C-style string. - */ -void -scsi_str(src, dst, len) - char *src, *dst; - size_t len; -{ - - while (src[len - 1] == ' ') { - if (--len == 0) { - *dst = '\0'; - return; - } - } - bcopy(src, dst, len); - dst[len] = '\0'; -} - -/* - * Probe for a device at the given ctlr/target/lun, and fill in the inqbuf. - */ -int -scsi_probe_device(ctlr, targ, lun, inqbuf, inqlen) - int ctlr, targ, lun; - struct scsi_inquiry *inqbuf; - int inqlen; -{ - static struct scsi_fmt_cdb inq = { - 6, { CMD_INQUIRY, 0, 0, 0, 0, 0 } - }; - int i, tries = 10, isrm = 0; - - inq.cdb[4] = inqlen & 0xff; - - scsi_delay(-1); - - /* - * See if the unit exists. - */ - while ((i = scsi_test_unit_rdy(ctlr, targ, lun)) != 0) { - if (i == -1 || --tries < 0) { - if (isrm) - break; - /* doesn't exist or not a CCS device */ - goto failed; - } - if (i == STS_CHECKCOND) { - u_char sensebuf[128]; - struct scsi_xsense *sp = - (struct scsi_xsense *)sensebuf; - - scsi_request_sense(ctlr, targ, lun, (u_char *)sensebuf, - sizeof(sensebuf)); - if (sp->class == 7) { - switch (sp->key) { - /* - * Not ready -- might be removable media - * device with no media. Assume as much, - * if it really isn't, the inquiry command - * below will fail. - */ - case 2: - isrm = 1; - break; - /* drive doing an RTZ -- give it a while */ - case 6: - delay(1000000); - break; - default: - break; - } - } - } - delay(1000); - } - - /* - * Find out about the device. - */ - if (scsi_immed_command(ctlr, targ, lun, &inq, (u_char *)inqbuf, - inqlen, B_READ)) - goto failed; - - scsi_delay(0); - return (0); - - failed: - scsi_delay(0); - return (-1); -} |