summaryrefslogtreecommitdiff
path: root/sys/dev/ic/ncr53c9x.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ic/ncr53c9x.c')
-rw-r--r--sys/dev/ic/ncr53c9x.c185
1 files changed, 130 insertions, 55 deletions
diff --git a/sys/dev/ic/ncr53c9x.c b/sys/dev/ic/ncr53c9x.c
index 8520714102a..d38b087d559 100644
--- a/sys/dev/ic/ncr53c9x.c
+++ b/sys/dev/ic/ncr53c9x.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: ncr53c9x.c,v 1.2 1997/08/08 08:13:05 downsj Exp $ */
-/* $NetBSD: ncr53c9x.c,v 1.16 1997/07/30 12:01:53 pk Exp $ */
+/* $OpenBSD: ncr53c9x.c,v 1.3 1998/02/03 22:02:46 jason Exp $ */
+/* $NetBSD: ncr53c9x.c,v 1.22 1998/01/24 15:33:35 pk Exp $ */
/*
* Copyright (c) 1996 Charles M. Hannum. All rights reserved.
@@ -134,6 +134,7 @@ const char *ncr53c9x_variant_names[] = {
"NCR53C96",
"ESP406",
"FAS408",
+ "FAS216",
};
/*
@@ -238,6 +239,7 @@ ncr53c9x_reset(sc)
case NCR_VARIANT_ESP406:
case NCR_VARIANT_FAS408:
NCR_SCSIREGS(sc);
+ case NCR_VARIANT_FAS216:
case NCR_VARIANT_NCR53C94:
case NCR_VARIANT_NCR53C96:
case NCR_VARIANT_ESP200:
@@ -423,13 +425,13 @@ ncr53c9x_select(sc, ecb)
struct scsi_link *sc_link = ecb->xs->sc_link;
int target = sc_link->target;
struct ncr53c9x_tinfo *ti = &sc->sc_tinfo[target];
+ int tiflags = ti->flags;
u_char *cmd;
int clen;
NCR_TRACE(("[ncr53c9x_select(t%d,l%d,cmd:%x)] ",
- sc_link->target, sc_link->lun, ecb->cmd.cmd.opcode));
+ sc_link->target, sc_link->lun, ecb->cmd.cmd.opcode));
- /* new state NCR_SELECTING */
sc->sc_state = NCR_SELECTING;
/*
@@ -441,8 +443,6 @@ ncr53c9x_select(sc, ecb)
timeout(ncr53c9x_timeout, ecb,
(ecb->timeout * hz) / 1000);
- NCRCMD(sc, NCRCMD_FLUSH);
-
/*
* The docs say the target register is never reset, and I
* can't think of a better place to set it
@@ -450,29 +450,34 @@ ncr53c9x_select(sc, ecb)
NCR_WRITE_REG(sc, NCR_SELID, target);
ncr53c9x_setsync(sc, ti);
- if (ncr53c9x_dmaselect && (ti->flags & T_NEGOTIATE) == 0) {
- size_t dmacl;
+ if (ncr53c9x_dmaselect && (tiflags & T_NEGOTIATE) == 0) {
+ size_t dmasize;
+
ecb->cmd.id =
MSG_IDENTIFY(sc_link->lun, (ti->flags & T_RSELECTOFF)?0:1);
/* setup DMA transfer for command */
- clen = ecb->clen + 1;
+ dmasize = clen = ecb->clen + 1;
sc->sc_cmdlen = clen;
sc->sc_cmdp = (caddr_t)&ecb->cmd;
- dmacl = clen;
- NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0, &dmacl);
+ NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0, &dmasize);
+
/* Program the SCSI counter */
- NCR_WRITE_REG(sc, NCR_TCL, clen);
- NCR_WRITE_REG(sc, NCR_TCM, clen >> 8);
+ NCR_WRITE_REG(sc, NCR_TCL, dmasize);
+ NCR_WRITE_REG(sc, NCR_TCM, dmasize >> 8);
if (sc->sc_cfg2 & NCRCFG2_FE) {
- NCR_WRITE_REG(sc, NCR_TCH, clen >> 16);
+ NCR_WRITE_REG(sc, NCR_TCH, dmasize >> 16);
}
+ /* load the count in */
+ NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA);
+
/* And get the targets attention */
NCRCMD(sc, NCRCMD_SELATN | NCRCMD_DMA);
NCRDMA_GO(sc);
return;
}
+
/*
* Who am I. This is where we tell the target that we are
* happy for it to disconnect etc.
@@ -671,7 +676,8 @@ ncr53c9x_sched(sc)
break;
} else
NCR_MISC(("%d:%d busy\n",
- sc_link->target, sc_link->lun));
+ sc_link->target,
+ sc_link->lun));
}
}
@@ -1089,8 +1095,7 @@ gotit:
printf("max sync rate %d.%02dMb/s\n",
r, s);
#endif
- if ((sc->sc_flags&NCR_SYNCHNEGO)
- == 0) {
+ if ((sc->sc_flags&NCR_SYNCHNEGO) == 0) {
/*
* target initiated negotiation
*/
@@ -1174,6 +1179,13 @@ ncr53c9x_msgout(sc)
NCR_TRACE(("[ncr53c9x_msgout(priq:%x, prevphase:%x)]",
sc->sc_msgpriq, sc->sc_prevphase));
+ /*
+ * XXX - the NCR_ATN flag is not in sync with the actual ATN
+ * condition on the SCSI bus. The 53c9x chip
+ * automatically turns off ATN before sending the
+ * message byte. (see also the comment below in the
+ * default case when picking out a message to send)
+ */
if (sc->sc_flags & NCR_ATN) {
if (sc->sc_prevphase != MESSAGE_OUT_PHASE) {
new:
@@ -1244,7 +1256,19 @@ ncr53c9x_msgout(sc)
sc->sc_omess[0] = MSG_MESSAGE_REJECT;
break;
default:
- NCRCMD(sc, NCRCMD_RSTATN);
+ /*
+ * We normally do not get here, since the chip
+ * automatically turns off ATN before the last
+ * byte of a message is sent to the target.
+ * However, if the target rejects our (multi-byte)
+ * message early by switching to MSG IN phase
+ * ATN remains on, so the target may return to
+ * MSG OUT phase. If there are no scheduled messages
+ * left we send a NO-OP.
+ *
+ * XXX - Note that this leaves no useful purpose for
+ * the NCR_ATN flag.
+ */
sc->sc_flags &= ~NCR_ATN;
sc->sc_omess[0] = MSG_NOOP;
break;
@@ -1252,7 +1276,6 @@ ncr53c9x_msgout(sc)
sc->sc_omp = sc->sc_omess;
}
-#if 1
/* (re)send the message */
size = min(sc->sc_omlen, sc->sc_maxxfer);
NCRDMA_SETUP(sc, &sc->sc_omp, &sc->sc_omlen, 0, &size);
@@ -1262,18 +1285,10 @@ ncr53c9x_msgout(sc)
if (sc->sc_cfg2 & NCRCFG2_FE) {
NCR_WRITE_REG(sc, NCR_TCH, size >> 16);
}
- /* load the count in */
+ /* Load the count in and start the message-out transfer */
NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA);
NCRCMD(sc, NCRCMD_TRANS|NCRCMD_DMA);
NCRDMA_GO(sc);
-#else
- { int i;
- for (i = 0; i < sc->sc_omlen; i++)
- NCR_WRITE_REG(sc, FIFO, sc->sc_omess[i]);
- NCRCMD(sc, NCRCMD_TRANS);
- sc->sc_omlen = 0;
- }
-#endif
}
/*
@@ -1394,7 +1409,16 @@ ncr53c9x_intr(sc)
if (sc->sc_espintr & NCRINTR_ILL) {
if (sc->sc_flags & NCR_EXPECT_ILLCMD) {
-printf("%s: ILL: ESP100 work-around activated\n", sc->sc_dev.dv_xname);
+ /*
+ * Eat away "Illegal command" interrupt
+ * on a ESP100 caused by a re-selection
+ * while we were trying to select
+ * another target.
+ */
+#ifdef DEBUG
+ printf("%s: ESP100 work-around activated\n",
+ sc->sc_dev.dv_xname);
+#endif
sc->sc_flags &= ~NCR_EXPECT_ILLCMD;
continue;
}
@@ -1432,23 +1456,53 @@ printf("%s: ILL: ESP100 work-around activated\n", sc->sc_dev.dv_xname);
if (NCRDMA_ISACTIVE(sc))
return 1;
- /*
- * Note that this can happen during normal operation
- * if we are reselected while using DMA to select
- * a target. If this is the case, don't issue the
- * warning.
- */
- if (sc->sc_dleft == 0 &&
- (sc->sc_espstat & NCRSTAT_TC) == 0 &&
- sc->sc_state != NCR_SELECTING)
- printf("%s: !TC [intr %x, stat %x, step %d]"
- " prevphase %x, resid %x\n",
- sc->sc_dev.dv_xname,
- sc->sc_espintr,
- sc->sc_espstat,
- sc->sc_espstep,
- sc->sc_prevphase,
- ecb?ecb->dleft:-1);
+ if ((sc->sc_espstat & NCRSTAT_TC) == 0) {
+ /*
+ * DMA not completed. If we can not find a
+ * acceptable explanation, print a diagnostic.
+ */
+ if (sc->sc_state == NCR_SELECTING)
+ /*
+ * This can happen if we are reselected
+ * while using DMA to select a target.
+ */
+ /*void*/;
+ else if (sc->sc_prevphase == MESSAGE_OUT_PHASE){
+ /*
+ * Our (multi-byte) message (eg SDTR)
+ * was interrupted by the target to
+ * send a MSG REJECT.
+ * Print diagnostic if current phase
+ * is not MESSAGE IN.
+ */
+ if (sc->sc_phase != MESSAGE_IN_PHASE)
+ printf("%s: !TC on MSG OUT"
+ " [intr %x, stat %x, step %d]"
+ " prevphase %x, resid %x\n",
+ sc->sc_dev.dv_xname,
+ sc->sc_espintr,
+ sc->sc_espstat,
+ sc->sc_espstep,
+ sc->sc_prevphase,
+ sc->sc_omlen);
+ } else if (sc->sc_dleft == 0) {
+ /*
+ * The DMA operation was started for
+ * a DATA transfer. Print a diagnostic
+ * if the DMA counter and TC bit
+ * appear to be out of sync.
+ */
+ printf("%s: !TC on DATA XFER"
+ " [intr %x, stat %x, step %d]"
+ " prevphase %x, resid %x\n",
+ sc->sc_dev.dv_xname,
+ sc->sc_espintr,
+ sc->sc_espstat,
+ sc->sc_espstep,
+ sc->sc_prevphase,
+ ecb?ecb->dleft:-1);
+ }
+ }
}
#if 0 /* Unreliable on some NCR revisions? */
@@ -1483,6 +1537,7 @@ printf("%s: ILL: ESP100 work-around activated\n", sc->sc_dev.dv_xname);
* 250mS of a disconnect. So here you are...
*/
NCRCMD(sc, NCRCMD_ENSEL);
+
switch (sc->sc_state) {
case NCR_RESELECTED:
goto sched;
@@ -1571,7 +1626,6 @@ printf("<<RESELECT CONT'd>>");
break;
case NCR_IDLE:
-if (sc->sc_flags & NCR_ICCS) printf("[[esp: BUMMER]]");
case NCR_SELECTING:
sc->sc_msgpriq = sc->sc_msgout = sc->sc_msgoutq = 0;
sc->sc_flags = 0;
@@ -1635,8 +1689,15 @@ if (sc->sc_flags & NCR_ICCS) printf("[[esp: BUMMER]]");
/* Handle identify message */
ncr53c9x_msgin(sc);
if (nfifo != 2) {
+ /*
+ * Note: this should not happen
+ * with `dmaselect' on.
+ */
sc->sc_flags |= NCR_EXPECT_ILLCMD;
NCRCMD(sc, NCRCMD_FLUSH);
+ } else if (ncr53c9x_dmaselect &&
+ sc->sc_rev == NCR_VARIANT_ESP100) {
+ sc->sc_flags |= NCR_EXPECT_ILLCMD;
}
if (sc->sc_state != NCR_CONNECTED) {
@@ -1660,11 +1721,17 @@ if (sc->sc_flags & NCR_ICCS) printf("[[esp: BUMMER]]");
switch (sc->sc_espstep) {
case 0:
- printf("%s: select timeout/no "
- "disconnect\n",
- sc->sc_dev.dv_xname);
- ecb->xs->error = XS_SELTIMEOUT;
- goto finish;
+ /*
+ * The target did not respond with a
+ * message out phase - probably an old
+ * device that doesn't recognize ATN.
+ * Clear ATN and just continue, the
+ * target should be in the command
+ * phase.
+ * XXXX check for command phase?
+ */
+ NCRCMD(sc, NCRCMD_RSTATN);
+ break;
case 1:
if ((ti->flags & T_NEGOTIATE) == 0) {
printf("%s: step 1 & !NEG\n",
@@ -1721,11 +1788,15 @@ if (sc->sc_flags & NCR_ICCS) printf("[[esp: BUMMER]]");
if (ncr53c9x_dmaselect &&
sc->sc_cmdlen != 0)
printf("(%s:%d:%d): select; "
- "%d left in DMA buffer\n",
+ "%d left in DMA buffer "
+ "[intr %x, stat %x, step %d]\n",
sc->sc_dev.dv_xname,
sc_link->target,
sc_link->lun,
- sc->sc_cmdlen);
+ sc->sc_cmdlen,
+ sc->sc_espintr,
+ sc->sc_espstat,
+ sc->sc_espstep);
/* So far, everything went fine */
break;
}
@@ -1854,7 +1925,7 @@ if (sc->sc_flags & NCR_ICCS) printf("[[esp: BUMMER]]");
* Send the command block. Normally we don't see this
* phase because the SEL_ATN command takes care of
* all this. However, we end up here if either the
- * target or we wanted exchange some more messages
+ * target or we wanted to exchange some more messages
* first (e.g. to start negotiations).
*/
@@ -1879,6 +1950,10 @@ if (sc->sc_flags & NCR_ICCS) printf("[[esp: BUMMER]]");
NCR_WRITE_REG(sc, NCR_TCH, size >> 16);
}
+ /* load the count in */
+ NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA);
+
+ /* start the command transfer */
NCRCMD(sc, NCRCMD_TRANS | NCRCMD_DMA);
NCRDMA_GO(sc);
} else {