summaryrefslogtreecommitdiff
path: root/sys/arch/hp300/stand/scsi.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/arch/hp300/stand/scsi.c
initial import of NetBSD tree
Diffstat (limited to 'sys/arch/hp300/stand/scsi.c')
-rw-r--r--sys/arch/hp300/stand/scsi.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/sys/arch/hp300/stand/scsi.c b/sys/arch/hp300/stand/scsi.c
new file mode 100644
index 00000000000..0aa7c2e5401
--- /dev/null
+++ b/sys/arch/hp300/stand/scsi.c
@@ -0,0 +1,459 @@
+/* $NetBSD: scsi.c,v 1.5 1994/10/26 07:27:56 cgd Exp $ */
+
+/*
+ * Copyright (c) 1988 University of Utah.
+ * 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 and the Systems
+ * Programming Group of the University of Utah Computer Science Department.
+ *
+ * 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.
+ *
+ * from: Utah $Hdr: scsi.c 1.3 90/01/27$
+ *
+ * @(#)scsi.c 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * SCSI bus driver for standalone programs.
+ */
+
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <hp300/dev/device.h>
+#define _IOCTL_
+#include <hp300/dev/scsireg.h>
+#include "scsivar.h"
+
+#include "stand.h"
+#include "samachdep.h"
+
+struct scsi_softc scsi_softc[NSCSI];
+
+void scsireset();
+int scsi_cmd_wait = 50000; /* use the "real" driver init_wait value */
+int scsi_data_wait = 50000; /* use the "real" driver init_wait value */
+
+scsiinit()
+{
+ extern struct hp_hw sc_table[];
+ register struct hp_hw *hw;
+ register struct scsi_softc *hs;
+ register int i, addr;
+ static int waitset = 0;
+
+ i = 0;
+ for (hw = sc_table; i < NSCSI && hw < &sc_table[MAXCTLRS]; hw++) {
+ if (!HW_ISSCSI(hw))
+ continue;
+ hs = &scsi_softc[i];
+ hs->sc_addr = hw->hw_kva;
+ scsireset(i);
+ if (howto & RB_ASKNAME)
+ printf("scsi%d at sc%d\n", i, hw->hw_sc);
+ hw->hw_pa = (caddr_t) i; /* XXX for autoconfig */
+ hs->sc_alive = 1;
+ i++;
+ }
+ /*
+ * Adjust the wait values
+ */
+ if (!waitset) {
+ scsi_cmd_wait *= cpuspeed;
+ scsi_data_wait *= cpuspeed;
+ waitset = 1;
+ }
+}
+
+scsialive(unit)
+ register int unit;
+{
+ if (unit >= NSCSI || scsi_softc[unit].sc_alive == 0)
+ return (0);
+ return (1);
+}
+
+void
+scsireset(unit)
+ register int unit;
+{
+ volatile register struct scsidevice *hd;
+ register struct scsi_softc *hs;
+ u_int i;
+
+ hs = &scsi_softc[unit];
+ hd = (struct scsidevice *)hs->sc_addr;
+ 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;
+
+ /*
+ * 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;
+ 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;
+}
+
+
+int
+scsiabort(hs, hd)
+ register struct scsi_softc *hs;
+ volatile register struct scsidevice *hd;
+{
+ printf("scsi%d error: scsiabort\n", hs - scsi_softc);
+
+ scsireset(hs - scsi_softc);
+ DELAY(1000000);
+}
+
+static int
+issue_select(hd, target, our_addr)
+ volatile register 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 = 0;
+ hd->scsi_tcm = 32;
+ hd->scsi_tcl = 4;
+
+ hd->scsi_scmd = SCMD_SELECT;
+ return (0);
+}
+
+static int
+wait_for_select(hd)
+ volatile register struct scsidevice *hd;
+{
+ register int wait;
+ u_char ints;
+
+ wait = scsi_data_wait;
+ while ((ints = hd->scsi_ints) == 0) {
+ if (--wait < 0)
+ return (1);
+ DELAY(1);
+ }
+ hd->scsi_ints = ints;
+ return (!(hd->scsi_ssts & SSTS_INITIATOR));
+}
+
+static int
+ixfer_start(hd, len, phase, wait)
+ volatile register struct scsidevice *hd;
+ int len;
+ u_char phase;
+ register 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)
+ return (0);
+ DELAY(1);
+ }
+ return (1);
+}
+
+static int
+ixfer_out(hd, len, buf)
+ volatile register struct scsidevice *hd;
+ int len;
+ register u_char *buf;
+{
+ register int wait = scsi_data_wait;
+
+ for (; len > 0; --len) {
+ while (hd->scsi_ssts & SSTS_DREG_FULL) {
+ if (hd->scsi_ints || --wait < 0)
+ return (len);
+ DELAY(1);
+ }
+ hd->scsi_dreg = *buf++;
+ }
+ return (0);
+}
+
+static int
+ixfer_in(hd, len, buf)
+ volatile register struct scsidevice *hd;
+ int len;
+ register u_char *buf;
+{
+ register 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;
+ }
+ return (len);
+ }
+ DELAY(1);
+ }
+ *buf++ = hd->scsi_dreg;
+ }
+ return (len);
+}
+
+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 register struct scsidevice *hd =
+ (struct scsidevice *)hs->sc_addr;
+ u_char phase, ints;
+ register 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 (-2);
+ if (wait_for_select(hd))
+ return (-2);
+ /*
+ * Wait for a phase change (or error) then let the device
+ * sequence us through the various SCSI phases.
+ */
+ hs->sc_stat = -1;
+ 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("scsi%d: unexpected scsi phase %d\n",
+ hs - scsi_softc, phase);
+ goto abort;
+ }
+#ifdef SLOWSCSI
+ /*
+ * XXX we have wierd transient problems with booting from
+ * slow scsi disks on fast machines. I have never been
+ * able to pin the problem down, but a large delay here
+ * seems to always work.
+ */
+ DELAY(1000);
+#endif
+ /* wait for last command to complete */
+ while ((ints = hd->scsi_ints) == 0) {
+ if (--wait < 0)
+ goto abort;
+ DELAY(1);
+ }
+ 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)
+ goto abort;
+ }
+abort:
+ scsiabort(hs, hd);
+out:
+ return (hs->sc_stat);
+}
+
+int
+scsi_test_unit_rdy(ctlr, slave)
+ int ctlr, slave;
+{
+ register struct scsi_softc *hs = &scsi_softc[ctlr];
+ static struct scsi_cdb6 cdb = { CMD_TEST_UNIT_READY };
+
+ return (scsiicmd(hs, slave, &cdb, sizeof(cdb), (u_char *)0, 0,
+ STATUS_PHASE));
+}
+
+int
+scsi_request_sense(ctlr, slave, buf, len)
+ int ctlr, slave;
+ u_char *buf;
+ unsigned len;
+{
+ register struct scsi_softc *hs = &scsi_softc[ctlr];
+ static struct scsi_cdb6 cdb = { CMD_REQUEST_SENSE };
+
+ cdb.len = len;
+ return (scsiicmd(hs, slave, &cdb, sizeof(cdb), buf, len,
+ DATA_IN_PHASE));
+}
+
+int
+scsi_read_capacity(ctlr, slave, buf, len)
+ int ctlr, slave;
+ u_char *buf;
+ unsigned len;
+{
+ register struct scsi_softc *hs = &scsi_softc[ctlr];
+ static struct scsi_cdb10 cdb = { CMD_READ_CAPACITY };
+
+ return (scsiicmd(hs, slave, &cdb, sizeof(cdb), buf, len,
+ DATA_IN_PHASE));
+}
+
+int
+scsi_tt_read(ctlr, slave, buf, len, blk, nblk)
+ int ctlr, slave;
+ u_char *buf;
+ u_int len;
+ daddr_t blk;
+ u_int nblk;
+{
+ register struct scsi_softc *hs = &scsi_softc[ctlr];
+ struct scsi_cdb10 cdb;
+
+ bzero(&cdb, sizeof(cdb));
+ cdb.cmd = CMD_READ_EXT;
+ cdb.lbah = blk >> 24;
+ cdb.lbahm = blk >> 16;
+ cdb.lbalm = blk >> 8;
+ cdb.lbal = blk;
+ cdb.lenh = nblk >> (8 + DEV_BSHIFT);
+ cdb.lenl = nblk >> DEV_BSHIFT;
+ return (scsiicmd(hs, slave, &cdb, sizeof(cdb), buf, len,
+ DATA_IN_PHASE));
+}
+
+int
+scsi_tt_write(ctlr, slave, buf, len, blk, nblk)
+ int ctlr, slave;
+ u_char *buf;
+ u_int len;
+ daddr_t blk;
+ u_int nblk;
+{
+ register struct scsi_softc *hs = &scsi_softc[ctlr];
+ struct scsi_cdb10 cdb;
+
+ bzero(&cdb, sizeof(cdb));
+ cdb.cmd = CMD_WRITE_EXT;
+ cdb.lbah = blk >> 24;
+ cdb.lbahm = blk >> 16;
+ cdb.lbalm = blk >> 8;
+ cdb.lbal = blk;
+ cdb.lenh = nblk >> (8 + DEV_BSHIFT);
+ cdb.lenl = nblk >> DEV_BSHIFT;
+ return (scsiicmd(hs, slave, &cdb, sizeof(cdb), buf, len,
+ DATA_OUT_PHASE));
+}