156819Sralph /*-
263640Sbostic * Copyright (c) 1992, 1993
363640Sbostic * The Regents of the University of California. All rights reserved.
452130Smckusick *
552130Smckusick * This code is derived from software contributed to Berkeley by
656819Sralph * Ralph Campbell and Rick Macklem.
752130Smckusick *
852130Smckusick * %sccs.include.redist.c%
952130Smckusick *
10*68013Smckusick * @(#)sii.c 8.4 (Berkeley) 11/30/94
1152130Smckusick *
1252130Smckusick * from: $Header: /sprite/src/kernel/dev/ds3100.md/RCS/devSII.c,
1352754Sralph * v 9.2 89/09/14 13:37:41 jhh Exp $ SPRITE (DECWRL)";
1452130Smckusick */
1552130Smckusick
1652130Smckusick #include "sii.h"
1752130Smckusick #if NSII > 0
1852130Smckusick /*
1952130Smckusick * SCSI interface driver
2052130Smckusick */
2156522Sbostic #include <sys/param.h>
2256522Sbostic #include <sys/systm.h>
2356522Sbostic #include <sys/dkstat.h>
2456522Sbostic #include <sys/buf.h>
2556522Sbostic #include <sys/conf.h>
2656522Sbostic #include <sys/errno.h>
2752130Smckusick
2856522Sbostic #include <machine/machConst.h>
2956525Sbostic #include <pmax/dev/device.h>
3056525Sbostic #include <pmax/dev/scsi.h>
3156525Sbostic #include <pmax/dev/siireg.h>
3252130Smckusick
3356819Sralph #include <pmax/pmax/kn01.h>
3456819Sralph
3552130Smckusick int siiprobe();
3652130Smckusick void siistart();
3752130Smckusick struct driver siidriver = {
3852130Smckusick "sii", siiprobe, siistart, 0,
3952130Smckusick };
4052130Smckusick
4152130Smckusick typedef struct scsi_state {
4252130Smckusick int statusByte; /* status byte returned during STATUS_PHASE */
4352130Smckusick int dmaDataPhase; /* which data phase to expect */
4452130Smckusick int dmaCurPhase; /* SCSI phase if DMA is in progress */
4552130Smckusick int dmaPrevPhase; /* SCSI phase of DMA suspended by disconnect */
4652130Smckusick u_short *dmaAddr[2]; /* DMA buffer memory address */
4752130Smckusick int dmaBufIndex; /* which of the above is currently in use */
4852130Smckusick int dmalen; /* amount to transfer in this chunk */
4952130Smckusick int cmdlen; /* total remaining amount of cmd to transfer */
5052130Smckusick u_char *cmd; /* current pointer within scsicmd->cmd */
5152130Smckusick int buflen; /* total remaining amount of data to transfer */
5252130Smckusick char *buf; /* current pointer within scsicmd->buf */
5352130Smckusick u_short flags; /* see below */
5452130Smckusick u_short prevComm; /* command reg before disconnect */
5552130Smckusick u_short dmaCtrl; /* DMA control register if disconnect */
5652130Smckusick u_short dmaAddrL; /* DMA address register if disconnect */
5752130Smckusick u_short dmaAddrH; /* DMA address register if disconnect */
5852130Smckusick u_short dmaCnt; /* DMA count if disconnect */
5952130Smckusick u_short dmaByte; /* DMA byte if disconnect on odd boundary */
6052130Smckusick u_short dmaReqAck; /* DMA synchronous xfer offset or 0 if async */
6152130Smckusick } State;
6252130Smckusick
6352130Smckusick /* state flags */
6452130Smckusick #define FIRST_DMA 0x01 /* true if no data DMA started yet */
6567205Smckusick #define PARITY_ERR 0x02 /* true if parity error seen */
6652130Smckusick
6752130Smckusick #define SII_NCMD 7
6852130Smckusick struct siisoftc {
6952130Smckusick SIIRegs *sc_regs; /* HW address of SII controller chip */
7052130Smckusick int sc_flags;
7152130Smckusick int sc_target; /* target SCSI ID if connected */
7252130Smckusick ScsiCmd *sc_cmd[SII_NCMD]; /* active command indexed by ID */
7352130Smckusick State sc_st[SII_NCMD]; /* state info for each active command */
7452130Smckusick } sii_softc[NSII];
7552130Smckusick
7652130Smckusick /*
7752130Smckusick * MACROS for timing out spin loops.
7852130Smckusick *
7952130Smckusick * Wait until expression is true.
8052130Smckusick *
8152130Smckusick * Control register bits can change at any time so when the CPU
8252130Smckusick * reads a register, the bits might change and
8352130Smckusick * invalidate the setup and hold times for the CPU.
8452130Smckusick * This macro reads the register twice to be sure the value is stable.
8552130Smckusick *
8652130Smckusick * args: var - variable to save control register contents
8752130Smckusick * reg - control register to read
8852130Smckusick * expr - expression to spin on
8952130Smckusick * spincount - maximum number of times through the loop
9052130Smckusick * cntr - variable for number of tries
9152130Smckusick */
9252130Smckusick #define SII_WAIT_UNTIL(var, reg, expr, spincount, cntr) { \
9367205Smckusick register u_int tmp = reg; \
9452130Smckusick for (cntr = 0; cntr < spincount; cntr++) { \
9552130Smckusick while (tmp != (var = reg)) \
9652130Smckusick tmp = var; \
9752130Smckusick if (expr) \
9852130Smckusick break; \
9952130Smckusick if (cntr >= 100) \
10052130Smckusick DELAY(100); \
10152130Smckusick } \
10252130Smckusick }
10352130Smckusick
10452130Smckusick #ifdef DEBUG
10552130Smckusick int sii_debug = 1;
10652130Smckusick int sii_debug_cmd;
10752130Smckusick int sii_debug_bn;
10852130Smckusick int sii_debug_sz;
10952130Smckusick #define NLOG 16
11052130Smckusick struct sii_log {
11152130Smckusick u_short cstat;
11252130Smckusick u_short dstat;
11352130Smckusick u_short comm;
11452130Smckusick u_short msg;
11567205Smckusick int rlen;
11667205Smckusick int dlen;
11752130Smckusick int target;
11852130Smckusick } sii_log[NLOG], *sii_logp = sii_log;
11952130Smckusick #endif
12052130Smckusick
12152130Smckusick u_char sii_buf[256]; /* used for extended messages */
12252130Smckusick
12352130Smckusick #define NORESET 0
12452130Smckusick #define RESET 1
12552130Smckusick #define NOWAIT 0
12652130Smckusick #define WAIT 1
12752130Smckusick
12852130Smckusick /* define a safe address in the SCSI buffer for doing status & message DMA */
12956819Sralph #define SII_BUF_ADDR (MACH_PHYS_TO_UNCACHED(KN01_SYS_SII_B_START) \
13056819Sralph + SII_MAX_DMA_XFER_LENGTH * 14)
13152130Smckusick
13256819Sralph static void sii_Reset();
13356819Sralph static void sii_StartCmd();
13456819Sralph static void sii_CmdDone();
13556819Sralph static void sii_DoIntr();
13656819Sralph static void sii_StateChg();
13756819Sralph static void sii_DoSync();
13856819Sralph static void sii_StartDMA();
13956819Sralph static int sii_GetByte();
14052130Smckusick
14152130Smckusick /*
14252130Smckusick * Test to see if device is present.
14352130Smckusick * Return true if found and initialized ok.
14452130Smckusick */
siiprobe(cp)14552130Smckusick siiprobe(cp)
14652130Smckusick register struct pmax_ctlr *cp;
14752130Smckusick {
14852130Smckusick register struct siisoftc *sc;
14952130Smckusick register int i;
15052130Smckusick
15152130Smckusick if (cp->pmax_unit >= NSII)
15252130Smckusick return (0);
15352130Smckusick sc = &sii_softc[cp->pmax_unit];
15452130Smckusick sc->sc_regs = (SIIRegs *)cp->pmax_addr;
15552130Smckusick sc->sc_flags = cp->pmax_flags;
15652130Smckusick sc->sc_target = -1; /* no command active */
15752130Smckusick /*
15852130Smckusick * Give each target its own DMA buffer region.
15952130Smckusick * Make it big enough for 2 max transfers so we can ping pong buffers
16052130Smckusick * while we copy the data.
16152130Smckusick */
16252130Smckusick for (i = 0; i < SII_NCMD; i++) {
16356819Sralph sc->sc_st[i].dmaAddr[0] = (u_short *)
16456819Sralph MACH_PHYS_TO_UNCACHED(KN01_SYS_SII_B_START) +
16552130Smckusick 2 * SII_MAX_DMA_XFER_LENGTH * i;
16652130Smckusick sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] +
16752130Smckusick SII_MAX_DMA_XFER_LENGTH;
16852130Smckusick }
16952130Smckusick
17052130Smckusick printf("sii%d at nexus0 csr 0x%x\n", cp->pmax_unit, cp->pmax_addr);
17152130Smckusick sii_Reset(sc->sc_regs, RESET);
17252130Smckusick return (1);
17352130Smckusick }
17452130Smckusick
17552130Smckusick /*
17652130Smckusick * Start activity on a SCSI device.
17752130Smckusick * We maintain information on each device separately since devices can
17852130Smckusick * connect/disconnect during an operation.
17952130Smckusick */
18052130Smckusick void
siistart(scsicmd)18152130Smckusick siistart(scsicmd)
18252130Smckusick register ScsiCmd *scsicmd; /* command to start */
18352130Smckusick {
18452130Smckusick register struct scsi_device *sdp = scsicmd->sd;
18552130Smckusick register struct siisoftc *sc = &sii_softc[sdp->sd_ctlr];
18652130Smckusick int s;
18752130Smckusick
18852130Smckusick s = splbio();
18952130Smckusick /*
19052130Smckusick * Check if another command is already in progress.
19152130Smckusick * We may have to change this if we allow SCSI devices with
19252130Smckusick * separate LUNs.
19352130Smckusick */
19452130Smckusick if (sc->sc_cmd[sdp->sd_drive]) {
19552130Smckusick printf("sii%d: device %s busy at start\n", sdp->sd_ctlr,
19652130Smckusick sdp->sd_driver->d_name);
19752130Smckusick (*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY,
19852130Smckusick scsicmd->buflen, 0);
19952130Smckusick splx(s);
20052130Smckusick }
20152130Smckusick sc->sc_cmd[sdp->sd_drive] = scsicmd;
20252130Smckusick sii_StartCmd(sc, sdp->sd_drive);
20352130Smckusick splx(s);
20452130Smckusick }
20552130Smckusick
20652130Smckusick /*
20752130Smckusick * Check to see if any SII chips have pending interrupts
20852130Smckusick * and process as appropriate.
20952130Smckusick */
21052130Smckusick void
siiintr(unit)21152697Sralph siiintr(unit)
21252697Sralph int unit;
21352130Smckusick {
21452697Sralph register struct siisoftc *sc = &sii_softc[unit];
21567205Smckusick u_int dstat;
21652130Smckusick
21752130Smckusick /*
21852130Smckusick * Find which controller caused the interrupt.
21952130Smckusick */
22052697Sralph dstat = sc->sc_regs->dstat;
22152697Sralph if (dstat & (SII_CI | SII_DI))
22252697Sralph sii_DoIntr(sc, dstat);
22352130Smckusick }
22452130Smckusick
22552130Smckusick /*
22652130Smckusick * Reset the SII chip and do a SCSI reset if 'reset' is true.
22752130Smckusick * NOTE: if !cold && reset, should probably probe for devices
22852130Smckusick * since a SCSI bus reset will set UNIT_ATTENTION.
22952130Smckusick */
23052130Smckusick static void
sii_Reset(regs,reset)23152130Smckusick sii_Reset(regs, reset)
23252130Smckusick register SIIRegs *regs;
23352130Smckusick int reset; /* TRUE => reset SCSI bus */
23452130Smckusick {
23552130Smckusick
23652130Smckusick #ifdef DEBUG
23752130Smckusick if (sii_debug > 1)
23852130Smckusick printf("sii: RESET\n");
23952130Smckusick #endif
24052130Smckusick /*
24152130Smckusick * Reset the SII chip.
24252130Smckusick */
24352130Smckusick regs->comm = SII_CHRESET;
24452130Smckusick /*
24552130Smckusick * Set arbitrated bus mode.
24652130Smckusick */
24752130Smckusick regs->csr = SII_HPM;
24852130Smckusick /*
24952130Smckusick * SII is always ID 7.
25052130Smckusick */
25152130Smckusick regs->id = SII_ID_IO | 7;
25252130Smckusick /*
25352130Smckusick * Enable SII to drive the SCSI bus.
25452130Smckusick */
25552130Smckusick regs->dictrl = SII_PRE;
25652130Smckusick regs->dmctrl = 0;
25752130Smckusick
25852130Smckusick if (reset) {
25952130Smckusick register int i;
26052130Smckusick
26152130Smckusick /*
26252130Smckusick * Assert SCSI bus reset for at least 25 Usec to clear the
26352130Smckusick * world. SII_DO_RST is self clearing.
26452130Smckusick * Delay 250 ms before doing any commands.
26552130Smckusick */
26652130Smckusick regs->comm = SII_DO_RST;
26752130Smckusick MachEmptyWriteBuffer();
26852130Smckusick DELAY(250000);
26952130Smckusick
27052130Smckusick /* rearbitrate synchronous offset */
27152130Smckusick for (i = 0; i < SII_NCMD; i++)
27252130Smckusick sii_softc[0].sc_st[i].dmaReqAck = 0;
27352130Smckusick }
27452130Smckusick
27552130Smckusick /*
27652130Smckusick * Clear any pending interrupts from the reset.
27752130Smckusick */
27852130Smckusick regs->cstat = regs->cstat;
27952130Smckusick regs->dstat = regs->dstat;
28052130Smckusick /*
28152130Smckusick * Set up SII for arbitrated bus mode, SCSI parity checking,
28252130Smckusick * Reselect Enable, and Interrupt Enable.
28352130Smckusick */
28452130Smckusick regs->csr = SII_HPM | SII_RSE | SII_PCE | SII_IE;
28552130Smckusick MachEmptyWriteBuffer();
28652130Smckusick }
28752130Smckusick
28852130Smckusick /*
28952130Smckusick * Start a SCSI command by sending the cmd data
29052130Smckusick * to a SCSI controller via the SII.
29152130Smckusick * Call the device done proceedure if it can't be started.
29252130Smckusick * NOTE: we should be called with interrupts disabled.
29352130Smckusick */
29452130Smckusick static void
sii_StartCmd(sc,target)29552130Smckusick sii_StartCmd(sc, target)
29652130Smckusick register struct siisoftc *sc; /* which SII to use */
29752130Smckusick register int target; /* which command to start */
29852130Smckusick {
29952130Smckusick register SIIRegs *regs;
30052130Smckusick register ScsiCmd *scsicmd;
30152130Smckusick register State *state;
30267205Smckusick register u_int status;
30352130Smckusick int error, retval;
30452130Smckusick
30552130Smckusick /* if another command is currently in progress, just wait */
30652130Smckusick if (sc->sc_target >= 0)
30752130Smckusick return;
30852130Smckusick
30952130Smckusick /* initialize state information for this command */
31052130Smckusick scsicmd = sc->sc_cmd[target];
31152130Smckusick state = &sc->sc_st[target];
31252130Smckusick state->flags = FIRST_DMA;
31352130Smckusick state->prevComm = 0;
31452130Smckusick state->dmalen = 0;
31552130Smckusick state->dmaCurPhase = -1;
31652130Smckusick state->dmaPrevPhase = -1;
31752130Smckusick state->dmaBufIndex = 0;
31852130Smckusick state->cmd = scsicmd->cmd;
31952130Smckusick state->cmdlen = scsicmd->cmdlen;
32052130Smckusick if ((state->buflen = scsicmd->buflen) == 0) {
32152130Smckusick state->dmaDataPhase = -1; /* illegal phase. shouldn't happen */
32252130Smckusick state->buf = (char *)0;
32352130Smckusick } else {
32452130Smckusick state->dmaDataPhase =
32552130Smckusick (scsicmd->flags & SCSICMD_DATA_TO_DEVICE) ?
32652130Smckusick SII_DATA_OUT_PHASE : SII_DATA_IN_PHASE;
32752130Smckusick state->buf = scsicmd->buf;
32852130Smckusick }
32952130Smckusick
33052130Smckusick #ifdef DEBUG
33152130Smckusick if (sii_debug > 1) {
33252130Smckusick printf("sii_StartCmd: %s target %d cmd 0x%x addr %x size %d dma %d\n",
33352130Smckusick scsicmd->sd->sd_driver->d_name, target,
33452130Smckusick scsicmd->cmd[0], scsicmd->buf, scsicmd->buflen,
33552130Smckusick state->dmaDataPhase);
33652130Smckusick }
33752130Smckusick sii_debug_cmd = scsicmd->cmd[0];
33867205Smckusick if (scsicmd->cmd[0] == SCSI_READ_EXT ||
33967205Smckusick scsicmd->cmd[0] == SCSI_WRITE_EXT) {
34052130Smckusick sii_debug_bn = (scsicmd->cmd[2] << 24) |
34152130Smckusick (scsicmd->cmd[3] << 16) |
34252130Smckusick (scsicmd->cmd[4] << 8) |
34352130Smckusick scsicmd->cmd[5];
34452130Smckusick sii_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8];
34567205Smckusick } else {
34667205Smckusick sii_debug_bn = 0;
34767205Smckusick sii_debug_sz = 0;
34852130Smckusick }
34952130Smckusick #endif
35052130Smckusick
35152130Smckusick /* try to select the target */
35252130Smckusick regs = sc->sc_regs;
35352130Smckusick
35452130Smckusick /*
35552130Smckusick * Another device may have selected us; in which case,
35652130Smckusick * this command will be restarted later.
35752130Smckusick */
35852130Smckusick if ((status = regs->dstat) & (SII_CI | SII_DI)) {
35952130Smckusick sii_DoIntr(sc, status);
36052130Smckusick return;
36152130Smckusick }
36252130Smckusick
36352130Smckusick sc->sc_target = target;
36467205Smckusick #if 0
36567205Smckusick /* seem to have problems with synchronous transfers */
36652130Smckusick if (scsicmd->flags & SCSICMD_USE_SYNC) {
36752130Smckusick printf("sii_StartCmd: doing extended msg\n"); /* XXX */
36852130Smckusick /*
36952130Smckusick * Setup to send both the identify message and the synchronous
37052130Smckusick * data transfer request.
37152130Smckusick */
37252130Smckusick sii_buf[0] = SCSI_DIS_REC_IDENTIFY;
37352130Smckusick sii_buf[1] = SCSI_EXTENDED_MSG;
37452130Smckusick sii_buf[2] = 3; /* message length */
37552130Smckusick sii_buf[3] = SCSI_SYNCHRONOUS_XFER;
37652130Smckusick sii_buf[4] = 0;
37752130Smckusick sii_buf[5] = 3; /* maximum SII chip supports */
37852130Smckusick
37952130Smckusick state->dmaCurPhase = SII_MSG_OUT_PHASE,
38052130Smckusick state->dmalen = 6;
38152130Smckusick CopyToBuffer((u_short *)sii_buf,
38252130Smckusick (volatile u_short *)SII_BUF_ADDR, 6);
38352130Smckusick regs->slcsr = target;
38467205Smckusick regs->dmctrl = state->dmaReqAck;
38567205Smckusick regs->dmaddrl = (u_short)(SII_BUF_ADDR >> 1);
38667205Smckusick regs->dmaddrh = (u_short)(SII_BUF_ADDR >> 17) & 03;
38752130Smckusick regs->dmlotc = 6;
38852130Smckusick regs->comm = SII_DMA | SII_INXFER | SII_SELECT | SII_ATN |
38952130Smckusick SII_CON | SII_MSG_OUT_PHASE;
39067205Smckusick } else
39167205Smckusick #endif
39267205Smckusick {
39352130Smckusick /* do a chained, select with ATN and programmed I/O command */
39452130Smckusick regs->data = SCSI_DIS_REC_IDENTIFY;
39552130Smckusick regs->slcsr = target;
39667205Smckusick regs->dmctrl = state->dmaReqAck;
39752130Smckusick regs->comm = SII_INXFER | SII_SELECT | SII_ATN | SII_CON |
39852130Smckusick SII_MSG_OUT_PHASE;
39952130Smckusick }
40052130Smckusick MachEmptyWriteBuffer();
40152130Smckusick
40252130Smckusick /*
40352130Smckusick * Wait for something to happen
40452130Smckusick * (should happen soon or we would use interrupts).
40552130Smckusick */
40652130Smckusick SII_WAIT_UNTIL(status, regs->cstat, status & (SII_CI | SII_DI),
40753085Sralph SII_WAIT_COUNT/4, retval);
40852130Smckusick
40952130Smckusick /* check to see if we are connected OK */
41052130Smckusick if ((status & (SII_RST | SII_SCH | SII_STATE_MSK)) ==
41152130Smckusick (SII_SCH | SII_CON)) {
41252130Smckusick regs->cstat = status;
41352130Smckusick MachEmptyWriteBuffer();
41452130Smckusick
41552130Smckusick #ifdef DEBUG
41652130Smckusick sii_logp->target = target;
41752130Smckusick sii_logp->cstat = status;
41852130Smckusick sii_logp->dstat = 0;
41952130Smckusick sii_logp->comm = regs->comm;
42052130Smckusick sii_logp->msg = -1;
42167205Smckusick sii_logp->rlen = state->buflen;
42267205Smckusick sii_logp->dlen = state->dmalen;
42352130Smckusick if (++sii_logp >= &sii_log[NLOG])
42452130Smckusick sii_logp = sii_log;
42552130Smckusick #endif
42652130Smckusick
42752130Smckusick /* wait a short time for command phase */
42852130Smckusick SII_WAIT_UNTIL(status, regs->dstat, status & SII_MIS,
42952130Smckusick SII_WAIT_COUNT, retval);
43052130Smckusick #ifdef DEBUG
43152130Smckusick if (sii_debug > 2)
43252130Smckusick printf("sii_StartCmd: ds %x cnt %d\n", status, retval);
43352130Smckusick #endif
43452130Smckusick if ((status & (SII_CI | SII_MIS | SII_PHASE_MSK)) !=
43552130Smckusick (SII_MIS | SII_CMD_PHASE)) {
43652130Smckusick printf("sii_StartCmd: timeout cs %x ds %x cnt %d\n",
43752130Smckusick regs->cstat, status, retval); /* XXX */
43852130Smckusick /* process interrupt or continue until it happens */
43952130Smckusick if (status & (SII_CI | SII_DI))
44052130Smckusick sii_DoIntr(sc, status);
44152130Smckusick return;
44252130Smckusick }
44352130Smckusick regs->dstat = SII_DNE; /* clear Msg Out DMA done */
44452130Smckusick
44552130Smckusick /* send command data */
44652130Smckusick CopyToBuffer((u_short *)state->cmd,
44752130Smckusick (volatile u_short *)state->dmaAddr[0], state->cmdlen);
44852130Smckusick sii_StartDMA(regs, state->dmaCurPhase = SII_CMD_PHASE,
44952130Smckusick state->dmaAddr[0], state->dmalen = scsicmd->cmdlen);
45052130Smckusick
45152130Smckusick /* wait a little while for DMA to finish */
45252130Smckusick SII_WAIT_UNTIL(status, regs->dstat, status & (SII_CI | SII_DI),
45352130Smckusick SII_WAIT_COUNT, retval);
45452130Smckusick #ifdef DEBUG
45552130Smckusick if (sii_debug > 2)
45652130Smckusick printf("sii_StartCmd: ds %x, cnt %d\n", status, retval);
45752130Smckusick #endif
45852130Smckusick if (status & (SII_CI | SII_DI))
45952130Smckusick sii_DoIntr(sc, status);
46052130Smckusick #ifdef DEBUG
46152130Smckusick if (sii_debug > 2)
46252130Smckusick printf("sii_StartCmd: DONE ds %x\n", regs->dstat);
46352130Smckusick #endif
46452130Smckusick return;
46552130Smckusick }
46652130Smckusick
46752130Smckusick /*
46852130Smckusick * Another device may have selected us; in which case,
46952130Smckusick * this command will be restarted later.
47052130Smckusick */
47152130Smckusick if (status & (SII_CI | SII_DI)) {
47252130Smckusick sii_DoIntr(sc, regs->dstat);
47352130Smckusick return;
47452130Smckusick }
47552130Smckusick
47652130Smckusick /*
47752130Smckusick * Disconnect if selection command still in progress.
47852130Smckusick */
47952130Smckusick if (status & SII_SIP) {
48052130Smckusick error = ENXIO; /* device didn't respond */
48152130Smckusick regs->comm = SII_DISCON;
48252130Smckusick MachEmptyWriteBuffer();
48352130Smckusick SII_WAIT_UNTIL(status, regs->cstat,
48452130Smckusick !(status & (SII_CON | SII_SIP)),
48552130Smckusick SII_WAIT_COUNT, retval);
48652130Smckusick } else
48752130Smckusick error = EBUSY; /* couldn't get the bus */
48852130Smckusick #ifdef DEBUG
48953085Sralph if (sii_debug > 1)
49052130Smckusick printf("sii_StartCmd: Couldn't select target %d error %d\n",
49152130Smckusick target, error);
49252130Smckusick #endif
49352130Smckusick sc->sc_target = -1;
49452130Smckusick regs->cstat = 0xffff;
49552130Smckusick regs->dstat = 0xffff;
49652130Smckusick regs->comm = 0;
49752130Smckusick MachEmptyWriteBuffer();
49852130Smckusick sii_CmdDone(sc, target, error);
49952130Smckusick }
50052130Smckusick
50152130Smckusick /*
50252130Smckusick * Process interrupt conditions.
50352130Smckusick */
50452130Smckusick static void
sii_DoIntr(sc,dstat)50552130Smckusick sii_DoIntr(sc, dstat)
50652130Smckusick register struct siisoftc *sc;
50767205Smckusick register u_int dstat;
50852130Smckusick {
50952130Smckusick register SIIRegs *regs = sc->sc_regs;
51052130Smckusick register State *state;
51167205Smckusick register u_int cstat;
51267205Smckusick int i, msg;
51367205Smckusick u_int comm;
51452130Smckusick
51552130Smckusick again:
51652130Smckusick comm = regs->comm;
51752130Smckusick
51852130Smckusick #ifdef DEBUG
51952130Smckusick if (sii_debug > 3)
52052130Smckusick printf("sii_DoIntr: cs %x, ds %x cm %x ",
52152130Smckusick regs->cstat, dstat, comm);
52252130Smckusick sii_logp->target = sc->sc_target;
52352130Smckusick sii_logp->cstat = regs->cstat;
52452130Smckusick sii_logp->dstat = dstat;
52552130Smckusick sii_logp->comm = comm;
52652130Smckusick sii_logp->msg = -1;
52767205Smckusick if (sc->sc_target >= 0) {
52867205Smckusick sii_logp->rlen = sc->sc_st[sc->sc_target].buflen;
52967205Smckusick sii_logp->dlen = sc->sc_st[sc->sc_target].dmalen;
53067205Smckusick } else {
53167205Smckusick sii_logp->rlen = 0;
53267205Smckusick sii_logp->dlen = 0;
53367205Smckusick }
53452130Smckusick if (++sii_logp >= &sii_log[NLOG])
53552130Smckusick sii_logp = sii_log;
53652130Smckusick #endif
53752130Smckusick
53852130Smckusick regs->dstat = dstat; /* acknowledge everything */
53952130Smckusick MachEmptyWriteBuffer();
54052130Smckusick
54152130Smckusick if (dstat & SII_CI) {
54252130Smckusick /* deglitch cstat register */
54352130Smckusick msg = regs->cstat;
54452130Smckusick while (msg != (cstat = regs->cstat))
54552130Smckusick msg = cstat;
54652130Smckusick regs->cstat = cstat; /* acknowledge everything */
54752130Smckusick MachEmptyWriteBuffer();
54852130Smckusick #ifdef DEBUG
54952130Smckusick if (sii_logp > sii_log)
55052130Smckusick sii_logp[-1].cstat = cstat;
55152130Smckusick else
55252130Smckusick sii_log[NLOG - 1].cstat = cstat;
55352130Smckusick #endif
55452130Smckusick
55552130Smckusick /* check for a BUS RESET */
55652130Smckusick if (cstat & SII_RST) {
55752130Smckusick printf("sii%d: SCSI bus reset!!\n", sc - sii_softc);
55852130Smckusick /* need to flush disconnected commands */
55952697Sralph for (i = 0; i < SII_NCMD; i++) {
56052697Sralph if (!sc->sc_cmd[i])
56152697Sralph continue;
56252130Smckusick sii_CmdDone(sc, i, EIO);
56352697Sralph }
56452130Smckusick /* rearbitrate synchronous offset */
56552130Smckusick for (i = 0; i < SII_NCMD; i++)
56652130Smckusick sc->sc_st[i].dmaReqAck = 0;
56752130Smckusick sc->sc_target = -1;
56852130Smckusick return;
56952130Smckusick }
57052130Smckusick
57152130Smckusick #ifdef notdef
57252130Smckusick /*
57352130Smckusick * Check for a BUS ERROR.
57452130Smckusick * According to DEC, this feature doesn't really work
57552130Smckusick * and to just clear the bit if it's set.
57652130Smckusick */
57752130Smckusick if (cstat & SII_BER) {
57852130Smckusick }
57952130Smckusick #endif
58052130Smckusick
58152130Smckusick /* check for state change */
58252130Smckusick if (cstat & SII_SCH) {
58352130Smckusick sii_StateChg(sc, cstat);
58452130Smckusick comm = regs->comm;
58552130Smckusick }
58652130Smckusick }
58752130Smckusick
58852130Smckusick /* check for DMA completion */
58952130Smckusick if (dstat & SII_DNE) {
59052130Smckusick u_short *dma;
59152130Smckusick char *buf;
59252130Smckusick
59352130Smckusick /*
59452130Smckusick * There is a race condition with SII_SCH. There is a short
59552130Smckusick * window between the time a SII_SCH is seen after a disconnect
59652130Smckusick * and when the SII_SCH is cleared. A reselect can happen
59752130Smckusick * in this window and we will clear the SII_SCH without
59852130Smckusick * processing the reconnect.
59952130Smckusick */
60052130Smckusick if (sc->sc_target < 0) {
60152130Smckusick cstat = regs->cstat;
60252130Smckusick printf("sii%d: target %d DNE?? dev %d,%d cs %x\n",
60352130Smckusick sc - sii_softc, sc->sc_target,
60452130Smckusick regs->slcsr, regs->destat,
60552130Smckusick cstat); /* XXX */
60652130Smckusick if (cstat & SII_DST) {
60752130Smckusick sc->sc_target = regs->destat;
60852130Smckusick state->prevComm = 0;
60952130Smckusick } else
61052130Smckusick panic("sc_target 1");
61152130Smckusick }
61252130Smckusick state = &sc->sc_st[sc->sc_target];
61367205Smckusick /* check for a PARITY ERROR */
61467205Smckusick if (dstat & SII_IPE) {
61567205Smckusick state->flags |= PARITY_ERR;
61667205Smckusick printf("sii%d: Parity error!!\n", sc - sii_softc);
61767205Smckusick goto abort;
61867205Smckusick }
61952130Smckusick /* dmalen = amount left to transfer, i = amount transfered */
62052130Smckusick i = state->dmalen;
62152130Smckusick state->dmalen = 0;
62252130Smckusick state->dmaCurPhase = -1;
62352130Smckusick #ifdef DEBUG
62452130Smckusick if (sii_debug > 4) {
62552130Smckusick printf("DNE: amt %d ", i);
62652130Smckusick if (!(dstat & SII_TCZ))
62752130Smckusick printf("no TCZ?? (%d) ", regs->dmlotc);
62852130Smckusick } else if (!(dstat & SII_TCZ)) {
62952130Smckusick printf("sii%d: device %d: no TCZ?? (%d)\n",
63052130Smckusick sc - sii_softc, sc->sc_target, regs->dmlotc);
63152130Smckusick sii_DumpLog(); /* XXX */
63252130Smckusick }
63352130Smckusick #endif
63452130Smckusick switch (comm & SII_PHASE_MSK) {
63552130Smckusick case SII_CMD_PHASE:
63652130Smckusick state->cmdlen -= i;
63752130Smckusick break;
63852130Smckusick
63952130Smckusick case SII_DATA_IN_PHASE:
64052130Smckusick /* check for more data for the same phase */
64152130Smckusick dma = state->dmaAddr[state->dmaBufIndex];
64252130Smckusick buf = state->buf;
64352130Smckusick state->buf += i;
64452130Smckusick state->buflen -= i;
64552130Smckusick if (state->buflen > 0 && !(dstat & SII_MIS)) {
64652130Smckusick int len;
64752130Smckusick
64852130Smckusick /* start reading next chunk */
64952130Smckusick len = state->buflen;
65052130Smckusick if (len > SII_MAX_DMA_XFER_LENGTH)
65152130Smckusick len = SII_MAX_DMA_XFER_LENGTH;
65252130Smckusick state->dmaBufIndex = !state->dmaBufIndex;
65352130Smckusick sii_StartDMA(regs,
65452130Smckusick state->dmaCurPhase = SII_DATA_IN_PHASE,
65552130Smckusick state->dmaAddr[state->dmaBufIndex],
65652130Smckusick state->dmalen = len);
65752130Smckusick dstat &= ~(SII_IBF | SII_TBE);
65852130Smckusick }
65952130Smckusick /* copy in the data */
66052130Smckusick CopyFromBuffer((volatile u_short *)dma, buf, i);
66152130Smckusick break;
66252130Smckusick
66352130Smckusick case SII_DATA_OUT_PHASE:
66452130Smckusick state->dmaBufIndex = !state->dmaBufIndex;
66552130Smckusick state->buf += i;
66652130Smckusick state->buflen -= i;
66752130Smckusick
66852130Smckusick /* check for more data for the same phase */
66952130Smckusick if (state->buflen <= 0 || (dstat & SII_MIS))
67052130Smckusick break;
67152130Smckusick
67252130Smckusick /* start next chunk */
67352130Smckusick i = state->buflen;
67452130Smckusick if (i > SII_MAX_DMA_XFER_LENGTH) {
67552130Smckusick sii_StartDMA(regs, state->dmaCurPhase =
67652130Smckusick SII_DATA_OUT_PHASE,
67752130Smckusick state->dmaAddr[state->dmaBufIndex],
67852130Smckusick state->dmalen =
67952130Smckusick SII_MAX_DMA_XFER_LENGTH);
68052130Smckusick /* prepare for next chunk */
68152130Smckusick i -= SII_MAX_DMA_XFER_LENGTH;
68252130Smckusick if (i > SII_MAX_DMA_XFER_LENGTH)
68352130Smckusick i = SII_MAX_DMA_XFER_LENGTH;
68452130Smckusick CopyToBuffer((u_short *)(state->buf +
68552130Smckusick SII_MAX_DMA_XFER_LENGTH),
68652130Smckusick (volatile u_short *)
68752130Smckusick state->dmaAddr[!state->dmaBufIndex], i);
68852130Smckusick } else {
68952130Smckusick sii_StartDMA(regs, state->dmaCurPhase =
69052130Smckusick SII_DATA_OUT_PHASE,
69152130Smckusick state->dmaAddr[state->dmaBufIndex],
69252130Smckusick state->dmalen = i);
69352130Smckusick }
69452130Smckusick dstat &= ~(SII_IBF | SII_TBE);
69552130Smckusick }
69652130Smckusick }
69752130Smckusick
69852130Smckusick /* check for phase change or another MsgIn/Out */
69952130Smckusick if (dstat & (SII_MIS | SII_IBF | SII_TBE)) {
70052130Smckusick /*
70152130Smckusick * There is a race condition with SII_SCH. There is a short
70252130Smckusick * window between the time a SII_SCH is seen after a disconnect
70352130Smckusick * and when the SII_SCH is cleared. A reselect can happen
70452130Smckusick * in this window and we will clear the SII_SCH without
70552130Smckusick * processing the reconnect.
70652130Smckusick */
70752130Smckusick if (sc->sc_target < 0) {
70852130Smckusick cstat = regs->cstat;
70952130Smckusick printf("sii%d: target %d MIS?? dev %d,%d cs %x ds %x\n",
71052130Smckusick sc - sii_softc, sc->sc_target,
71152130Smckusick regs->slcsr, regs->destat,
71252130Smckusick cstat, dstat); /* XXX */
71352130Smckusick if (cstat & SII_DST) {
71452130Smckusick sc->sc_target = regs->destat;
71552130Smckusick state->prevComm = 0;
71667205Smckusick } else {
717*68013Smckusick #ifdef DEBUG
71867205Smckusick sii_DumpLog();
719*68013Smckusick #endif
72052130Smckusick panic("sc_target 2");
72167205Smckusick }
72252130Smckusick }
72352130Smckusick state = &sc->sc_st[sc->sc_target];
72452130Smckusick switch (dstat & SII_PHASE_MSK) {
72552130Smckusick case SII_CMD_PHASE:
72652130Smckusick if (state->dmaPrevPhase >= 0) {
72752130Smckusick /* restart DMA after disconnect/reconnect */
72852130Smckusick if (state->dmaPrevPhase != SII_CMD_PHASE) {
72952130Smckusick printf("sii%d: device %d: dma reselect phase doesn't match\n",
73052130Smckusick sc - sii_softc, sc->sc_target);
73152130Smckusick goto abort;
73252130Smckusick }
73352130Smckusick state->dmaCurPhase = SII_CMD_PHASE;
73452130Smckusick state->dmaPrevPhase = -1;
73552130Smckusick regs->dmaddrl = state->dmaAddrL;
73652130Smckusick regs->dmaddrh = state->dmaAddrH;
73752130Smckusick regs->dmlotc = state->dmaCnt;
73852130Smckusick if (state->dmaCnt & 1)
73952130Smckusick regs->dmabyte = state->dmaByte;
74052130Smckusick regs->comm = SII_DMA | SII_INXFER |
74152130Smckusick (comm & SII_STATE_MSK) | SII_CMD_PHASE;
74252130Smckusick MachEmptyWriteBuffer();
74352130Smckusick #ifdef DEBUG
74452130Smckusick if (sii_debug > 4)
74552130Smckusick printf("Cmd dcnt %d dadr %x ",
74652130Smckusick state->dmaCnt,
74752130Smckusick (state->dmaAddrH << 16) |
74852130Smckusick state->dmaAddrL);
74952130Smckusick #endif
75052130Smckusick } else {
75152130Smckusick /* send command data */
75252130Smckusick i = state->cmdlen;
75352130Smckusick if (i == 0) {
75452130Smckusick printf("sii%d: device %d: cmd count exceeded\n",
75552130Smckusick sc - sii_softc, sc->sc_target);
75652130Smckusick goto abort;
75752130Smckusick }
75852130Smckusick CopyToBuffer((u_short *)state->cmd,
75952130Smckusick (volatile u_short *)state->dmaAddr[0],
76052130Smckusick i);
76152130Smckusick sii_StartDMA(regs, state->dmaCurPhase =
76252130Smckusick SII_CMD_PHASE, state->dmaAddr[0],
76352130Smckusick state->dmalen = i);
76452130Smckusick }
76552130Smckusick /* wait a short time for XFER complete */
76652130Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
76752130Smckusick dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i);
76852130Smckusick if (dstat & (SII_CI | SII_DI)) {
76952130Smckusick #ifdef DEBUG
77052130Smckusick if (sii_debug > 4)
77152130Smckusick printf("cnt %d\n", i);
77252130Smckusick else if (sii_debug > 0)
77352130Smckusick printf("sii_DoIntr: cmd wait ds %x cnt %d\n",
77452130Smckusick dstat, i);
77552130Smckusick #endif
77652130Smckusick goto again;
77752130Smckusick }
77852130Smckusick break;
77952130Smckusick
78052130Smckusick case SII_DATA_IN_PHASE:
78152130Smckusick case SII_DATA_OUT_PHASE:
78252130Smckusick if (state->cmdlen > 0) {
78352130Smckusick printf("sii%d: device %d: cmd %x: command data not all sent (%d) 1\n",
78452130Smckusick sc - sii_softc, sc->sc_target,
78552130Smckusick sc->sc_cmd[sc->sc_target]->cmd[0],
78652130Smckusick state->cmdlen);
78752130Smckusick state->cmdlen = 0;
78852130Smckusick #ifdef DEBUG
78952130Smckusick sii_DumpLog();
79052130Smckusick #endif
79152130Smckusick }
79252130Smckusick if (state->dmaPrevPhase >= 0) {
79352130Smckusick /* restart DMA after disconnect/reconnect */
79452130Smckusick if (state->dmaPrevPhase !=
79552130Smckusick (dstat & SII_PHASE_MSK)) {
79652130Smckusick printf("sii%d: device %d: dma reselect phase doesn't match\n",
79752130Smckusick sc - sii_softc, sc->sc_target);
79852130Smckusick goto abort;
79952130Smckusick }
80052130Smckusick state->dmaCurPhase = state->dmaPrevPhase;
80152130Smckusick state->dmaPrevPhase = -1;
80252130Smckusick regs->dmaddrl = state->dmaAddrL;
80352130Smckusick regs->dmaddrh = state->dmaAddrH;
80452130Smckusick regs->dmlotc = state->dmaCnt;
80552130Smckusick if (state->dmaCnt & 1)
80652130Smckusick regs->dmabyte = state->dmaByte;
80752130Smckusick regs->comm = SII_DMA | SII_INXFER |
80852130Smckusick (comm & SII_STATE_MSK) |
80952130Smckusick state->dmaCurPhase;
81052130Smckusick MachEmptyWriteBuffer();
81152130Smckusick #ifdef DEBUG
81252130Smckusick if (sii_debug > 4)
81352130Smckusick printf("Data %d dcnt %d dadr %x ",
81452130Smckusick state->dmaDataPhase,
81552130Smckusick state->dmaCnt,
81652130Smckusick (state->dmaAddrH << 16) |
81752130Smckusick state->dmaAddrL);
81852130Smckusick #endif
81952130Smckusick break;
82052130Smckusick }
82152130Smckusick if (state->dmaDataPhase != (dstat & SII_PHASE_MSK)) {
82252130Smckusick printf("sii%d: device %d: cmd %x: dma phase doesn't match\n",
82352130Smckusick sc - sii_softc, sc->sc_target,
82452130Smckusick sc->sc_cmd[sc->sc_target]->cmd[0]);
82552130Smckusick goto abort;
82652130Smckusick }
82752130Smckusick #ifdef DEBUG
82852130Smckusick if (sii_debug > 4) {
82952130Smckusick printf("Data %d ", state->dmaDataPhase);
83052130Smckusick if (sii_debug > 5)
83152130Smckusick printf("\n");
83252130Smckusick }
83352130Smckusick #endif
83452130Smckusick i = state->buflen;
83552130Smckusick if (i == 0) {
83652130Smckusick printf("sii%d: device %d: data count exceeded\n",
83752130Smckusick sc - sii_softc, sc->sc_target);
83852130Smckusick goto abort;
83952130Smckusick }
84052130Smckusick if (i > SII_MAX_DMA_XFER_LENGTH)
84152130Smckusick i = SII_MAX_DMA_XFER_LENGTH;
84252130Smckusick if ((dstat & SII_PHASE_MSK) == SII_DATA_IN_PHASE) {
84352130Smckusick sii_StartDMA(regs,
84452130Smckusick state->dmaCurPhase = SII_DATA_IN_PHASE,
84552130Smckusick state->dmaAddr[state->dmaBufIndex],
84652130Smckusick state->dmalen = i);
84752130Smckusick break;
84852130Smckusick }
84952130Smckusick /* start first chunk */
85052130Smckusick if (state->flags & FIRST_DMA) {
85152130Smckusick state->flags &= ~FIRST_DMA;
85252130Smckusick CopyToBuffer((u_short *)state->buf,
85352130Smckusick (volatile u_short *)
85452130Smckusick state->dmaAddr[state->dmaBufIndex], i);
85552130Smckusick }
85652130Smckusick sii_StartDMA(regs,
85752130Smckusick state->dmaCurPhase = SII_DATA_OUT_PHASE,
85852130Smckusick state->dmaAddr[state->dmaBufIndex],
85952130Smckusick state->dmalen = i);
86052130Smckusick i = state->buflen - SII_MAX_DMA_XFER_LENGTH;
86152130Smckusick if (i > 0) {
86252130Smckusick /* prepare for next chunk */
86352130Smckusick if (i > SII_MAX_DMA_XFER_LENGTH)
86452130Smckusick i = SII_MAX_DMA_XFER_LENGTH;
86552130Smckusick CopyToBuffer((u_short *)(state->buf +
86652130Smckusick SII_MAX_DMA_XFER_LENGTH),
86752130Smckusick (volatile u_short *)
86852130Smckusick state->dmaAddr[!state->dmaBufIndex], i);
86952130Smckusick }
87052130Smckusick break;
87152130Smckusick
87252130Smckusick case SII_STATUS_PHASE:
87352130Smckusick if (state->cmdlen > 0) {
87452130Smckusick printf("sii%d: device %d: cmd %x: command data not all sent (%d) 2\n",
87552130Smckusick sc - sii_softc, sc->sc_target,
87652130Smckusick sc->sc_cmd[sc->sc_target]->cmd[0],
87752130Smckusick state->cmdlen);
87852130Smckusick state->cmdlen = 0;
87952130Smckusick #ifdef DEBUG
88052130Smckusick sii_DumpLog();
88152130Smckusick #endif
88252130Smckusick }
88352130Smckusick
88452130Smckusick /* read amount transfered if DMA didn't finish */
88552130Smckusick if (state->dmalen > 0) {
88652130Smckusick i = state->dmalen - regs->dmlotc;
88752130Smckusick state->dmalen = 0;
88852130Smckusick state->dmaCurPhase = -1;
88952130Smckusick regs->dmlotc = 0;
89052130Smckusick regs->comm = comm &
89152130Smckusick (SII_STATE_MSK | SII_PHASE_MSK);
89252130Smckusick MachEmptyWriteBuffer();
89352130Smckusick regs->dstat = SII_DNE;
89452130Smckusick MachEmptyWriteBuffer();
89552130Smckusick #ifdef DEBUG
89652130Smckusick if (sii_debug > 4)
89752130Smckusick printf("DMA amt %d ", i);
89852130Smckusick #endif
89952130Smckusick switch (comm & SII_PHASE_MSK) {
90052130Smckusick case SII_DATA_IN_PHASE:
90152130Smckusick /* copy in the data */
90252130Smckusick CopyFromBuffer((volatile u_short *)
90352130Smckusick state->dmaAddr[state->dmaBufIndex],
90452130Smckusick state->buf, i);
90552130Smckusick
90652130Smckusick case SII_CMD_PHASE:
90752130Smckusick case SII_DATA_OUT_PHASE:
90852130Smckusick state->buflen -= i;
90952130Smckusick }
91052130Smckusick }
91152130Smckusick
91252130Smckusick /* read a one byte status message */
91352130Smckusick state->statusByte = msg =
91467205Smckusick sii_GetByte(regs, SII_STATUS_PHASE, 1);
91552130Smckusick if (msg < 0) {
91652130Smckusick dstat = regs->dstat;
91752130Smckusick goto again;
91852130Smckusick }
91952130Smckusick #ifdef DEBUG
92052130Smckusick if (sii_debug > 4)
92152130Smckusick printf("Status %x ", msg);
92252130Smckusick if (sii_logp > sii_log)
92352130Smckusick sii_logp[-1].msg = msg;
92452130Smckusick else
92552130Smckusick sii_log[NLOG - 1].msg = msg;
92652130Smckusick #endif
92752130Smckusick
92852130Smckusick /* do a quick wait for COMMAND_COMPLETE */
92952130Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
93052130Smckusick dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i);
93152130Smckusick if (dstat & (SII_CI | SII_DI)) {
93252130Smckusick #ifdef DEBUG
93352130Smckusick if (sii_debug > 4)
93452130Smckusick printf("cnt2 %d\n", i);
93552130Smckusick #endif
93652130Smckusick goto again;
93752130Smckusick }
93852130Smckusick break;
93952130Smckusick
94052130Smckusick case SII_MSG_IN_PHASE:
94152130Smckusick /*
94252130Smckusick * Save DMA state if DMA didn't finish.
94352130Smckusick * Be careful not to save state again after reconnect
94452130Smckusick * and see RESTORE_POINTER message.
94552130Smckusick * Note that the SII DMA address is not incremented
94652130Smckusick * as DMA proceeds.
94752130Smckusick */
94852130Smckusick if (state->dmaCurPhase > 0) {
94952130Smckusick /* save dma registers */
95052130Smckusick state->dmaPrevPhase = state->dmaCurPhase;
95152130Smckusick state->dmaCurPhase = -1;
95252130Smckusick state->dmaCnt = i = regs->dmlotc;
95352130Smckusick if (dstat & SII_OBB)
95452130Smckusick state->dmaByte = regs->dmabyte;
95552130Smckusick if (i == 0)
95652130Smckusick i = SII_MAX_DMA_XFER_LENGTH;
95752130Smckusick i = state->dmalen - i;
95852130Smckusick /* note: no carry from dmaddrl to dmaddrh */
95952130Smckusick state->dmaAddrL = regs->dmaddrl + i;
96052130Smckusick state->dmaAddrH = regs->dmaddrh;
96152130Smckusick regs->comm = comm &
96252130Smckusick (SII_STATE_MSK | SII_PHASE_MSK);
96352130Smckusick MachEmptyWriteBuffer();
96452130Smckusick regs->dstat = SII_DNE;
96552130Smckusick MachEmptyWriteBuffer();
96652130Smckusick #ifdef DEBUG
96752130Smckusick if (sii_debug > 4) {
96852130Smckusick printf("SavP dcnt %d dadr %x ",
96952130Smckusick state->dmaCnt,
97052130Smckusick (state->dmaAddrH << 16) |
97152130Smckusick state->dmaAddrL);
97252130Smckusick if (((dstat & SII_OBB) != 0) ^
97352130Smckusick (state->dmaCnt & 1))
97452130Smckusick printf("OBB??? ");
97552130Smckusick } else if (sii_debug > 0) {
97652130Smckusick if (((dstat & SII_OBB) != 0) ^
97752130Smckusick (state->dmaCnt & 1)) {
97852130Smckusick printf("sii_DoIntr: OBB??? ds %x cnt %d\n",
97952130Smckusick dstat, state->dmaCnt);
98052130Smckusick sii_DumpLog();
98152130Smckusick }
98252130Smckusick }
98352130Smckusick #endif
98452130Smckusick }
98552130Smckusick
98652130Smckusick /* read a one byte message */
98767205Smckusick msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 0);
98852130Smckusick if (msg < 0) {
98952130Smckusick dstat = regs->dstat;
99052130Smckusick goto again;
99152130Smckusick }
99252130Smckusick #ifdef DEBUG
99352130Smckusick if (sii_debug > 4)
99452130Smckusick printf("MsgIn %x ", msg);
99552130Smckusick if (sii_logp > sii_log)
99652130Smckusick sii_logp[-1].msg = msg;
99752130Smckusick else
99852130Smckusick sii_log[NLOG - 1].msg = msg;
99952130Smckusick #endif
100052130Smckusick
100152130Smckusick /* process message */
100252130Smckusick switch (msg) {
100352130Smckusick case SCSI_COMMAND_COMPLETE:
100467205Smckusick /* acknowledge last byte */
100567205Smckusick regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
100667205Smckusick (comm & SII_STATE_MSK);
100767205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
100867205Smckusick dstat & SII_DNE, SII_WAIT_COUNT, i);
100967205Smckusick regs->dstat = SII_DNE;
101067205Smckusick MachEmptyWriteBuffer();
101152130Smckusick msg = sc->sc_target;
101252130Smckusick sc->sc_target = -1;
101352130Smckusick /*
101452130Smckusick * Wait a short time for disconnect.
101552130Smckusick * Don't be fooled if SII_BER happens first.
101652130Smckusick * Note: a reselect may happen here.
101752130Smckusick */
101852130Smckusick SII_WAIT_UNTIL(cstat, regs->cstat,
101952130Smckusick cstat & (SII_RST | SII_SCH),
102052130Smckusick SII_WAIT_COUNT, i);
102152130Smckusick if ((cstat & (SII_RST | SII_SCH |
102252130Smckusick SII_STATE_MSK)) == SII_SCH) {
102352130Smckusick regs->cstat = SII_SCH | SII_BER;
102452130Smckusick regs->comm = 0;
102552130Smckusick MachEmptyWriteBuffer();
102652130Smckusick /*
102752130Smckusick * Double check that we didn't miss a
102852130Smckusick * state change between seeing it and
102952130Smckusick * clearing the SII_SCH bit.
103052130Smckusick */
103152130Smckusick i = regs->cstat;
103252130Smckusick if (!(i & SII_SCH) &&
103352130Smckusick (i & SII_STATE_MSK) !=
103452130Smckusick (cstat & SII_STATE_MSK))
103552130Smckusick sii_StateChg(sc, i);
103652130Smckusick }
103752130Smckusick #ifdef DEBUG
103852130Smckusick if (sii_debug > 4)
103952130Smckusick printf("cs %x\n", cstat);
104052130Smckusick #endif
104152130Smckusick sii_CmdDone(sc, msg, 0);
104252130Smckusick break;
104352130Smckusick
104452130Smckusick case SCSI_EXTENDED_MSG:
104567205Smckusick /* acknowledge last byte */
104667205Smckusick regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
104767205Smckusick (comm & SII_STATE_MSK);
104867205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
104967205Smckusick dstat & SII_DNE, SII_WAIT_COUNT, i);
105067205Smckusick regs->dstat = SII_DNE;
105167205Smckusick MachEmptyWriteBuffer();
105252130Smckusick /* read the message length */
105367205Smckusick msg = sii_GetByte(regs, SII_MSG_IN_PHASE, 1);
105452130Smckusick if (msg < 0) {
105552130Smckusick dstat = regs->dstat;
105652130Smckusick goto again;
105752130Smckusick }
105867205Smckusick sii_buf[1] = msg; /* message length */
105952130Smckusick if (msg == 0)
106052130Smckusick msg = 256;
106167205Smckusick /*
106267205Smckusick * We read and acknowlege all the bytes
106367205Smckusick * except the last so we can assert ATN
106467205Smckusick * if needed before acknowledging the last.
106567205Smckusick */
106667205Smckusick for (i = 0; i < msg; i++) {
106767205Smckusick dstat = sii_GetByte(regs,
106867205Smckusick SII_MSG_IN_PHASE, i < msg - 1);
106967205Smckusick if ((int)dstat < 0) {
107067205Smckusick dstat = regs->dstat;
107167205Smckusick goto again;
107267205Smckusick }
107367205Smckusick sii_buf[i + 2] = dstat;
107452130Smckusick }
107552130Smckusick
107652130Smckusick switch (sii_buf[2]) {
107752130Smckusick case SCSI_MODIFY_DATA_PTR:
107867205Smckusick /* acknowledge last byte */
107967205Smckusick regs->comm = SII_INXFER |
108067205Smckusick SII_MSG_IN_PHASE |
108167205Smckusick (comm & SII_STATE_MSK);
108267205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
108367205Smckusick dstat & SII_DNE,
108467205Smckusick SII_WAIT_COUNT, i);
108567205Smckusick regs->dstat = SII_DNE;
108667205Smckusick MachEmptyWriteBuffer();
108752130Smckusick i = (sii_buf[3] << 24) |
108852130Smckusick (sii_buf[4] << 16) |
108952130Smckusick (sii_buf[5] << 8) |
109052130Smckusick sii_buf[6];
109152130Smckusick if (state->dmaPrevPhase >= 0) {
109252130Smckusick state->dmaAddrL += i;
109352130Smckusick state->dmaCnt -= i;
109452130Smckusick }
109552130Smckusick break;
109652130Smckusick
109752130Smckusick case SCSI_SYNCHRONOUS_XFER:
109867205Smckusick /*
109967205Smckusick * Acknowledge last byte and
110067205Smckusick * signal a request for MSG_OUT.
110167205Smckusick */
110267205Smckusick regs->comm = SII_INXFER | SII_ATN |
110367205Smckusick SII_MSG_IN_PHASE |
110467205Smckusick (comm & SII_STATE_MSK);
110567205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
110667205Smckusick dstat & SII_DNE,
110767205Smckusick SII_WAIT_COUNT, i);
110867205Smckusick regs->dstat = SII_DNE;
110967205Smckusick MachEmptyWriteBuffer();
111052130Smckusick sii_DoSync(regs, state);
111152130Smckusick break;
111252130Smckusick
111352130Smckusick default:
111452130Smckusick reject:
111567205Smckusick /*
111667205Smckusick * Acknowledge last byte and
111767205Smckusick * signal a request for MSG_OUT.
111867205Smckusick */
111967205Smckusick regs->comm = SII_INXFER | SII_ATN |
112067205Smckusick SII_MSG_IN_PHASE |
112167205Smckusick (comm & SII_STATE_MSK);
112267205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
112367205Smckusick dstat & SII_DNE,
112467205Smckusick SII_WAIT_COUNT, i);
112567205Smckusick regs->dstat = SII_DNE;
112667205Smckusick MachEmptyWriteBuffer();
112767205Smckusick
112867205Smckusick /* wait for MSG_OUT phase */
112967205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
113067205Smckusick dstat & SII_TBE,
113167205Smckusick SII_WAIT_COUNT, i);
113267205Smckusick
113352130Smckusick /* send a reject message */
113452130Smckusick regs->data = SCSI_MESSAGE_REJECT;
113567205Smckusick regs->comm = SII_INXFER |
113652130Smckusick (regs->cstat & SII_STATE_MSK) |
113752130Smckusick SII_MSG_OUT_PHASE;
113852130Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
113967205Smckusick dstat & SII_DNE,
114052130Smckusick SII_WAIT_COUNT, i);
114152130Smckusick regs->dstat = SII_DNE;
114252130Smckusick MachEmptyWriteBuffer();
114352130Smckusick }
114452130Smckusick break;
114552130Smckusick
114652130Smckusick case SCSI_SAVE_DATA_POINTER:
114752130Smckusick case SCSI_RESTORE_POINTERS:
114867205Smckusick /* acknowledge last byte */
114967205Smckusick regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
115067205Smckusick (comm & SII_STATE_MSK);
115167205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
115267205Smckusick dstat & SII_DNE, SII_WAIT_COUNT, i);
115367205Smckusick regs->dstat = SII_DNE;
115467205Smckusick MachEmptyWriteBuffer();
115552130Smckusick /* wait a short time for another msg */
115652130Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
115752130Smckusick dstat & (SII_CI | SII_DI),
115852130Smckusick SII_WAIT_COUNT, i);
115952130Smckusick if (dstat & (SII_CI | SII_DI)) {
116052130Smckusick #ifdef DEBUG
116152130Smckusick if (sii_debug > 4)
116252130Smckusick printf("cnt %d\n", i);
116352130Smckusick #endif
116452130Smckusick goto again;
116552130Smckusick }
116652130Smckusick break;
116752130Smckusick
116852130Smckusick case SCSI_DISCONNECT:
116967205Smckusick /* acknowledge last byte */
117067205Smckusick regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
117167205Smckusick (comm & SII_STATE_MSK);
117267205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
117367205Smckusick dstat & SII_DNE, SII_WAIT_COUNT, i);
117467205Smckusick regs->dstat = SII_DNE;
117567205Smckusick MachEmptyWriteBuffer();
117652130Smckusick state->prevComm = comm;
117752130Smckusick #ifdef DEBUG
117852130Smckusick if (sii_debug > 4)
117952130Smckusick printf("disconn %d ", sc->sc_target);
118052130Smckusick #endif
118152130Smckusick /*
118252130Smckusick * Wait a short time for disconnect.
118352130Smckusick * Don't be fooled if SII_BER happens first.
118452130Smckusick * Note: a reselect may happen here.
118552130Smckusick */
118652130Smckusick SII_WAIT_UNTIL(cstat, regs->cstat,
118752130Smckusick cstat & (SII_RST | SII_SCH),
118852130Smckusick SII_WAIT_COUNT, i);
118952130Smckusick if ((cstat & (SII_RST | SII_SCH |
119052130Smckusick SII_STATE_MSK)) != SII_SCH) {
119152130Smckusick #ifdef DEBUG
119252130Smckusick if (sii_debug > 4)
119352130Smckusick printf("cnt %d\n", i);
119452130Smckusick #endif
119552130Smckusick dstat = regs->dstat;
119652130Smckusick goto again;
119752130Smckusick }
119852130Smckusick regs->cstat = SII_SCH | SII_BER;
119952130Smckusick regs->comm = 0;
120052130Smckusick MachEmptyWriteBuffer();
120152130Smckusick sc->sc_target = -1;
120252130Smckusick /*
120352130Smckusick * Double check that we didn't miss a state
120452130Smckusick * change between seeing it and clearing
120552130Smckusick * the SII_SCH bit.
120652130Smckusick */
120752130Smckusick i = regs->cstat;
120852130Smckusick if (!(i & SII_SCH) && (i & SII_STATE_MSK) !=
120952130Smckusick (cstat & SII_STATE_MSK))
121052130Smckusick sii_StateChg(sc, i);
121152130Smckusick break;
121252130Smckusick
121352130Smckusick case SCSI_MESSAGE_REJECT:
121467205Smckusick /* acknowledge last byte */
121567205Smckusick regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
121667205Smckusick (comm & SII_STATE_MSK);
121767205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
121867205Smckusick dstat & SII_DNE, SII_WAIT_COUNT, i);
121967205Smckusick regs->dstat = SII_DNE;
122067205Smckusick MachEmptyWriteBuffer();
122152130Smckusick printf("sii%d: device %d: message reject.\n",
122252130Smckusick sc - sii_softc, sc->sc_target);
122367205Smckusick break;
122452130Smckusick
122552130Smckusick default:
122652130Smckusick if (!(msg & SCSI_IDENTIFY)) {
122767205Smckusick printf("sii%d: device %d: couldn't handle message 0x%x... rejecting.\n",
122852130Smckusick sc - sii_softc, sc->sc_target,
122952130Smckusick msg);
123052130Smckusick #ifdef DEBUG
123152130Smckusick sii_DumpLog();
123252130Smckusick #endif
123367205Smckusick goto reject;
123452130Smckusick }
123567205Smckusick /* acknowledge last byte */
123667205Smckusick regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
123767205Smckusick (comm & SII_STATE_MSK);
123867205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
123967205Smckusick dstat & SII_DNE, SII_WAIT_COUNT, i);
124067205Smckusick regs->dstat = SII_DNE;
124167205Smckusick MachEmptyWriteBuffer();
124252130Smckusick /* may want to check LUN some day */
124352130Smckusick /* wait a short time for another msg */
124452130Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
124552130Smckusick dstat & (SII_CI | SII_DI),
124652130Smckusick SII_WAIT_COUNT, i);
124752130Smckusick if (dstat & (SII_CI | SII_DI)) {
124852130Smckusick #ifdef DEBUG
124952130Smckusick if (sii_debug > 4)
125052130Smckusick printf("cnt %d\n", i);
125152130Smckusick #endif
125252130Smckusick goto again;
125352130Smckusick }
125452130Smckusick }
125552130Smckusick break;
125652130Smckusick
125752130Smckusick case SII_MSG_OUT_PHASE:
125852130Smckusick #ifdef DEBUG
125952130Smckusick if (sii_debug > 4)
126052130Smckusick printf("MsgOut\n");
126152130Smckusick #endif
126267205Smckusick printf("MsgOut %x\n", state->flags); /* XXX */
126352130Smckusick
126467205Smckusick /*
126567205Smckusick * Check for parity error.
126667205Smckusick * Hardware will automatically set ATN
126767205Smckusick * to request the device for a MSG_OUT phase.
126867205Smckusick */
126967205Smckusick if (state->flags & PARITY_ERR) {
127067205Smckusick state->flags &= ~PARITY_ERR;
127167205Smckusick regs->data = SCSI_MESSAGE_PARITY_ERROR;
127267205Smckusick } else
127367205Smckusick regs->data = SCSI_NO_OP;
127452130Smckusick regs->comm = SII_INXFER | (comm & SII_STATE_MSK) |
127552130Smckusick SII_MSG_OUT_PHASE;
127652130Smckusick MachEmptyWriteBuffer();
127752130Smckusick
127852130Smckusick /* wait a short time for XFER complete */
127952130Smckusick SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE,
128052130Smckusick SII_WAIT_COUNT, i);
128152130Smckusick #ifdef DEBUG
128252130Smckusick if (sii_debug > 4)
128352130Smckusick printf("ds %x i %d\n", dstat, i);
128452130Smckusick #endif
128552130Smckusick /* just clear the DNE bit and check errors later */
128652130Smckusick if (dstat & SII_DNE) {
128752130Smckusick regs->dstat = SII_DNE;
128852130Smckusick MachEmptyWriteBuffer();
128952130Smckusick }
129052130Smckusick break;
129152130Smckusick
129252130Smckusick default:
129352130Smckusick printf("sii%d: Couldn't handle phase %d... ignoring.\n",
129452130Smckusick sc - sii_softc, dstat & SII_PHASE_MSK);
129552130Smckusick }
129652130Smckusick }
129752130Smckusick
129852130Smckusick #ifdef DEBUG
129952130Smckusick if (sii_debug > 3)
130052130Smckusick printf("\n");
130152130Smckusick #endif
130252130Smckusick /*
130352130Smckusick * Check to make sure we won't be interrupted again.
130452130Smckusick * Deglitch dstat register.
130552130Smckusick */
130652130Smckusick msg = regs->dstat;
130752130Smckusick while (msg != (dstat = regs->dstat))
130852130Smckusick msg = dstat;
130952130Smckusick if (dstat & (SII_CI | SII_DI))
131052130Smckusick goto again;
131152130Smckusick
131252130Smckusick if (sc->sc_target < 0) {
131352130Smckusick /* look for another device that is ready */
131452130Smckusick for (i = 0; i < SII_NCMD; i++) {
131552130Smckusick /* don't restart a disconnected command */
131652130Smckusick if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm)
131752130Smckusick continue;
131852130Smckusick sii_StartCmd(sc, i);
131952130Smckusick break;
132052130Smckusick }
132152130Smckusick }
132252130Smckusick return;
132352130Smckusick
132452130Smckusick abort:
132552130Smckusick /* jump here to abort the current command */
132652130Smckusick printf("sii%d: device %d: current command terminated\n",
132752130Smckusick sc - sii_softc, sc->sc_target);
132852130Smckusick #ifdef DEBUG
132952130Smckusick sii_DumpLog();
133052130Smckusick #endif
133152130Smckusick
133252130Smckusick if ((cstat = regs->cstat) & SII_CON) {
133352130Smckusick /* try to send an abort msg for awhile */
133452130Smckusick regs->dstat = SII_DNE;
133552130Smckusick regs->data = SCSI_ABORT;
133652130Smckusick regs->comm = SII_INXFER | SII_ATN | (cstat & SII_STATE_MSK) |
133752130Smckusick SII_MSG_OUT_PHASE;
133852130Smckusick MachEmptyWriteBuffer();
133952130Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
134052130Smckusick (dstat & (SII_DNE | SII_PHASE_MSK)) ==
134152130Smckusick (SII_DNE | SII_MSG_OUT_PHASE),
134252130Smckusick 2 * SII_WAIT_COUNT, i);
134352130Smckusick #ifdef DEBUG
134452130Smckusick if (sii_debug > 0)
134552130Smckusick printf("Abort: cs %x ds %x i %d\n", cstat, dstat, i);
134652130Smckusick #endif
134752130Smckusick if (dstat & (SII_DNE | SII_PHASE_MSK) ==
134852130Smckusick (SII_DNE | SII_MSG_OUT_PHASE)) {
134952130Smckusick /* disconnect if command in progress */
135052130Smckusick regs->comm = SII_DISCON;
135152130Smckusick MachEmptyWriteBuffer();
135252130Smckusick SII_WAIT_UNTIL(cstat, regs->cstat,
135352130Smckusick !(cstat & SII_CON), SII_WAIT_COUNT, i);
135452130Smckusick }
135552130Smckusick } else {
135652130Smckusick #ifdef DEBUG
135752130Smckusick if (sii_debug > 0)
135852130Smckusick printf("Abort: cs %x\n", cstat);
135952130Smckusick #endif
136052130Smckusick }
136152130Smckusick regs->cstat = 0xffff;
136252130Smckusick regs->dstat = 0xffff;
136352130Smckusick regs->comm = 0;
136452130Smckusick MachEmptyWriteBuffer();
136552130Smckusick
136652130Smckusick i = sc->sc_target;
136752130Smckusick sc->sc_target = -1;
136852130Smckusick sii_CmdDone(sc, i, EIO);
136952130Smckusick #ifdef DEBUG
137052130Smckusick if (sii_debug > 4)
137152130Smckusick printf("sii_DoIntr: after CmdDone target %d\n", sc->sc_target);
137252130Smckusick #endif
137352130Smckusick }
137452130Smckusick
137552130Smckusick static void
sii_StateChg(sc,cstat)137652130Smckusick sii_StateChg(sc, cstat)
137752130Smckusick register struct siisoftc *sc;
137867205Smckusick register u_int cstat;
137952130Smckusick {
138052130Smckusick register SIIRegs *regs = sc->sc_regs;
138152130Smckusick register State *state;
138252130Smckusick register int i;
138352130Smckusick
138452130Smckusick #ifdef DEBUG
138552130Smckusick if (sii_debug > 4)
138652130Smckusick printf("SCH: ");
138752130Smckusick #endif
138852130Smckusick
138952130Smckusick switch (cstat & SII_STATE_MSK) {
139052130Smckusick case 0:
139152130Smckusick /* disconnect */
139252130Smckusick i = sc->sc_target;
139352130Smckusick sc->sc_target = -1;
139452130Smckusick #ifdef DEBUG
139552130Smckusick if (sii_debug > 4)
139652130Smckusick printf("disconn %d ", i);
139752130Smckusick #endif
139852130Smckusick if (i >= 0 && !sc->sc_st[i].prevComm) {
139952130Smckusick printf("sii%d: device %d: spurrious disconnect (%d)\n",
140052130Smckusick sc - sii_softc, i, regs->slcsr);
140152130Smckusick sc->sc_st[i].prevComm = 0;
140252130Smckusick }
140352130Smckusick break;
140452130Smckusick
140552130Smckusick case SII_CON:
140652130Smckusick /* connected as initiator */
140752130Smckusick i = regs->slcsr;
140852130Smckusick if (sc->sc_target == i)
140952130Smckusick break;
141052130Smckusick printf("sii%d: device %d: connect to device %d??\n",
141152130Smckusick sc - sii_softc, sc->sc_target, i);
141252130Smckusick sc->sc_target = i;
141352130Smckusick break;
141452130Smckusick
141552130Smckusick case SII_DST:
141652130Smckusick /*
141752130Smckusick * Wait for CON to become valid,
141852130Smckusick * chip is slow sometimes.
141952130Smckusick */
142052130Smckusick SII_WAIT_UNTIL(cstat, regs->cstat,
142152130Smckusick cstat & SII_CON, SII_WAIT_COUNT, i);
142252130Smckusick if (!(cstat & SII_CON))
142352130Smckusick panic("sii resel");
142452130Smckusick /* FALLTHROUGH */
142552130Smckusick
142652130Smckusick case SII_CON | SII_DST:
142752130Smckusick /*
142852130Smckusick * Its a reselection. Save the ID and wait for
142952130Smckusick * interrupts to tell us what to do next
143052130Smckusick * (should be MSG_IN of IDENTIFY).
143152130Smckusick * NOTE: sc_target may be >= 0 if we were in
143252130Smckusick * the process of trying to start a command
143352130Smckusick * and were reselected before the select
143452130Smckusick * command finished.
143552130Smckusick */
143652130Smckusick sc->sc_target = i = regs->destat;
143767205Smckusick state = &sc->sc_st[i];
143852130Smckusick regs->comm = SII_CON | SII_DST | SII_MSG_IN_PHASE;
143967205Smckusick regs->dmctrl = state->dmaReqAck;
144052130Smckusick MachEmptyWriteBuffer();
144152130Smckusick if (!state->prevComm) {
144252130Smckusick printf("sii%d: device %d: spurrious reselection\n",
144352130Smckusick sc - sii_softc, i);
144452130Smckusick break;
144552130Smckusick }
144652130Smckusick state->prevComm = 0;
144752130Smckusick #ifdef DEBUG
144852130Smckusick if (sii_debug > 4)
144952130Smckusick printf("resel %d ", sc->sc_target);
145052130Smckusick #endif
145152130Smckusick break;
145252130Smckusick
145352130Smckusick #ifdef notyet
145452130Smckusick case SII_DST | SII_TGT:
145552130Smckusick case SII_CON | SII_DST | SII_TGT:
145652130Smckusick /* connected as target */
145752130Smckusick printf("sii%d: Selected by device %d as target!!\n",
145852130Smckusick sc - sii_softc, regs->destat);
145952130Smckusick regs->comm = SII_DISCON;
146052130Smckusick MachEmptyWriteBuffer();
146152130Smckusick SII_WAIT_UNTIL(!(regs->cstat & SII_CON),
146252130Smckusick SII_WAIT_COUNT, i);
146352130Smckusick regs->cstat = 0xffff;
146452130Smckusick regs->dstat = 0xffff;
146552130Smckusick regs->comm = 0;
146652130Smckusick break;
146752130Smckusick #endif
146852130Smckusick
146952130Smckusick default:
147052130Smckusick printf("sii%d: Unknown state change (cs %x)!!\n",
147152130Smckusick sc - sii_softc, cstat);
147252130Smckusick #ifdef DEBUG
147352130Smckusick sii_DumpLog();
147452130Smckusick #endif
147552130Smckusick }
147652130Smckusick }
147752130Smckusick
147852130Smckusick /*
147952130Smckusick * Read one byte of data.
148067205Smckusick * If 'ack' is true, acknowledge the byte.
148152130Smckusick */
148252130Smckusick static int
sii_GetByte(regs,phase,ack)148367205Smckusick sii_GetByte(regs, phase, ack)
148452130Smckusick register SIIRegs *regs;
148567205Smckusick int phase, ack;
148652130Smckusick {
148767205Smckusick register u_int dstat;
148867205Smckusick register u_int state;
148967205Smckusick register int i;
149067205Smckusick register int data;
149152130Smckusick
149252130Smckusick dstat = regs->dstat;
149352130Smckusick state = regs->cstat & SII_STATE_MSK;
149452130Smckusick if (!(dstat & SII_IBF) || (dstat & SII_MIS)) {
149552130Smckusick regs->comm = state | phase;
149652130Smckusick MachEmptyWriteBuffer();
149752130Smckusick /* wait a short time for IBF */
149852130Smckusick SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_IBF,
149952130Smckusick SII_WAIT_COUNT, i);
150052130Smckusick #ifdef DEBUG
150152130Smckusick if (!(dstat & SII_IBF))
150252130Smckusick printf("status no IBF\n");
150352130Smckusick #endif
150452130Smckusick }
150567205Smckusick if (dstat & SII_DNE) { /* XXX */
150652130Smckusick printf("sii_GetByte: DNE set 5\n");
1507*68013Smckusick #ifdef DEBUG
150852130Smckusick sii_DumpLog();
1509*68013Smckusick #endif
151052130Smckusick regs->dstat = SII_DNE;
151152130Smckusick }
151267205Smckusick data = regs->data;
151367205Smckusick /* check for parity error */
151467205Smckusick if (dstat & SII_IPE) {
151552130Smckusick #ifdef DEBUG
151652130Smckusick if (sii_debug > 4)
151752130Smckusick printf("cnt0 %d\n", i);
151852130Smckusick #endif
151967205Smckusick printf("sii_GetByte: data %x ?? ds %x cm %x i %d\n",
152067205Smckusick data, dstat, regs->comm, i); /* XXX */
152167205Smckusick data = -1;
152267205Smckusick ack = 1;
152352130Smckusick }
152452130Smckusick
152567205Smckusick if (ack) {
152667205Smckusick regs->comm = SII_INXFER | state | phase;
152767205Smckusick MachEmptyWriteBuffer();
152852130Smckusick
152967205Smckusick /* wait a short time for XFER complete */
153067205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE,
153167205Smckusick SII_WAIT_COUNT, i);
153252130Smckusick
153367205Smckusick /* clear the DNE */
153467205Smckusick if (dstat & SII_DNE) {
153567205Smckusick regs->dstat = SII_DNE;
153667205Smckusick MachEmptyWriteBuffer();
153767205Smckusick }
153852130Smckusick }
153952130Smckusick
154067205Smckusick return (data);
154152130Smckusick }
154252130Smckusick
154352130Smckusick /*
154452130Smckusick * Exchange messages to initiate synchronous data transfers.
154552130Smckusick */
154652130Smckusick static void
sii_DoSync(regs,state)154752130Smckusick sii_DoSync(regs, state)
154852130Smckusick register SIIRegs *regs;
154952130Smckusick register State *state;
155052130Smckusick {
155167205Smckusick register u_int dstat, comm;
155267205Smckusick register int i, j;
155367205Smckusick u_int len;
155452130Smckusick
155567205Smckusick #ifdef DEBUG
155667205Smckusick if (sii_debug)
155767205Smckusick printf("sii_DoSync: len %d per %d req/ack %d\n",
155867205Smckusick sii_buf[1], sii_buf[3], sii_buf[4]);
155967205Smckusick #endif
156067205Smckusick
156167205Smckusick /* SII chip can only handle a minimum transfer period of ??? */
156267205Smckusick if (sii_buf[3] < 64)
156367205Smckusick sii_buf[3] = 64;
156467205Smckusick /* SII chip can only handle a maximum REQ/ACK offset of 3 */
156552130Smckusick len = sii_buf[4];
156652130Smckusick if (len > 3)
156767205Smckusick len = 3;
156852130Smckusick
156952130Smckusick sii_buf[0] = SCSI_EXTENDED_MSG;
157052130Smckusick sii_buf[1] = 3; /* message length */
157152130Smckusick sii_buf[2] = SCSI_SYNCHRONOUS_XFER;
157252130Smckusick sii_buf[4] = len;
157367205Smckusick #if 1
157467205Smckusick comm = SII_INXFER | SII_ATN | SII_MSG_OUT_PHASE |
157567205Smckusick (regs->cstat & SII_STATE_MSK);
157667205Smckusick regs->comm = comm & ~SII_INXFER;
157767205Smckusick for (j = 0; j < 5; j++) {
157867205Smckusick /* wait for target to request the next byte */
157967205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_TBE,
158067205Smckusick SII_WAIT_COUNT, i);
158167205Smckusick if (!(dstat & SII_TBE) ||
158267205Smckusick (dstat & SII_PHASE_MSK) != SII_MSG_OUT_PHASE) {
158367205Smckusick printf("sii_DoSync: TBE? ds %x cm %x i %d\n",
158467205Smckusick dstat, comm, i); /* XXX */
158567205Smckusick return;
158667205Smckusick }
158767205Smckusick
158867205Smckusick /* the last message byte should have ATN off */
158967205Smckusick if (j == 4)
159067205Smckusick comm &= ~SII_ATN;
159167205Smckusick
159267205Smckusick regs->data = sii_buf[j];
159367205Smckusick regs->comm = comm;
159467205Smckusick MachEmptyWriteBuffer();
159567205Smckusick
159667205Smckusick /* wait a short time for XFER complete */
159767205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE,
159867205Smckusick SII_WAIT_COUNT, i);
159967205Smckusick
160067205Smckusick if (!(dstat & SII_DNE)) {
160167205Smckusick printf("sii_DoSync: DNE? ds %x cm %x i %d\n",
160267205Smckusick dstat, comm, i); /* XXX */
160367205Smckusick return;
160467205Smckusick }
160567205Smckusick
160667205Smckusick /* clear the DNE, other errors handled later */
160767205Smckusick regs->dstat = SII_DNE;
160867205Smckusick MachEmptyWriteBuffer();
160967205Smckusick }
161067205Smckusick #else
161152130Smckusick CopyToBuffer((u_short *)sii_buf, (volatile u_short *)SII_BUF_ADDR, 5);
161267205Smckusick printf("sii_DoSync: %x %x %x ds %x\n",
161367205Smckusick ((volatile u_short *)SII_BUF_ADDR)[0],
161467205Smckusick ((volatile u_short *)SII_BUF_ADDR)[2],
161567205Smckusick ((volatile u_short *)SII_BUF_ADDR)[4],
161667205Smckusick regs->dstat); /* XXX */
161767205Smckusick regs->dmaddrl = (u_short)(SII_BUF_ADDR >> 1);
161867205Smckusick regs->dmaddrh = (u_short)(SII_BUF_ADDR >> 17) & 03;
161952130Smckusick regs->dmlotc = 5;
162052130Smckusick regs->comm = SII_DMA | SII_INXFER | SII_ATN |
162152130Smckusick (regs->cstat & SII_STATE_MSK) | SII_MSG_OUT_PHASE;
162252130Smckusick MachEmptyWriteBuffer();
162352130Smckusick
162452130Smckusick /* wait a short time for XFER complete */
162552130Smckusick SII_WAIT_UNTIL(dstat, regs->dstat,
162652130Smckusick (dstat & (SII_DNE | SII_TCZ)) == (SII_DNE | SII_TCZ),
162752130Smckusick SII_WAIT_COUNT, i);
162852130Smckusick
162952130Smckusick if ((dstat & (SII_DNE | SII_TCZ)) != (SII_DNE | SII_TCZ)) {
163052130Smckusick printf("sii_DoSync: ds %x cm %x i %d lotc %d\n",
163152130Smckusick dstat, regs->comm, i, regs->dmlotc); /* XXX */
1632*68013Smckusick #ifdef DEBUG
163352130Smckusick sii_DumpLog(); /* XXX */
1634*68013Smckusick #endif
163552130Smckusick return;
163652130Smckusick }
163752130Smckusick /* clear the DNE, other errors handled later */
163852130Smckusick regs->dstat = SII_DNE;
163952130Smckusick MachEmptyWriteBuffer();
164067205Smckusick #endif
164152130Smckusick
164267205Smckusick #if 0
164367205Smckusick SII_WAIT_UNTIL(dstat, regs->dstat, dstat & (SII_CI | SII_DI),
164467205Smckusick SII_WAIT_COUNT, i);
164567205Smckusick printf("sii_DoSync: ds %x cm %x i %d lotc %d\n",
164667205Smckusick dstat, regs->comm, i, regs->dmlotc); /* XXX */
164767205Smckusick #endif
164867205Smckusick
164952130Smckusick state->dmaReqAck = len;
165052130Smckusick }
165152130Smckusick
165252130Smckusick /*
165352130Smckusick * Issue the sequence of commands to the controller to start DMA.
165452130Smckusick * NOTE: the data buffer should be word-aligned for DMA out.
165552130Smckusick */
165652130Smckusick static void
sii_StartDMA(regs,phase,dmaAddr,size)165752130Smckusick sii_StartDMA(regs, phase, dmaAddr, size)
165852130Smckusick register SIIRegs *regs; /* which SII to use */
165952130Smckusick int phase; /* phase to send/receive data */
166052130Smckusick u_short *dmaAddr; /* DMA buffer address */
166152130Smckusick int size; /* # of bytes to transfer */
166252130Smckusick {
166352130Smckusick
166452130Smckusick if (regs->dstat & SII_DNE) { /* XXX */
166567205Smckusick regs->dstat = SII_DNE;
166652130Smckusick printf("sii_StartDMA: DNE set\n");
1667*68013Smckusick #ifdef DEBUG
166852130Smckusick sii_DumpLog();
1669*68013Smckusick #endif
167052130Smckusick }
167167205Smckusick regs->dmaddrl = ((u_long)dmaAddr >> 1);
167267205Smckusick regs->dmaddrh = ((u_long)dmaAddr >> 17) & 03;
167352130Smckusick regs->dmlotc = size;
167452130Smckusick regs->comm = SII_DMA | SII_INXFER | (regs->cstat & SII_STATE_MSK) |
167552130Smckusick phase;
167652130Smckusick MachEmptyWriteBuffer();
167752130Smckusick
167852130Smckusick #ifdef DEBUG
167952130Smckusick if (sii_debug > 5) {
168052130Smckusick printf("sii_StartDMA: cs 0x%x, ds 0x%x, cm 0x%x, size %d\n",
168152130Smckusick regs->cstat, regs->dstat, regs->comm, size);
168252130Smckusick }
168352130Smckusick #endif
168452130Smckusick }
168552130Smckusick
168652130Smckusick /*
168752130Smckusick * Call the device driver's 'done' routine to let it know the command is done.
168852130Smckusick * The 'done' routine may try to start another command.
168952130Smckusick * To be fair, we should start pending commands for other devices
169052130Smckusick * before allowing the same device to start another command.
169152130Smckusick */
169252130Smckusick static void
sii_CmdDone(sc,target,error)169352130Smckusick sii_CmdDone(sc, target, error)
169452130Smckusick register struct siisoftc *sc; /* which SII to use */
169552130Smckusick int target; /* which device is done */
169652130Smckusick int error; /* error code if any errors */
169752130Smckusick {
169852130Smckusick register ScsiCmd *scsicmd;
169952130Smckusick register int i;
170052130Smckusick
170152130Smckusick scsicmd = sc->sc_cmd[target];
170252130Smckusick #ifdef DIAGNOSTIC
170352130Smckusick if (target < 0 || !scsicmd)
170452130Smckusick panic("sii_CmdDone");
170552130Smckusick #endif
170652130Smckusick sc->sc_cmd[target] = (ScsiCmd *)0;
170752130Smckusick #ifdef DEBUG
170852130Smckusick if (sii_debug > 1) {
170952130Smckusick printf("sii_CmdDone: %s target %d cmd %x err %d resid %d\n",
171052130Smckusick scsicmd->sd->sd_driver->d_name, target,
171152130Smckusick scsicmd->cmd[0], error, sc->sc_st[target].buflen);
171252130Smckusick }
171352130Smckusick #endif
171452130Smckusick
171552130Smckusick /* look for another device that is ready */
171652130Smckusick for (i = 0; i < SII_NCMD; i++) {
171752130Smckusick /* don't restart a disconnected command */
171852130Smckusick if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm)
171952130Smckusick continue;
172052130Smckusick sii_StartCmd(sc, i);
172152130Smckusick break;
172252130Smckusick }
172352130Smckusick
172452130Smckusick (*scsicmd->sd->sd_driver->d_done)(scsicmd->unit, error,
172552130Smckusick sc->sc_st[target].buflen, sc->sc_st[target].statusByte);
172652130Smckusick }
172752130Smckusick
172852130Smckusick #ifdef DEBUG
sii_DumpLog()172952130Smckusick sii_DumpLog()
173052130Smckusick {
173152130Smckusick register struct sii_log *lp;
173252130Smckusick
173352130Smckusick printf("sii: cmd %x bn %d cnt %d\n", sii_debug_cmd, sii_debug_bn,
173452130Smckusick sii_debug_sz);
173563639Smckusick lp = sii_logp;
173663639Smckusick do {
173767205Smckusick printf("target %d cs %x ds %x cm %x msg %x rlen %x dlen %x\n",
173867205Smckusick lp->target, lp->cstat, lp->dstat, lp->comm, lp->msg,
173967205Smckusick lp->rlen, lp->dlen);
174052130Smckusick if (++lp >= &sii_log[NLOG])
174152130Smckusick lp = sii_log;
174263639Smckusick } while (lp != sii_logp);
174352130Smckusick }
174452130Smckusick #endif
174552130Smckusick #endif
1746