/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Ralph Campbell and Rick Macklem.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)sii.c 8.2 (Berkeley) 11/30/93
*
* from: Header: /sprite/src/kernel/dev/ds3100.md/RCS/devSII.c,
* v 9.2 89/09/14 13:37:41 jhh Exp $ SPRITE (DECWRL)";
*/
/* XXX not in dev/scsipi/scsi_message.h */
#define MSG_EXT_MODIFY_DATA_PTR 0x00
extern struct cfdriver sii_cd;
/*
* MACROS for timing out spin loops.
*
* Wait until expression is true.
*
* Control register bits can change at any time so when the CPU
* reads a register, the bits might change and
* invalidate the setup and hold times for the CPU.
* This macro reads the register twice to be sure the value is stable.
*
* args: var - variable to save control register contents
* reg - control register to read
* expr - expression to spin on
* spincount - maximum number of times through the loop
* cntr - variable for number of tries
*/
#define SII_WAIT_UNTIL(var, reg, expr, spincount, cntr) { \
u_int tmp = reg; \
for (cntr = 0; cntr < spincount; cntr++) { \
while (tmp != (var = reg)) \
tmp = var; \
if (expr) \
break; \
if (cntr >= 100) \
DELAY(100); \
} \
}
#ifdef DEBUG
int sii_debug = 1;
int sii_debug_cmd;
int sii_debug_bn;
int sii_debug_sz;
#define NLOG 16
struct sii_log {
u_short cstat;
u_short dstat;
u_short comm;
u_short msg;
int rlen;
int dlen;
int target;
} sii_log[NLOG], *sii_logp = sii_log;
#endif
static u_char sii_buf[256]; /* used for extended messages */
/*
* Define a safe address in the SCSI buffer for doing status & message DMA
* XXX why not add another field to softc?
*/
#define SII_BUF_ADDR(sc) ((sc)->sc_buf + SII_MAX_DMA_XFER_LENGTH * 14)
/*
* Forward references
*/
static void sii_Reset(struct siisoftc *sc, int resetbus);
static void sii_StartCmd(struct siisoftc *sc, int target);
static void sii_CmdDone(struct siisoftc *sc, int target, int error);
static void sii_DoIntr(struct siisoftc *sc, u_int dstat);
static void sii_StateChg(struct siisoftc *sc, u_int cstat);
static int sii_GetByte(SIIRegs *regs, int phase, int ack);
static void sii_DoSync(SIIRegs *regs, State *state);
static void sii_StartDMA(SIIRegs *regs, int phase, u_short *dmaAddr,
int size);
/*
* Match driver based on name
*/
void
siiattach(struct siisoftc *sc)
{
int i;
sc->sc_target = -1; /* no command active */
/*
* Give each target its own DMA buffer region.
* Make it big enough for 2 max transfers so we can ping pong buffers
* while we copy the data.
*/
for (i = 0; i < SII_NCMD; i++) {
sc->sc_st[i].dmaAddr[0] = (u_short *)
sc->sc_buf + 2 * SII_MAX_DMA_XFER_LENGTH * i;
sc->sc_st[i].dmaAddr[1] = sc->sc_st[i].dmaAddr[0] +
SII_MAX_DMA_XFER_LENGTH;
}
/*
* Now try to attach all the sub-devices
*/
config_found(sc->sc_dev, &sc->sc_channel, scsiprint, CFARGS_NONE);
}
/*
* Start activity on a SCSI device.
* We maintain information on each device separately since devices can
* connect/disconnect during an operation.
*/
void
sii_scsi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg)
{
struct scsipi_xfer *xs;
struct scsipi_periph *periph;
struct siisoftc *sc;
int target;
int s;
int count;
switch (req) {
case ADAPTER_REQ_RUN_XFER:
xs = arg;
periph = xs->xs_periph;
target = periph->periph_target;
s = splbio();
if (sc->sc_cmd[target]) {
splx(s);
xs->error = XS_RESOURCE_SHORTAGE;
scsipi_done(xs);
printf("[busy at start]\n");
return;
}
/*
* Build a ScsiCmd for this command and start it.
*/
sc->sc_xs[target] = xs;
sc->sc_cmd[target] = &sc->sc_cmd_fake[target]; /* XXX */
sc->sc_cmd[target]->unit = 0;
sc->sc_cmd[target]->flags = 0;
sc->sc_cmd[target]->buflen = xs->datalen;
sc->sc_cmd[target]->buf = xs->data;
sc->sc_cmd[target]->cmdlen = xs->cmdlen;
sc->sc_cmd[target]->cmd = (u_char *)xs->cmd;
sc->sc_cmd[target]->lun = xs->xs_periph->periph_lun;
sii_StartCmd(sc, target);
splx(s);
if ((xs->xs_control & XS_CTL_POLL) == 0)
return;
count = xs->timeout;
while (count) {
if ((xs->xs_status & XS_STS_DONE) != 0)
return;
siiintr(sc);
/* XXX schedule another command? */
DELAY(1000);
--count;
}
xs->error = XS_TIMEOUT;
scsipi_done(xs);
return;
case ADAPTER_REQ_GROW_RESOURCES:
/* XXX Not supported. */
return;
case ADAPTER_REQ_SET_XFER_MODE:
/* XXX Not supported. */
return;
}
}
/*
* Check to see if any SII chips have pending interrupts
* and process as appropriate.
*/
int
siiintr(void *xxxsc)
{
struct siisoftc *sc = xxxsc;
u_int dstat;
/*
* Find which controller caused the interrupt.
*/
dstat = sc->sc_regs->dstat;
if (dstat & (SII_CI | SII_DI)) {
sii_DoIntr(sc, dstat);
return (0); /* XXX */
}
return (1); /* XXX spurious interrupt? */
}
/*
* Reset the SII chip and do a SCSI reset if 'reset' is true.
* NOTE: if !cold && reset, should probably probe for devices
* since a SCSI bus reset will set UNIT_ATTENTION.
*/
static void
sii_Reset(struct siisoftc* sc, int reset)
/* reset: TRUE => reset SCSI bus */
{
SIIRegs *regs = sc->sc_regs;
#ifdef DEBUG
if (sii_debug > 1)
printf("sii: RESET\n");
#endif
/*
* Reset the SII chip.
*/
regs->comm = SII_CHRESET;
/*
* Set arbitrated bus mode.
*/
regs->csr = SII_HPM;
/*
* Set host adapter ID (from PROM sciiidN variable).
*/
/* XXX device_unit() abuse */
regs->id = SII_ID_IO | prom_scsiid(device_unit(sc->sc_dev));
/*
* Enable SII to drive the SCSI bus.
*/
regs->dictrl = SII_PRE;
regs->dmctrl = 0;
if (reset) {
int i;
/*
* Assert SCSI bus reset for at least 25 Usec to clear the
* world. SII_DO_RST is self clearing.
* Delay 250 ms before doing any commands.
*/
regs->comm = SII_DO_RST;
wbflush();
DELAY(250000);
/* rearbitrate synchronous offset */
for (i = 0; i < SII_NCMD; i++)
sc->sc_st[i].dmaReqAck = 0;
}
/*
* Clear any pending interrupts from the reset.
*/
regs->cstat = regs->cstat;
regs->dstat = regs->dstat;
/*
* Set up SII for arbitrated bus mode, SCSI parity checking,
* Reselect Enable, and Interrupt Enable.
*/
regs->csr = SII_HPM | SII_RSE | SII_PCE | SII_IE;
wbflush();
}
/*
* Start a SCSI command by sending the cmd data
* to a SCSI controller via the SII.
* Call the device done procedure if it can't be started.
* NOTE: we should be called with interrupts disabled.
*/
static void
sii_StartCmd(struct siisoftc *sc, int target)
/* sc: which SII to use */
/* target: which command to start */
{
SIIRegs *regs;
ScsiCmd *scsicmd;
State *state;
u_int status;
int error, retval;
/* if another command is currently in progress, just wait */
if (sc->sc_target >= 0)
return;
/* try to select the target */
regs = sc->sc_regs;
/*
* Another device may have selected us; in which case,
* this command will be restarted later.
*/
if ((status = regs->dstat) & (SII_CI | SII_DI)) {
sii_DoIntr(sc, status);
return;
}
sc->sc_target = target;
#if 0
/* seem to have problems with synchronous transfers */
if (scsicmd->flags & SCSICMD_USE_SYNC) {
printf("sii_StartCmd: doing extended msg\n"); /* XXX */
/*
* Setup to send both the identify message and the synchronous
* data transfer request.
*/
sii_buf[0] = MSG_IDENTIFYFLAG | MSG_IDENTIFY_DISCFLAG;
sii_buf[1] = MSG_EXTENDED;
sii_buf[2] = MSG_EXT_SDTR_LEN;
sii_buf[3] = MSG_EXT_SDTR;
sii_buf[4] = 0;
sii_buf[5] = 3; /* maximum SII chip supports */
/*
* Wait for something to happen
* (should happen soon or we would use interrupts).
*/
SII_WAIT_UNTIL(status, regs->cstat, status & (SII_CI | SII_DI),
SII_WAIT_COUNT/4, retval);
/* check to see if we are connected OK */
if ((status & (SII_RST | SII_SCH | SII_STATE_MSK)) ==
(SII_SCH | SII_CON)) {
regs->cstat = status;
wbflush();
/* wait a little while for DMA to finish */
SII_WAIT_UNTIL(status, regs->dstat, status & (SII_CI | SII_DI),
SII_WAIT_COUNT, retval);
#ifdef DEBUG
if (sii_debug > 2)
printf("sii_StartCmd: ds %x, cnt %d\n", status, retval);
#endif
if (status & (SII_CI | SII_DI))
sii_DoIntr(sc, status);
#ifdef DEBUG
if (sii_debug > 2)
printf("sii_StartCmd: DONE ds %x\n", regs->dstat);
#endif
return;
}
/*
* Another device may have selected us; in which case,
* this command will be restarted later.
*/
if (status & (SII_CI | SII_DI)) {
sii_DoIntr(sc, regs->dstat);
return;
}
/*
* Disconnect if selection command still in progress.
*/
if (status & SII_SIP) {
error = ENXIO; /* device didn't respond */
regs->comm = SII_DISCON;
wbflush();
SII_WAIT_UNTIL(status, regs->cstat,
!(status & (SII_CON | SII_SIP)),
SII_WAIT_COUNT, retval);
} else
error = EBUSY; /* couldn't get the bus */
#ifdef DEBUG
if (sii_debug > 1)
printf("sii_StartCmd: Couldn't select target %d error %d\n",
target, error);
#endif
sc->sc_target = -1;
regs->cstat = 0xffff;
regs->dstat = 0xffff;
regs->comm = 0;
wbflush();
sii_CmdDone(sc, target, error);
}
/*
* Process interrupt conditions.
*/
static void
sii_DoIntr(struct siisoftc *sc, u_int dstat)
{
SIIRegs *regs = sc->sc_regs;
State *state;
u_int cstat;
int i, msg;
u_int comm;
/* check for a BUS RESET */
if (cstat & SII_RST) {
printf("%s: SCSI bus reset!!\n",
device_xname(sc->sc_dev));
/* need to flush disconnected commands */
for (i = 0; i < SII_NCMD; i++) {
if (!sc->sc_cmd[i])
continue;
sii_CmdDone(sc, i, EIO);
}
/* rearbitrate synchronous offset */
for (i = 0; i < SII_NCMD; i++)
sc->sc_st[i].dmaReqAck = 0;
sc->sc_target = -1;
return;
}
#ifdef notdef
/*
* Check for a BUS ERROR.
* According to DEC, this feature doesn't really work
* and to just clear the bit if it's set.
*/
if (cstat & SII_BER) {
regs->cstat = SII_BER;
wbflush();
}
#endif
/* check for state change */
if (cstat & SII_SCH) {
sii_StateChg(sc, cstat);
comm = regs->comm;
}
}
/* check for DMA completion */
if (dstat & SII_DNE) {
u_short *dma;
char *buf;
/*
* There is a race condition with SII_SCH. There is a short
* window between the time a SII_SCH is seen after a disconnect
* and when the SII_SCH is cleared. A reselect can happen
* in this window and we will clear the SII_SCH without
* processing the reconnect.
*/
if (sc->sc_target < 0) {
cstat = regs->cstat;
printf("%s: target %d DNE?? dev %d,%d cs %x\n",
device_xname(sc->sc_dev), sc->sc_target,
regs->slcsr, regs->destat,
cstat); /* XXX */
if (cstat & SII_DST) {
sc->sc_target = regs->destat;
state = &sc->sc_st[sc->sc_target];
state->prevComm = 0;
} else
panic("sc_target 1");
}
state = &sc->sc_st[sc->sc_target];
/* check for a PARITY ERROR */
if (dstat & SII_IPE) {
state->flags |= PARITY_ERR;
printf("%s: Parity error!!\n",
device_xname(sc->sc_dev));
goto abort;
}
/* dmalen = amount left to transfer, i = amount transferred */
i = state->dmalen;
state->dmalen = 0;
state->dmaCurPhase = -1;
#ifdef DEBUG
if (sii_debug > 4) {
printf("DNE: amt %d ", i);
if (!(dstat & SII_TCZ))
printf("no TCZ?? (%d) ", regs->dmlotc);
} else if (!(dstat & SII_TCZ)) {
printf("%s: device %d: no TCZ?? (%d)\n",
device_xname(sc->sc_dev),
sc->sc_target, regs->dmlotc);
sii_DumpLog(); /* XXX */
}
#endif
switch (comm & SII_PHASE_MSK) {
case SII_CMD_PHASE:
state->cmdlen -= i;
break;
case SII_DATA_IN_PHASE:
/* check for more data for the same phase */
dma = state->dmaAddr[state->dmaBufIndex];
buf = state->buf;
state->buf += i;
state->buflen -= i;
if (state->buflen > 0 && !(dstat & SII_MIS)) {
int len;
/* start reading next chunk */
len = state->buflen;
if (len > SII_MAX_DMA_XFER_LENGTH)
len = SII_MAX_DMA_XFER_LENGTH;
state->dmaBufIndex = !state->dmaBufIndex;
sii_StartDMA(regs,
state->dmaCurPhase = SII_DATA_IN_PHASE,
state->dmaAddr[state->dmaBufIndex],
state->dmaCnt = state->dmalen = len);
dstat &= ~(SII_IBF | SII_TBE);
}
/* copy in the data */
sc->sii_copyfrombuf((volatile u_short *)dma, buf, i);
break;
default:
if (!(msg & MSG_IDENTIFYFLAG)) {
printf("%s: device %d: couldn't handle "
"message 0x%x... rejecting.\n",
device_xname(sc->sc_dev),
sc->sc_target,
msg);
#ifdef DEBUG
sii_DumpLog();
#endif
goto reject;
}
/* acknowledge last byte */
regs->comm = SII_INXFER | SII_MSG_IN_PHASE |
(comm & SII_STATE_MSK);
SII_WAIT_UNTIL(dstat, regs->dstat,
dstat & SII_DNE, SII_WAIT_COUNT, i);
regs->dstat = SII_DNE;
wbflush();
/* may want to check LUN some day */
/* wait a short time for another msg */
SII_WAIT_UNTIL(dstat, regs->dstat,
dstat & (SII_CI | SII_DI),
SII_WAIT_COUNT, i);
if (dstat & (SII_CI | SII_DI)) {
#ifdef DEBUG
if (sii_debug > 4)
printf("cnt %d\n", i);
#endif
goto again;
}
}
break;
case SII_MSG_OUT_PHASE:
#ifdef DEBUG
if (sii_debug > 4)
printf("MsgOut\n");
#endif
printf("MsgOut %x\n", state->flags); /* XXX */
/*
* Check for parity error.
* Hardware will automatically set ATN
* to request the device for a MSG_OUT phase.
*/
if (state->flags & PARITY_ERR) {
state->flags &= ~PARITY_ERR;
regs->data = MSG_PARITY_ERROR;
} else
regs->data = MSG_NOOP;
regs->comm = SII_INXFER | (comm & SII_STATE_MSK) |
SII_MSG_OUT_PHASE;
wbflush();
/* wait a short time for XFER complete */
SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE,
SII_WAIT_COUNT, i);
#ifdef DEBUG
if (sii_debug > 4)
printf("ds %x i %d\n", dstat, i);
#endif
/* just clear the DNE bit and check errors later */
if (dstat & SII_DNE) {
regs->dstat = SII_DNE;
wbflush();
}
break;
#ifdef DEBUG
if (sii_debug > 3)
printf("\n");
#endif
/*
* Check to make sure we won't be interrupted again.
* Deglitch dstat register.
*/
msg = regs->dstat;
while (msg != (dstat = regs->dstat))
msg = dstat;
if (dstat & (SII_CI | SII_DI))
goto again;
if (sc->sc_target < 0) {
/* look for another device that is ready */
for (i = 0; i < SII_NCMD; i++) {
/* don't restart a disconnected command */
if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm)
continue;
sii_StartCmd(sc, i);
break;
}
}
return;
abort:
/* jump here to abort the current command */
printf("%s: device %d: current command terminated\n",
device_xname(sc->sc_dev), sc->sc_target);
#ifdef DEBUG
sii_DumpLog();
#endif
i = sc->sc_target;
sc->sc_target = -1;
sii_CmdDone(sc, i, EIO);
#ifdef DEBUG
if (sii_debug > 4)
printf("sii_DoIntr: after CmdDone target %d\n", sc->sc_target);
#endif
}
static void
sii_StateChg(struct siisoftc *sc, u_int cstat)
{
SIIRegs *regs = sc->sc_regs;
State *state;
int i;
#ifdef DEBUG
if (sii_debug > 4)
printf("SCH: ");
#endif
switch (cstat & SII_STATE_MSK) {
case 0:
/* disconnect */
i = sc->sc_target;
sc->sc_target = -1;
#ifdef DEBUG
if (sii_debug > 4)
printf("disconn %d ", i);
#endif
if (i >= 0 && !sc->sc_st[i].prevComm) {
printf("%s: device %d: spurrious disconnect (%d)\n",
device_xname(sc->sc_dev), i, regs->slcsr);
sc->sc_st[i].prevComm = 0;
}
break;
case SII_CON:
/* connected as initiator */
i = regs->slcsr;
if (sc->sc_target == i)
break;
printf("%s: device %d: connect to device %d??\n",
device_xname(sc->sc_dev), sc->sc_target, i);
sc->sc_target = i;
break;
case SII_DST:
/*
* Wait for CON to become valid,
* chip is slow sometimes.
*/
SII_WAIT_UNTIL(cstat, regs->cstat,
cstat & SII_CON, SII_WAIT_COUNT, i);
if (!(cstat & SII_CON))
panic("sii resel");
/* FALLTHROUGH */
case SII_CON | SII_DST:
/*
* Its a reselection. Save the ID and wait for
* interrupts to tell us what to do next
* (should be MSG_IN of IDENTIFY).
* NOTE: sc_target may be >= 0 if we were in
* the process of trying to start a command
* and were reselected before the select
* command finished.
*/
sc->sc_target = i = regs->destat;
state = &sc->sc_st[i];
regs->comm = SII_CON | SII_DST | SII_MSG_IN_PHASE;
regs->dmctrl = state->dmaReqAck;
wbflush();
if (!state->prevComm) {
printf("%s: device %d: spurious reselection\n",
device_xname(sc->sc_dev), i);
break;
}
state->prevComm = 0;
#ifdef DEBUG
if (sii_debug > 4)
printf("resel %d ", sc->sc_target);
#endif
break;
#ifdef notyet
case SII_DST | SII_TGT:
case SII_CON | SII_DST | SII_TGT:
/* connected as target */
printf("%s: Selected by device %d as target!!\n",
device_xname(sc->sc_dev), regs->destat);
regs->comm = SII_DISCON;
wbflush();
SII_WAIT_UNTIL(!(regs->cstat & SII_CON),
SII_WAIT_COUNT, i);
regs->cstat = 0xffff;
regs->dstat = 0xffff;
regs->comm = 0;
break;
#endif
/*
* Read one byte of data.
* If 'ack' is true, acknowledge the byte.
*/
static int
sii_GetByte(SIIRegs *regs, int phase, int ack)
{
u_int dstat;
u_int state;
int i;
int data;
dstat = regs->dstat;
state = regs->cstat & SII_STATE_MSK;
i = -1;
if (!(dstat & SII_IBF) || (dstat & SII_MIS)) {
regs->comm = state | phase;
wbflush();
/* wait a short time for IBF */
SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_IBF,
SII_WAIT_COUNT, i);
#ifdef DEBUG
if (!(dstat & SII_IBF))
printf("status no IBF\n");
#endif
}
if (dstat & SII_DNE) { /* XXX */
printf("sii_GetByte: DNE set 5\n");
#ifdef DEBUG
sii_DumpLog();
#endif
regs->dstat = SII_DNE;
}
data = regs->data;
/* check for parity error */
if (dstat & SII_IPE) {
#ifdef DEBUG
if (sii_debug > 4)
printf("cnt0 %d\n", i);
#endif
printf("sii_GetByte: data %x ?? ds %x cm %x i %d\n",
data, dstat, regs->comm, i); /* XXX */
data = -1;
ack = 1;
}
if (ack) {
regs->comm = SII_INXFER | state | phase;
wbflush();
/* wait a short time for XFER complete */
SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_DNE,
SII_WAIT_COUNT, i);
/* clear the DNE */
if (dstat & SII_DNE) {
regs->dstat = SII_DNE;
wbflush();
}
}
return (data);
}
/*
* Exchange messages to initiate synchronous data transfers.
*/
static void
sii_DoSync(SIIRegs *regs, State *state)
{
u_int dstat, comm;
int i, j;
u_int len;
#ifdef DEBUG
if (sii_debug)
printf("sii_DoSync: len %d per %d req/ack %d\n",
sii_buf[1], sii_buf[3], sii_buf[4]);
#endif
/* SII chip can only handle a minimum transfer period of ??? */
if (sii_buf[3] < 64)
sii_buf[3] = 64;
/* SII chip can only handle a maximum REQ/ACK offset of 3 */
len = sii_buf[4];
if (len > 3)
len = 3;
sii_buf[0] = MSG_EXTENDED;
sii_buf[1] = MSG_EXT_SDTR_LEN;
sii_buf[2] = MSG_EXT_SDTR;
sii_buf[4] = len;
#if 1
comm = SII_INXFER | SII_ATN | SII_MSG_OUT_PHASE |
(regs->cstat & SII_STATE_MSK);
regs->comm = comm & ~SII_INXFER;
for (j = 0; j < 5; j++) {
/* wait for target to request the next byte */
SII_WAIT_UNTIL(dstat, regs->dstat, dstat & SII_TBE,
SII_WAIT_COUNT, i);
if (!(dstat & SII_TBE) ||
(dstat & SII_PHASE_MSK) != SII_MSG_OUT_PHASE) {
printf("sii_DoSync: TBE? ds %x cm %x i %d\n",
dstat, comm, i); /* XXX */
return;
}
/* the last message byte should have ATN off */
if (j == 4)
comm &= ~SII_ATN;
/* wait a short time for XFER complete */
SII_WAIT_UNTIL(dstat, regs->dstat,
(dstat & (SII_DNE | SII_TCZ)) == (SII_DNE | SII_TCZ),
SII_WAIT_COUNT, i);
if ((dstat & (SII_DNE | SII_TCZ)) != (SII_DNE | SII_TCZ)) {
printf("sii_DoSync: ds %x cm %x i %d lotc %d\n",
dstat, regs->comm, i, regs->dmlotc); /* XXX */
sii_DumpLog(); /* XXX */
return;
}
/* clear the DNE, other errors handled later */
regs->dstat = SII_DNE;
wbflush();
#endif /* 0 */
#if 0
SII_WAIT_UNTIL(dstat, regs->dstat, dstat & (SII_CI | SII_DI),
SII_WAIT_COUNT, i);
printf("sii_DoSync: ds %x cm %x i %d lotc %d\n",
dstat, regs->comm, i, regs->dmlotc); /* XXX */
#endif
state->dmaReqAck = len;
}
/*
* Issue the sequence of commands to the controller to start DMA.
* NOTE: the data buffer should be word-aligned for DMA out.
*/
static void
sii_StartDMA(SIIRegs *regs, int phase, u_short *dmaAddr, int size)
/* regs: which SII to use */
/* phase: phase to send/receive data */
/* dmaAddr: DMA buffer address */
/* size: # of bytes to transfer */
{
/*
* Call the device driver's 'done' routine to let it know the command is done.
* The 'done' routine may try to start another command.
* To be fair, we should start pending commands for other devices
* before allowing the same device to start another command.
*/
static void
sii_CmdDone(struct siisoftc *sc, int target, int error)
/* sc: which SII to use */
/* target: which device is done */
/* error: error code if any errors */
{
int i;
/* look for another device that is ready */
for (i = 0; i < SII_NCMD; i++) {
/* don't restart a disconnected command */
if (!sc->sc_cmd[i] || sc->sc_st[i].prevComm)
continue;
sii_StartCmd(sc, i);
break;
}
sc->sc_xs[target]->status = sc->sc_st[target].statusByte;
/*
* Convert SII driver error code to MI SCSI XS_*.
*/
switch (error) {
case 0:
sc->sc_xs[target]->error = XS_NOERROR;
break;
case ENXIO:
sc->sc_xs[target]->error = XS_SELTIMEOUT;
break;
case EBUSY:
sc->sc_xs[target]->error = XS_BUSY;
break;
case EIO:
sc->sc_xs[target]->error = XS_DRIVER_STUFFUP;
break;
default:
sc->sc_xs[target]->error = XS_DRIVER_STUFFUP;
}
sc->sc_xs[target]->resid = sc->sc_st[target].buflen;
scsipi_done(sc->sc_xs[target]);
}