📄 scsi.c
字号:
/* * 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. 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 8.2 (Berkeley) 1/12/94 */#ifndef DEBUG#define DEBUG#endif/* * HP9000/3xx 98658 SCSI host adaptor driver. */#include "scsi.h"#if NSCSI > 0#ifndef lintstatic char rcsid[] = "$Header: /usr/src/sys/hp300/dev/RCS/scsi.c,v 1.2 92/04/10 20:48:29 mike Exp $";#endif#include <sys/param.h>#include <sys/systm.h>#include <sys/buf.h>#include <hp/dev/device.h>#include <hp300/dev/scsivar.h>#include <hp300/dev/scsireg.h>#include <hp300/dev/dmavar.h>#include <machine/cpu.h>#include <hp300/hp300/isr.h>/* * SCSI delays * In u-seconds, primarily for state changes on the SPC. */#define SCSI_CMD_WAIT 1000 /* wait per step of 'immediate' cmds */#define SCSI_DATA_WAIT 1000 /* wait per data in/out step */#define SCSI_INIT_WAIT 50000 /* wait per step (both) during init */extern void isrlink();extern void _insque();extern void _remque();int scsiinit(), scsigo(), scsiintr(), scsixfer();void scsistart(), scsidone(), scsifree(), scsireset();struct driver scsidriver = { scsiinit, "scsi", (int (*)())scsistart, scsigo, scsiintr, (int (*)())scsidone,};struct scsi_softc scsi_softc[NSCSI];struct isr scsi_isr[NSCSI];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 DEBUGint scsi_debug = 0;#define WAITHIST#endif#ifdef WAITHIST#define MAXWAIT 1022u_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_residstatic voidscsiabort(hs, hd, where) register struct scsi_softc *hs; volatile register 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("scsi%d: abort from %s: phase=0x%x, ssts=0x%x, ints=0x%x\n", hs->sc_hc->hp_unit, 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("scsi%d: abort failed. phase=0x%x, ssts=0x%x\n", hs->sc_hc->hp_unit, hd->scsi_psns, hd->scsi_ssts); if (! ((junk = hd->scsi_ints) & INTS_RESEL)) { hd->scsi_sctl |= SCTL_CTRLRST; DELAY(1); 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). */voidscsi_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; }}intscsiinit(hc) register struct hp_ctlr *hc;{ register struct scsi_softc *hs = &scsi_softc[hc->hp_unit]; register struct scsidevice *hd = (struct scsidevice *)hc->hp_addr; if ((hd->scsi_id & ID_MASK) != SCSI_ID) return(0); hc->hp_ipl = SCSI_IPL(hd->scsi_csr); hs->sc_hc = hc; hs->sc_dq.dq_unit = hc->hp_unit; hs->sc_dq.dq_driver = &scsidriver; hs->sc_sq.dq_forw = hs->sc_sq.dq_back = &hs->sc_sq; scsi_isr[hc->hp_unit].isr_intr = scsiintr; scsi_isr[hc->hp_unit].isr_ipl = hc->hp_ipl; scsi_isr[hc->hp_unit].isr_arg = hc->hp_unit; isrlink(&scsi_isr[hc->hp_unit]); scsireset(hc->hp_unit); /* * 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; return(1);}voidscsireset(unit) register int unit;{ register struct scsi_softc *hs = &scsi_softc[unit]; volatile register struct scsidevice *hd = (struct scsidevice *)hs->sc_hc->hp_addr; u_int i; if (hs->sc_flags & SCSI_ALIVE) scsiabort(hs, hd, "reset"); printf("scsi%d: ", unit); 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; printf("32 bit dma, "); } /* 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 */ printf("250ns sync"); break; case 1: hs->sc_sync = TMOD_SYNC | 0x5e; /* 375 nsecs */ printf("375ns sync"); break; case 2: hs->sc_sync = TMOD_SYNC | 0x7d; /* 500 nsecs */ printf("500ns sync"); break; case 3: hs->sc_sync = 0; printf("async"); 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; 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; printf(", no parity"); } hd->scsi_sctl &=~ SCTL_DISABLE; printf(", scsi id %d\n", i); hs->sc_flags |= SCSI_ALIVE;}static voidscsierror(hs, hd, ints) register struct scsi_softc *hs; volatile register struct scsidevice *hd; u_char ints;{ int unit = hs->sc_hc->hp_unit; char *sep = ""; printf("scsi%d: ", unit); 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 intissue_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);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -