📄 sii.c
字号:
/*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Ralph Campbell and Rick Macklem. * * 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. * * @(#)sii.c 8.2 (Berkeley) 11/30/93 * * from: $Header: /sprite/src/kernel/dev/ds3100.md/RCS/devSII.c, * v 9.2 89/09/14 13:37:41 jhh Exp $ SPRITE (DECWRL)"; */#include "sii.h"#if NSII > 0/* * SCSI interface driver */#include <sys/param.h>#include <sys/systm.h>#include <sys/dkstat.h>#include <sys/buf.h>#include <sys/conf.h>#include <sys/errno.h>#include <machine/machConst.h>#include <pmax/dev/device.h>#include <pmax/dev/scsi.h>#include <pmax/dev/siireg.h>#include <pmax/pmax/kn01.h>int siiprobe();void siistart();struct driver siidriver = { "sii", siiprobe, siistart, 0,};typedef struct scsi_state { int statusByte; /* status byte returned during STATUS_PHASE */ int dmaDataPhase; /* which data phase to expect */ int dmaCurPhase; /* SCSI phase if DMA is in progress */ int dmaPrevPhase; /* SCSI phase of DMA suspended by disconnect */ u_short *dmaAddr[2]; /* DMA buffer memory address */ int dmaBufIndex; /* which of the above is currently in use */ int dmalen; /* amount to transfer in this chunk */ int cmdlen; /* total remaining amount of cmd to transfer */ u_char *cmd; /* current pointer within scsicmd->cmd */ int buflen; /* total remaining amount of data to transfer */ char *buf; /* current pointer within scsicmd->buf */ u_short flags; /* see below */ u_short prevComm; /* command reg before disconnect */ u_short dmaCtrl; /* DMA control register if disconnect */ u_short dmaAddrL; /* DMA address register if disconnect */ u_short dmaAddrH; /* DMA address register if disconnect */ u_short dmaCnt; /* DMA count if disconnect */ u_short dmaByte; /* DMA byte if disconnect on odd boundary */ u_short dmaReqAck; /* DMA synchronous xfer offset or 0 if async */} State;/* state flags */#define FIRST_DMA 0x01 /* true if no data DMA started yet */#define SII_NCMD 7struct siisoftc { SIIRegs *sc_regs; /* HW address of SII controller chip */ int sc_flags; int sc_target; /* target SCSI ID if connected */ ScsiCmd *sc_cmd[SII_NCMD]; /* active command indexed by ID */ State sc_st[SII_NCMD]; /* state info for each active command */} sii_softc[NSII];/* * MACROS for timing out spin loops. * * Wait until expression is true. * * Control register bits can change at any time so when the CPU * reads a register, the bits might change and * invalidate the setup and hold times for the CPU. * This macro reads the register twice to be sure the value is stable. * * args: var - variable to save control register contents * reg - control register to read * expr - expression to spin on * spincount - maximum number of times through the loop * cntr - variable for number of tries */#define SII_WAIT_UNTIL(var, reg, expr, spincount, cntr) { \ register unsigned tmp = reg; \ for (cntr = 0; cntr < spincount; cntr++) { \ while (tmp != (var = reg)) \ tmp = var; \ if (expr) \ break; \ if (cntr >= 100) \ DELAY(100); \ } \ }#ifdef DEBUGint sii_debug = 1;int sii_debug_cmd;int sii_debug_bn;int sii_debug_sz;#define NLOG 16struct sii_log { u_short cstat; u_short dstat; u_short comm; u_short msg; int target;} sii_log[NLOG], *sii_logp = sii_log;#endifu_char sii_buf[256]; /* used for extended messages */#define NORESET 0#define RESET 1#define NOWAIT 0#define WAIT 1/* define a safe address in the SCSI buffer for doing status & message DMA */#define SII_BUF_ADDR (MACH_PHYS_TO_UNCACHED(KN01_SYS_SII_B_START) \ + SII_MAX_DMA_XFER_LENGTH * 14)static void sii_Reset();static void sii_StartCmd();static void sii_CmdDone();static void sii_DoIntr();static void sii_StateChg();static void sii_DoSync();static void sii_StartDMA();static int sii_GetByte();/* * Test to see if device is present. * Return true if found and initialized ok. */siiprobe(cp) register struct pmax_ctlr *cp;{ register struct siisoftc *sc; register int i; if (cp->pmax_unit >= NSII) return (0); sc = &sii_softc[cp->pmax_unit]; sc->sc_regs = (SIIRegs *)cp->pmax_addr; sc->sc_flags = cp->pmax_flags; sc->sc_target = -1; /* no command active */ /* * Give each target its own DMA buffer region. * Make it big enough for 2 max transfers so we can ping pong buffers * while we copy the data. */ for (i = 0; i < SII_NCMD; i++) { sc->sc_st[i].dmaAddr[0] = (u_short *) MACH_PHYS_TO_UNCACHED(KN01_SYS_SII_B_START) + 2 * SII_MAX_DMA_XFER_LENGTH * i; sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] + SII_MAX_DMA_XFER_LENGTH; } printf("sii%d at nexus0 csr 0x%x\n", cp->pmax_unit, cp->pmax_addr); sii_Reset(sc->sc_regs, RESET); return (1);}/* * Start activity on a SCSI device. * We maintain information on each device separately since devices can * connect/disconnect during an operation. */voidsiistart(scsicmd) register ScsiCmd *scsicmd; /* command to start */{ register struct scsi_device *sdp = scsicmd->sd; register struct siisoftc *sc = &sii_softc[sdp->sd_ctlr]; int s; s = splbio(); /* * Check if another command is already in progress. * We may have to change this if we allow SCSI devices with * separate LUNs. */ if (sc->sc_cmd[sdp->sd_drive]) { printf("sii%d: device %s busy at start\n", sdp->sd_ctlr, sdp->sd_driver->d_name); (*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY, scsicmd->buflen, 0); splx(s); } sc->sc_cmd[sdp->sd_drive] = scsicmd; sii_StartCmd(sc, sdp->sd_drive); splx(s);}/* * Check to see if any SII chips have pending interrupts * and process as appropriate. */voidsiiintr(unit) int unit;{ register struct siisoftc *sc = &sii_softc[unit]; unsigned dstat; /* * Find which controller caused the interrupt. */ dstat = sc->sc_regs->dstat; if (dstat & (SII_CI | SII_DI)) sii_DoIntr(sc, dstat);}/* * Reset the SII chip and do a SCSI reset if 'reset' is true. * NOTE: if !cold && reset, should probably probe for devices * since a SCSI bus reset will set UNIT_ATTENTION. */static voidsii_Reset(regs, reset) register SIIRegs *regs; int reset; /* TRUE => reset SCSI bus */{#ifdef DEBUG if (sii_debug > 1) printf("sii: RESET\n");#endif /* * Reset the SII chip. */ regs->comm = SII_CHRESET; /* * Set arbitrated bus mode. */ regs->csr = SII_HPM; /* * SII is always ID 7. */ regs->id = SII_ID_IO | 7; /* * Enable SII to drive the SCSI bus. */ regs->dictrl = SII_PRE; regs->dmctrl = 0; if (reset) { register int i; /* * Assert SCSI bus reset for at least 25 Usec to clear the * world. SII_DO_RST is self clearing. * Delay 250 ms before doing any commands. */ regs->comm = SII_DO_RST; MachEmptyWriteBuffer(); DELAY(250000); /* rearbitrate synchronous offset */ for (i = 0; i < SII_NCMD; i++) sii_softc[0].sc_st[i].dmaReqAck = 0; } /* * Clear any pending interrupts from the reset. */ regs->cstat = regs->cstat; regs->dstat = regs->dstat; /* * Set up SII for arbitrated bus mode, SCSI parity checking, * Reselect Enable, and Interrupt Enable. */ regs->csr = SII_HPM | SII_RSE | SII_PCE | SII_IE; MachEmptyWriteBuffer();}/* * Start a SCSI command by sending the cmd data * to a SCSI controller via the SII. * Call the device done proceedure if it can't be started. * NOTE: we should be called with interrupts disabled. */static voidsii_StartCmd(sc, target) register struct siisoftc *sc; /* which SII to use */ register int target; /* which command to start */{ register SIIRegs *regs; register ScsiCmd *scsicmd; register State *state; register unsigned status; int error, retval; /* if another command is currently in progress, just wait */ if (sc->sc_target >= 0) return; /* initialize state information for this command */ scsicmd = sc->sc_cmd[target]; state = &sc->sc_st[target]; state->flags = FIRST_DMA; state->prevComm = 0; state->dmalen = 0; state->dmaCurPhase = -1; state->dmaPrevPhase = -1; state->dmaBufIndex = 0; state->cmd = scsicmd->cmd; state->cmdlen = scsicmd->cmdlen; if ((state->buflen = scsicmd->buflen) == 0) { state->dmaDataPhase = -1; /* illegal phase. shouldn't happen */ state->buf = (char *)0; } else { state->dmaDataPhase = (scsicmd->flags & SCSICMD_DATA_TO_DEVICE) ? SII_DATA_OUT_PHASE : SII_DATA_IN_PHASE; state->buf = scsicmd->buf; }#ifdef DEBUG if (sii_debug > 1) { printf("sii_StartCmd: %s target %d cmd 0x%x addr %x size %d dma %d\n", scsicmd->sd->sd_driver->d_name, target, scsicmd->cmd[0], scsicmd->buf, scsicmd->buflen, state->dmaDataPhase); } sii_debug_cmd = scsicmd->cmd[0]; if (scsicmd->cmd[0] == SCSI_READ_EXT) { sii_debug_bn = (scsicmd->cmd[2] << 24) | (scsicmd->cmd[3] << 16) | (scsicmd->cmd[4] << 8) | scsicmd->cmd[5]; sii_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8]; }#endif /* try to select the target */ regs = sc->sc_regs; /* * Another device may have selected us; in which case, * this command will be restarted later. */ if ((status = regs->dstat) & (SII_CI | SII_DI)) { sii_DoIntr(sc, status); return; } sc->sc_target = target; if (scsicmd->flags & SCSICMD_USE_SYNC) { printf("sii_StartCmd: doing extended msg\n"); /* XXX */ /* * Setup to send both the identify message and the synchronous * data transfer request. */ sii_buf[0] = SCSI_DIS_REC_IDENTIFY; sii_buf[1] = SCSI_EXTENDED_MSG; sii_buf[2] = 3; /* message length */ sii_buf[3] = SCSI_SYNCHRONOUS_XFER; sii_buf[4] = 0; sii_buf[5] = 3; /* maximum SII chip supports */ state->dmaCurPhase = SII_MSG_OUT_PHASE, state->dmalen = 6; CopyToBuffer((u_short *)sii_buf, (volatile u_short *)SII_BUF_ADDR, 6); regs->slcsr = target; regs->dmctrl = 0; regs->dmaddrl = ((u_short)SII_BUF_ADDR >> 1); regs->dmaddrh = ((u_short)SII_BUF_ADDR >> 17) & 03; regs->dmlotc = 6; regs->comm = SII_DMA | SII_INXFER | SII_SELECT | SII_ATN | SII_CON | SII_MSG_OUT_PHASE; } else { /* do a chained, select with ATN and programmed I/O command */ regs->data = SCSI_DIS_REC_IDENTIFY; regs->slcsr = target; regs->dmctrl = 0; regs->comm = SII_INXFER | SII_SELECT | SII_ATN | SII_CON | SII_MSG_OUT_PHASE; } MachEmptyWriteBuffer(); /* * Wait for something to happen * (should happen soon or we would use interrupts). */ SII_WAIT_UNTIL(status, regs->cstat, status & (SII_CI | SII_DI), SII_WAIT_COUNT/4, retval); /* check to see if we are connected OK */ if ((status & (SII_RST | SII_SCH | SII_STATE_MSK)) == (SII_SCH | SII_CON)) { regs->cstat = status; MachEmptyWriteBuffer();#ifdef DEBUG sii_logp->target = target; sii_logp->cstat = status; sii_logp->dstat = 0; sii_logp->comm = regs->comm; sii_logp->msg = -1; if (++sii_logp >= &sii_log[NLOG]) sii_logp = sii_log;#endif /* wait a short time for command phase */ SII_WAIT_UNTIL(status, regs->dstat, status & SII_MIS, SII_WAIT_COUNT, retval);#ifdef DEBUG if (sii_debug > 2) printf("sii_StartCmd: ds %x cnt %d\n", status, retval);#endif if ((status & (SII_CI | SII_MIS | SII_PHASE_MSK)) != (SII_MIS | SII_CMD_PHASE)) { printf("sii_StartCmd: timeout cs %x ds %x cnt %d\n", regs->cstat, status, retval); /* XXX */ /* process interrupt or continue until it happens */ if (status & (SII_CI | SII_DI)) sii_DoIntr(sc, status); return; } regs->dstat = SII_DNE; /* clear Msg Out DMA done */ /* send command data */ CopyToBuffer((u_short *)state->cmd, (volatile u_short *)state->dmaAddr[0], state->cmdlen); sii_StartDMA(regs, state->dmaCurPhase = SII_CMD_PHASE, state->dmaAddr[0], state->dmalen = scsicmd->cmdlen); /* wait a little while for DMA to finish */ SII_WAIT_UNTIL(status, regs->dstat, status & (SII_CI | SII_DI), SII_WAIT_COUNT, retval);#ifdef DEBUG if (sii_debug > 2) printf("sii_StartCmd: ds %x, cnt %d\n", status, retval);#endif if (status & (SII_CI | SII_DI)) sii_DoIntr(sc, status);#ifdef DEBUG if (sii_debug > 2) printf("sii_StartCmd: DONE ds %x\n", regs->dstat);#endif return; } /* * Another device may have selected us; in which case, * this command will be restarted later. */ if (status & (SII_CI | SII_DI)) { sii_DoIntr(sc, regs->dstat); return; } /* * Disconnect if selection command still in progress. */ if (status & SII_SIP) { error = ENXIO; /* device didn't respond */ regs->comm = SII_DISCON; MachEmptyWriteBuffer(); SII_WAIT_UNTIL(status, regs->cstat, !(status & (SII_CON | SII_SIP)), SII_WAIT_COUNT, retval); } else error = EBUSY; /* couldn't get the bus */#ifdef DEBUG if (sii_debug > 1) printf("sii_StartCmd: Couldn't select target %d error %d\n", target, error);#endif sc->sc_target = -1; regs->cstat = 0xffff; regs->dstat = 0xffff; regs->comm = 0; MachEmptyWriteBuffer(); sii_CmdDone(sc, target, error);}/* * Process interrupt conditions. */static voidsii_DoIntr(sc, dstat) register struct siisoftc *sc; register unsigned dstat;{ register SIIRegs *regs = sc->sc_regs; register State *state; register unsigned cstat; register int i; unsigned comm, msg;again: comm = regs->comm;#ifdef DEBUG if (sii_debug > 3) printf("sii_DoIntr: cs %x, ds %x cm %x ", regs->cstat, dstat, comm); sii_logp->target = sc->sc_target; sii_logp->cstat = regs->cstat; sii_logp->dstat = dstat; sii_logp->comm = comm; sii_logp->msg = -1; if (++sii_logp >= &sii_log[NLOG]) sii_logp = sii_log;#endif regs->dstat = dstat; /* acknowledge everything */ MachEmptyWriteBuffer(); if (dstat & SII_CI) { /* deglitch cstat register */ msg = regs->cstat; while (msg != (cstat = regs->cstat)) msg = cstat; regs->cstat = cstat; /* acknowledge everything */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -