summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/sparc/dev/esp.c4
-rw-r--r--sys/dev/ic/ncr53c9x.c1213
-rw-r--r--sys/dev/ic/ncr53c9xreg.h26
-rw-r--r--sys/dev/ic/ncr53c9xvar.h127
-rw-r--r--sys/dev/pci/pcscp.c4
-rw-r--r--sys/dev/sbus/esp_sbus.c7
6 files changed, 1027 insertions, 354 deletions
diff --git a/sys/arch/sparc/dev/esp.c b/sys/arch/sparc/dev/esp.c
index 475d4e894ed..bfe094a03f4 100644
--- a/sys/arch/sparc/dev/esp.c
+++ b/sys/arch/sparc/dev/esp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: esp.c,v 1.16 2000/06/12 06:10:46 fgsch Exp $ */
+/* $OpenBSD: esp.c,v 1.17 2001/09/27 04:01:42 jason Exp $ */
/* $NetBSD: esp.c,v 1.69 1997/08/27 11:24:18 bouyer Exp $ */
/*
@@ -402,7 +402,7 @@ espattach(parent, self, aux)
ncr53c9x_attach(sc, &esp_switch, &esp_dev);
/* Turn on target selection using the `dma' method */
- ncr53c9x_dmaselect = 1;
+ sc->sc_features |= NCR_F_DMASELECT;
bootpath_store(1, NULL);
}
diff --git a/sys/dev/ic/ncr53c9x.c b/sys/dev/ic/ncr53c9x.c
index 6f1d3056902..aefb82b5c5c 100644
--- a/sys/dev/ic/ncr53c9x.c
+++ b/sys/dev/ic/ncr53c9x.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: ncr53c9x.c,v 1.9 2000/07/21 11:20:35 art Exp $ */
-/* $NetBSD: ncr53c9x.c,v 1.26 1998/05/26 23:17:34 thorpej Exp $ */
+/* $OpenBSD: ncr53c9x.c,v 1.10 2001/09/27 04:01:42 jason Exp $ */
+/* $NetBSD: ncr53c9x.c,v 1.56 2000/11/30 14:41:46 thorpej Exp $ */
/*
* Copyright (c) 1996 Charles M. Hannum. All rights reserved.
@@ -82,6 +82,7 @@
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/queue.h>
+#include <sys/pool.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
@@ -93,13 +94,15 @@
#include <dev/ic/ncr53c9xvar.h>
int ncr53c9x_debug = 0; /*NCR_SHOWPHASE|NCR_SHOWMISC|NCR_SHOWTRAC|NCR_SHOWCMDS;*/
+#ifdef DEBUG
+int ncr53c9x_notag = 0;
+#endif
/*static*/ void ncr53c9x_readregs __P((struct ncr53c9x_softc *));
/*static*/ void ncr53c9x_select __P((struct ncr53c9x_softc *,
struct ncr53c9x_ecb *));
-/*static*/ int ncr53c9x_reselect __P((struct ncr53c9x_softc *, int));
+/*static*/ int ncr53c9x_reselect __P((struct ncr53c9x_softc *, int, int, int));
/*static*/ void ncr53c9x_scsi_reset __P((struct ncr53c9x_softc *));
-/*static*/ void ncr53c9x_init __P((struct ncr53c9x_softc *, int));
/*static*/ int ncr53c9x_poll __P((struct ncr53c9x_softc *,
struct scsi_xfer *, int));
/*static*/ void ncr53c9x_sched __P((struct ncr53c9x_softc *));
@@ -108,6 +111,7 @@ int ncr53c9x_debug = 0; /*NCR_SHOWPHASE|NCR_SHOWMISC|NCR_SHOWTRAC|NCR_SHOWCMDS;*
/*static*/ void ncr53c9x_msgin __P((struct ncr53c9x_softc *));
/*static*/ void ncr53c9x_msgout __P((struct ncr53c9x_softc *));
/*static*/ void ncr53c9x_timeout __P((void *arg));
+/*static*/ void ncr53c9x_watch __P((void *arg));
/*static*/ void ncr53c9x_abort __P((struct ncr53c9x_softc *,
struct ncr53c9x_ecb *));
/*static*/ void ncr53c9x_dequeue __P((struct ncr53c9x_softc *,
@@ -122,6 +126,28 @@ struct ncr53c9x_ecb *ncr53c9x_get_ecb __P((struct ncr53c9x_softc *, int));
static inline int ncr53c9x_stp2cpb __P((struct ncr53c9x_softc *, int));
static inline void ncr53c9x_setsync __P((struct ncr53c9x_softc *,
struct ncr53c9x_tinfo *));
+static struct ncr53c9x_linfo *ncr53c9x_lunsearch __P((struct ncr53c9x_tinfo *,
+ int64_t lun));
+
+static void ncr53c9x_wrfifo(struct ncr53c9x_softc *, u_char *, int);
+static int ncr53c9x_rdfifo(struct ncr53c9x_softc *, int);
+#define NCR_RDFIFO_START 0
+#define NCR_RDFIFO_CONTINUE 1
+
+#define NCR_SET_COUNT(sc, size) do { \
+ NCR_WRITE_REG((sc), NCR_TCL, (size)); \
+ NCR_WRITE_REG((sc), NCR_TCM, (size) >> 8); \
+ if ((sc->sc_cfg2 & NCRCFG2_FE) || \
+ (sc->sc_rev == NCR_VARIANT_FAS366)) { \
+ NCR_WRITE_REG((sc), NCR_TCH, (size) >> 16); \
+ } \
+ if (sc->sc_rev == NCR_VARIANT_FAS366) { \
+ NCR_WRITE_REG(sc, NCR_RCH, 0); \
+ } \
+} while (0)
+
+static int ecb_pool_initialized = 0;
+static struct pool ecb_pool;
struct cfdriver esp_cd = {
NULL, "esp", DV_DULL
@@ -141,9 +167,25 @@ const char *ncr53c9x_variant_names[] = {
"FAS408",
"FAS216",
"AM53C974",
+ "FAS366/HME",
};
/*
+ * Search linked list for LUN info by LUN id.
+ */
+static struct ncr53c9x_linfo *
+ncr53c9x_lunsearch(ti, lun)
+ struct ncr53c9x_tinfo *ti;
+ int64_t lun;
+{
+ struct ncr53c9x_linfo *li;
+ LIST_FOREACH(li, &ti->luns, link)
+ if (li->lun == lun)
+ return (li);
+ return (NULL);
+}
+
+/*
* Attach this instance, and then all the sub-devices
*/
void
@@ -152,7 +194,7 @@ ncr53c9x_attach(sc, adapter, dev)
struct scsi_adapter *adapter;
struct scsi_device *dev;
{
-
+ timeout_set(&sc->sc_watchdog, ncr53c9x_watch, sc);
/*
* Allocate SCSI message buffers.
* Front-ends can override allocation to avoid alignment
@@ -222,11 +264,12 @@ ncr53c9x_attach(sc, adapter, dev)
* Now try to attach all the sub-devices
*/
config_found(&sc->sc_dev, &sc->sc_link, scsiprint);
+ timeout_add(&sc->sc_watchdog, 60*hz);
}
/*
- * This is the generic esp reset function. It does not reset the SCSI bus,
- * only this controllers, but kills any on-going commands, and also stops
+ * This is the generic ncr53c9x reset function. It does not reset the SCSI bus,
+ * only this controller, but kills any on-going commands, and also stops
* and resets the DMA.
*
* After reset, registers are loaded with the defaults from the attach
@@ -259,6 +302,7 @@ ncr53c9x_reset(sc)
sc->sc_features |= NCR_F_HASCFG3;
NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3);
case NCR_VARIANT_ESP100A:
+ sc->sc_features |= NCR_F_SELATN3;
NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2);
case NCR_VARIANT_ESP100:
NCR_WRITE_REG(sc, NCR_CFG1, sc->sc_cfg1);
@@ -266,6 +310,19 @@ ncr53c9x_reset(sc)
NCR_WRITE_REG(sc, NCR_SYNCOFF, 0);
NCR_WRITE_REG(sc, NCR_TIMEOUT, sc->sc_timeout);
break;
+ case NCR_VARIANT_FAS366:
+ sc->sc_features |=
+ NCR_F_SELATN3 | NCR_F_HASCFG3 | NCR_F_FASTSCSI;
+ sc->sc_cfg3 = NCRFASCFG3_FASTCLK | NCRFASCFG3_OBAUTO;
+ sc->sc_cfg3_fscsi = NCRFASCFG3_FASTSCSI;
+ NCR_WRITE_REG(sc, NCR_CFG3, sc->sc_cfg3);
+ sc->sc_cfg2 = 0; /* NCRCFG2_HMEFE | NCRCFG2_HME32 */
+ NCR_WRITE_REG(sc, NCR_CFG2, sc->sc_cfg2);
+ NCR_WRITE_REG(sc, NCR_CFG1, sc->sc_cfg1);
+ NCR_WRITE_REG(sc, NCR_CCF, sc->sc_ccf);
+ NCR_WRITE_REG(sc, NCR_SYNCOFF, 0);
+ NCR_WRITE_REG(sc, NCR_TIMEOUT, sc->sc_timeout);
+ break;
default:
printf("%s: unknown revision code, assuming ESP100\n",
sc->sc_dev.dv_xname);
@@ -277,6 +334,15 @@ ncr53c9x_reset(sc)
if (sc->sc_rev == NCR_VARIANT_AM53C974)
NCR_WRITE_REG(sc, NCR_AMDCFG4, sc->sc_cfg4);
+
+#if 0
+ printf("%s: ncr53c9x_reset: revision %d\n",
+ sc->sc_dev.dv_xname, sc->sc_rev);
+ printf("%s: ncr53c9x_reset: cfg1 0x%x, cfg2 0x%x, cfg3 0x%x, ccf 0x%x, timeout 0x%x\n",
+ sc->sc_dev.dv_xname,
+ sc->sc_cfg1, sc->sc_cfg2, sc->sc_cfg3,
+ sc->sc_ccf, sc->sc_timeout);
+#endif
}
/*
@@ -294,7 +360,7 @@ ncr53c9x_scsi_reset(sc)
}
/*
- * Initialize esp state machine
+ * Initialize ncr53c9x state machine
*/
void
ncr53c9x_init(sc, doreset)
@@ -302,34 +368,56 @@ ncr53c9x_init(sc, doreset)
int doreset;
{
struct ncr53c9x_ecb *ecb;
- int r;
+ struct ncr53c9x_linfo *li;
+ int r, i;
- NCR_TRACE(("[NCR_INIT(%d)] ", doreset));
+ NCR_TRACE(("[NCR_INIT(%d) %d] ", doreset, sc->sc_state));
+
+ if (!ecb_pool_initialized) {
+ /* All instances share this pool */
+ pool_init(&ecb_pool, sizeof(struct ncr53c9x_ecb), 0, 0, 0,
+ "ncr53c9x_ecb", 0, NULL, NULL, 0);
+ ecb_pool_initialized = 1;
+ }
if (sc->sc_state == 0) {
/* First time through; initialize. */
+
TAILQ_INIT(&sc->ready_list);
- TAILQ_INIT(&sc->nexus_list);
- TAILQ_INIT(&sc->free_list);
sc->sc_nexus = NULL;
- ecb = sc->sc_ecb;
- bzero(ecb, sizeof(sc->sc_ecb));
- for (r = 0; r < sizeof(sc->sc_ecb) / sizeof(*ecb); r++) {
- timeout_set(&ecb->to, ncr53c9x_timeout, ecb);
- TAILQ_INSERT_TAIL(&sc->free_list, ecb, chain);
- ecb++;
- }
bzero(sc->sc_tinfo, sizeof(sc->sc_tinfo));
+ for (r = 0; r < NCR_NTARG; r++) {
+ LIST_INIT(&sc->sc_tinfo[r].luns);
+ }
} else {
/* Cancel any active commands. */
sc->sc_state = NCR_CLEANING;
+ sc->sc_msgify = 0;
if ((ecb = sc->sc_nexus) != NULL) {
ecb->xs->error = XS_TIMEOUT;
ncr53c9x_done(sc, ecb);
}
- while ((ecb = sc->nexus_list.tqh_first) != NULL) {
- ecb->xs->error = XS_TIMEOUT;
- ncr53c9x_done(sc, ecb);
+ for (r = 0; r < 8; r++) {
+ LIST_FOREACH(li, &sc->sc_tinfo[r].luns, link) {
+ if ((ecb = li->untagged)) {
+ li->untagged = NULL;
+ /*
+ * XXXXXXX
+ * Should we terminate a command
+ * that never reached the disk?
+ */
+ li->busy = 0;
+ ecb->xs->error = XS_TIMEOUT;
+ ncr53c9x_done(sc, ecb);
+ }
+ for (i = 0; i<256; i++)
+ if ((ecb = li->queued[i])) {
+ li->queued[i] = NULL;
+ ecb->xs->error = XS_TIMEOUT;
+ ncr53c9x_done(sc, ecb);
+ }
+ li->used = 0;
+ }
}
}
@@ -343,12 +431,17 @@ ncr53c9x_init(sc, doreset)
struct ncr53c9x_tinfo *ti = &sc->sc_tinfo[r];
/* XXX - config flags per target: low bits: no reselect; high bits: no synch */
- ti->flags = ((sc->sc_minsync && !(sc->sc_cfflags & (1<<(r+8))))
- ? T_NEGOTIATE : 0) |
- ((sc->sc_cfflags & (1<<r)) ? T_RSELECTOFF : 0) |
- T_NEED_TO_RESET;
+ ti->flags = ((sc->sc_minsync && !(sc->sc_cfflags & (1<<(r+8))))
+ ? 0 : T_SYNCHOFF) |
+ ((sc->sc_cfflags & (1<<r)) ? T_RSELECTOFF : 0) |
+ T_NEED_TO_RESET;
+#ifdef DEBUG
+ if (ncr53c9x_notag)
+ ti->flags &= ~T_TAG;
+#endif
ti->period = sc->sc_minsync;
ti->offset = 0;
+ ti->cfg3 = 0;
}
if (doreset) {
@@ -377,6 +470,10 @@ ncr53c9x_readregs(sc)
sc->sc_espstat = NCR_READ_REG(sc, NCR_STAT);
/* Only the stepo bits are of interest */
sc->sc_espstep = NCR_READ_REG(sc, NCR_STEP) & NCRSTEP_MASK;
+
+ if (sc->sc_rev == NCR_VARIANT_FAS366)
+ sc->sc_espstat2 = NCR_READ_REG(sc, NCR_STAT2);
+
sc->sc_espintr = NCR_READ_REG(sc, NCR_INTR);
if (sc->sc_glue->gl_clear_latched_intr != NULL)
@@ -391,8 +488,8 @@ ncr53c9x_readregs(sc)
? /* Disconnected */ BUSFREE_PHASE
: sc->sc_espstat & NCRSTAT_PHASE;
- NCR_MISC(("regs[intr=%02x,stat=%02x,step=%02x] ",
- sc->sc_espintr, sc->sc_espstat, sc->sc_espstep));
+ NCR_MISC(("regs[intr=%02x,stat=%02x,step=%02x,stat2=%02x] ",
+ sc->sc_espintr, sc->sc_espstat, sc->sc_espstep, sc->sc_espstat2));
}
/*
@@ -416,7 +513,8 @@ ncr53c9x_setsync(sc, ti)
struct ncr53c9x_softc *sc;
struct ncr53c9x_tinfo *ti;
{
- u_char syncoff, synctp, cfg3 = sc->sc_cfg3;
+ u_char syncoff, synctp;
+ u_char cfg3 = sc->sc_cfg3 | ti->cfg3;
if (ti->flags & T_SYNCMODE) {
syncoff = ti->offset;
@@ -434,7 +532,8 @@ ncr53c9x_setsync(sc, ti)
* chip into Fast SCSI mode so that it doesn't
* have to be figured out here each time.
*/
- cfg3 |= sc->sc_cfg3_fscsi;
+ cfg3 |= (sc->sc_rev == NCR_VARIANT_AM53C974) ?
+ NCRAMDCFG3_FSCSI : NCRCFG3_FSCSI;
}
/*
@@ -456,7 +555,6 @@ ncr53c9x_setsync(sc, ti)
NCR_WRITE_REG(sc, NCR_SYNCTP, synctp);
}
-int ncr53c9x_dmaselect = 0;
/*
* Send a command to a target, set the driver state to NCR_SELECTING
* and let the caller take care of the rest.
@@ -476,89 +574,123 @@ ncr53c9x_select(sc, ecb)
int tiflags = ti->flags;
u_char *cmd;
int clen;
+ int selatn3, selatns;
size_t dmasize;
- NCR_TRACE(("[ncr53c9x_select(t%d,l%d,cmd:%x)] ",
- target, lun, ecb->cmd.cmd.opcode));
+ NCR_TRACE(("[ncr53c9x_select(t%d,l%d,cmd:%x,tag%x,%x)] ",
+ target, lun, ecb->cmd.cmd.opcode, ecb->tag[0], ecb->tag[1]));
sc->sc_state = NCR_SELECTING;
-
/*
* Schedule the timeout now, the first time we will go away
* expecting to come back due to an interrupt, because it is
* always possible that the interrupt may never happen.
*/
- if ((ecb->xs->flags & SCSI_POLL) == 0)
- timeout_add(&ecb->to, (ecb->timeout * hz) / 1000);
+ if ((ecb->xs->flags & SCSI_POLL) == 0) {
+ int timeout = ecb->timeout;
+
+ if (hz > 100 && timeout > 1000)
+ timeout = (timeout / 1000) * hz;
+ else
+ timeout = (timeout * hz) / 1000;
+ timeout_add(&ecb->to, timeout);
+ }
/*
* The docs say the target register is never reset, and I
* can't think of a better place to set it
*/
- NCR_WRITE_REG(sc, NCR_SELID, target);
+ if (sc->sc_rev == NCR_VARIANT_FAS366) {
+ NCRCMD(sc, NCRCMD_FLUSH);
+ NCR_WRITE_REG(sc, NCR_SELID, target | NCR_BUSID_HME);
+ } else {
+ NCR_WRITE_REG(sc, NCR_SELID, target);
+ }
ncr53c9x_setsync(sc, ti);
- if (ecb->flags & ECB_SENSE) {
+ if ((ecb->flags & ECB_SENSE) != 0) {
/*
* For REQUEST SENSE, we should not send an IDENTIFY or
* otherwise mangle the target. There should be no MESSAGE IN
* phase.
*/
- if (ncr53c9x_dmaselect) {
+ if (sc->sc_features & NCR_F_DMASELECT) {
/* setup DMA transfer for command */
dmasize = clen = ecb->clen;
sc->sc_cmdlen = clen;
- sc->sc_cmdp = (caddr_t)&ecb->cmd + 1;
- NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0,
- &dmasize);
+ sc->sc_cmdp = (caddr_t)&ecb->cmd.cmd;
/* Program the SCSI counter */
- 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, dmasize >> 16);
- }
+ NCR_SET_COUNT(sc, dmasize);
- /* load the count in */
- NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA);
+ if (sc->sc_rev != NCR_VARIANT_FAS366)
+ NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA);
/* And get the targets attention */
NCRCMD(sc, NCRCMD_SELNATN | NCRCMD_DMA);
+ NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0, &dmasize);
NCRDMA_GO(sc);
} else {
- /* Now the command into the FIFO */
- cmd = (u_char *)&ecb->cmd.cmd;
- clen = ecb->clen;
- while (clen--)
- NCR_WRITE_REG(sc, NCR_FIFO, *cmd++);
-
+ ncr53c9x_wrfifo(sc, (u_char *)&ecb->cmd.cmd, ecb->clen);
NCRCMD(sc, NCRCMD_SELNATN);
}
return;
}
- if (ncr53c9x_dmaselect && (tiflags & T_NEGOTIATE) == 0) {
- ecb->cmd.id =
- MSG_IDENTIFY(lun, (tiflags & T_RSELECTOFF)?0:1);
+ selatn3 = selatns = 0;
+ if (ecb->tag[0] != 0) {
+ if (sc->sc_features & NCR_F_SELATN3)
+ /* use SELATN3 to send tag messages */
+ selatn3 = 1;
+ else
+ /* We don't have SELATN3; use SELATNS to send tags */
+ selatns = 1;
+ }
+
+ if (ti->flags & T_NEGOTIATE) {
+ /* We have to use SELATNS to send sync/wide messages */
+ selatn3 = 0;
+ selatns = 1;
+ }
+
+ cmd = (u_char *)&ecb->cmd.cmd;
+
+ if (selatn3) {
+ /* We'll use tags with SELATN3 */
+ clen = ecb->clen + 3;
+ cmd -= 3;
+ cmd[0] = MSG_IDENTIFY(lun, 1); /* msg[0] */
+ cmd[1] = ecb->tag[0]; /* msg[1] */
+ cmd[2] = ecb->tag[1]; /* msg[2] */
+ } else {
+ /* We don't have tags, or will send messages with SELATNS */
+ clen = ecb->clen + 1;
+ cmd -= 1;
+ cmd[0] = MSG_IDENTIFY(lun, (tiflags & T_RSELECTOFF) == 0);
+ }
+
+ if ((sc->sc_features & NCR_F_DMASELECT) && !selatns) {
/* setup DMA transfer for command */
- dmasize = clen = ecb->clen + 1;
+ dmasize = clen;
sc->sc_cmdlen = clen;
- sc->sc_cmdp = (caddr_t)&ecb->cmd;
- NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0, &dmasize);
+ sc->sc_cmdp = cmd;
/* Program the SCSI counter */
- 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, dmasize >> 16);
- }
+ NCR_SET_COUNT(sc, dmasize);
/* load the count in */
- NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA);
+ /* if (sc->sc_rev != NCR_VARIANT_FAS366) */
+ NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA);
/* And get the targets attention */
- NCRCMD(sc, NCRCMD_SELATN | NCRCMD_DMA);
+ if (selatn3) {
+ sc->sc_msgout = SEND_TAG;
+ sc->sc_flags |= NCR_ATN;
+ NCRCMD(sc, NCRCMD_SELATN3 | NCRCMD_DMA);
+ } else
+ NCRCMD(sc, NCRCMD_SELATN | NCRCMD_DMA);
+ NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0, &dmasize);
NCRDMA_GO(sc);
return;
}
@@ -567,23 +699,21 @@ ncr53c9x_select(sc, ecb)
* Who am I. This is where we tell the target that we are
* happy for it to disconnect etc.
*/
- NCR_WRITE_REG(sc, NCR_FIFO,
- MSG_IDENTIFY(lun, (tiflags & T_RSELECTOFF)?0:1));
- if (ti->flags & T_NEGOTIATE) {
- /* Arbitrate, select and stop after IDENTIFY message */
- NCRCMD(sc, NCRCMD_SELATNS);
- return;
- }
-
- /* Now the command into the FIFO */
- cmd = (u_char *)&ecb->cmd.cmd;
- clen = ecb->clen;
- while (clen--)
- NCR_WRITE_REG(sc, NCR_FIFO, *cmd++);
+ /* Now get the command into the FIFO */
+ ncr53c9x_wrfifo(sc, cmd, clen);
/* And get the targets attention */
- NCRCMD(sc, NCRCMD_SELATN);
+ if (selatns) {
+ NCR_MISC(("SELATNS \n"));
+ /* Arbitrate, select and stop after IDENTIFY message */
+ NCRCMD(sc, NCRCMD_SELATNS);
+ } else if (selatn3) {
+ sc->sc_msgout = SEND_TAG;
+ sc->sc_flags |= NCR_ATN;
+ NCRCMD(sc, NCRCMD_SELATN3);
+ } else
+ NCRCMD(sc, NCRCMD_SELATN);
}
void
@@ -595,17 +725,8 @@ ncr53c9x_free_ecb(sc, ecb, flags)
int s;
s = splbio();
-
ecb->flags = 0;
- TAILQ_INSERT_HEAD(&sc->free_list, ecb, chain);
-
- /*
- * If there were none, wake anybody waiting for one to come free,
- * starting with queued entries.
- */
- if (ecb->chain.tqe_next == 0)
- wakeup(&sc->free_list);
-
+ pool_put(&ecb_pool, (void *)ecb);
splx(s);
}
@@ -615,19 +736,19 @@ ncr53c9x_get_ecb(sc, flags)
int flags;
{
struct ncr53c9x_ecb *ecb;
- int s;
+ int s, wait = 0;
- s = splbio();
-
- while ((ecb = sc->free_list.tqh_first) == NULL &&
- (flags & SCSI_NOSLEEP) == 0)
- tsleep(&sc->free_list, PRIBIO, "especb", 0);
- if (ecb) {
- TAILQ_REMOVE(&sc->free_list, ecb, chain);
- ecb->flags |= ECB_ALLOC;
- }
+ if ((curproc != NULL) && ((flags & SCSI_NOSLEEP) == 0))
+ wait = PR_WAITOK;
+ s = splbio();
+ ecb = (struct ncr53c9x_ecb *)pool_get(&ecb_pool, wait);
splx(s);
+ if (ecb == NULL)
+ return (NULL);
+ bzero(ecb, sizeof(*ecb));
+ timeout_set(&ecb->to, ncr53c9x_timeout, ecb);
+ ecb->flags |= ECB_ALLOC;
return (ecb);
}
@@ -647,6 +768,9 @@ ncr53c9x_scsi_cmd(xs)
struct scsi_link *sc_link = xs->sc_link;
struct ncr53c9x_softc *sc = sc_link->adapter_softc;
struct ncr53c9x_ecb *ecb;
+ struct ncr53c9x_tinfo *ti;
+ struct ncr53c9x_linfo *li;
+ int64_t lun = sc_link->lun;
int s, flags;
NCR_TRACE(("[ncr53c9x_scsi_cmd] "));
@@ -654,6 +778,27 @@ ncr53c9x_scsi_cmd(xs)
sc_link->target));
flags = xs->flags;
+ ti = &sc->sc_tinfo[sc_link->target];
+ li = TINFO_LUN(ti, lun);
+ if (li == NULL) {
+ int wait = M_NOWAIT;
+
+ /* Initialize LUN info and add to list. */
+ if ((curproc != NULL) && ((flags & SCSI_NOSLEEP) == 0))
+ wait = M_WAITOK;
+ if ((li = malloc(sizeof(*li), M_DEVBUF, M_NOWAIT)) == NULL) {
+ return (TRY_AGAIN_LATER);
+ }
+ bzero(li, sizeof(*li));
+ li->last_used = time.tv_sec;
+ li->lun = lun;
+ s = splbio();
+ LIST_INSERT_HEAD(&ti->luns, li, link);
+ if (lun < NCR_NLUN)
+ ti->lun[lun] = li;
+ splx(s);
+ }
+
if ((ecb = ncr53c9x_get_ecb(sc, flags)) == NULL)
return (TRY_AGAIN_LATER);
@@ -676,6 +821,7 @@ ncr53c9x_scsi_cmd(xs)
s = splbio();
TAILQ_INSERT_TAIL(&sc->ready_list, ecb, chain);
+ ecb->flags |= ECB_READY;
if (sc->sc_state == NCR_IDLE)
ncr53c9x_sched(sc);
@@ -742,6 +888,9 @@ ncr53c9x_sched(sc)
struct ncr53c9x_ecb *ecb;
struct scsi_link *sc_link;
struct ncr53c9x_tinfo *ti;
+ int lun;
+ struct ncr53c9x_linfo *li;
+ int s, tag;
NCR_TRACE(("[ncr53c9x_sched] "));
if (sc->sc_state != NCR_IDLE)
@@ -754,8 +903,109 @@ ncr53c9x_sched(sc)
for (ecb = sc->ready_list.tqh_first; ecb; ecb = ecb->chain.tqe_next) {
sc_link = ecb->xs->sc_link;
ti = &sc->sc_tinfo[sc_link->target];
- if ((ti->lubusy & (1 << sc_link->lun)) == 0) {
+ lun = sc_link->lun;
+
+ /* Select type of tag for this command */
+ if ((ti->flags & (T_RSELECTOFF)) != 0)
+ tag = 0;
+ else if ((ti->flags & T_TAG) == 0)
+ tag = 0;
+ else if ((ecb->flags & ECB_SENSE) != 0)
+ tag = 0;
+ else if (ecb->xs->flags & SCSI_URGENT)
+ tag = MSG_HEAD_OF_Q_TAG;
+ else
+ tag = MSG_SIMPLE_Q_TAG;
+#if 0
+ /* XXXX Use tags for polled commands? */
+ if (ecb->xs->flags & SCSI_POLL)
+ tag = 0;
+#endif
+ s = splbio();
+ li = TINFO_LUN(ti, lun);
+ if (!li) {
+ int wait = M_NOWAIT;
+ int flags = ecb->flags;
+
+ /* Initialize LUN info and add to list. */
+ if ((curproc != NULL) && ((flags & SCSI_NOSLEEP) == 0))
+ wait = M_WAITOK;
+ if ((li = malloc(sizeof(*li), M_DEVBUF, M_NOWAIT)) == NULL) {
+ splx(s);
+ continue;
+ }
+ bzero(li, sizeof(*li));
+ li->lun = lun;
+
+ LIST_INSERT_HEAD(&ti->luns, li, link);
+ if (lun < NCR_NLUN)
+ ti->lun[lun] = li;
+ }
+ li->last_used = time.tv_sec;
+ if (!tag) {
+ /* Try to issue this as an un-tagged command */
+ if (!li->untagged)
+ li->untagged = ecb;
+ }
+ if (li->untagged) {
+ tag = 0;
+ if ((li->busy != 1) && !li->used) {
+ /* We need to issue this untagged command now */
+ ecb = li->untagged;
+ sc_link = ecb->xs->sc_link;
+ }
+ else {
+ /* Not ready yet */
+ splx(s);
+ continue;
+ }
+ }
+ ecb->tag[0] = tag;
+ if (tag) {
+ int i;
+
+ /* Allocate a tag */
+ if (li->used == 255) {
+ /* no free tags */
+ splx(s);
+ continue;
+ }
+ /* Start from the last used location */
+ for (i=li->avail; i<256; i++) {
+ if (li->queued[i] == NULL)
+ break;
+ }
+ /* Couldn't find one, start again from the beginning */
+ if (i == 256) {
+ for (i = 0; i<256; i++) {
+ if (li->queued[i] == NULL)
+ break;
+ }
+ }
+#ifdef DIAGNOSTIC
+ if (i == 256)
+ panic("ncr53c9x_sched: tag alloc failure\n");
+#endif
+
+ /* Save where to start next time. */
+ li->avail = i+1;
+ li->used++;
+
+ li->queued[i] = ecb;
+ ecb->tag[1] = i;
+ }
+ splx(s);
+ if (li->untagged && (li->busy != 1)) {
+ li->busy = 1;
TAILQ_REMOVE(&sc->ready_list, ecb, chain);
+ ecb->flags &= ~ECB_READY;
+ sc->sc_nexus = ecb;
+ ncr53c9x_select(sc, ecb);
+ break;
+ }
+ if (!li->untagged && tag) {
+ TAILQ_REMOVE(&sc->ready_list, ecb, chain);
+ ecb->flags &= ~ECB_READY;
sc->sc_nexus = ecb;
ncr53c9x_select(sc, ecb);
break;
@@ -775,6 +1025,8 @@ ncr53c9x_sense(sc, ecb)
struct scsi_link *sc_link = xs->sc_link;
struct ncr53c9x_tinfo *ti = &sc->sc_tinfo[sc_link->target];
struct scsi_sense *ss = (void *)&ecb->cmd.cmd;
+ struct ncr53c9x_linfo *li;
+ int lun = sc_link->lun;
NCR_MISC(("requesting sense "));
/* Next, setup a request sense command block */
@@ -788,14 +1040,16 @@ ncr53c9x_sense(sc, ecb)
ecb->flags |= ECB_SENSE;
ecb->timeout = NCR_SENSE_TIMEOUT;
ti->senses++;
- if (ecb->flags & ECB_NEXUS)
- ti->lubusy &= ~(1 << sc_link->lun);
+ li = TINFO_LUN(ti, lun);
+ if (li->busy) li->busy = 0;
+ ncr53c9x_dequeue(sc, ecb);
+ li->untagged = ecb;
+ li->busy = 2;
if (ecb == sc->sc_nexus) {
- ecb->flags &= ~ECB_NEXUS;
ncr53c9x_select(sc, ecb);
} else {
- ncr53c9x_dequeue(sc, ecb);
TAILQ_INSERT_HEAD(&sc->ready_list, ecb, chain);
+ ecb->flags |= ECB_READY;
if (sc->sc_state == NCR_IDLE)
ncr53c9x_sched(sc);
}
@@ -812,11 +1066,23 @@ ncr53c9x_done(sc, ecb)
struct scsi_xfer *xs = ecb->xs;
struct scsi_link *sc_link = xs->sc_link;
struct ncr53c9x_tinfo *ti = &sc->sc_tinfo[sc_link->target];
+ int lun = sc_link->lun;
+ struct ncr53c9x_linfo *li = TINFO_LUN(ti, lun);
NCR_TRACE(("[ncr53c9x_done(error:%x)] ", xs->error));
timeout_del(&ecb->to);
+ if (ecb->stat == SCSI_QUEUE_FULL) {
+ /*
+ * Set current throttle -- we should reset
+ * this periodically
+ */
+ sc_link->openings = li->used - 1;
+ printf("\n%s: QFULL -- throttling to %d commands\n",
+ sc->sc_dev.dv_xname, sc_link->openings);
+ }
+
/*
* Now, if we've come here with no error code, i.e. we've kept the
* initial XS_NOERROR, and the status code signals that we should
@@ -857,17 +1123,25 @@ ncr53c9x_done(sc, ecb)
/*
* Remove the ECB from whatever queue it's on.
*/
- if (ecb->flags & ECB_NEXUS)
- ti->lubusy &= ~(1 << sc_link->lun);
+ ncr53c9x_dequeue(sc, ecb);
if (ecb == sc->sc_nexus) {
sc->sc_nexus = NULL;
if (sc->sc_state != NCR_CLEANING) {
sc->sc_state = NCR_IDLE;
ncr53c9x_sched(sc);
}
- } else
- ncr53c9x_dequeue(sc, ecb);
-
+ }
+
+ if (xs->error == XS_SELTIMEOUT) {
+ /* Selection timeout -- discard this LUN if empty */
+ if (!li->untagged && !li->used) {
+ if (lun < NCR_NLUN)
+ ti->lun[lun] = NULL;
+ LIST_REMOVE(li, link);
+ free(li, M_DEVBUF);
+ }
+ }
+
ncr53c9x_free_ecb(sc, ecb, xs->flags);
ti->cmds++;
scsi_done(xs);
@@ -878,14 +1152,38 @@ ncr53c9x_dequeue(sc, ecb)
struct ncr53c9x_softc *sc;
struct ncr53c9x_ecb *ecb;
{
+ struct ncr53c9x_tinfo *ti =
+ &sc->sc_tinfo[ecb->xs->sc_link->target];
+ struct ncr53c9x_linfo *li;
+ int64_t lun = ecb->xs->sc_link->lun;
+
+ li = TINFO_LUN(ti, lun);
+#ifdef DIAGNOSTIC
+ if ((!li) || (li->lun != lun))
+ panic("ncr53c9x_dequeue: lun %qx for ecb %p does not exist\n",
+ (long long)lun, ecb);
+#endif
+ if (li->untagged == ecb) {
+ li->busy = 0;
+ li->untagged = NULL;
+ }
+ if (ecb->tag[0] && li->queued[ecb->tag[1]]) {
+#ifdef DIAGNOSTIC
+ if (li->queued[ecb->tag[1]] && (li->queued[ecb->tag[1]] != ecb))
+ panic("ncr53c9x_dequeue: slot %d for lun %qx has %p "
+ "instead of ecb %p\n", ecb->tag[1],
+ (long long)lun,
+ li->queued[ecb->tag[1]], ecb);
+#endif
+ li->queued[ecb->tag[1]] = NULL;
+ li->used --;
- if (ecb->flags & ECB_NEXUS) {
- TAILQ_REMOVE(&sc->nexus_list, ecb, chain);
- ecb->flags &= ~ECB_NEXUS;
- } else {
+ }
+ if (ecb->flags & ECB_READY) {
+ ecb->flags &= ~ECB_READY;
TAILQ_REMOVE(&sc->ready_list, ecb, chain);
}
-}
+ }
/*
* INTERRUPT/PROTOCOL ENGINE
@@ -898,33 +1196,138 @@ ncr53c9x_dequeue(sc, ecb)
*/
#define ncr53c9x_sched_msgout(m) \
do { \
- NCR_MISC(("ncr53c9x_sched_msgout %d ", m)); \
+ NCR_MISC(("ncr53c9x_sched_msgout %x %d ", m, __LINE__)); \
NCRCMD(sc, NCRCMD_SETATN); \
sc->sc_flags |= NCR_ATN; \
sc->sc_msgpriq |= (m); \
} while (0)
+static void
+ncr53c9x_flushfifo(struct ncr53c9x_softc *sc)
+{
+ NCR_MISC(("[flushfifo] "));
+
+ NCRCMD(sc, NCRCMD_FLUSH);
+
+ if (sc->sc_phase == COMMAND_PHASE ||
+ sc->sc_phase == MESSAGE_OUT_PHASE)
+ DELAY(2);
+}
+
+static int
+ncr53c9x_rdfifo(struct ncr53c9x_softc *sc, int how)
+{
+ int i, n;
+ u_char *buf;
+
+ switch(how) {
+ case NCR_RDFIFO_START:
+ buf = sc->sc_imess;
+ sc->sc_imlen = 0;
+ break;
+ case NCR_RDFIFO_CONTINUE:
+ buf = sc->sc_imess + sc->sc_imlen;
+ break;
+ default:
+ panic("ncr53c9x_rdfifo: bad flag\n");
+ break;
+ }
+
+ /*
+ * XXX buffer (sc_imess) size for message
+ */
+
+ n = NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF;
+
+ if (sc->sc_rev == NCR_VARIANT_FAS366) {
+ n *= 2;
+
+ for (i = 0; i < n; i++)
+ buf[i] = NCR_READ_REG(sc, NCR_FIFO);
+
+ if (sc->sc_espstat2 & FAS_STAT2_ISHUTTLE) {
+
+ NCR_WRITE_REG(sc, NCR_FIFO, 0);
+ buf[i++] = NCR_READ_REG(sc, NCR_FIFO);
+
+ NCR_READ_REG(sc, NCR_FIFO);
+
+ ncr53c9x_flushfifo(sc);
+ }
+ } else {
+ for (i = 0; i < n; i++)
+ buf[i] = NCR_READ_REG(sc, NCR_FIFO);
+ }
+
+ sc->sc_imlen += i;
+
+#ifdef NCR53C9X_DEBUG
+ {
+ int j;
+
+ NCR_TRACE(("\n[rdfifo %s (%d):",
+ (how == NCR_RDFIFO_START) ? "start" : "cont",
+ (int)sc->sc_imlen));
+ if (ncr53c9x_debug & NCR_SHOWTRAC) {
+ for (j = 0; j < sc->sc_imlen; j++)
+ printf(" %02x", sc->sc_imess[j]);
+ printf("]\n");
+ }
+ }
+#endif
+ return sc->sc_imlen;
+}
+
+static void
+ncr53c9x_wrfifo(struct ncr53c9x_softc *sc, u_char *p, int len)
+{
+ int i;
+
+#ifdef NCR53C9X_DEBUG
+ NCR_MISC(("[wrfifo(%d):", len));
+ if (ncr53c9x_debug & NCR_SHOWTRAC) {
+ for (i = 0; i < len; i++)
+ printf(" %02x", p[i]);
+ printf("]\n");
+ }
+#endif
+
+ for (i = 0; i < len; i++) {
+ NCR_WRITE_REG(sc, NCR_FIFO, p[i]);
+
+ if (sc->sc_rev == NCR_VARIANT_FAS366)
+ NCR_WRITE_REG(sc, NCR_FIFO, 0);
+ }
+}
+
int
-ncr53c9x_reselect(sc, message)
+ncr53c9x_reselect(sc, message, tagtype, tagid)
struct ncr53c9x_softc *sc;
int message;
{
u_char selid, target, lun;
- struct ncr53c9x_ecb *ecb;
- struct scsi_link *sc_link;
+ struct ncr53c9x_ecb *ecb = NULL;
struct ncr53c9x_tinfo *ti;
+ struct ncr53c9x_linfo *li;
- /*
- * The SCSI chip made a snapshot of the data bus while the reselection
- * was being negotiated. This enables us to determine which target did
- * the reselect.
- */
- selid = sc->sc_selid & ~(1 << sc->sc_id);
- if (selid & (selid - 1)) {
- printf("%s: reselect with invalid selid %02x;"
- " sending DEVICE RESET\n", sc->sc_dev.dv_xname, selid);
- goto reset;
+ if (sc->sc_rev == NCR_VARIANT_FAS366) {
+ target = sc->sc_selid;
+ } else {
+ /*
+ * The SCSI chip made a snapshot of the data bus while the reselection
+ * was being negotiated. This enables us to determine which target did
+ * the reselect.
+ */
+ selid = sc->sc_selid & ~(1 << sc->sc_id);
+ if (selid & (selid - 1)) {
+ printf("%s: reselect with invalid selid %02x;"
+ " sending DEVICE RESET\n", sc->sc_dev.dv_xname, selid);
+ goto reset;
+
+ }
+ target = ffs(selid) - 1;
}
+ lun = message & 0x07;
/*
* Search wait queue for disconnected cmd
@@ -932,33 +1335,33 @@ ncr53c9x_reselect(sc, message)
* any more sophisticated structures than a simple
* singly linked list.
*/
- target = ffs(selid) - 1;
- lun = message & 0x07;
- for (ecb = sc->nexus_list.tqh_first; ecb != NULL;
- ecb = ecb->chain.tqe_next) {
- sc_link = ecb->xs->sc_link;
- if (sc_link->target == target &&
- sc_link->lun == lun)
- break;
+ ti = &sc->sc_tinfo[target];
+ li = TINFO_LUN(ti, lun);
+
+ /*
+ * We can get as far as the LUN with the IDENTIFY
+ * message. Check to see if we're running an
+ * un-tagged command. Otherwise ack the IDENTIFY
+ * and wait for a tag message.
+ */
+ if (li != NULL) {
+ if (li->untagged != NULL && li->busy)
+ ecb = li->untagged;
+ else if (tagtype != MSG_SIMPLE_Q_TAG) {
+ /* Wait for tag to come by */
+ sc->sc_state = NCR_IDENTIFIED;
+ return (0);
+ } else if (tagtype) ecb = li->queued[tagid];
}
if (ecb == NULL) {
- printf("%s: reselect from target %d lun %d with no nexus;"
- " sending ABORT\n", sc->sc_dev.dv_xname, target, lun);
+ printf("%s: reselect from target %d lun %d tag %x:%x with no nexus;"
+ " sending ABORT\n", sc->sc_dev.dv_xname, target, lun, tagtype, tagid);
goto abort;
}
/* Make this nexus active again. */
- TAILQ_REMOVE(&sc->nexus_list, ecb, chain);
sc->sc_state = NCR_CONNECTED;
sc->sc_nexus = ecb;
- ti = &sc->sc_tinfo[target];
-#ifdef NCR53C9X_DEBUG
- if ((ti->lubusy & (1 << lun)) == 0) {
- printf("%s: reselect: target %d, lun %d: should be busy\n",
- sc->sc_dev.dv_xname, target, lun);
- ti->lubusy |= (1 << lun);
- }
-#endif
ncr53c9x_setsync(sc, ti);
if (ecb->flags & ECB_RESET)
@@ -985,6 +1388,21 @@ abort:
#define IS2BYTEMSG(m) (((m) & 0xf0) == 0x20)
#define ISEXTMSG(m) ((m) == 1)
+static inline int
+__verify_msg_format(u_char *p, int len)
+{
+
+ if (len == 1 && IS1BYTEMSG(p[0]))
+ return 1;
+ if (len == 2 && IS2BYTEMSG(p[0]))
+ return 1;
+ if (len >= 3 && ISEXTMSG(p[0]) &&
+ len == p[1] + 2)
+ return 1;
+
+ return 0;
+}
+
/*
* Get an incoming message as initiator.
*
@@ -993,15 +1411,13 @@ abort:
*/
void
ncr53c9x_msgin(sc)
- register struct ncr53c9x_softc *sc;
+ struct ncr53c9x_softc *sc;
{
- register int v;
NCR_TRACE(("[ncr53c9x_msgin(curmsglen:%ld)] ", (long)sc->sc_imlen));
- if ((NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF) == 0) {
- printf("%s: msgin: no msg byte available\n",
- sc->sc_dev.dv_xname);
+ if (sc->sc_imlen == 0) {
+ printf("%s: msgin: no msg byte available\n", sc->sc_dev.dv_xname);
return;
}
@@ -1011,32 +1427,19 @@ ncr53c9x_msgin(sc)
* MESSAGE_IN_PHASE. If we have been in some other phase,
* then this is a new message.
*/
- if (sc->sc_prevphase != MESSAGE_IN_PHASE) {
+ if (sc->sc_prevphase != MESSAGE_IN_PHASE && sc->sc_state != NCR_RESELECTED) {
+ printf("%s: phase change, dropping message, prev %d, state %d\n",
+ sc->sc_dev.dv_xname, sc->sc_prevphase, sc->sc_state);
sc->sc_flags &= ~NCR_DROP_MSGI;
sc->sc_imlen = 0;
}
- v = NCR_READ_REG(sc, NCR_FIFO);
- NCR_MISC(("<msgbyte:0x%02x>", v));
-
-#if 0
- if (sc->sc_state == NCR_RESELECTED && sc->sc_imlen == 0) {
- /*
- * Which target is reselecting us? (The ID bit really)
- */
- sc->sc_selid = v;
- NCR_MISC(("selid=0x%2x ", sc->sc_selid));
- return;
- }
-#endif
-
- sc->sc_imess[sc->sc_imlen] = v;
+ NCR_TRACE(("<msgbyte:0x%02x>", sc->sc_imess[0]));
/*
* If we're going to reject the message, don't bother storing
* the incoming bytes. But still, we need to ACK them.
*/
-
if ((sc->sc_flags & NCR_DROP_MSGI)) {
NCRCMD(sc, NCRCMD_MSGOK);
printf("<dropping msg byte %x>",
@@ -1048,27 +1451,37 @@ ncr53c9x_msgin(sc)
ncr53c9x_sched_msgout(SEND_REJECT);
sc->sc_flags |= NCR_DROP_MSGI;
} else {
- sc->sc_imlen++;
- /*
- * This testing is suboptimal, but most
- * messages will be of the one byte variety, so
- * it should not effect performance
- * significantly.
- */
- if (sc->sc_imlen == 1 && IS1BYTEMSG(sc->sc_imess[0]))
- goto gotit;
- if (sc->sc_imlen == 2 && IS2BYTEMSG(sc->sc_imess[0]))
- goto gotit;
- if (sc->sc_imlen >= 3 && ISEXTMSG(sc->sc_imess[0]) &&
- sc->sc_imlen == sc->sc_imess[1] + 2)
+ u_char *pb;
+ int plen;
+
+ switch (sc->sc_state) {
+ /*
+ * if received message is the first of reselection
+ * then first byte is selid, and then message
+ */
+ case NCR_RESELECTED:
+ pb = sc->sc_imess + 1;
+ plen = sc->sc_imlen - 1;
+ break;
+ default:
+ pb = sc->sc_imess;
+ plen = sc->sc_imlen;
+ break;
+ }
+
+ if (__verify_msg_format(pb, plen))
goto gotit;
}
+
/* Ack what we have so far */
NCRCMD(sc, NCRCMD_MSGOK);
return;
gotit:
- NCR_MSGS(("gotmsg(%x)", sc->sc_imess[0]));
+ NCR_MSGS(("gotmsg(%x) state %d", sc->sc_imess[0], sc->sc_state));
+ /* we got complete message, flush the imess, XXX nobody uses imlen below */
+ sc->sc_imlen = 0;
+
/*
* Now we should have a complete message (1 byte, 2 byte
* and moderately long extended messages). We only handle
@@ -1078,6 +1491,8 @@ gotit:
switch (sc->sc_state) {
struct ncr53c9x_ecb *ecb;
struct ncr53c9x_tinfo *ti;
+ struct ncr53c9x_linfo *li;
+ int lun;
case NCR_CONNECTED:
ecb = sc->sc_nexus;
@@ -1087,10 +1502,9 @@ gotit:
case MSG_CMDCOMPLETE:
NCR_MSGS(("cmdcomplete "));
if (sc->sc_dleft < 0) {
- struct scsi_link *sc_link = ecb->xs->sc_link;
- printf("%s: %ld extra bytes from %d:%d\n",
- sc->sc_dev.dv_xname, -(long)sc->sc_dleft,
- sc_link->target, sc_link->lun);
+ sc_print_addr(ecb->xs->sc_link);
+ printf("got %ld extra bytes\n",
+ -(long)sc->sc_dleft);
sc->sc_dleft = 0;
}
ecb->dleft = (ecb->flags & ECB_TENTATIVE_DONE)
@@ -1104,11 +1518,46 @@ gotit:
case MSG_MESSAGE_REJECT:
NCR_MSGS(("msg reject (msgout=%x) ", sc->sc_msgout));
switch (sc->sc_msgout) {
+ case SEND_TAG:
+ /* Target does not like tagged queuing.
+ * - Flush the command queue
+ * - Disable tagged queuing for the target
+ * - Dequeue ecb from the queued array.
+ */
+ printf("%s: tagged queuing rejected: target %d\n",
+ sc->sc_dev.dv_xname, ecb->xs->sc_link->target);
+
+ NCR_MSGS(("(rejected sent tag)"));
+ NCRCMD(sc, NCRCMD_FLUSH);
+ DELAY(1);
+ ti->flags &= ~T_TAG;
+ lun = ecb->xs->sc_link->lun;
+ li = TINFO_LUN(ti, lun);
+ if (ecb->tag[0] &&
+ li->queued[ecb->tag[1]] != NULL) {
+ li->queued[ecb->tag[1]] = NULL;
+ li->used--;
+ }
+ ecb->tag[0] = ecb->tag[1] = 0;
+ li->untagged = ecb;
+ li->busy = 1;
+ break;
+
case SEND_SDTR:
+ printf("%s: sync transfer rejected: target %d\n",
+ sc->sc_dev.dv_xname, ecb->xs->sc_link->target);
sc->sc_flags &= ~NCR_SYNCHNEGO;
ti->flags &= ~(T_NEGOTIATE | T_SYNCMODE);
ncr53c9x_setsync(sc, ti);
break;
+
+ case SEND_WDTR:
+ printf("%s: wide transfer rejected: target %d\n",
+ sc->sc_dev.dv_xname, ecb->xs->sc_link->target);
+ ti->flags &= ~T_WIDE;
+ ti->width = 0;
+ break;
+
case SEND_INIT_DET_ERR:
goto abort;
}
@@ -1118,6 +1567,12 @@ gotit:
NCR_MSGS(("noop "));
break;
+ case MSG_HEAD_OF_Q_TAG:
+ case MSG_SIMPLE_Q_TAG:
+ case MSG_ORDERED_Q_TAG:
+ NCR_MSGS(("TAG %x:%x", sc->sc_imess[0], sc->sc_imess[1]));
+ break;
+
case MSG_DISCONNECT:
NCR_MSGS(("disconnect "));
ti->dconns++;
@@ -1160,8 +1615,10 @@ gotit:
if (sc->sc_minsync == 0 ||
ti->offset == 0 ||
ti->period > 124) {
- printf("%s:%d: async\n", "esp",
- ecb->xs->sc_link->target);
+#ifdef NCR53C9X_DEBUG
+ sc_print_addr(ecb->xs->sc_link);
+ printf("async mode\n");
+#endif
if ((sc->sc_flags&NCR_SYNCHNEGO)
== 0) {
/*
@@ -1184,7 +1641,7 @@ gotit:
ti->period = ncr53c9x_cpb2stp(sc, p);
#ifdef NCR53C9X_DEBUG
sc_print_addr(ecb->xs->sc_link);
- printf("max sync rate %d.%02dMb/s\n",
+ printf("max sync rate %d.%02dMB/s\n",
r, s);
#endif
if ((sc->sc_flags&NCR_SYNCHNEGO) == 0) {
@@ -1209,34 +1666,67 @@ gotit:
ncr53c9x_setsync(sc, ti);
break;
+ case MSG_EXT_WDTR:
+ printf("%s: wide mode %d\n",
+ sc->sc_dev.dv_xname, sc->sc_imess[3]);
+ if (sc->sc_imess[3] == 1) {
+ ti->cfg3 |= NCRFASCFG3_EWIDE;
+ ncr53c9x_setsync(sc, ti);
+ } else
+ ti->width = 0;
+ ti->flags &= ~T_WIDE;
+ break;
default:
- printf("%s: unrecognized MESSAGE EXTENDED;"
- " sending REJECT\n", sc->sc_dev.dv_xname);
+ sc_print_addr(ecb->xs->sc_link);
+ printf("unrecognized MESSAGE EXTENDED;"
+ " sending REJECT\n");
goto reject;
}
break;
default:
NCR_MSGS(("ident "));
- printf("%s: unrecognized MESSAGE; sending REJECT\n",
- sc->sc_dev.dv_xname);
+ sc_print_addr(ecb->xs->sc_link);
+ printf("unrecognized MESSAGE; sending REJECT\n");
reject:
ncr53c9x_sched_msgout(SEND_REJECT);
break;
}
break;
+ case NCR_IDENTIFIED:
+ /*
+ * IDENTIFY message was recived and queue tag is expected now
+ */
+ if ((sc->sc_imess[0] != MSG_SIMPLE_Q_TAG) ||
+ (sc->sc_msgify == 0)) {
+ printf("%s: TAG reselect without IDENTIFY;"
+ " MSG %x;"
+ " sending DEVICE RESET\n",
+ sc->sc_dev.dv_xname,
+ sc->sc_imess[0]);
+ goto reset;
+ }
+ (void) ncr53c9x_reselect(sc, sc->sc_msgify,
+ sc->sc_imess[0], sc->sc_imess[1]);
+ break;
+
case NCR_RESELECTED:
- if (!MSG_ISIDENTIFY(sc->sc_imess[0])) {
+ if (MSG_ISIDENTIFY(sc->sc_imess[1])) {
+ sc->sc_msgify = sc->sc_imess[1];
+ } else {
printf("%s: reselect without IDENTIFY;"
- " sending DEVICE RESET\n", sc->sc_dev.dv_xname);
+ " MSG %x;"
+ " sending DEVICE RESET\n",
+ sc->sc_dev.dv_xname,
+ sc->sc_imess[1]);
goto reset;
}
-
- (void) ncr53c9x_reselect(sc, sc->sc_imess[0]);
+ (void) ncr53c9x_reselect(sc, sc->sc_msgify, 0, 0);
break;
default:
+ sc_print_addr(ecb->xs->sc_link);
printf("%s: unexpected MESSAGE IN; sending DEVICE RESET\n",
sc->sc_dev.dv_xname);
reset:
@@ -1248,6 +1738,10 @@ gotit:
break;
}
+ /* if we have more messages to send set ATN */
+ if (sc->sc_msgpriq)
+ NCRCMD(sc, NCRCMD_SETATN);
+
/* Ack last message byte */
NCRCMD(sc, NCRCMD_MSGOK);
@@ -1262,7 +1756,7 @@ gotit:
*/
void
ncr53c9x_msgout(sc)
- register struct ncr53c9x_softc *sc;
+ struct ncr53c9x_softc *sc;
{
struct ncr53c9x_tinfo *ti;
struct ncr53c9x_ecb *ecb;
@@ -1282,7 +1776,7 @@ ncr53c9x_msgout(sc)
if (sc->sc_prevphase != MESSAGE_OUT_PHASE) {
new:
NCRCMD(sc, NCRCMD_FLUSH);
- DELAY(1);
+/* DELAY(1); */
sc->sc_msgoutq = 0;
sc->sc_omlen = 0;
}
@@ -1317,6 +1811,15 @@ ncr53c9x_msgout(sc)
ncr53c9x_setsync(sc, ti);
}
break;
+ case SEND_WDTR:
+ ecb = sc->sc_nexus;
+ ti = &sc->sc_tinfo[ecb->xs->sc_link->target];
+ sc->sc_omess[0] = MSG_EXTENDED;
+ sc->sc_omess[1] = 2;
+ sc->sc_omess[2] = MSG_EXT_WDTR;
+ sc->sc_omess[3] = ti->width;
+ sc->sc_omlen = 4;
+ break;
case SEND_IDENTIFY:
if (sc->sc_state != NCR_CONNECTED) {
printf("%s at line %d: no nexus\n",
@@ -1326,13 +1829,25 @@ ncr53c9x_msgout(sc)
sc->sc_omess[0] =
MSG_IDENTIFY(ecb->xs->sc_link->lun, 0);
break;
+ case SEND_TAG:
+ if (sc->sc_state != NCR_CONNECTED) {
+ printf("%s at line %d: no nexus\n",
+ sc->sc_dev.dv_xname, __LINE__);
+ }
+ ecb = sc->sc_nexus;
+ sc->sc_omess[0] = ecb->tag[0];
+ sc->sc_omess[1] = ecb->tag[1];
+ sc->sc_omlen = 2;
+ break;
case SEND_DEV_RESET:
sc->sc_flags |= NCR_ABORTING;
sc->sc_omess[0] = MSG_BUS_DEV_RESET;
ecb = sc->sc_nexus;
ti = &sc->sc_tinfo[ecb->xs->sc_link->target];
ti->flags &= ~T_SYNCMODE;
- ti->flags |= T_NEGOTIATE;
+ if ((ti->flags & T_SYNCHOFF) == 0)
+ /* We can re-start sync negotiation */
+ ti->flags |= T_NEGOTIATE;
break;
case SEND_PARITY_ERROR:
sc->sc_omess[0] = MSG_PARITY_ERROR;
@@ -1368,19 +1883,33 @@ ncr53c9x_msgout(sc)
sc->sc_omp = sc->sc_omess;
}
- /* (re)send the message */
- size = min(sc->sc_omlen, sc->sc_maxxfer);
- NCRDMA_SETUP(sc, &sc->sc_omp, &sc->sc_omlen, 0, &size);
- /* Program the SCSI counter */
- NCR_WRITE_REG(sc, NCR_TCL, size);
- NCR_WRITE_REG(sc, NCR_TCM, size >> 8);
- if (sc->sc_cfg2 & NCRCFG2_FE) {
- NCR_WRITE_REG(sc, NCR_TCH, size >> 16);
+#ifdef DEBUG
+ {
+ int i;
+
+ for (i = 0; i<sc->sc_omlen; i++)
+ NCR_MISC(("<msgbyte:0x%02x>", sc->sc_omess[i]));
+ }
+#endif
+ if (sc->sc_rev == NCR_VARIANT_FAS366) {
+ /*
+ * XXX fifo size
+ */
+ ncr53c9x_flushfifo(sc);
+ ncr53c9x_wrfifo(sc, sc->sc_omp, sc->sc_omlen);
+ NCRCMD(sc, NCRCMD_TRANS);
+ } else {
+ /* (re)send the message */
+ size = min(sc->sc_omlen, sc->sc_maxxfer);
+ NCRDMA_SETUP(sc, &sc->sc_omp, &sc->sc_omlen, 0, &size);
+ /* Program the SCSI counter */
+ NCR_SET_COUNT(sc, size);
+
+ /* 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);
}
- /* 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);
}
/*
@@ -1397,14 +1926,14 @@ int
ncr53c9x_intr(arg)
void *arg;
{
- register struct ncr53c9x_softc *sc = arg;
- register struct ncr53c9x_ecb *ecb;
- register struct scsi_link *sc_link;
+ struct ncr53c9x_softc *sc = arg;
+ struct ncr53c9x_ecb *ecb;
+ struct scsi_link *sc_link;
struct ncr53c9x_tinfo *ti;
size_t size;
int nfifo;
- NCR_TRACE(("[ncr53c9x_intr] "));
+ NCR_TRACE(("[ncr53c9x_intr: state %d] ", sc->sc_state));
if (!NCRDMA_ISINTR(sc))
return (0);
@@ -1547,13 +2076,13 @@ again:
if (sc->sc_phase != MESSAGE_IN_PHASE)
printf("%s: !TC on MSG OUT"
" [intr %x, stat %x, step %d]"
- " prevphase %x, resid %x\n",
+ " prevphase %x, resid %lx\n",
sc->sc_dev.dv_xname,
sc->sc_espintr,
sc->sc_espstat,
sc->sc_espstep,
sc->sc_prevphase,
- sc->sc_omlen);
+ (u_long)sc->sc_omlen);
} else if (sc->sc_dleft == 0) {
/*
* The DMA operation was started for
@@ -1586,11 +2115,12 @@ again:
}
if (sc->sc_espintr & NCRINTR_DIS) {
+ sc->sc_msgify = 0;
NCR_MISC(("<DISC [intr %x, stat %x, step %d]>",
sc->sc_espintr,sc->sc_espstat,sc->sc_espstep));
if (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF) {
NCRCMD(sc, NCRCMD_FLUSH);
- DELAY(1);
+/* DELAY(1); */
}
/*
* This command must (apparently) be issued within
@@ -1603,9 +2133,29 @@ again:
goto sched;
case NCR_SELECTING:
+ {
+ struct ncr53c9x_linfo *li;
+
ecb->xs->error = XS_SELTIMEOUT;
- goto finish;
+ /* Selection timeout -- discard all LUNs if empty */
+ sc_link = ecb->xs->sc_link;
+ ti = &sc->sc_tinfo[sc_link->target];
+ li = ti->luns.lh_first;
+ while (li) {
+ if (!li->untagged && !li->used) {
+ if (li->lun < NCR_NLUN)
+ ti->lun[li->lun] = NULL;
+ LIST_REMOVE(li, link);
+ free(li, M_DEVBUF);
+ /* Restart the search at the beginning */
+ li = ti->luns.lh_first;
+ continue;
+ }
+ li = li->link.le_next;
+ }
+ goto finish;
+ }
case NCR_CONNECTED:
if ((sc->sc_flags & NCR_SYNCHNEGO)) {
#ifdef NCR53C9X_DEBUG
@@ -1645,7 +2195,6 @@ again:
goto finish;
case NCR_DISCONNECT:
- TAILQ_INSERT_HEAD(&sc->nexus_list, ecb, chain);
sc->sc_nexus = NULL;
goto sched;
@@ -1684,17 +2233,32 @@ printf("<<RESELECT CONT'd>>");
#endif
break;
+ case NCR_IDENTIFIED:
+ ecb = sc->sc_nexus;
+ if (sc->sc_phase != MESSAGE_IN_PHASE) {
+ int i = (NCR_READ_REG(sc, NCR_FFLAG)
+ & NCRFIFO_FF);
+ /*
+ * Things are seriously fucked up.
+ * Pull the brakes, i.e. reset
+ */
+ printf("%s: target didn't send tag: %d bytes in fifo\n",
+ sc->sc_dev.dv_xname, i);
+ /* Drain and display fifo */
+ while (i-- > 0)
+ printf("[%d] ", NCR_READ_REG(sc, NCR_FIFO));
+ ncr53c9x_init(sc, 1);
+ return (1);
+ } else
+ goto msgin;
+
+ break;
case NCR_IDLE:
case NCR_SELECTING:
- sc->sc_msgpriq = sc->sc_msgout = sc->sc_msgoutq = 0;
- sc->sc_flags = 0;
ecb = sc->sc_nexus;
- if (ecb != NULL && (ecb->flags & ECB_NEXUS)) {
- sc_print_addr(ecb->xs->sc_link);
- printf("ECB_NEXUS while in state %x\n", sc->sc_state);
- }
-
if (sc->sc_espintr & NCRINTR_RESEL) {
+ sc->sc_msgpriq = sc->sc_msgout = sc->sc_msgoutq = 0;
+ sc->sc_flags = 0;
/*
* If we're trying to select a
* target ourselves, push our command
@@ -1703,9 +2267,9 @@ printf("<<RESELECT CONT'd>>");
if (sc->sc_state == NCR_SELECTING) {
NCR_MISC(("backoff selector "));
timeout_del(&ecb->to);
- sc_link = ecb->xs->sc_link;
- ti = &sc->sc_tinfo[sc_link->target];
+ ncr53c9x_dequeue(sc, ecb);
TAILQ_INSERT_HEAD(&sc->ready_list, ecb, chain);
+ ecb->flags |= ECB_READY;
ecb = sc->sc_nexus = NULL;
}
sc->sc_state = NCR_RESELECTED;
@@ -1729,7 +2293,8 @@ printf("<<RESELECT CONT'd>>");
* appear in the FIFO here, after the
* interrupt is taken.
*/
- nfifo = NCR_READ_REG(sc,NCR_FFLAG) & NCRFIFO_FF;
+ nfifo = ncr53c9x_rdfifo(sc, NCR_RDFIFO_START);
+
if (nfifo < 2 ||
(nfifo > 2 &&
sc->sc_rev != NCR_VARIANT_ESP100)) {
@@ -1745,8 +2310,8 @@ printf("<<RESELECT CONT'd>>");
ncr53c9x_init(sc, 1);
return (1);
}
- sc->sc_selid = NCR_READ_REG(sc, NCR_FIFO);
- NCR_MISC(("selid=0x%2x ", sc->sc_selid));
+ sc->sc_selid = sc->sc_imess[0];
+ NCR_MISC(("selid=%2x ", sc->sc_selid));
/* Handle identify message */
ncr53c9x_msgin(sc);
@@ -1757,15 +2322,17 @@ printf("<<RESELECT CONT'd>>");
*/
sc->sc_flags |= NCR_EXPECT_ILLCMD;
NCRCMD(sc, NCRCMD_FLUSH);
- } else if (ncr53c9x_dmaselect &&
+ } else if (sc->sc_features & NCR_F_DMASELECT &&
sc->sc_rev == NCR_VARIANT_ESP100) {
sc->sc_flags |= NCR_EXPECT_ILLCMD;
}
- if (sc->sc_state != NCR_CONNECTED) {
+ if (sc->sc_state != NCR_CONNECTED &&
+ sc->sc_state != NCR_IDENTIFIED) {
/* IDENTIFY fail?! */
- printf("%s: identify failed\n",
- sc->sc_dev.dv_xname);
+ printf("%s: identify failed, state %d, intr %02x\n",
+ sc->sc_dev.dv_xname, sc->sc_state,
+ sc->sc_espintr);
ncr53c9x_init(sc, 1);
return (1);
}
@@ -1780,7 +2347,7 @@ printf("<<RESELECT CONT'd>>");
*/
ecb = sc->sc_nexus;
if (!ecb)
- panic("esp: no nexus");
+ panic("ncr53c9x: no nexus");
sc_link = ecb->xs->sc_link;
ti = &sc->sc_tinfo[sc_link->target];
@@ -1799,7 +2366,8 @@ printf("<<RESELECT CONT'd>>");
NCRCMD(sc, NCRCMD_RSTATN);
break;
case 1:
- if ((ti->flags & T_NEGOTIATE) == 0) {
+ if ((ti->flags & T_NEGOTIATE) == 0 &&
+ ecb->tag[0] == 0) {
printf("%s: step 1 & !NEG\n",
sc->sc_dev.dv_xname);
goto reset;
@@ -1809,11 +2377,23 @@ printf("<<RESELECT CONT'd>>");
sc->sc_dev.dv_xname);
goto reset;
}
- /* Start negotiating */
- ti->period = sc->sc_minsync;
- ti->offset = 15;
- sc->sc_flags |= NCR_SYNCHNEGO;
- ncr53c9x_sched_msgout(SEND_SDTR);
+ if (ti->flags & T_WIDE) {
+ ncr53c9x_sched_msgout(SEND_WDTR);
+ }
+ if (ti->flags & T_NEGOTIATE) {
+ /* Start negotiating */
+ ti->period = sc->sc_minsync;
+ ti->offset = 15;
+ sc->sc_flags |= NCR_SYNCHNEGO;
+ if (ecb->tag[0])
+ ncr53c9x_sched_msgout(SEND_TAG|SEND_SDTR);
+ else
+ ncr53c9x_sched_msgout(SEND_SDTR);
+ } else {
+ /* Could not do ATN3 so send TAG */
+ ncr53c9x_sched_msgout(SEND_TAG);
+ }
+ sc->sc_prevphase = MESSAGE_OUT_PHASE; /* XXXX */
break;
case 3:
/*
@@ -1824,7 +2404,7 @@ printf("<<RESELECT CONT'd>>");
* Look at FIFO to see if command went out.
* (Timing problems?)
*/
- if (ncr53c9x_dmaselect) {
+ if (sc->sc_features & NCR_F_DMASELECT) {
if (sc->sc_cmdlen == 0)
/* Hope for the best.. */
break;
@@ -1851,15 +2431,15 @@ printf("<<RESELECT CONT'd>>");
NCRCMD(sc, NCRCMD_FLUSH);
break;
case 4:
- if (ncr53c9x_dmaselect &&
+ if (sc->sc_features & NCR_F_DMASELECT &&
sc->sc_cmdlen != 0)
printf("(%s:%d:%d): select; "
- "%d left in DMA buffer "
+ "%lu 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,
+ (u_long)sc->sc_cmdlen,
sc->sc_espintr,
sc->sc_espstat,
sc->sc_espstep);
@@ -1867,9 +2447,6 @@ printf("<<RESELECT CONT'd>>");
break;
}
- ecb->flags |= ECB_NEXUS;
- ti->lubusy |= (1 << sc_link->lun);
-
sc->sc_prevphase = INVALID_PHASE; /* ?? */
/* Do an implicit RESTORE POINTERS. */
sc->sc_dp = ecb->daddr;
@@ -1909,16 +2486,12 @@ printf("<<RESELECT CONT'd>>");
sc->sc_espintr, sc->sc_espstat,
sc->sc_espstep);
}
- if ((NCR_READ_REG(sc, NCR_FFLAG)
- & NCRFIFO_FF) != 2) {
- /* Drop excess bytes from the queue */
- int i = (NCR_READ_REG(sc, NCR_FFLAG)
- & NCRFIFO_FF) - 2;
- while (i-- > 0)
- (void) NCR_READ_REG(sc, NCR_FIFO);
- }
- ecb->stat = NCR_READ_REG(sc, NCR_FIFO);
- msg = NCR_READ_REG(sc, NCR_FIFO);
+ ncr53c9x_rdfifo(sc, NCR_RDFIFO_START);
+ if (sc->sc_imlen < 2)
+ printf("%s: can't get status, only %d bytes\n",
+ sc->sc_dev.dv_xname, (int)sc->sc_imlen);
+ ecb->stat = sc->sc_imess[sc->sc_imlen - 2];
+ msg = sc->sc_imess[sc->sc_imlen - 1];
NCR_PHASE(("<stat:(%x,%x)>", ecb->stat, msg));
if (msg == MSG_CMDCOMPLETE) {
ecb->dleft = (ecb->flags & ECB_TENTATIVE_DONE)
@@ -1930,14 +2503,19 @@ printf("<<RESELECT CONT'd>>");
} else
printf("%s: STATUS_PHASE: msg %d\n",
sc->sc_dev.dv_xname, msg);
+ sc->sc_imlen = 0;
NCRCMD(sc, NCRCMD_MSGOK);
goto shortcut; /* ie. wait for disconnect */
}
break;
default:
- panic("%s: invalid state: %d",
+ /* Don't panic: reset. */
+ printf("%s: invalid state: %d",
sc->sc_dev.dv_xname,
sc->sc_state);
+ ncr53c9x_scsi_reset(sc);
+ goto out;
+ break;
}
/*
@@ -1945,7 +2523,7 @@ printf("<<RESELECT CONT'd>>");
* have a current command working the SCSI bus.
*/
if (sc->sc_state != NCR_CONNECTED || ecb == NULL) {
- panic("esp no nexus");
+ panic("ncr53c9x no nexus");
}
switch (sc->sc_phase) {
@@ -1955,10 +2533,13 @@ printf("<<RESELECT CONT'd>>");
sc->sc_prevphase = MESSAGE_OUT_PHASE;
break;
case MESSAGE_IN_PHASE:
+msgin:
NCR_PHASE(("MESSAGE_IN_PHASE "));
- sc->sc_prevphase = MESSAGE_IN_PHASE;
if (sc->sc_espintr & NCRINTR_BS) {
- NCRCMD(sc, NCRCMD_FLUSH);
+ if ((sc->sc_rev != NCR_VARIANT_FAS366) ||
+ !(sc->sc_espstat2 & FAS_STAT2_EMPTY)) {
+ NCRCMD(sc, NCRCMD_FLUSH);
+ }
sc->sc_flags |= NCR_WAITI;
NCRCMD(sc, NCRCMD_TRANS);
} else if (sc->sc_espintr & NCRINTR_FC) {
@@ -1970,6 +2551,9 @@ printf("<<RESELECT CONT'd>>");
sc->sc_espstep);
}
sc->sc_flags &= ~NCR_WAITI;
+ ncr53c9x_rdfifo(sc,
+ (sc->sc_prevphase == sc->sc_phase) ?
+ NCR_RDFIFO_CONTINUE : NCR_RDFIFO_START);
ncr53c9x_msgin(sc);
} else {
printf("%s: MSGIN: weird bits: "
@@ -1978,6 +2562,7 @@ printf("<<RESELECT CONT'd>>");
sc->sc_espintr, sc->sc_espstat,
sc->sc_espstep);
}
+ sc->sc_prevphase = MESSAGE_IN_PHASE;
goto shortcut; /* i.e. expect data to be ready */
break;
case COMMAND_PHASE:
@@ -1993,9 +2578,9 @@ printf("<<RESELECT CONT'd>>");
ecb->cmd.cmd.opcode, ecb->clen));
if (NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF) {
NCRCMD(sc, NCRCMD_FLUSH);
- DELAY(1);
+/* DELAY(1); */
}
- if (ncr53c9x_dmaselect) {
+ if (sc->sc_features & NCR_F_DMASELECT) {
size_t size;
/* setup DMA transfer for command */
size = ecb->clen;
@@ -2004,11 +2589,7 @@ printf("<<RESELECT CONT'd>>");
NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen,
0, &size);
/* Program the SCSI counter */
- NCR_WRITE_REG(sc, NCR_TCL, size);
- NCR_WRITE_REG(sc, NCR_TCM, size >> 8);
- if (sc->sc_cfg2 & NCRCFG2_FE) {
- NCR_WRITE_REG(sc, NCR_TCH, size >> 16);
- }
+ NCR_SET_COUNT(sc, size);
/* load the count in */
NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA);
@@ -2017,11 +2598,7 @@ printf("<<RESELECT CONT'd>>");
NCRCMD(sc, NCRCMD_TRANS | NCRCMD_DMA);
NCRDMA_GO(sc);
} else {
- u_char *cmd = (u_char *)&ecb->cmd.cmd;
- int i;
- /* Now the command into the FIFO */
- for (i = 0; i < ecb->clen; i++)
- NCR_WRITE_REG(sc, NCR_FIFO, *cmd++);
+ ncr53c9x_wrfifo(sc, (u_char *)&ecb->cmd.cmd, ecb->clen);
NCRCMD(sc, NCRCMD_TRANS);
}
sc->sc_prevphase = COMMAND_PHASE;
@@ -2047,11 +2624,8 @@ printf("<<RESELECT CONT'd>>");
ecb->flags &= ~ECB_TENTATIVE_DONE;
/* Program the SCSI counter */
- NCR_WRITE_REG(sc, NCR_TCL, size);
- NCR_WRITE_REG(sc, NCR_TCM, size >> 8);
- if (sc->sc_cfg2 & NCRCFG2_FE) {
- NCR_WRITE_REG(sc, NCR_TCH, size >> 16);
- }
+ NCR_SET_COUNT(sc, size);
+
/* load the count in */
NCRCMD(sc, NCRCMD_NOP|NCRCMD_DMA);
@@ -2107,9 +2681,22 @@ shortcut:
* The delay is a heuristic. It is 2 when at 20Mhz, 2 at 25Mhz and 1
* at 40Mhz. This needs testing.
*/
- DELAY(50/sc->sc_freq);
- if (NCRDMA_ISINTR(sc))
- goto again;
+ {
+ struct timeval wait, cur;
+
+ microtime(&wait);
+ wait.tv_usec += 50/sc->sc_freq;
+ if (wait.tv_usec > 1000000) {
+ wait.tv_sec++;
+ wait.tv_usec -= 1000000;
+ }
+ do {
+ if (NCRDMA_ISINTR(sc))
+ goto again;
+ microtime(&cur);
+ } while (cur.tv_sec <= wait.tv_sec &&
+ cur.tv_usec <= wait.tv_usec);
+ }
goto out;
}
@@ -2124,6 +2711,8 @@ ncr53c9x_abort(sc, ecb)
ecb->flags |= ECB_ABORT;
if (ecb == sc->sc_nexus) {
+ int timeout;
+
/*
* If we're still selecting, the message will be scheduled
* after selection is complete.
@@ -2134,16 +2723,14 @@ ncr53c9x_abort(sc, ecb)
/*
* Reschedule timeout.
*/
- timeout_add(&ecb->to, (ecb->timeout * hz) / 1000);
+ if (hz > 100 && timeout > 1000)
+ timeout = (timeout / 1000) * hz;
+ else
+ timeout = (timeout * hz) / 1000;
+ timeout_add(&ecb->to, timeout);
} else {
- /* The command should be on the nexus list */
- if ((ecb->flags & ECB_NEXUS) == 0) {
- sc_print_addr(ecb->xs->sc_link);
- printf("ncr53c9x_abort: not NEXUS\n");
- ncr53c9x_init(sc, 1);
- }
/*
- * Just leave the command on the nexus list.
+ * Just leave the command where it is.
* XXX - what choice do we have but to reset the SCSI
* eventually?
*/
@@ -2203,3 +2790,35 @@ ncr53c9x_timeout(arg)
splx(s);
}
+
+void
+ncr53c9x_watch(arg)
+ void *arg;
+{
+ struct ncr53c9x_softc *sc = (struct ncr53c9x_softc *)arg;
+ struct ncr53c9x_tinfo *ti;
+ struct ncr53c9x_linfo *li;
+ int t, s;
+ /* Delete any structures that have not been used in 10min. */
+ time_t old = time.tv_sec - (10*60);
+
+ s = splbio();
+ for (t=0; t<NCR_NTARG; t++) {
+ ti = &sc->sc_tinfo[t];
+ li = ti->luns.lh_first;
+ while (li) {
+ if (li->last_used < old && !li->untagged && !li->used) {
+ if (li->lun < NCR_NLUN)
+ ti->lun[li->lun] = NULL;
+ LIST_REMOVE(li, link);
+ free(li, M_DEVBUF);
+ /* Restart the search at the beginning */
+ li = ti->luns.lh_first;
+ continue;
+ }
+ li = li->link.le_next;
+ }
+ }
+ splx(s);
+ timeout_add(&sc->sc_watchdog, 60*hz);
+}
diff --git a/sys/dev/ic/ncr53c9xreg.h b/sys/dev/ic/ncr53c9xreg.h
index cfebd6983c5..fe21b94a01f 100644
--- a/sys/dev/ic/ncr53c9xreg.h
+++ b/sys/dev/ic/ncr53c9xreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ncr53c9xreg.h,v 1.5 2001/08/21 14:22:27 jason Exp $ */
+/* $OpenBSD: ncr53c9xreg.h,v 1.6 2001/09/27 04:01:42 jason Exp $ */
/* $NetBSD: ncr53c9xreg.h,v 1.4 1997/05/17 20:56:55 pk Exp $ */
/*
@@ -83,6 +83,8 @@
#define NCRSTAT_PHASE 0x07 /* Phase bits */
#define NCR_SELID 0x04 /* WO - Select/Reselect Bus ID */
+#define NCR_BUSID_HME 0x10 /* XXX HME reselect ID */
+#define NCR_BUSID_HME32 0x40 /* XXX HME to select more than 16 */
#define NCR_INTR 0x05 /* RO - Interrupt */
#define NCRINTR_SBR 0x80 /* SCSI Bus Reset */
@@ -139,6 +141,8 @@
#define NCRCFG2_BPA 0x04 /* Target Bad Parity Abort */
#define NCRCFG2_RPE 0x02 /* Register Parity Error */
#define NCRCFG2_DPE 0x01 /* DMA Parity Error */
+#define NCRCFG2_HMEFE 0x10 /* HME feature enable */
+#define NCRCFG2_HME32 0x80 /* HME 32 extended */
/* Config #3 only on 53C9X */
#define NCR_CFG3 0x0c /* RW - Configuration #3 */
@@ -178,6 +182,16 @@
#define NCRF9XCFG3_ADMA 0x02 /* Alternate DMA Mode */
#define NCRF9XCFG3_T8M 0x01 /* Threshold 8 Mode */
+/* Config #3 on FAS366 */
+#define NCRFASCFG3_OBAUTO 0x80 /* auto push odd-byte to dma */
+#define NCRFASCFG3_EWIDE 0x40 /* Enable Wide-SCSI */
+#define NCRFASCFG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID */
+#define NCRFASCFG3_IDRESCHK 0x10 /* ID message checking */
+#define NCRFASCFG3_QUENB 0x08 /* 3-byte msg support */
+#define NCRFASCFG3_CDB10 0x04 /* group 2 scsi-2 support */
+#define NCRFASCFG3_FASTSCSI 0x02 /* 10 MB/S fast scsi mode */
+#define NCRFASCFG3_FASTCLK 0x01 /* fast clock mode */
+
/* Config #4 only on ESP406/FAS408 */
#define NCR_CFG4 0x0d /* RW - Configuration #4 */
#define NCRCFG4_CRS1 0x80 /* Select register set #1 */
@@ -260,3 +274,13 @@
#define NCR_RCH 0xf /* Recommand counter high */
#define NCR_UID NCR_RCL /* fas366 part-uniq id */
+/* status register #2 definitions (read only) */
+#define NCR_STAT2 NCR_CCF
+#define FAS_STAT2_SEQCNT 0x01 /* Sequence counter bit 7-3 enabled */
+#define FAS_STAT2_FLATCHED 0x02 /* FIFO flags register latched */
+#define FAS_STAT2_CLATCHED 0x04 /* Xfer cntr & recommand ctr latched */
+#define FAS_STAT2_CACTIVE 0x08 /* Command register is active */
+#define FAS_STAT2_SCSI16 0x10 /* SCSI interface is wide */
+#define FAS_STAT2_ISHUTTLE 0x20 /* FIFO Top register contains 1 byte */
+#define FAS_STAT2_OSHUTTLE 0x40 /* next byte from FIFO is MSB */
+#define FAS_STAT2_EMPTY 0x80 /* FIFO is empty */
diff --git a/sys/dev/ic/ncr53c9xvar.h b/sys/dev/ic/ncr53c9xvar.h
index 7bf24d63808..59d9e3556c7 100644
--- a/sys/dev/ic/ncr53c9xvar.h
+++ b/sys/dev/ic/ncr53c9xvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ncr53c9xvar.h,v 1.8 2001/08/21 14:22:27 jason Exp $ */
+/* $OpenBSD: ncr53c9xvar.h,v 1.9 2001/09/27 04:01:42 jason Exp $ */
/* $NetBSD: ncr53c9xvar.h,v 1.13 1998/05/26 23:17:34 thorpej Exp $ */
/*-
@@ -72,6 +72,10 @@
/* Set this to 1 for normal debug, or 2 for per-target tracing. */
#define NCR53C9X_DEBUG 1
+/* Wide or differential can have 16 targets */
+#define NCR_NTARG 8
+#define NCR_NLUN 8
+
#define NCR_ABORT_TIMEOUT 2000 /* time to wait for abort */
#define NCR_SENSE_TIMEOUT 1000 /* time to wait for sense */
@@ -106,7 +110,7 @@ struct ncr53c9x_ecb {
struct scsi_xfer *xs; /* SCSI xfer ctrl block from above */
int flags;
#define ECB_ALLOC 0x01
-#define ECB_NEXUS 0x02
+#define ECB_READY 0x02
#define ECB_SENSE 0x04
#define ECB_ABORT 0x40
#define ECB_RESET 0x80
@@ -115,14 +119,15 @@ struct ncr53c9x_ecb {
struct timeout to;
struct {
- u_char id; /* Selection Id msg */
+ u_char msg[3]; /* Selection Id msg */
struct scsi_generic cmd; /* SCSI command block */
} cmd;
- int clen; /* Size of command in cmd.cmd */
char *daddr; /* Saved data pointer */
- int dleft; /* Residue */
- u_char stat; /* SCSI status byte */
- u_char pad[3];
+ int clen; /* Size of command in cmd.cmd */
+ int dleft; /* Residue */
+ u_char stat; /* SCSI status byte */
+ u_char tag[2]; /* TAG bytes */
+ u_char pad[1];
#if NCR53C9X_DEBUG > 1
char trace[1000];
@@ -140,17 +145,34 @@ struct ncr53c9x_ecb {
#endif
/*
- * Some info about each (possible) target on the SCSI bus. This should
- * probably have been a "per target+lunit" structure, but we'll leave it at
- * this for now. Is there a way to reliably hook it up to sc->fordriver??
+ * Some info about ech (possible) target and LUN on the SCSI bus.
+ *
+ * SCSI I and II devices can have up to 8 LUNs, each with up to 256
+ * outstanding tags. SCSI III devices have 64-bit LUN identifiers
+ * that can be sparsely allocated.
+ *
+ * Since SCSI II devices can have up to 8 LUNs, we use an array
+ * of 8 pointers to ncr53c9x_linfo structures for fast lookup.
+ * Longer LUNs need to traverse the linked list.
*/
+
+struct ncr53c9x_linfo {
+ int64_t lun;
+ LIST_ENTRY(ncr53c9x_linfo) link;
+ time_t last_used;
+ unsigned char used; /* # slots in use */
+ unsigned char avail; /* where to start scanning */
+ unsigned char busy;
+ struct ncr53c9x_ecb *untagged;
+ struct ncr53c9x_ecb *queued[256];
+};
+
struct ncr53c9x_tinfo {
- int cmds; /* #commands processed */
- int dconns; /* #disconnects */
- int touts; /* #timeouts */
- int perrs; /* #parity errors */
- int senses; /* #request sense commands sent */
- ushort lubusy; /* What local units/subr. are busy? */
+ int cmds; /* # of commands processed */
+ int dconns; /* # of disconnects */
+ int touts; /* # of timeouts */
+ int perrs; /* # of parity errors */
+ int senses; /* # of request sense commands sent */
u_char flags;
#define T_NEED_TO_RESET 0x01 /* Should send a BUS_DEV_RESET */
#define T_NEGOTIATE 0x02 /* (Re)Negotiate synchronous options */
@@ -158,10 +180,20 @@ struct ncr53c9x_tinfo {
#define T_SYNCMODE 0x08 /* sync mode has been negotiated */
#define T_SYNCHOFF 0x10 /* .. */
#define T_RSELECTOFF 0x20 /* .. */
+#define T_TAG 0x40 /* TAG QUEUEs are on */
+#define T_WIDE 0x80 /* Negotiate wide options */
u_char period; /* Period suggestion */
u_char offset; /* Offset suggestion */
- u_char pad[3];
-} tinfo_t;
+ u_char cfg3; /* per target config 3 */
+ u_char nextag; /* Next available tag */
+ u_char width; /* width suggestion */
+ LIST_HEAD(lun_list, ncr53c9x_linfo) luns;
+ struct ncr53c9x_linfo *lun[NCR_NLUN]; /* For speedy lookups */
+};
+
+/* Look up a lun in a tinfo */
+#define TINFO_LUN(t, l) ((((l) < NCR_NLUN) && (((t)->lun[(l)]) != NULL)) ? \
+ ((t)->lun[(l)]) : ncr53c9x_lunsearch((t), (int64_t)(l)))
/* Register a linenumber (for debugging) */
#define LOGLINE(p)
@@ -237,6 +269,7 @@ struct ncr53c9x_softc {
struct device sc_dev; /* us as a device */
struct evcnt sc_intrcnt; /* intr count */
+ struct timeout sc_watchdog; /* periodic timer */
struct scsi_link sc_link; /* scsi lint struct */
struct ncr53c9x_glue *sc_glue; /* glue to MD code */
@@ -257,16 +290,14 @@ struct ncr53c9x_softc {
u_char sc_espintr;
u_char sc_espstat;
u_char sc_espstep;
+ u_char sc_espstat2;
u_char sc_espfflags;
/* Lists of command blocks */
- TAILQ_HEAD(ecb_list, ncr53c9x_ecb) free_list,
- ready_list,
- nexus_list;
+ TAILQ_HEAD(ecb_list, ncr53c9x_ecb) ready_list;
struct ncr53c9x_ecb *sc_nexus; /* Current command */
- struct ncr53c9x_ecb sc_ecb[3*8]; /* Three per target */
- struct ncr53c9x_tinfo sc_tinfo[8];
+ struct ncr53c9x_tinfo sc_tinfo[NCR_NTARG];
/* Data about the current nexus (updated for every cmd switch) */
caddr_t sc_dp; /* Current data pointer */
@@ -281,9 +312,11 @@ struct ncr53c9x_softc {
u_char sc_lastcmd;
/* Message stuff */
- u_char sc_msgpriq; /* One or more messages to send (encoded) */
- u_char sc_msgout; /* What message is on its way out? */
- u_char sc_msgoutq; /* What messages have been sent so far? */
+ u_short sc_msgify; /* IDENTIFY message associated with this nexus */
+ u_short sc_msgout; /* What message is on its way out? */
+ u_short sc_msgpriq; /* One or more messages to send (encoded) */
+ u_short sc_msgoutq; /* What messages have been sent so far? */
+
u_char *sc_omess; /* MSGOUT buffer */
caddr_t sc_omp; /* Message pointer (for multibyte messages) */
size_t sc_omlen;
@@ -307,11 +340,12 @@ struct ncr53c9x_softc {
#define NCR_IDLE 1 /* waiting for something to do */
#define NCR_SELECTING 2 /* SCSI command is arbiting */
#define NCR_RESELECTED 3 /* Has been reselected */
-#define NCR_CONNECTED 4 /* Actively using the SCSI bus */
-#define NCR_DISCONNECT 5 /* MSG_DISCONNECT received */
-#define NCR_CMDCOMPLETE 6 /* MSG_CMDCOMPLETE received */
-#define NCR_CLEANING 7
-#define NCR_SBR 8 /* Expect a SCSI RST because we commanded it */
+#define NCR_IDENTIFIED 4 /* Has gotten IFY but not TAG */
+#define NCR_CONNECTED 5 /* Actively using the SCSI bus */
+#define NCR_DISCONNECT 6 /* MSG_DISCONNECT received */
+#define NCR_CMDCOMPLETE 7 /* MSG_CMDCOMPLETE received */
+#define NCR_CLEANING 8
+#define NCR_SBR 9 /* Expect a SCSI RST because we commanded it */
/* values for sc_flags */
#define NCR_DROP_MSGI 0x01 /* Discard all msgs (parity err detected) */
@@ -326,16 +360,19 @@ struct ncr53c9x_softc {
/* values for sc_features */
#define NCR_F_HASCFG3 0x01 /* chip has CFG3 register */
#define NCR_F_FASTSCSI 0x02 /* chip supports Fast mode */
+#define NCR_F_DMASELECT 0x04 /* can do dma select */
+#define NCR_F_SELATN3 0x08 /* can do selatn3 */
/* values for sc_msgout */
-#define SEND_DEV_RESET 0x01
-#define SEND_PARITY_ERROR 0x02
-#define SEND_INIT_DET_ERR 0x04
-#define SEND_REJECT 0x08
-#define SEND_IDENTIFY 0x10
-#define SEND_ABORT 0x20
-#define SEND_SDTR 0x40
-#define SEND_WDTR 0x80
+#define SEND_DEV_RESET 0x0001
+#define SEND_PARITY_ERROR 0x0002
+#define SEND_INIT_DET_ERR 0x0004
+#define SEND_REJECT 0x0008
+#define SEND_IDENTIFY 0x0010
+#define SEND_ABORT 0x0020
+#define SEND_WDTR 0x0040
+#define SEND_SDTR 0x0080
+#define SEND_TAG 0x0100
/* SCSI Status codes */
#define ST_MASK 0x3e /* bit 0,6,7 is reserved */
@@ -369,11 +406,11 @@ struct ncr53c9x_softc {
(*(sc)->sc_glue->gl_write_reg)((sc), (reg), (val))
#ifdef NCR53C9X_DEBUG
-#define NCRCMD(sc, cmd) do { \
- if (ncr53c9x_debug & NCR_SHOWCCMDS) \
- printf("<cmd:0x%x>", (unsigned)cmd); \
- sc->sc_lastcmd = cmd; \
- NCR_WRITE_REG(sc, NCR_CMD, cmd); \
+#define NCRCMD(sc, cmd) do { \
+ if (ncr53c9x_debug & NCR_SHOWCCMDS) \
+ printf("<cmd:0x%x %d>", (unsigned)cmd, __LINE__); \
+ sc->sc_lastcmd = cmd; \
+ NCR_WRITE_REG(sc, NCR_CMD, cmd); \
} while (0)
#else
#define NCRCMD(sc, cmd) NCR_WRITE_REG(sc, NCR_CMD, cmd)
@@ -403,5 +440,3 @@ int ncr53c9x_scsi_cmd __P((struct scsi_xfer *));
void ncr53c9x_reset __P((struct ncr53c9x_softc *));
int ncr53c9x_intr __P((void *));
void ncr53c9x_init __P((struct ncr53c9x_softc *, int));
-
-extern int ncr53c9x_dmaselect;
diff --git a/sys/dev/pci/pcscp.c b/sys/dev/pci/pcscp.c
index dd540935cd3..1f1763d09e4 100644
--- a/sys/dev/pci/pcscp.c
+++ b/sys/dev/pci/pcscp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pcscp.c,v 1.6 2001/08/26 03:32:22 jason Exp $ */
+/* $OpenBSD: pcscp.c,v 1.7 2001/09/27 04:01:42 jason Exp $ */
/* $NetBSD: pcscp.c,v 1.11 2000/11/14 18:42:58 thorpej Exp $ */
/*-
@@ -346,7 +346,7 @@ pcscp_attach(parent, self, aux)
ncr53c9x_attach(sc, &pcscp_adapter, NULL);
/* Turn on target selection using the `dma' method */
- ncr53c9x_dmaselect = 1;
+ sc->sc_features |= NCR_F_DMASELECT;
}
/*
diff --git a/sys/dev/sbus/esp_sbus.c b/sys/dev/sbus/esp_sbus.c
index 8d8f0b2a47a..d9550d80493 100644
--- a/sys/dev/sbus/esp_sbus.c
+++ b/sys/dev/sbus/esp_sbus.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: esp_sbus.c,v 1.4 2001/09/26 00:03:34 jason Exp $ */
+/* $OpenBSD: esp_sbus.c,v 1.5 2001/09/27 04:01:42 jason Exp $ */
/* $NetBSD: esp_sbus.c,v 1.14 2001/04/25 17:53:37 bouyer Exp $ */
/*-
@@ -538,13 +538,8 @@ espattach(esc, gluep)
evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt);
/* Turn on target selection using the `dma' method */
-#ifdef notyet
if (sc->sc_rev != NCR_VARIANT_FAS366)
sc->sc_features |= NCR_F_DMASELECT;
-#else
- if (sc->sc_rev != NCR_VARIANT_FAS366)
- ncr53c9x_dmaselect = 1;
-#endif
/* Do the common parts of attachment. */
ncr53c9x_attach(sc, &esp_switch, &esp_dev);