xref: /csrg-svn/sys/pmax/dev/sii.c (revision 52754)
152130Smckusick /*
252130Smckusick  * Copyright (c) 1992 Regents of the University of California.
352130Smckusick  * All rights reserved.
452130Smckusick  *
552130Smckusick  * This code is derived from software contributed to Berkeley by
652130Smckusick  * Ralph Campbell.
752130Smckusick  *
852130Smckusick  * %sccs.include.redist.c%
952130Smckusick  *
10*52754Sralph  *	@(#)sii.c	7.3 (Berkeley) 02/29/92
1152130Smckusick  *
1252130Smckusick  * from: $Header: /sprite/src/kernel/dev/ds3100.md/RCS/devSII.c,
13*52754Sralph  *	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  */
2152130Smckusick #include "param.h"
2252130Smckusick #include "systm.h"
2352130Smckusick #include "dkstat.h"
2452130Smckusick #include "buf.h"
2552130Smckusick #include "conf.h"
2652130Smckusick #include "errno.h"
2752130Smckusick 
2852130Smckusick #include "machine/machConst.h"
2952130Smckusick #include "device.h"
3052130Smckusick #include "scsi.h"
3152130Smckusick #include "siireg.h"
3252130Smckusick 
3352130Smckusick int	siiprobe();
3452130Smckusick void	siistart();
3552130Smckusick struct	driver siidriver = {
3652130Smckusick 	"sii", siiprobe, siistart, 0,
3752130Smckusick };
3852130Smckusick 
3952130Smckusick typedef struct scsi_state {
4052130Smckusick 	int	statusByte;	/* status byte returned during STATUS_PHASE */
4152130Smckusick 	int	dmaDataPhase;	/* which data phase to expect */
4252130Smckusick 	int	dmaCurPhase;	/* SCSI phase if DMA is in progress */
4352130Smckusick 	int	dmaPrevPhase;	/* SCSI phase of DMA suspended by disconnect */
4452130Smckusick 	u_short	*dmaAddr[2];	/* DMA buffer memory address */
4552130Smckusick 	int	dmaBufIndex;	/* which of the above is currently in use */
4652130Smckusick 	int	dmalen;		/* amount to transfer in this chunk */
4752130Smckusick 	int	cmdlen;		/* total remaining amount of cmd to transfer */
4852130Smckusick 	u_char	*cmd;		/* current pointer within scsicmd->cmd */
4952130Smckusick 	int	buflen;		/* total remaining amount of data to transfer */
5052130Smckusick 	char	*buf;		/* current pointer within scsicmd->buf */
5152130Smckusick 	u_short	flags;		/* see below */
5252130Smckusick 	u_short	prevComm;	/* command reg before disconnect */
5352130Smckusick 	u_short	dmaCtrl;	/* DMA control register if disconnect */
5452130Smckusick 	u_short	dmaAddrL;	/* DMA address register if disconnect */
5552130Smckusick 	u_short	dmaAddrH;	/* DMA address register if disconnect */
5652130Smckusick 	u_short	dmaCnt;		/* DMA count if disconnect */
5752130Smckusick 	u_short	dmaByte;	/* DMA byte if disconnect on odd boundary */
5852130Smckusick 	u_short	dmaReqAck;	/* DMA synchronous xfer offset or 0 if async */
5952130Smckusick } State;
6052130Smckusick 
6152130Smckusick /* state flags */
6252130Smckusick #define FIRST_DMA	0x01	/* true if no data DMA started yet */
6352130Smckusick 
6452130Smckusick #define SII_NCMD	7
6552130Smckusick struct siisoftc {
6652130Smckusick 	SIIRegs	*sc_regs;		/* HW address of SII controller chip */
6752130Smckusick 	int	sc_flags;
6852130Smckusick 	int	sc_target;		/* target SCSI ID if connected */
6952130Smckusick 	ScsiCmd	*sc_cmd[SII_NCMD];	/* active command indexed by ID */
7052130Smckusick 	State	sc_st[SII_NCMD];	/* state info for each active command */
7152130Smckusick } sii_softc[NSII];
7252130Smckusick 
7352130Smckusick /*
7452130Smckusick  * MACROS for timing out spin loops.
7552130Smckusick  *
7652130Smckusick  * Wait until expression is true.
7752130Smckusick  *
7852130Smckusick  * Control register bits can change at any time so when the CPU
7952130Smckusick  * reads a register, the bits might change and
8052130Smckusick  * invalidate the setup and hold times for the CPU.
8152130Smckusick  * This macro reads the register twice to be sure the value is stable.
8252130Smckusick  *
8352130Smckusick  *	args:	var 		- variable to save control register contents
8452130Smckusick  *		reg		- control register to read
8552130Smckusick  *		expr 		- expression to spin on
8652130Smckusick  *		spincount 	- maximum number of times through the loop
8752130Smckusick  *		cntr		- variable for number of tries
8852130Smckusick  */
8952130Smckusick #define	SII_WAIT_UNTIL(var, reg, expr, spincount, cntr) {	\
9052130Smckusick 		register unsigned tmp = reg;			\
9152130Smckusick 		for (cntr = 0; cntr < spincount; cntr++) {	\
9252130Smckusick 			while (tmp != (var = reg))		\
9352130Smckusick 				tmp = var;			\
9452130Smckusick 			if (expr)				\
9552130Smckusick 				break;				\
9652130Smckusick 			if (cntr >= 100)			\
9752130Smckusick 				DELAY(100);			\
9852130Smckusick 		}						\
9952130Smckusick 	}
10052130Smckusick 
10152130Smckusick #ifdef DEBUG
10252130Smckusick int	sii_debug = 1;
10352130Smckusick int	sii_debug_cmd;
10452130Smckusick int	sii_debug_bn;
10552130Smckusick int	sii_debug_sz;
10652130Smckusick #define NLOG 16
10752130Smckusick struct sii_log {
10852130Smckusick 	u_short	cstat;
10952130Smckusick 	u_short	dstat;
11052130Smckusick 	u_short	comm;
11152130Smckusick 	u_short	msg;
11252130Smckusick 	int	target;
11352130Smckusick } sii_log[NLOG], *sii_logp = sii_log;
11452130Smckusick #endif
11552130Smckusick 
11652130Smckusick u_char	sii_buf[256];	/* used for extended messages */
11752130Smckusick 
11852130Smckusick #define NORESET	0
11952130Smckusick #define RESET	1
12052130Smckusick #define NOWAIT	0
12152130Smckusick #define WAIT	1
12252130Smckusick 
12352130Smckusick /* define a safe address in the SCSI buffer for doing status & message DMA */
12452130Smckusick #define SII_BUF_ADDR	(MACH_SCSI_BUFFER_ADDR + SII_MAX_DMA_XFER_LENGTH * 14)
12552130Smckusick 
12652130Smckusick extern void sii_Reset();
12752130Smckusick extern void sii_StartCmd();
12852130Smckusick extern void sii_CmdDone();
12952130Smckusick extern void sii_DoIntr();
13052130Smckusick extern void sii_StateChg();
13152130Smckusick extern void sii_DoSync();
13252130Smckusick extern void sii_StartDMA();
13352130Smckusick 
13452130Smckusick /*
13552130Smckusick  * Test to see if device is present.
13652130Smckusick  * Return true if found and initialized ok.
13752130Smckusick  */
13852130Smckusick siiprobe(cp)
13952130Smckusick 	register struct pmax_ctlr *cp;
14052130Smckusick {
14152130Smckusick 	register struct siisoftc *sc;
14252130Smckusick 	register int i;
14352130Smckusick 
14452130Smckusick 	if (cp->pmax_unit >= NSII)
14552130Smckusick 		return (0);
14652130Smckusick 	sc = &sii_softc[cp->pmax_unit];
14752130Smckusick 	sc->sc_regs = (SIIRegs *)cp->pmax_addr;
14852130Smckusick 	sc->sc_flags = cp->pmax_flags;
14952130Smckusick 	sc->sc_target = -1;	/* no command active */
15052130Smckusick 	/*
15152130Smckusick 	 * Give each target its own DMA buffer region.
15252130Smckusick 	 * Make it big enough for 2 max transfers so we can ping pong buffers
15352130Smckusick 	 * while we copy the data.
15452130Smckusick 	 */
15552130Smckusick 	for (i = 0; i < SII_NCMD; i++) {
15652130Smckusick 		sc->sc_st[i].dmaAddr[0] = (u_short *)MACH_SCSI_BUFFER_ADDR +
15752130Smckusick 			2 * SII_MAX_DMA_XFER_LENGTH * i;
15852130Smckusick 		sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] +
15952130Smckusick 			SII_MAX_DMA_XFER_LENGTH;
16052130Smckusick 	}
16152130Smckusick 
16252130Smckusick 	printf("sii%d at nexus0 csr 0x%x\n", cp->pmax_unit, cp->pmax_addr);
16352130Smckusick 	sii_Reset(sc->sc_regs, RESET);
16452130Smckusick 	return (1);
16552130Smckusick }
16652130Smckusick 
16752130Smckusick /*
16852130Smckusick  * Start activity on a SCSI device.
16952130Smckusick  * We maintain information on each device separately since devices can
17052130Smckusick  * connect/disconnect during an operation.
17152130Smckusick  */
17252130Smckusick void
17352130Smckusick siistart(scsicmd)
17452130Smckusick 	register ScsiCmd *scsicmd;	/* command to start */
17552130Smckusick {
17652130Smckusick 	register struct scsi_device *sdp = scsicmd->sd;
17752130Smckusick 	register struct siisoftc *sc = &sii_softc[sdp->sd_ctlr];
17852130Smckusick 	int s;
17952130Smckusick 
18052130Smckusick 	s = splbio();
18152130Smckusick 	/*
18252130Smckusick 	 * Check if another command is already in progress.
18352130Smckusick 	 * We may have to change this if we allow SCSI devices with
18452130Smckusick 	 * separate LUNs.
18552130Smckusick 	 */
18652130Smckusick 	if (sc->sc_cmd[sdp->sd_drive]) {
18752130Smckusick 		printf("sii%d: device %s busy at start\n", sdp->sd_ctlr,
18852130Smckusick 			sdp->sd_driver->d_name);
18952130Smckusick 		(*sdp->sd_driver->d_done)(scsicmd->unit, EBUSY,
19052130Smckusick 			scsicmd->buflen, 0);
19152130Smckusick 		splx(s);
19252130Smckusick 	}
19352130Smckusick 	sc->sc_cmd[sdp->sd_drive] = scsicmd;
19452130Smckusick 	sii_StartCmd(sc, sdp->sd_drive);
19552130Smckusick 	splx(s);
19652130Smckusick }
19752130Smckusick 
19852130Smckusick /*
19952130Smckusick  * Check to see if any SII chips have pending interrupts
20052130Smckusick  * and process as appropriate.
20152130Smckusick  */
20252130Smckusick void
20352697Sralph siiintr(unit)
20452697Sralph 	int unit;
20552130Smckusick {
20652697Sralph 	register struct siisoftc *sc = &sii_softc[unit];
20752130Smckusick 	unsigned dstat;
20852130Smckusick 
20952130Smckusick 	/*
21052130Smckusick 	 * Find which controller caused the interrupt.
21152130Smckusick 	 */
21252697Sralph 	dstat = sc->sc_regs->dstat;
21352697Sralph 	if (dstat & (SII_CI | SII_DI))
21452697Sralph 		sii_DoIntr(sc, dstat);
21552130Smckusick }
21652130Smckusick 
21752130Smckusick /*
21852130Smckusick  * Reset the SII chip and do a SCSI reset if 'reset' is true.
21952130Smckusick  * NOTE: if !cold && reset, should probably probe for devices
22052130Smckusick  * since a SCSI bus reset will set UNIT_ATTENTION.
22152130Smckusick  */
22252130Smckusick static void
22352130Smckusick sii_Reset(regs, reset)
22452130Smckusick 	register SIIRegs *regs;
22552130Smckusick 	int reset;				/* TRUE => reset SCSI bus */
22652130Smckusick {
22752130Smckusick 
22852130Smckusick #ifdef DEBUG
22952130Smckusick 	if (sii_debug > 1)
23052130Smckusick 		printf("sii: RESET\n");
23152130Smckusick #endif
23252130Smckusick 	/*
23352130Smckusick 	 * Reset the SII chip.
23452130Smckusick 	 */
23552130Smckusick 	regs->comm = SII_CHRESET;
23652130Smckusick 	/*
23752130Smckusick 	 * Set arbitrated bus mode.
23852130Smckusick 	 */
23952130Smckusick 	regs->csr = SII_HPM;
24052130Smckusick 	/*
24152130Smckusick 	 * SII is always ID 7.
24252130Smckusick 	 */
24352130Smckusick 	regs->id = SII_ID_IO | 7;
24452130Smckusick 	/*
24552130Smckusick 	 * Enable SII to drive the SCSI bus.
24652130Smckusick 	 */
24752130Smckusick 	regs->dictrl = SII_PRE;
24852130Smckusick 	regs->dmctrl = 0;
24952130Smckusick 
25052130Smckusick 	if (reset) {
25152130Smckusick 		register int i;
25252130Smckusick 
25352130Smckusick 		/*
25452130Smckusick 		 * Assert SCSI bus reset for at least 25 Usec to clear the
25552130Smckusick 		 * world. SII_DO_RST is self clearing.
25652130Smckusick 		 * Delay 250 ms before doing any commands.
25752130Smckusick 		 */
25852130Smckusick 		regs->comm = SII_DO_RST;
25952130Smckusick 		MachEmptyWriteBuffer();
26052130Smckusick 		DELAY(250000);
26152130Smckusick 
26252130Smckusick 		/* rearbitrate synchronous offset */
26352130Smckusick 		for (i = 0; i < SII_NCMD; i++)
26452130Smckusick 			sii_softc[0].sc_st[i].dmaReqAck = 0;
26552130Smckusick 	}
26652130Smckusick 
26752130Smckusick 	/*
26852130Smckusick 	 * Clear any pending interrupts from the reset.
26952130Smckusick 	 */
27052130Smckusick 	regs->cstat = regs->cstat;
27152130Smckusick 	regs->dstat = regs->dstat;
27252130Smckusick 	/*
27352130Smckusick 	 * Set up SII for arbitrated bus mode, SCSI parity checking,
27452130Smckusick 	 * Reselect Enable, and Interrupt Enable.
27552130Smckusick 	 */
27652130Smckusick 	regs->csr = SII_HPM | SII_RSE | SII_PCE | SII_IE;
27752130Smckusick 	MachEmptyWriteBuffer();
27852130Smckusick }
27952130Smckusick 
28052130Smckusick /*
28152130Smckusick  * Start a SCSI command by sending the cmd data
28252130Smckusick  * to a SCSI controller via the SII.
28352130Smckusick  * Call the device done proceedure if it can't be started.
28452130Smckusick  * NOTE: we should be called with interrupts disabled.
28552130Smckusick  */
28652130Smckusick static void
28752130Smckusick sii_StartCmd(sc, target)
28852130Smckusick 	register struct siisoftc *sc;	/* which SII to use */
28952130Smckusick 	register int target;		/* which command to start */
29052130Smckusick {
29152130Smckusick 	register SIIRegs *regs;
29252130Smckusick 	register ScsiCmd *scsicmd;
29352130Smckusick 	register State *state;
29452130Smckusick 	register unsigned status;
29552130Smckusick 	int error, retval;
29652130Smckusick 
29752130Smckusick 	/* if another command is currently in progress, just wait */
29852130Smckusick 	if (sc->sc_target >= 0)
29952130Smckusick 		return;
30052130Smckusick 
30152130Smckusick 	/* initialize state information for this command */
30252130Smckusick 	scsicmd = sc->sc_cmd[target];
30352130Smckusick 	state = &sc->sc_st[target];
30452130Smckusick 	state->flags = FIRST_DMA;
30552130Smckusick 	state->prevComm = 0;
30652130Smckusick 	state->dmalen = 0;
30752130Smckusick 	state->dmaCurPhase = -1;
30852130Smckusick 	state->dmaPrevPhase = -1;
30952130Smckusick 	state->dmaBufIndex = 0;
31052130Smckusick 	state->cmd = scsicmd->cmd;
31152130Smckusick 	state->cmdlen = scsicmd->cmdlen;
31252130Smckusick 	if ((state->buflen = scsicmd->buflen) == 0) {
31352130Smckusick 		state->dmaDataPhase = -1; /* illegal phase. shouldn't happen */
31452130Smckusick 		state->buf = (char *)0;
31552130Smckusick 	} else {
31652130Smckusick 		state->dmaDataPhase =
31752130Smckusick 			(scsicmd->flags & SCSICMD_DATA_TO_DEVICE) ?
31852130Smckusick 			SII_DATA_OUT_PHASE : SII_DATA_IN_PHASE;
31952130Smckusick 		state->buf = scsicmd->buf;
32052130Smckusick 		if (state->buflen > SII_MAX_DMA_XFER_LENGTH &&
32152130Smckusick 		    (scsicmd->flags & SCSICMD_DATA_TO_DEVICE))
32252130Smckusick 			printf("sii_StartCmd: target %d dma %d\n",
32352130Smckusick 				target, state->buflen); /* XXX */
32452130Smckusick 	}
32552130Smckusick 
32652130Smckusick #ifdef DEBUG
32752130Smckusick 	if (sii_debug > 1) {
32852130Smckusick 		printf("sii_StartCmd: %s target %d cmd 0x%x addr %x size %d dma %d\n",
32952130Smckusick 			scsicmd->sd->sd_driver->d_name, target,
33052130Smckusick 			scsicmd->cmd[0], scsicmd->buf, scsicmd->buflen,
33152130Smckusick 			state->dmaDataPhase);
33252130Smckusick 	}
33352130Smckusick 	sii_debug_cmd = scsicmd->cmd[0];
33452130Smckusick 	if (scsicmd->cmd[0] == SCSI_READ_EXT) {
33552130Smckusick 		sii_debug_bn = (scsicmd->cmd[2] << 24) |
33652130Smckusick 			(scsicmd->cmd[3] << 16) |
33752130Smckusick 			(scsicmd->cmd[4] << 8) |
33852130Smckusick 			scsicmd->cmd[5];
33952130Smckusick 		sii_debug_sz = (scsicmd->cmd[7] << 8) | scsicmd->cmd[8];
34052130Smckusick 	}
34152130Smckusick #endif
34252130Smckusick 
34352130Smckusick 	/* try to select the target */
34452130Smckusick 	regs = sc->sc_regs;
34552130Smckusick 
34652130Smckusick 	/*
34752130Smckusick 	 * Another device may have selected us; in which case,
34852130Smckusick 	 * this command will be restarted later.
34952130Smckusick 	 */
35052130Smckusick 	if ((status = regs->dstat) & (SII_CI | SII_DI)) {
35152130Smckusick 		sii_DoIntr(sc, status);
35252130Smckusick 		return;
35352130Smckusick 	}
35452130Smckusick 
35552130Smckusick 	sc->sc_target = target;
35652130Smckusick 	if (scsicmd->flags & SCSICMD_USE_SYNC) {
35752130Smckusick 		printf("sii_StartCmd: doing extended msg\n"); /* XXX */
35852130Smckusick 		/*
35952130Smckusick 		 * Setup to send both the identify message and the synchronous
36052130Smckusick 		 * data transfer request.
36152130Smckusick 		 */
36252130Smckusick 		sii_buf[0] = SCSI_DIS_REC_IDENTIFY;
36352130Smckusick 		sii_buf[1] = SCSI_EXTENDED_MSG;
36452130Smckusick 		sii_buf[2] = 3;		/* message length */
36552130Smckusick 		sii_buf[3] = SCSI_SYNCHRONOUS_XFER;
36652130Smckusick 		sii_buf[4] = 0;
36752130Smckusick 		sii_buf[5] = 3;		/* maximum SII chip supports */
36852130Smckusick 
36952130Smckusick 		state->dmaCurPhase = SII_MSG_OUT_PHASE,
37052130Smckusick 		state->dmalen = 6;
37152130Smckusick 		CopyToBuffer((u_short *)sii_buf,
37252130Smckusick 			(volatile u_short *)SII_BUF_ADDR, 6);
37352130Smckusick 		regs->slcsr = target;
37452130Smckusick 		regs->dmctrl = 0;
37552130Smckusick 		regs->dmaddrl = ((unsigned)SII_BUF_ADDR >> 1);
37652130Smckusick 		regs->dmaddrh = ((unsigned)SII_BUF_ADDR >> 17) & 03;
37752130Smckusick 		regs->dmlotc = 6;
37852130Smckusick 		regs->comm = SII_DMA | SII_INXFER | SII_SELECT | SII_ATN |
37952130Smckusick 			SII_CON | SII_MSG_OUT_PHASE;
38052130Smckusick 	} else {
38152130Smckusick 		/* do a chained, select with ATN and programmed I/O command */
38252130Smckusick 		regs->data = SCSI_DIS_REC_IDENTIFY;
38352130Smckusick 		regs->slcsr = target;
38452130Smckusick 		regs->dmctrl = 0;
38552130Smckusick 		regs->comm = SII_INXFER | SII_SELECT | SII_ATN | SII_CON |
38652130Smckusick 			SII_MSG_OUT_PHASE;
38752130Smckusick 	}
38852130Smckusick 	MachEmptyWriteBuffer();
38952130Smckusick 
39052130Smckusick 	/*
39152130Smckusick 	 * Wait for something to happen
39252130Smckusick 	 * (should happen soon or we would use interrupts).
39352130Smckusick 	 */
39452130Smckusick 	SII_WAIT_UNTIL(status, regs->cstat, status & (SII_CI | SII_DI),
39552130Smckusick 		SII_WAIT_COUNT/2, retval);
39652130Smckusick 
39752130Smckusick 	/* check to see if we are connected OK */
39852130Smckusick 	if ((status & (SII_RST | SII_SCH | SII_STATE_MSK)) ==
39952130Smckusick 	    (SII_SCH | SII_CON)) {
40052130Smckusick 		regs->cstat = status;
40152130Smckusick 		MachEmptyWriteBuffer();
40252130Smckusick 
40352130Smckusick #ifdef DEBUG
40452130Smckusick 		sii_logp->target = target;
40552130Smckusick 		sii_logp->cstat = status;
40652130Smckusick 		sii_logp->dstat = 0;
40752130Smckusick 		sii_logp->comm = regs->comm;
40852130Smckusick 		sii_logp->msg = -1;
40952130Smckusick 		if (++sii_logp >= &sii_log[NLOG])
41052130Smckusick 			sii_logp = sii_log;
41152130Smckusick #endif
41252130Smckusick 
41352130Smckusick 		/* wait a short time for command phase */
41452130Smckusick 		SII_WAIT_UNTIL(status, regs->dstat, status & SII_MIS,
41552130Smckusick 			SII_WAIT_COUNT, retval);
41652130Smckusick #ifdef DEBUG
41752130Smckusick 		if (sii_debug > 2)
41852130Smckusick 			printf("sii_StartCmd: ds %x cnt %d\n", status, retval);
41952130Smckusick #endif
42052130Smckusick 		if ((status & (SII_CI | SII_MIS | SII_PHASE_MSK)) !=
42152130Smckusick 		    (SII_MIS | SII_CMD_PHASE)) {
42252130Smckusick 			printf("sii_StartCmd: timeout cs %x ds %x cnt %d\n",
42352130Smckusick 				regs->cstat, status, retval); /* XXX */
42452130Smckusick 			/* process interrupt or continue until it happens */
42552130Smckusick 			if (status & (SII_CI | SII_DI))
42652130Smckusick 				sii_DoIntr(sc, status);
42752130Smckusick 			return;
42852130Smckusick 		}
42952130Smckusick 		regs->dstat = SII_DNE;	/* clear Msg Out DMA done */
43052130Smckusick 
43152130Smckusick 		/* send command data */
43252130Smckusick 		CopyToBuffer((u_short *)state->cmd,
43352130Smckusick 			(volatile u_short *)state->dmaAddr[0], state->cmdlen);
43452130Smckusick 		sii_StartDMA(regs, state->dmaCurPhase = SII_CMD_PHASE,
43552130Smckusick 			state->dmaAddr[0], state->dmalen = scsicmd->cmdlen);
43652130Smckusick 
43752130Smckusick 		/* wait a little while for DMA to finish */
43852130Smckusick 		SII_WAIT_UNTIL(status, regs->dstat, status & (SII_CI | SII_DI),
43952130Smckusick 			SII_WAIT_COUNT, retval);
44052130Smckusick #ifdef DEBUG
44152130Smckusick 		if (sii_debug > 2)
44252130Smckusick 			printf("sii_StartCmd: ds %x, cnt %d\n", status, retval);
44352130Smckusick #endif
44452130Smckusick 		if (status & (SII_CI | SII_DI))
44552130Smckusick 			sii_DoIntr(sc, status);
44652130Smckusick #ifdef DEBUG
44752130Smckusick 		if (sii_debug > 2)
44852130Smckusick 			printf("sii_StartCmd: DONE ds %x\n", regs->dstat);
44952130Smckusick #endif
45052130Smckusick 		return;
45152130Smckusick 	}
45252130Smckusick 
45352130Smckusick 	/*
45452130Smckusick 	 * Another device may have selected us; in which case,
45552130Smckusick 	 * this command will be restarted later.
45652130Smckusick 	 */
45752130Smckusick 	if (status & (SII_CI | SII_DI)) {
45852130Smckusick 		sii_DoIntr(sc, regs->dstat);
45952130Smckusick 		return;
46052130Smckusick 	}
46152130Smckusick 
46252130Smckusick 	/*
46352130Smckusick 	 * Disconnect if selection command still in progress.
46452130Smckusick 	 */
46552130Smckusick 	if (status & SII_SIP) {
46652130Smckusick 		error = ENXIO;	/* device didn't respond */
46752130Smckusick 		regs->comm = SII_DISCON;
46852130Smckusick 		MachEmptyWriteBuffer();
46952130Smckusick 		SII_WAIT_UNTIL(status, regs->cstat,
47052130Smckusick 			!(status & (SII_CON | SII_SIP)),
47152130Smckusick 			SII_WAIT_COUNT, retval);
47252130Smckusick 	} else
47352130Smckusick 		error = EBUSY;	/* couldn't get the bus */
47452130Smckusick #ifdef DEBUG
47552130Smckusick 	if (sii_debug > 0)
47652130Smckusick 		printf("sii_StartCmd: Couldn't select target %d error %d\n",
47752130Smckusick 			target, error);
47852130Smckusick #endif
47952130Smckusick 	sc->sc_target = -1;
48052130Smckusick 	regs->cstat = 0xffff;
48152130Smckusick 	regs->dstat = 0xffff;
48252130Smckusick 	regs->comm = 0;
48352130Smckusick 	MachEmptyWriteBuffer();
48452130Smckusick 	sii_CmdDone(sc, target, error);
48552130Smckusick }
48652130Smckusick 
48752130Smckusick /*
48852130Smckusick  * Process interrupt conditions.
48952130Smckusick  */
49052130Smckusick static void
49152130Smckusick sii_DoIntr(sc, dstat)
49252130Smckusick 	register struct siisoftc *sc;
49352130Smckusick 	register unsigned dstat;
49452130Smckusick {
49552130Smckusick 	register SIIRegs *regs = sc->sc_regs;
49652130Smckusick 	register State *state;
49752130Smckusick 	register unsigned cstat;
49852130Smckusick 	register int i;
49952130Smckusick 	unsigned comm, msg;
50052130Smckusick 
50152130Smckusick again:
50252130Smckusick 	comm = regs->comm;
50352130Smckusick 
50452130Smckusick #ifdef DEBUG
50552130Smckusick 	if (sii_debug > 3)
50652130Smckusick 		printf("sii_DoIntr: cs %x, ds %x cm %x ",
50752130Smckusick 			regs->cstat, dstat, comm);
50852130Smckusick 	sii_logp->target = sc->sc_target;
50952130Smckusick 	sii_logp->cstat = regs->cstat;
51052130Smckusick 	sii_logp->dstat = dstat;
51152130Smckusick 	sii_logp->comm = comm;
51252130Smckusick 	sii_logp->msg = -1;
51352130Smckusick 	if (++sii_logp >= &sii_log[NLOG])
51452130Smckusick 		sii_logp = sii_log;
51552130Smckusick #endif
51652130Smckusick 
51752130Smckusick 	regs->dstat = dstat;	/* acknowledge everything */
51852130Smckusick 	MachEmptyWriteBuffer();
51952130Smckusick 
52052130Smckusick 	if (dstat & SII_CI) {
52152130Smckusick 		/* deglitch cstat register */
52252130Smckusick 		msg = regs->cstat;
52352130Smckusick 		while (msg != (cstat = regs->cstat))
52452130Smckusick 			msg = cstat;
52552130Smckusick 		regs->cstat = cstat;	/* acknowledge everything */
52652130Smckusick 		MachEmptyWriteBuffer();
52752130Smckusick #ifdef DEBUG
52852130Smckusick 		if (sii_logp > sii_log)
52952130Smckusick 			sii_logp[-1].cstat = cstat;
53052130Smckusick 		else
53152130Smckusick 			sii_log[NLOG - 1].cstat = cstat;
53252130Smckusick #endif
53352130Smckusick 
53452130Smckusick 		/* check for a BUS RESET */
53552130Smckusick 		if (cstat & SII_RST) {
53652130Smckusick 			printf("sii%d: SCSI bus reset!!\n", sc - sii_softc);
53752130Smckusick 			/* need to flush disconnected commands */
53852697Sralph 			for (i = 0; i < SII_NCMD; i++) {
53952697Sralph 				if (!sc->sc_cmd[i])
54052697Sralph 					continue;
54152130Smckusick 				sii_CmdDone(sc, i, EIO);
54252697Sralph 			}
54352130Smckusick 			/* rearbitrate synchronous offset */
54452130Smckusick 			for (i = 0; i < SII_NCMD; i++)
54552130Smckusick 				sc->sc_st[i].dmaReqAck = 0;
54652130Smckusick 			sc->sc_target = -1;
54752130Smckusick 			return;
54852130Smckusick 		}
54952130Smckusick 
55052130Smckusick #ifdef notdef
55152130Smckusick 		/*
55252130Smckusick 		 * Check for a BUS ERROR.
55352130Smckusick 		 * According to DEC, this feature doesn't really work
55452130Smckusick 		 * and to just clear the bit if it's set.
55552130Smckusick 		 */
55652130Smckusick 		if (cstat & SII_BER) {
55752130Smckusick 		}
55852130Smckusick #endif
55952130Smckusick 
56052130Smckusick 		/* check for state change */
56152130Smckusick 		if (cstat & SII_SCH) {
56252130Smckusick 			sii_StateChg(sc, cstat);
56352130Smckusick 			comm = regs->comm;
56452130Smckusick 		}
56552130Smckusick 	}
56652130Smckusick 
56752130Smckusick 	/* check for DMA completion */
56852130Smckusick 	if (dstat & SII_DNE) {
56952130Smckusick 		u_short *dma;
57052130Smckusick 		char *buf;
57152130Smckusick 
57252130Smckusick 		/* check for a PARITY ERROR */
57352130Smckusick 		if (dstat & SII_IPE) {
57452130Smckusick 			printf("sii%d: Parity error!!\n", sc - sii_softc);
57552130Smckusick 			goto abort;
57652130Smckusick 		}
57752130Smckusick 		/*
57852130Smckusick 		 * There is a race condition with SII_SCH. There is a short
57952130Smckusick 		 * window between the time a SII_SCH is seen after a disconnect
58052130Smckusick 		 * and when the SII_SCH is cleared. A reselect can happen
58152130Smckusick 		 * in this window and we will clear the SII_SCH without
58252130Smckusick 		 * processing the reconnect.
58352130Smckusick 		 */
58452130Smckusick 		if (sc->sc_target < 0) {
58552130Smckusick 			cstat = regs->cstat;
58652130Smckusick 			printf("sii%d: target %d DNE?? dev %d,%d cs %x\n",
58752130Smckusick 				sc - sii_softc, sc->sc_target,
58852130Smckusick 				regs->slcsr, regs->destat,
58952130Smckusick 				cstat); /* XXX */
59052130Smckusick 			if (cstat & SII_DST) {
59152130Smckusick 				sc->sc_target = regs->destat;
59252130Smckusick 				state->prevComm = 0;
59352130Smckusick 			} else
59452130Smckusick 				panic("sc_target 1");
59552130Smckusick 		}
59652130Smckusick 		state = &sc->sc_st[sc->sc_target];
59752130Smckusick 		/* dmalen = amount left to transfer, i = amount transfered */
59852130Smckusick 		i = state->dmalen;
59952130Smckusick 		state->dmalen = 0;
60052130Smckusick 		state->dmaCurPhase = -1;
60152130Smckusick #ifdef DEBUG
60252130Smckusick 		if (sii_debug > 4) {
60352130Smckusick 			printf("DNE: amt %d ", i);
60452130Smckusick 			if (!(dstat & SII_TCZ))
60552130Smckusick 				printf("no TCZ?? (%d) ", regs->dmlotc);
60652130Smckusick 		} else if (!(dstat & SII_TCZ)) {
60752130Smckusick 			printf("sii%d: device %d: no TCZ?? (%d)\n",
60852130Smckusick 				sc - sii_softc, sc->sc_target, regs->dmlotc);
60952130Smckusick 			sii_DumpLog(); /* XXX */
61052130Smckusick 		}
61152130Smckusick #endif
61252130Smckusick 		switch (comm & SII_PHASE_MSK) {
61352130Smckusick 		case SII_CMD_PHASE:
61452130Smckusick 			state->cmdlen -= i;
61552130Smckusick 			break;
61652130Smckusick 
61752130Smckusick 		case SII_DATA_IN_PHASE:
61852130Smckusick 			/* check for more data for the same phase */
61952130Smckusick 			dma = state->dmaAddr[state->dmaBufIndex];
62052130Smckusick 			buf = state->buf;
62152130Smckusick 			state->buf += i;
62252130Smckusick 			state->buflen -= i;
62352130Smckusick 			if (state->buflen > 0 && !(dstat & SII_MIS)) {
62452130Smckusick 				int len;
62552130Smckusick 
62652130Smckusick 				/* start reading next chunk */
62752130Smckusick 				len = state->buflen;
62852130Smckusick 				if (len > SII_MAX_DMA_XFER_LENGTH)
62952130Smckusick 					len = SII_MAX_DMA_XFER_LENGTH;
63052130Smckusick 				state->dmaBufIndex = !state->dmaBufIndex;
63152130Smckusick 				sii_StartDMA(regs,
63252130Smckusick 					state->dmaCurPhase = SII_DATA_IN_PHASE,
63352130Smckusick 					state->dmaAddr[state->dmaBufIndex],
63452130Smckusick 					state->dmalen = len);
63552130Smckusick 				dstat &= ~(SII_IBF | SII_TBE);
63652130Smckusick 			}
63752130Smckusick 			/* copy in the data */
63852130Smckusick 			CopyFromBuffer((volatile u_short *)dma, buf, i);
63952130Smckusick 			break;
64052130Smckusick 
64152130Smckusick 		case SII_DATA_OUT_PHASE:
64252130Smckusick 			state->dmaBufIndex = !state->dmaBufIndex;
64352130Smckusick 			state->buf += i;
64452130Smckusick 			state->buflen -= i;
64552130Smckusick 
64652130Smckusick 			/* check for more data for the same phase */
64752130Smckusick 			if (state->buflen <= 0 || (dstat & SII_MIS))
64852130Smckusick 				break;
64952130Smckusick 
65052130Smckusick 			/* start next chunk */
65152130Smckusick 			i = state->buflen;
65252130Smckusick 			if (i > SII_MAX_DMA_XFER_LENGTH) {
65352130Smckusick 				sii_StartDMA(regs, state->dmaCurPhase =
65452130Smckusick 					SII_DATA_OUT_PHASE,
65552130Smckusick 					state->dmaAddr[state->dmaBufIndex],
65652130Smckusick 					state->dmalen =
65752130Smckusick 					SII_MAX_DMA_XFER_LENGTH);
65852130Smckusick 				/* prepare for next chunk */
65952130Smckusick 				i -= SII_MAX_DMA_XFER_LENGTH;
66052130Smckusick 				if (i > SII_MAX_DMA_XFER_LENGTH)
66152130Smckusick 					i = SII_MAX_DMA_XFER_LENGTH;
66252130Smckusick 				CopyToBuffer((u_short *)(state->buf +
66352130Smckusick 					SII_MAX_DMA_XFER_LENGTH),
66452130Smckusick 					(volatile u_short *)
66552130Smckusick 					state->dmaAddr[!state->dmaBufIndex], i);
66652130Smckusick 			} else {
66752130Smckusick 				sii_StartDMA(regs, state->dmaCurPhase =
66852130Smckusick 					SII_DATA_OUT_PHASE,
66952130Smckusick 					state->dmaAddr[state->dmaBufIndex],
67052130Smckusick 					state->dmalen = i);
67152130Smckusick 			}
67252130Smckusick 			dstat &= ~(SII_IBF | SII_TBE);
67352130Smckusick 			break;
67452130Smckusick 
67552130Smckusick 		default:
67652130Smckusick 			printf("sii%d: device %d: unexpected DNE\n",
67752130Smckusick 				sc - sii_softc, sc->sc_target);
67852130Smckusick #ifdef DEBUG
67952130Smckusick 			sii_DumpLog();
68052130Smckusick #endif
68152130Smckusick 		}
68252130Smckusick 	}
68352130Smckusick 
68452130Smckusick 	/* check for phase change or another MsgIn/Out */
68552130Smckusick 	if (dstat & (SII_MIS | SII_IBF | SII_TBE)) {
68652130Smckusick 		/*
68752130Smckusick 		 * There is a race condition with SII_SCH. There is a short
68852130Smckusick 		 * window between the time a SII_SCH is seen after a disconnect
68952130Smckusick 		 * and when the SII_SCH is cleared. A reselect can happen
69052130Smckusick 		 * in this window and we will clear the SII_SCH without
69152130Smckusick 		 * processing the reconnect.
69252130Smckusick 		 */
69352130Smckusick 		if (sc->sc_target < 0) {
69452130Smckusick 			cstat = regs->cstat;
69552130Smckusick 			printf("sii%d: target %d MIS?? dev %d,%d cs %x ds %x\n",
69652130Smckusick 				sc - sii_softc, sc->sc_target,
69752130Smckusick 				regs->slcsr, regs->destat,
69852130Smckusick 				cstat, dstat); /* XXX */
69952130Smckusick 			if (cstat & SII_DST) {
70052130Smckusick 				sc->sc_target = regs->destat;
70152130Smckusick 				state->prevComm = 0;
70252130Smckusick 			} else
70352130Smckusick 				panic("sc_target 2");
70452130Smckusick 		}
70552130Smckusick 		state = &sc->sc_st[sc->sc_target];
70652130Smckusick 		switch (dstat & SII_PHASE_MSK) {
70752130Smckusick 		case SII_CMD_PHASE:
70852130Smckusick 			if (state->dmaPrevPhase >= 0) {
70952130Smckusick 				/* restart DMA after disconnect/reconnect */
71052130Smckusick 				if (state->dmaPrevPhase != SII_CMD_PHASE) {
71152130Smckusick 					printf("sii%d: device %d: dma reselect phase doesn't match\n",
71252130Smckusick 						sc - sii_softc, sc->sc_target);
71352130Smckusick 					goto abort;
71452130Smckusick 				}
71552130Smckusick 				state->dmaCurPhase = SII_CMD_PHASE;
71652130Smckusick 				state->dmaPrevPhase = -1;
71752130Smckusick 				regs->dmaddrl = state->dmaAddrL;
71852130Smckusick 				regs->dmaddrh = state->dmaAddrH;
71952130Smckusick 				regs->dmlotc = state->dmaCnt;
72052130Smckusick 				if (state->dmaCnt & 1)
72152130Smckusick 					regs->dmabyte = state->dmaByte;
72252130Smckusick 				regs->comm = SII_DMA | SII_INXFER |
72352130Smckusick 					(comm & SII_STATE_MSK) | SII_CMD_PHASE;
72452130Smckusick 				MachEmptyWriteBuffer();
72552130Smckusick #ifdef DEBUG
72652130Smckusick 				if (sii_debug > 4)
72752130Smckusick 					printf("Cmd dcnt %d dadr %x ",
72852130Smckusick 						state->dmaCnt,
72952130Smckusick 						(state->dmaAddrH << 16) |
73052130Smckusick 							state->dmaAddrL);
73152130Smckusick #endif
73252130Smckusick 			} else {
73352130Smckusick 				/* send command data */
73452130Smckusick 				i = state->cmdlen;
73552130Smckusick 				if (i == 0) {
73652130Smckusick 					printf("sii%d: device %d: cmd count exceeded\n",
73752130Smckusick 						sc - sii_softc, sc->sc_target);
73852130Smckusick 					goto abort;
73952130Smckusick 				}
74052130Smckusick 				CopyToBuffer((u_short *)state->cmd,
74152130Smckusick 					(volatile u_short *)state->dmaAddr[0],
74252130Smckusick 					i);
74352130Smckusick 				sii_StartDMA(regs, state->dmaCurPhase =
74452130Smckusick 					SII_CMD_PHASE, state->dmaAddr[0],
74552130Smckusick 					state->dmalen = i);
74652130Smckusick 			}
74752130Smckusick 			/* wait a short time for XFER complete */
74852130Smckusick 			SII_WAIT_UNTIL(dstat, regs->dstat,
74952130Smckusick 				dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i);
75052130Smckusick 			if (dstat & (SII_CI | SII_DI)) {
75152130Smckusick #ifdef DEBUG
75252130Smckusick 				if (sii_debug > 4)
75352130Smckusick 					printf("cnt %d\n", i);
75452130Smckusick 				else if (sii_debug > 0)
75552130Smckusick 					printf("sii_DoIntr: cmd wait ds %x cnt %d\n",
75652130Smckusick 						dstat, i);
75752130Smckusick #endif
75852130Smckusick 				goto again;
75952130Smckusick 			}
76052130Smckusick 			break;
76152130Smckusick 
76252130Smckusick 		case SII_DATA_IN_PHASE:
76352130Smckusick 		case SII_DATA_OUT_PHASE:
76452130Smckusick 			regs->dmctrl = state->dmaReqAck;
76552130Smckusick 			if (state->cmdlen > 0) {
76652130Smckusick 				printf("sii%d: device %d: cmd %x: command data not all sent (%d) 1\n",
76752130Smckusick 					sc - sii_softc, sc->sc_target,
76852130Smckusick 					sc->sc_cmd[sc->sc_target]->cmd[0],
76952130Smckusick 					state->cmdlen);
77052130Smckusick 				state->cmdlen = 0;
77152130Smckusick #ifdef DEBUG
77252130Smckusick 				sii_DumpLog();
77352130Smckusick #endif
77452130Smckusick 			}
77552130Smckusick 			if (state->dmaPrevPhase >= 0) {
77652130Smckusick 				/* restart DMA after disconnect/reconnect */
77752130Smckusick 				if (state->dmaPrevPhase !=
77852130Smckusick 				    (dstat & SII_PHASE_MSK)) {
77952130Smckusick 					printf("sii%d: device %d: dma reselect phase doesn't match\n",
78052130Smckusick 						sc - sii_softc, sc->sc_target);
78152130Smckusick 					goto abort;
78252130Smckusick 				}
78352130Smckusick 				state->dmaCurPhase = state->dmaPrevPhase;
78452130Smckusick 				state->dmaPrevPhase = -1;
78552130Smckusick 				regs->dmaddrl = state->dmaAddrL;
78652130Smckusick 				regs->dmaddrh = state->dmaAddrH;
78752130Smckusick 				regs->dmlotc = state->dmaCnt;
78852130Smckusick 				if (state->dmaCnt & 1)
78952130Smckusick 					regs->dmabyte = state->dmaByte;
79052130Smckusick 				regs->comm = SII_DMA | SII_INXFER |
79152130Smckusick 					(comm & SII_STATE_MSK) |
79252130Smckusick 					state->dmaCurPhase;
79352130Smckusick 				MachEmptyWriteBuffer();
79452130Smckusick #ifdef DEBUG
79552130Smckusick 				if (sii_debug > 4)
79652130Smckusick 					printf("Data %d dcnt %d dadr %x ",
79752130Smckusick 						state->dmaDataPhase,
79852130Smckusick 						state->dmaCnt,
79952130Smckusick 						(state->dmaAddrH << 16) |
80052130Smckusick 							state->dmaAddrL);
80152130Smckusick #endif
80252130Smckusick 				break;
80352130Smckusick 			}
80452130Smckusick 			if (state->dmaDataPhase != (dstat & SII_PHASE_MSK)) {
80552130Smckusick 				printf("sii%d: device %d: cmd %x: dma phase doesn't match\n",
80652130Smckusick 					sc - sii_softc, sc->sc_target,
80752130Smckusick 					sc->sc_cmd[sc->sc_target]->cmd[0]);
80852130Smckusick 				goto abort;
80952130Smckusick 			}
81052130Smckusick #ifdef DEBUG
81152130Smckusick 			if (sii_debug > 4) {
81252130Smckusick 				printf("Data %d ", state->dmaDataPhase);
81352130Smckusick 				if (sii_debug > 5)
81452130Smckusick 					printf("\n");
81552130Smckusick 			}
81652130Smckusick #endif
81752130Smckusick 			i = state->buflen;
81852130Smckusick 			if (i == 0) {
81952130Smckusick 				printf("sii%d: device %d: data count exceeded\n",
82052130Smckusick 					sc - sii_softc, sc->sc_target);
82152130Smckusick 				goto abort;
82252130Smckusick 			}
82352130Smckusick 			if (i > SII_MAX_DMA_XFER_LENGTH)
82452130Smckusick 				i = SII_MAX_DMA_XFER_LENGTH;
82552130Smckusick 			if ((dstat & SII_PHASE_MSK) == SII_DATA_IN_PHASE) {
82652130Smckusick 				sii_StartDMA(regs,
82752130Smckusick 					state->dmaCurPhase = SII_DATA_IN_PHASE,
82852130Smckusick 					state->dmaAddr[state->dmaBufIndex],
82952130Smckusick 					state->dmalen = i);
83052130Smckusick 				break;
83152130Smckusick 			}
83252130Smckusick 			/* start first chunk */
83352130Smckusick 			if (state->flags & FIRST_DMA) {
83452130Smckusick 				state->flags &= ~FIRST_DMA;
83552130Smckusick 				CopyToBuffer((u_short *)state->buf,
83652130Smckusick 					(volatile u_short *)
83752130Smckusick 					state->dmaAddr[state->dmaBufIndex], i);
83852130Smckusick 			}
83952130Smckusick 			sii_StartDMA(regs,
84052130Smckusick 				state->dmaCurPhase = SII_DATA_OUT_PHASE,
84152130Smckusick 				state->dmaAddr[state->dmaBufIndex],
84252130Smckusick 				state->dmalen = i);
84352130Smckusick 			i = state->buflen - SII_MAX_DMA_XFER_LENGTH;
84452130Smckusick 			if (i > 0) {
84552130Smckusick 				/* prepare for next chunk */
84652130Smckusick 				if (i > SII_MAX_DMA_XFER_LENGTH)
84752130Smckusick 					i = SII_MAX_DMA_XFER_LENGTH;
84852130Smckusick 				CopyToBuffer((u_short *)(state->buf +
84952130Smckusick 					SII_MAX_DMA_XFER_LENGTH),
85052130Smckusick 					(volatile u_short *)
85152130Smckusick 					state->dmaAddr[!state->dmaBufIndex], i);
85252130Smckusick 			}
85352130Smckusick 			break;
85452130Smckusick 
85552130Smckusick 		case SII_STATUS_PHASE:
85652130Smckusick 			if (state->cmdlen > 0) {
85752130Smckusick 				printf("sii%d: device %d: cmd %x: command data not all sent (%d) 2\n",
85852130Smckusick 					sc - sii_softc, sc->sc_target,
85952130Smckusick 					sc->sc_cmd[sc->sc_target]->cmd[0],
86052130Smckusick 					state->cmdlen);
86152130Smckusick 				state->cmdlen = 0;
86252130Smckusick #ifdef DEBUG
86352130Smckusick 				sii_DumpLog();
86452130Smckusick #endif
86552130Smckusick 			}
86652130Smckusick 
86752130Smckusick 			/* read amount transfered if DMA didn't finish */
86852130Smckusick 			if (state->dmalen > 0) {
86952130Smckusick 				i = state->dmalen - regs->dmlotc;
87052130Smckusick 				state->dmalen = 0;
87152130Smckusick 				state->dmaCurPhase = -1;
87252130Smckusick 				regs->dmlotc = 0;
87352130Smckusick 				regs->comm = comm &
87452130Smckusick 					(SII_STATE_MSK | SII_PHASE_MSK);
87552130Smckusick 				MachEmptyWriteBuffer();
87652130Smckusick 				regs->dstat = SII_DNE;
87752130Smckusick 				MachEmptyWriteBuffer();
87852130Smckusick #ifdef DEBUG
87952130Smckusick 				if (sii_debug > 4)
88052130Smckusick 					printf("DMA amt %d ", i);
88152130Smckusick #endif
88252130Smckusick 				switch (comm & SII_PHASE_MSK) {
88352130Smckusick 				case SII_DATA_IN_PHASE:
88452130Smckusick 					/* copy in the data */
88552130Smckusick 					CopyFromBuffer((volatile u_short *)
88652130Smckusick 					    state->dmaAddr[state->dmaBufIndex],
88752130Smckusick 					    state->buf, i);
88852130Smckusick 
88952130Smckusick 				case SII_CMD_PHASE:
89052130Smckusick 				case SII_DATA_OUT_PHASE:
89152130Smckusick 					state->buflen -= i;
89252130Smckusick 				}
89352130Smckusick 			}
89452130Smckusick 
89552130Smckusick 			/* read a one byte status message */
89652130Smckusick 			state->statusByte = msg =
89752130Smckusick 				sii_GetByte(regs, SII_STATUS_PHASE);
89852130Smckusick 			if (msg < 0) {
89952130Smckusick 				dstat = regs->dstat;
90052130Smckusick 				goto again;
90152130Smckusick 			}
90252130Smckusick #ifdef DEBUG
90352130Smckusick 			if (sii_debug > 4)
90452130Smckusick 				printf("Status %x ", msg);
90552130Smckusick 			if (sii_logp > sii_log)
90652130Smckusick 				sii_logp[-1].msg = msg;
90752130Smckusick 			else
90852130Smckusick 				sii_log[NLOG - 1].msg = msg;
90952130Smckusick #endif
91052130Smckusick 
91152130Smckusick 			/* do a quick wait for COMMAND_COMPLETE */
91252130Smckusick 			SII_WAIT_UNTIL(dstat, regs->dstat,
91352130Smckusick 				dstat & (SII_CI | SII_DI), SII_WAIT_COUNT, i);
91452130Smckusick 			if (dstat & (SII_CI | SII_DI)) {
91552130Smckusick #ifdef DEBUG
91652130Smckusick 				if (sii_debug > 4)
91752130Smckusick 					printf("cnt2 %d\n", i);
91852130Smckusick #endif
91952130Smckusick 				goto again;
92052130Smckusick 			}
92152130Smckusick 			break;
92252130Smckusick 
92352130Smckusick 		case SII_MSG_IN_PHASE:
92452130Smckusick 			/*
92552130Smckusick 			 * Save DMA state if DMA didn't finish.
92652130Smckusick 			 * Be careful not to save state again after reconnect
92752130Smckusick 			 * and see RESTORE_POINTER message.
92852130Smckusick 			 * Note that the SII DMA address is not incremented
92952130Smckusick 			 * as DMA proceeds.
93052130Smckusick 			 */
93152130Smckusick 			if (state->dmaCurPhase > 0) {
93252130Smckusick 				/* save dma registers */
93352130Smckusick 				state->dmaPrevPhase = state->dmaCurPhase;
93452130Smckusick 				state->dmaCurPhase = -1;
93552130Smckusick 				state->dmaCnt = i = regs->dmlotc;
93652130Smckusick 				if (dstat & SII_OBB)
93752130Smckusick 					state->dmaByte = regs->dmabyte;
93852130Smckusick 				if (i == 0)
93952130Smckusick 					i = SII_MAX_DMA_XFER_LENGTH;
94052130Smckusick 				i = state->dmalen - i;
94152130Smckusick 				/* note: no carry from dmaddrl to dmaddrh */
94252130Smckusick 				state->dmaAddrL = regs->dmaddrl + i;
94352130Smckusick 				state->dmaAddrH = regs->dmaddrh;
94452130Smckusick 				regs->comm = comm &
94552130Smckusick 					(SII_STATE_MSK | SII_PHASE_MSK);
94652130Smckusick 				MachEmptyWriteBuffer();
94752130Smckusick 				regs->dstat = SII_DNE;
94852130Smckusick 				MachEmptyWriteBuffer();
94952130Smckusick #ifdef DEBUG
95052130Smckusick 				if (sii_debug > 4) {
95152130Smckusick 					printf("SavP dcnt %d dadr %x ",
95252130Smckusick 						state->dmaCnt,
95352130Smckusick 						(state->dmaAddrH << 16) |
95452130Smckusick 						state->dmaAddrL);
95552130Smckusick 					if (((dstat & SII_OBB) != 0) ^
95652130Smckusick 					    (state->dmaCnt & 1))
95752130Smckusick 						printf("OBB??? ");
95852130Smckusick 				} else if (sii_debug > 0) {
95952130Smckusick 					if (((dstat & SII_OBB) != 0) ^
96052130Smckusick 					    (state->dmaCnt & 1)) {
96152130Smckusick 						printf("sii_DoIntr: OBB??? ds %x cnt %d\n",
96252130Smckusick 							dstat, state->dmaCnt);
96352130Smckusick 						sii_DumpLog();
96452130Smckusick 					}
96552130Smckusick 				}
96652130Smckusick #endif
96752130Smckusick 			}
96852130Smckusick 
96952130Smckusick 			/* read a one byte message */
97052130Smckusick 			msg = sii_GetByte(regs, SII_MSG_IN_PHASE);
97152130Smckusick 			if (msg < 0) {
97252130Smckusick 				dstat = regs->dstat;
97352130Smckusick 				goto again;
97452130Smckusick 			}
97552130Smckusick #ifdef DEBUG
97652130Smckusick 			if (sii_debug > 4)
97752130Smckusick 				printf("MsgIn %x ", msg);
97852130Smckusick 			if (sii_logp > sii_log)
97952130Smckusick 				sii_logp[-1].msg = msg;
98052130Smckusick 			else
98152130Smckusick 				sii_log[NLOG - 1].msg = msg;
98252130Smckusick #endif
98352130Smckusick 
98452130Smckusick 			/* process message */
98552130Smckusick 			switch (msg) {
98652130Smckusick 			case SCSI_COMMAND_COMPLETE:
98752130Smckusick 				msg = sc->sc_target;
98852130Smckusick 				sc->sc_target = -1;
98952130Smckusick 				/*
99052130Smckusick 				 * Wait a short time for disconnect.
99152130Smckusick 				 * Don't be fooled if SII_BER happens first.
99252130Smckusick 				 * Note: a reselect may happen here.
99352130Smckusick 				 */
99452130Smckusick 				SII_WAIT_UNTIL(cstat, regs->cstat,
99552130Smckusick 					cstat & (SII_RST | SII_SCH),
99652130Smckusick 					SII_WAIT_COUNT, i);
99752130Smckusick 				if ((cstat & (SII_RST | SII_SCH |
99852130Smckusick 				    SII_STATE_MSK)) == SII_SCH) {
99952130Smckusick 					regs->cstat = SII_SCH | SII_BER;
100052130Smckusick 					regs->comm = 0;
100152130Smckusick 					MachEmptyWriteBuffer();
100252130Smckusick 					/*
100352130Smckusick 					 * Double check that we didn't miss a
100452130Smckusick 					 * state change between seeing it and
100552130Smckusick 					 * clearing the SII_SCH bit.
100652130Smckusick 					 */
100752130Smckusick 					i = regs->cstat;
100852130Smckusick 					if (!(i & SII_SCH) &&
100952130Smckusick 					    (i & SII_STATE_MSK) !=
101052130Smckusick 					    (cstat & SII_STATE_MSK))
101152130Smckusick 						sii_StateChg(sc, i);
101252130Smckusick 				}
101352130Smckusick #ifdef DEBUG
101452130Smckusick 				if (sii_debug > 4)
101552130Smckusick 					printf("cs %x\n", cstat);
101652130Smckusick #endif
101752130Smckusick 				sii_CmdDone(sc, msg, 0);
101852130Smckusick 				break;
101952130Smckusick 
102052130Smckusick 			case SCSI_EXTENDED_MSG:
102152130Smckusick 				/* read the message length */
102252130Smckusick 				msg = sii_GetByte(regs, SII_MSG_IN_PHASE);
102352130Smckusick 				if (msg < 0) {
102452130Smckusick 					dstat = regs->dstat;
102552130Smckusick 					goto again;
102652130Smckusick 				}
102752130Smckusick 				if (msg == 0)
102852130Smckusick 					msg = 256;
102952130Smckusick 				sii_StartDMA(regs, SII_MSG_IN_PHASE,
103052130Smckusick 					(u_short *)SII_BUF_ADDR, msg);
103152130Smckusick 				/* wait a short time for XFER complete */
103252130Smckusick 				SII_WAIT_UNTIL(dstat, regs->dstat,
103352130Smckusick 					(dstat & (SII_DNE | SII_TCZ)) ==
103452130Smckusick 					(SII_DNE | SII_TCZ),
103552130Smckusick 					SII_WAIT_COUNT, i);
103652130Smckusick 
103752130Smckusick 				if ((dstat & (SII_DNE | SII_TCZ | SII_IPE)) !=
103852130Smckusick 				    (SII_DNE | SII_TCZ)) {
103952130Smckusick #ifdef DEBUG
104052130Smckusick 					if (sii_debug > 4)
104152130Smckusick 						printf("cnt0 %d\n", i);
104252130Smckusick 					else if (sii_debug > 0)
104352130Smckusick 						printf("sii_DoIntr: emsg in ds %x cnt %d\n",
104452130Smckusick 							dstat, i);
104552130Smckusick #endif
104652130Smckusick 					printf("sii: ds %x cm %x i %d lotc %d\n",
104752130Smckusick 						dstat, comm, i, regs->dmlotc); /* XXX */
104852130Smckusick 					sii_DumpLog(); /* XXX */
104952130Smckusick 					goto again;
105052130Smckusick 				}
105152130Smckusick 
105252130Smckusick 				/* clear the DNE, other errors handled later */
105352130Smckusick 				regs->dstat = SII_DNE;
105452130Smckusick 				MachEmptyWriteBuffer();
105552130Smckusick 
105652130Smckusick 				CopyFromBuffer((volatile u_short *)SII_BUF_ADDR,
105752130Smckusick 					sii_buf + 2, msg);
105852130Smckusick 				switch (sii_buf[2]) {
105952130Smckusick 				case SCSI_MODIFY_DATA_PTR:
106052130Smckusick 					i = (sii_buf[3] << 24) |
106152130Smckusick 						(sii_buf[4] << 16) |
106252130Smckusick 						(sii_buf[5] << 8) |
106352130Smckusick 						sii_buf[6];
106452130Smckusick 					if (state->dmaPrevPhase >= 0) {
106552130Smckusick 						state->dmaAddrL += i;
106652130Smckusick 						state->dmaCnt -= i;
106752130Smckusick 					}
106852130Smckusick 					break;
106952130Smckusick 
107052130Smckusick 				case SCSI_SYNCHRONOUS_XFER:
107152130Smckusick 					sii_DoSync(regs, state);
107252130Smckusick 					break;
107352130Smckusick 
107452130Smckusick 				case SCSI_EXTENDED_IDENTIFY:
107552130Smckusick 				case SCSI_WIDE_XFER:
107652130Smckusick 				default:
107752130Smckusick 				reject:
107852130Smckusick 					/* send a reject message */
107952130Smckusick 					regs->data = SCSI_MESSAGE_REJECT;
108052130Smckusick 					regs->comm = SII_INXFER | SII_ATN |
108152130Smckusick 						(regs->cstat & SII_STATE_MSK) |
108252130Smckusick 						SII_MSG_OUT_PHASE;
108352130Smckusick 					MachEmptyWriteBuffer();
108452130Smckusick 					/* wait for XFER complete */
108552130Smckusick 					SII_WAIT_UNTIL(dstat, regs->dstat,
108652130Smckusick 						(dstat &
108752130Smckusick 						(SII_DNE | SII_PHASE_MSK)) ==
108852130Smckusick 						(SII_DNE | SII_MSG_OUT_PHASE),
108952130Smckusick 						SII_WAIT_COUNT, i);
109052130Smckusick 
109152130Smckusick 					if ((dstat &
109252130Smckusick 					    (SII_DNE | SII_PHASE_MSK)) !=
109352130Smckusick 					    (SII_DNE | SII_MSG_OUT_PHASE))
109452130Smckusick 						break;
109552130Smckusick 					regs->dstat = SII_DNE;
109652130Smckusick 					MachEmptyWriteBuffer();
109752130Smckusick 				}
109852130Smckusick 				break;
109952130Smckusick 
110052130Smckusick 			case SCSI_SAVE_DATA_POINTER:
110152130Smckusick 			case SCSI_RESTORE_POINTERS:
110252130Smckusick 				/* wait a short time for another msg */
110352130Smckusick 				SII_WAIT_UNTIL(dstat, regs->dstat,
110452130Smckusick 					dstat & (SII_CI | SII_DI),
110552130Smckusick 					SII_WAIT_COUNT, i);
110652130Smckusick 				if (dstat & (SII_CI | SII_DI)) {
110752130Smckusick #ifdef DEBUG
110852130Smckusick 					if (sii_debug > 4)
110952130Smckusick 						printf("cnt %d\n", i);
111052130Smckusick #endif
111152130Smckusick 					goto again;
111252130Smckusick 				}
111352130Smckusick 				break;
111452130Smckusick 
111552130Smckusick 			case SCSI_DISCONNECT:
111652130Smckusick 				state->prevComm = comm;
111752130Smckusick #ifdef DEBUG
111852130Smckusick 				if (sii_debug > 4)
111952130Smckusick 					printf("disconn %d ", sc->sc_target);
112052130Smckusick #endif
112152130Smckusick 				/*
112252130Smckusick 				 * Wait a short time for disconnect.
112352130Smckusick 				 * Don't be fooled if SII_BER happens first.
112452130Smckusick 				 * Note: a reselect may happen here.
112552130Smckusick 				 */
112652130Smckusick 				SII_WAIT_UNTIL(cstat, regs->cstat,
112752130Smckusick 					cstat & (SII_RST | SII_SCH),
112852130Smckusick 					SII_WAIT_COUNT, i);
112952130Smckusick 				if ((cstat & (SII_RST | SII_SCH |
113052130Smckusick 				    SII_STATE_MSK)) != SII_SCH) {
113152130Smckusick #ifdef DEBUG
113252130Smckusick 					if (sii_debug > 4)
113352130Smckusick 						printf("cnt %d\n", i);
113452130Smckusick #endif
113552130Smckusick 					dstat = regs->dstat;
113652130Smckusick 					goto again;
113752130Smckusick 				}
113852130Smckusick 				regs->cstat = SII_SCH | SII_BER;
113952130Smckusick 				regs->comm = 0;
114052130Smckusick 				MachEmptyWriteBuffer();
114152130Smckusick 				sc->sc_target = -1;
114252130Smckusick 				/*
114352130Smckusick 				 * Double check that we didn't miss a state
114452130Smckusick 				 * change between seeing it and clearing
114552130Smckusick 				 * the SII_SCH bit.
114652130Smckusick 				 */
114752130Smckusick 				i = regs->cstat;
114852130Smckusick 				if (!(i & SII_SCH) && (i & SII_STATE_MSK) !=
114952130Smckusick 				    (cstat & SII_STATE_MSK))
115052130Smckusick 					sii_StateChg(sc, i);
115152130Smckusick 				break;
115252130Smckusick 
115352130Smckusick 			case SCSI_MESSAGE_REJECT:
115452130Smckusick 				printf("sii%d: device %d: message reject.\n",
115552130Smckusick 					sc - sii_softc, sc->sc_target);
115652130Smckusick 				goto abort;
115752130Smckusick 
115852130Smckusick 			default:
115952130Smckusick 				if (!(msg & SCSI_IDENTIFY)) {
116052130Smckusick 					printf("sii%d: device %d: couldn't handle message 0x%x... ignoring.\n",
116152130Smckusick 						sc - sii_softc, sc->sc_target,
116252130Smckusick 						msg);
116352130Smckusick #ifdef DEBUG
116452130Smckusick 					sii_DumpLog();
116552130Smckusick #endif
116652130Smckusick 					break;
116752130Smckusick 				}
116852130Smckusick 				/* may want to check LUN some day */
116952130Smckusick 				/* wait a short time for another msg */
117052130Smckusick 				SII_WAIT_UNTIL(dstat, regs->dstat,
117152130Smckusick 					dstat & (SII_CI | SII_DI),
117252130Smckusick 					SII_WAIT_COUNT, i);
117352130Smckusick 				if (dstat & (SII_CI | SII_DI)) {
117452130Smckusick #ifdef DEBUG
117552130Smckusick 					if (sii_debug > 4)
117652130Smckusick 						printf("cnt %d\n", i);
117752130Smckusick #endif
117852130Smckusick 					goto again;
117952130Smckusick 				}
118052130Smckusick 			}
118152130Smckusick 			break;
118252130Smckusick 
118352130Smckusick 		case SII_MSG_OUT_PHASE:
118452130Smckusick #ifdef DEBUG
118552130Smckusick 			if (sii_debug > 4)
118652130Smckusick 				printf("MsgOut\n");
118752130Smckusick #endif
118852130Smckusick 
118952130Smckusick 			regs->data = SCSI_NO_OP;
119052130Smckusick 			regs->comm = SII_INXFER | (comm & SII_STATE_MSK) |
119152130Smckusick 				SII_MSG_OUT_PHASE;
119252130Smckusick 			MachEmptyWriteBuffer();
119352130Smckusick 
119452130Smckusick 			/* wait a short time for XFER complete */
119552130Smckusick 			SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE,
119652130Smckusick 				SII_WAIT_COUNT, i);
119752130Smckusick #ifdef DEBUG
119852130Smckusick 			if (sii_debug > 4)
119952130Smckusick 				printf("ds %x i %d\n", dstat, i);
120052130Smckusick #endif
120152130Smckusick 			/* just clear the DNE bit and check errors later */
120252130Smckusick 			if (dstat & SII_DNE) {
120352130Smckusick 				regs->dstat = SII_DNE;
120452130Smckusick 				MachEmptyWriteBuffer();
120552130Smckusick 			}
120652130Smckusick 			break;
120752130Smckusick 
120852130Smckusick 		default:
120952130Smckusick 			printf("sii%d: Couldn't handle phase %d... ignoring.\n",
121052130Smckusick 				   sc - sii_softc, dstat & SII_PHASE_MSK);
121152130Smckusick 		}
121252130Smckusick 	}
121352130Smckusick 
121452130Smckusick #ifdef DEBUG
121552130Smckusick 	if (sii_debug > 3)
121652130Smckusick 		printf("\n");
121752130Smckusick #endif
121852130Smckusick 	/*
121952130Smckusick 	 * Check to make sure we won't be interrupted again.
122052130Smckusick 	 * Deglitch dstat register.
122152130Smckusick 	 */
122252130Smckusick 	msg = regs->dstat;
122352130Smckusick 	while (msg != (dstat = regs->dstat))
122452130Smckusick 		msg = dstat;
122552130Smckusick 	if (dstat & (SII_CI | SII_DI))
122652130Smckusick 		goto again;
122752130Smckusick 
122852130Smckusick 	if (sc->sc_target < 0) {
122952130Smckusick 		/* look for another device that is ready */
123052130Smckusick 		for (i = 0; i < SII_NCMD; i++) {
123152130Smckusick 			/* don't restart a disconnected command */
123252130Smckusick 			if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm)
123352130Smckusick 				continue;
123452130Smckusick 			sii_StartCmd(sc, i);
123552130Smckusick 			break;
123652130Smckusick 		}
123752130Smckusick 	}
123852130Smckusick 	return;
123952130Smckusick 
124052130Smckusick abort:
124152130Smckusick 	/* jump here to abort the current command */
124252130Smckusick 	printf("sii%d: device %d: current command terminated\n",
124352130Smckusick 		sc - sii_softc, sc->sc_target);
124452130Smckusick #ifdef DEBUG
124552130Smckusick 	sii_DumpLog();
124652130Smckusick #endif
124752130Smckusick 
124852130Smckusick 	if ((cstat = regs->cstat) & SII_CON) {
124952130Smckusick 		/* try to send an abort msg for awhile */
125052130Smckusick 		regs->dstat = SII_DNE;
125152130Smckusick 		regs->data = SCSI_ABORT;
125252130Smckusick 		regs->comm = SII_INXFER | SII_ATN | (cstat & SII_STATE_MSK) |
125352130Smckusick 			SII_MSG_OUT_PHASE;
125452130Smckusick 		MachEmptyWriteBuffer();
125552130Smckusick 		SII_WAIT_UNTIL(dstat, regs->dstat,
125652130Smckusick 			(dstat & (SII_DNE | SII_PHASE_MSK)) ==
125752130Smckusick 			(SII_DNE | SII_MSG_OUT_PHASE),
125852130Smckusick 			2 * SII_WAIT_COUNT, i);
125952130Smckusick #ifdef DEBUG
126052130Smckusick 		if (sii_debug > 0)
126152130Smckusick 			printf("Abort: cs %x ds %x i %d\n", cstat, dstat, i);
126252130Smckusick #endif
126352130Smckusick 		if (dstat & (SII_DNE | SII_PHASE_MSK) ==
126452130Smckusick 		    (SII_DNE | SII_MSG_OUT_PHASE)) {
126552130Smckusick 			/* disconnect if command in progress */
126652130Smckusick 			regs->comm = SII_DISCON;
126752130Smckusick 			MachEmptyWriteBuffer();
126852130Smckusick 			SII_WAIT_UNTIL(cstat, regs->cstat,
126952130Smckusick 				!(cstat & SII_CON), SII_WAIT_COUNT, i);
127052130Smckusick 		}
127152130Smckusick 	} else {
127252130Smckusick #ifdef DEBUG
127352130Smckusick 		if (sii_debug > 0)
127452130Smckusick 			printf("Abort: cs %x\n", cstat);
127552130Smckusick #endif
127652130Smckusick 	}
127752130Smckusick 	regs->cstat = 0xffff;
127852130Smckusick 	regs->dstat = 0xffff;
127952130Smckusick 	regs->comm = 0;
128052130Smckusick 	MachEmptyWriteBuffer();
128152130Smckusick 
128252130Smckusick 	i = sc->sc_target;
128352130Smckusick 	sc->sc_target = -1;
128452130Smckusick 	sii_CmdDone(sc, i, EIO);
128552130Smckusick #ifdef DEBUG
128652130Smckusick 	if (sii_debug > 4)
128752130Smckusick 		printf("sii_DoIntr: after CmdDone target %d\n", sc->sc_target);
128852130Smckusick #endif
128952130Smckusick }
129052130Smckusick 
129152130Smckusick static void
129252130Smckusick sii_StateChg(sc, cstat)
129352130Smckusick 	register struct siisoftc *sc;
129452130Smckusick 	register unsigned cstat;
129552130Smckusick {
129652130Smckusick 	register SIIRegs *regs = sc->sc_regs;
129752130Smckusick 	register State *state;
129852130Smckusick 	register int i;
129952130Smckusick 
130052130Smckusick #ifdef DEBUG
130152130Smckusick 	if (sii_debug > 4)
130252130Smckusick 		printf("SCH: ");
130352130Smckusick #endif
130452130Smckusick 
130552130Smckusick 	switch (cstat & SII_STATE_MSK) {
130652130Smckusick 	case 0:
130752130Smckusick 		/* disconnect */
130852130Smckusick 		i = sc->sc_target;
130952130Smckusick 		sc->sc_target = -1;
131052130Smckusick #ifdef DEBUG
131152130Smckusick 		if (sii_debug > 4)
131252130Smckusick 			printf("disconn %d ", i);
131352130Smckusick #endif
131452130Smckusick 		if (i >= 0 && !sc->sc_st[i].prevComm) {
131552130Smckusick 			printf("sii%d: device %d: spurrious disconnect (%d)\n",
131652130Smckusick 				sc - sii_softc, i, regs->slcsr);
131752130Smckusick 			sc->sc_st[i].prevComm = 0;
131852130Smckusick 		}
131952130Smckusick 		break;
132052130Smckusick 
132152130Smckusick 	case SII_CON:
132252130Smckusick 		/* connected as initiator */
132352130Smckusick 		i = regs->slcsr;
132452130Smckusick 		if (sc->sc_target == i)
132552130Smckusick 			break;
132652130Smckusick 		printf("sii%d: device %d: connect to device %d??\n",
132752130Smckusick 			sc - sii_softc, sc->sc_target, i);
132852130Smckusick 		sc->sc_target = i;
132952130Smckusick 		break;
133052130Smckusick 
133152130Smckusick 	case SII_DST:
133252130Smckusick 		/*
133352130Smckusick 		 * Wait for CON to become valid,
133452130Smckusick 		 * chip is slow sometimes.
133552130Smckusick 		 */
133652130Smckusick 		SII_WAIT_UNTIL(cstat, regs->cstat,
133752130Smckusick 			cstat & SII_CON, SII_WAIT_COUNT, i);
133852130Smckusick 		if (!(cstat & SII_CON))
133952130Smckusick 			panic("sii resel");
134052130Smckusick 		/* FALLTHROUGH */
134152130Smckusick 
134252130Smckusick 	case SII_CON | SII_DST:
134352130Smckusick 		/*
134452130Smckusick 		 * Its a reselection. Save the ID and wait for
134552130Smckusick 		 * interrupts to tell us what to do next
134652130Smckusick 		 * (should be MSG_IN of IDENTIFY).
134752130Smckusick 		 * NOTE: sc_target may be >= 0 if we were in
134852130Smckusick 		 * the process of trying to start a command
134952130Smckusick 		 * and were reselected before the select
135052130Smckusick 		 * command finished.
135152130Smckusick 		 */
135252130Smckusick 		sc->sc_target = i = regs->destat;
135352130Smckusick 		regs->comm = SII_CON | SII_DST | SII_MSG_IN_PHASE;
135452130Smckusick 		MachEmptyWriteBuffer();
135552130Smckusick 		state = &sc->sc_st[i];
135652130Smckusick 		if (!state->prevComm) {
135752130Smckusick 			printf("sii%d: device %d: spurrious reselection\n",
135852130Smckusick 				sc - sii_softc, i);
135952130Smckusick 			break;
136052130Smckusick 		}
136152130Smckusick 		state->prevComm = 0;
136252130Smckusick #ifdef DEBUG
136352130Smckusick 		if (sii_debug > 4)
136452130Smckusick 			printf("resel %d ", sc->sc_target);
136552130Smckusick #endif
136652130Smckusick 		break;
136752130Smckusick 
136852130Smckusick #ifdef notyet
136952130Smckusick 	case SII_DST | SII_TGT:
137052130Smckusick 	case SII_CON | SII_DST | SII_TGT:
137152130Smckusick 		/* connected as target */
137252130Smckusick 		printf("sii%d: Selected by device %d as target!!\n",
137352130Smckusick 			sc - sii_softc, regs->destat);
137452130Smckusick 		regs->comm = SII_DISCON;
137552130Smckusick 		MachEmptyWriteBuffer();
137652130Smckusick 		SII_WAIT_UNTIL(!(regs->cstat & SII_CON),
137752130Smckusick 			SII_WAIT_COUNT, i);
137852130Smckusick 		regs->cstat = 0xffff;
137952130Smckusick 		regs->dstat = 0xffff;
138052130Smckusick 		regs->comm = 0;
138152130Smckusick 		break;
138252130Smckusick #endif
138352130Smckusick 
138452130Smckusick 	default:
138552130Smckusick 		printf("sii%d: Unknown state change (cs %x)!!\n",
138652130Smckusick 			sc - sii_softc, cstat);
138752130Smckusick #ifdef DEBUG
138852130Smckusick 		sii_DumpLog();
138952130Smckusick #endif
139052130Smckusick 	}
139152130Smckusick }
139252130Smckusick 
139352130Smckusick /*
139452130Smckusick  * Read one byte of data.
139552130Smckusick  */
139652130Smckusick static int
139752130Smckusick sii_GetByte(regs, phase)
139852130Smckusick 	register SIIRegs *regs;
139952130Smckusick 	int phase;
140052130Smckusick {
140152130Smckusick 	register unsigned dstat;
140252130Smckusick 	register unsigned state;
140352130Smckusick #ifdef PROGXFER
140452130Smckusick 	register unsigned data;
140552130Smckusick 
140652130Smckusick 	dstat = regs->dstat;
140752130Smckusick 	state = regs->cstat & SII_STATE_MSK;
140852130Smckusick 	regs->dmctrl = 0;
140952130Smckusick 	if (!(dstat & SII_IBF) || (dstat & SII_MIS)) {
141052130Smckusick 		regs->comm = state | phase;
141152130Smckusick 		MachEmptyWriteBuffer();
141252130Smckusick 		/* wait a short time for IBF */
141352130Smckusick 		SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_IBF,
141452130Smckusick 			SII_WAIT_COUNT, i);
141552130Smckusick #ifdef DEBUG
141652130Smckusick 		if (!(dstat & SII_IBF))
141752130Smckusick 			printf("status no IBF\n");
141852130Smckusick #endif
141952130Smckusick 	}
142052130Smckusick 	data = regs->data;
142152130Smckusick 	if (regs->dstat & SII_DNE) { /* XXX */
142252130Smckusick 		printf("sii_GetByte: DNE set 5\n");
142352130Smckusick 		sii_DumpLog();
142452130Smckusick 		regs->dstat = SII_DNE;
142552130Smckusick 	}
142652130Smckusick 	regs->comm = SII_INXFER | state | phase;
142752130Smckusick 	MachEmptyWriteBuffer();
142852130Smckusick 
142952130Smckusick 	/* wait a short time for XFER complete */
143052130Smckusick 	SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE,
143152130Smckusick 		SII_WAIT_COUNT, i);
143252130Smckusick 
143352130Smckusick 	if ((dstat & (SII_DNE | SII_IPE)) != SII_DNE) {
143452130Smckusick #ifdef DEBUG
143552130Smckusick 		if (sii_debug > 4)
143652130Smckusick 			printf("cnt0 %d\n", i);
143752130Smckusick #endif
143852130Smckusick 		printf("MsgIn %x ?? ds %x cm %x i %d\n",
143952130Smckusick 			data, dstat, comm, i); /* XXX */
144052130Smckusick 		sii_DumpLog(); /* XXX */
144152130Smckusick 		panic("status"); /* XXX */
144252130Smckusick 		goto again;
144352130Smckusick 	}
144452130Smckusick 
144552130Smckusick 	/* clear the DNE, other errors handled later */
144652130Smckusick 	regs->dstat = SII_DNE;
144752130Smckusick 	MachEmptyWriteBuffer();
144852130Smckusick 	return (data);
144952130Smckusick 
145052130Smckusick #else /* PROGXFER */
145152130Smckusick 	register int i;
145252130Smckusick 
145352130Smckusick 	state = regs->cstat & SII_STATE_MSK;
145452130Smckusick 	regs->dmctrl = 0;
145552130Smckusick 	if (regs->dstat & SII_DNE) {
145652130Smckusick 		printf("sii_GetByte: DNE cs %x ds %x cm %x\n",
145752130Smckusick 			regs->cstat, regs->dstat, regs->comm); /* XXX */
145852130Smckusick 		regs->dstat = SII_DNE;
145952130Smckusick 	}
146052130Smckusick 	regs->dmaddrl = ((unsigned)SII_BUF_ADDR >> 1);
146152130Smckusick 	regs->dmaddrh = ((unsigned)SII_BUF_ADDR >> 17) & 03;
146252130Smckusick 	regs->dmlotc = 1;
146352130Smckusick 	regs->comm = SII_DMA | SII_INXFER | state | phase;
146452130Smckusick 	MachEmptyWriteBuffer();
146552130Smckusick 
146652130Smckusick 	/* wait a short time for XFER complete */
146752130Smckusick 	SII_WAIT_UNTIL(dstat, regs->dstat,
146852130Smckusick 		(dstat & (SII_DNE | SII_TCZ)) == (SII_DNE | SII_TCZ),
146952130Smckusick 		SII_WAIT_COUNT, i);
147052130Smckusick 
147152130Smckusick 	if ((dstat & (SII_DNE | SII_TCZ | SII_IPE)) != (SII_DNE | SII_TCZ)) {
147252130Smckusick 		printf("sii_GetByte: ds %x cm %x i %d lotc %d\n",
147352130Smckusick 			dstat, regs->comm, i, regs->dmlotc); /* XXX */
147452130Smckusick 		sii_DumpLog(); /* XXX */
147552130Smckusick 		return (-1);
147652130Smckusick 	}
147752130Smckusick 
147852130Smckusick 	/* clear the DNE, other errors handled later */
147952130Smckusick 	regs->dstat = SII_DNE;
148052130Smckusick 	MachEmptyWriteBuffer();
148152130Smckusick 
148252130Smckusick 	/* return one byte of data (optimized CopyFromBuffer()) */
148352130Smckusick 	return (*(volatile u_short *)SII_BUF_ADDR & 0xFF);
148452130Smckusick #endif /* PROGXFER */
148552130Smckusick }
148652130Smckusick 
148752130Smckusick /*
148852130Smckusick  * Exchange messages to initiate synchronous data transfers.
148952130Smckusick  */
149052130Smckusick static void
149152130Smckusick sii_DoSync(regs, state)
149252130Smckusick 	register SIIRegs *regs;
149352130Smckusick 	register State *state;
149452130Smckusick {
149552130Smckusick 	register unsigned dstat;
149652130Smckusick 	register int i;
149752130Smckusick 	unsigned len;
149852130Smckusick 
149952130Smckusick 	printf("sii_DoSync: per %d req/ack %d\n",
150052130Smckusick 		sii_buf[3], sii_buf[4]); /* XXX */
150152130Smckusick 	len = sii_buf[4];
150252130Smckusick 	if (len > 3)
150352130Smckusick 		len = 3;	/* SII chip can only handle 3 max */
150452130Smckusick 
150552130Smckusick 	sii_buf[0] = SCSI_EXTENDED_MSG;
150652130Smckusick 	sii_buf[1] = 3;		/* message length */
150752130Smckusick 	sii_buf[2] = SCSI_SYNCHRONOUS_XFER;
150852130Smckusick 	sii_buf[4] = len;
150952130Smckusick 	CopyToBuffer((u_short *)sii_buf, (volatile u_short *)SII_BUF_ADDR, 5);
151052130Smckusick 	regs->dmaddrl = ((unsigned)SII_BUF_ADDR >> 1);
151152130Smckusick 	regs->dmaddrh = ((unsigned)SII_BUF_ADDR >> 17) & 03;
151252130Smckusick 	regs->dmlotc = 5;
151352130Smckusick 	regs->comm = SII_DMA | SII_INXFER | SII_ATN |
151452130Smckusick 		(regs->cstat & SII_STATE_MSK) | SII_MSG_OUT_PHASE;
151552130Smckusick 	MachEmptyWriteBuffer();
151652130Smckusick 
151752130Smckusick 	/* wait a short time for XFER complete */
151852130Smckusick 	SII_WAIT_UNTIL(dstat, regs->dstat,
151952130Smckusick 		(dstat & (SII_DNE | SII_TCZ)) == (SII_DNE | SII_TCZ),
152052130Smckusick 		SII_WAIT_COUNT, i);
152152130Smckusick 
152252130Smckusick 	if ((dstat & (SII_DNE | SII_TCZ)) != (SII_DNE | SII_TCZ)) {
152352130Smckusick 		printf("sii_DoSync: ds %x cm %x i %d lotc %d\n",
152452130Smckusick 			dstat, regs->comm, i, regs->dmlotc); /* XXX */
152552130Smckusick 		sii_DumpLog(); /* XXX */
152652130Smckusick 		return;
152752130Smckusick 	}
152852130Smckusick 
152952130Smckusick 	/* clear the DNE, other errors handled later */
153052130Smckusick 	regs->dstat = SII_DNE;
153152130Smckusick 	regs->comm = regs->comm & SII_STATE_MSK;
153252130Smckusick 	MachEmptyWriteBuffer();
153352130Smckusick 
153452130Smckusick 	state->dmaReqAck = len;
153552130Smckusick }
153652130Smckusick 
153752130Smckusick /*
153852130Smckusick  * Issue the sequence of commands to the controller to start DMA.
153952130Smckusick  * NOTE: the data buffer should be word-aligned for DMA out.
154052130Smckusick  */
154152130Smckusick static void
154252130Smckusick sii_StartDMA(regs, phase, dmaAddr, size)
154352130Smckusick 	register SIIRegs *regs;	/* which SII to use */
154452130Smckusick 	int phase;		/* phase to send/receive data */
154552130Smckusick 	u_short *dmaAddr;	/* DMA buffer address */
154652130Smckusick 	int size;		/* # of bytes to transfer */
154752130Smckusick {
154852130Smckusick 
154952130Smckusick 	if (regs->dstat & SII_DNE) { /* XXX */
155052130Smckusick 		printf("sii_StartDMA: DNE set\n");
155152130Smckusick 		sii_DumpLog();
155252130Smckusick 		regs->dstat = SII_DNE;
155352130Smckusick 	}
155452130Smckusick 	regs->dmaddrl = ((unsigned)dmaAddr >> 1);
155552130Smckusick 	regs->dmaddrh = ((unsigned)dmaAddr >> 17) & 03;
155652130Smckusick 	regs->dmlotc = size;
155752130Smckusick 	regs->comm = SII_DMA | SII_INXFER | (regs->cstat & SII_STATE_MSK) |
155852130Smckusick 		phase;
155952130Smckusick 	MachEmptyWriteBuffer();
156052130Smckusick 
156152130Smckusick #ifdef DEBUG
156252130Smckusick 	if (sii_debug > 5) {
156352130Smckusick 		printf("sii_StartDMA: cs 0x%x, ds 0x%x, cm 0x%x, size %d\n",
156452130Smckusick 			regs->cstat, regs->dstat, regs->comm, size);
156552130Smckusick 	}
156652130Smckusick #endif
156752130Smckusick }
156852130Smckusick 
156952130Smckusick /*
157052130Smckusick  * Call the device driver's 'done' routine to let it know the command is done.
157152130Smckusick  * The 'done' routine may try to start another command.
157252130Smckusick  * To be fair, we should start pending commands for other devices
157352130Smckusick  * before allowing the same device to start another command.
157452130Smckusick  */
157552130Smckusick static void
157652130Smckusick sii_CmdDone(sc, target, error)
157752130Smckusick 	register struct siisoftc *sc;	/* which SII to use */
157852130Smckusick 	int target;			/* which device is done */
157952130Smckusick 	int error;			/* error code if any errors */
158052130Smckusick {
158152130Smckusick 	register ScsiCmd *scsicmd;
158252130Smckusick 	register int i;
158352130Smckusick 
158452130Smckusick 	scsicmd = sc->sc_cmd[target];
158552130Smckusick #ifdef DIAGNOSTIC
158652130Smckusick 	if (target < 0 || !scsicmd)
158752130Smckusick 		panic("sii_CmdDone");
158852130Smckusick #endif
158952130Smckusick 	sc->sc_cmd[target] = (ScsiCmd *)0;
159052130Smckusick #ifdef DEBUG
159152130Smckusick 	if (sii_debug > 1) {
159252130Smckusick 		printf("sii_CmdDone: %s target %d cmd %x err %d resid %d\n",
159352130Smckusick 			scsicmd->sd->sd_driver->d_name, target,
159452130Smckusick 			scsicmd->cmd[0], error, sc->sc_st[target].buflen);
159552130Smckusick 	}
159652130Smckusick #endif
159752130Smckusick 
159852130Smckusick 	/* look for another device that is ready */
159952130Smckusick 	for (i = 0; i < SII_NCMD; i++) {
160052130Smckusick 		/* don't restart a disconnected command */
160152130Smckusick 		if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm)
160252130Smckusick 			continue;
160352130Smckusick 		sii_StartCmd(sc, i);
160452130Smckusick 		break;
160552130Smckusick 	}
160652130Smckusick 
160752130Smckusick 	(*scsicmd->sd->sd_driver->d_done)(scsicmd->unit, error,
160852130Smckusick 		sc->sc_st[target].buflen, sc->sc_st[target].statusByte);
160952130Smckusick }
161052130Smckusick 
161152130Smckusick #ifdef DEBUG
161252130Smckusick sii_DumpLog()
161352130Smckusick {
161452130Smckusick 	register struct sii_log *lp;
161552130Smckusick 
161652130Smckusick 	printf("sii: cmd %x bn %d cnt %d\n", sii_debug_cmd, sii_debug_bn,
161752130Smckusick 		sii_debug_sz);
161852130Smckusick 	lp = sii_logp + 1;
161952130Smckusick 	if (lp > &sii_log[NLOG])
162052130Smckusick 		lp = sii_log;
162152130Smckusick 	while (lp != sii_logp) {
162252130Smckusick 		printf("target %d cs %x ds %x cm %x msg %x\n",
162352130Smckusick 			lp->target, lp->cstat, lp->dstat, lp->comm, lp->msg);
162452130Smckusick 		if (++lp >= &sii_log[NLOG])
162552130Smckusick 			lp = sii_log;
162652130Smckusick 	}
162752130Smckusick }
162852130Smckusick #endif
162952130Smckusick #endif
1630