summaryrefslogtreecommitdiff
path: root/sys/arch/sparc/dev/esp.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 17:20:17 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 17:20:17 +0000
commit0c04730b6718e2a9cd357673c6601fb978d07256 (patch)
tree8522f5db91f122a7907fb298a279ee46fc8255f9 /sys/arch/sparc/dev/esp.c
parent3e81c8c2af67101fa47ce68b5ee9a02c44c97a62 (diff)
new driver by me
Diffstat (limited to 'sys/arch/sparc/dev/esp.c')
-rw-r--r--sys/arch/sparc/dev/esp.c1946
1 files changed, 1115 insertions, 831 deletions
diff --git a/sys/arch/sparc/dev/esp.c b/sys/arch/sparc/dev/esp.c
index 85e456471f3..0ef0265291a 100644
--- a/sys/arch/sparc/dev/esp.c
+++ b/sys/arch/sparc/dev/esp.c
@@ -1,7 +1,13 @@
-/* $NetBSD: esp.c,v 1.26 1995/09/14 20:38:53 pk Exp $ */
+/*
+ * FOR NOW, YOU MAY NOT USE THIS DRIVER WITHOUT EXPLICITLY GRANTED
+ * PERMISSION.
+ */
+
+
+/* $NetBSD: esp.c,v 1.18 1995/02/01 12:37:24 pk Exp $ */
/*
- * Copyright (c) 1994 Peter Galbavy
+ * Copyright (c) 1995 Theo de Raadt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -14,7 +20,7 @@
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
- * This product includes software developed by Peter Galbavy
+ * This product includes software developed by Theo de Raadt.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
@@ -32,11 +38,14 @@
*/
/*
- * Based on aic6360 by Jarle Greipsland
- *
- * Acknowledgements: Many of the algorithms used in this driver are
- * inspired by the work of Julian Elischer (julian@tfs.com) and
- * Charles Hannum (mycroft@duality.gnu.ai.mit.edu). Thanks a million!
+ * Based IN PART on aic6360 by Jarle Greipsland, an older esp driver
+ * by Peter Galbavy, and work by Charles Hannum on a few other drivers.
+ */
+
+/*
+ * todo:
+ * fix & enable sync
+ * confirm parity and bus failures do not lock up driver
*/
#include <sys/types.h>
@@ -53,181 +62,68 @@
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
+#include <scsi/scsi_message.h>
#include <machine/cpu.h>
#include <machine/autoconf.h>
#include <sparc/dev/sbusvar.h>
-#include <sparc/dev/dmareg.h>
#include <sparc/dev/dmavar.h>
+#include <sparc/dev/dmareg.h>
#include <sparc/dev/espreg.h>
#include <sparc/dev/espvar.h>
-int esp_debug = 0; /*ESP_SHOWPHASE|ESP_SHOWMISC|ESP_SHOWTRAC|ESP_SHOWCMDS;*/
-
-/*static*/ void espattach __P((struct device *, struct device *, void *));
-/*static*/ int espmatch __P((struct device *, void *, void *));
-/*static*/ int espprint __P((void *, char *));
-/*static*/ u_int esp_adapter_info __P((struct esp_softc *));
-/*static*/ void espreadregs __P((struct esp_softc *));
-/*static*/ int espgetbyte __P((struct esp_softc *, u_char *));
-/*static*/ void espselect __P((struct esp_softc *,
- u_char, u_char, caddr_t, u_char));
-/*static*/ void esp_scsi_reset __P((struct esp_softc *));
-/*static*/ void esp_reset __P((struct esp_softc *));
-/*static*/ void esp_init __P((struct esp_softc *, int));
-/*static*/ int esp_scsi_cmd __P((struct scsi_xfer *));
-/*static*/ int esp_poll __P((struct esp_softc *, struct ecb *));
-/*static*/ int espphase __P((struct esp_softc *));
-/*static*/ void esp_sched __P((struct esp_softc *));
-/*static*/ void esp_done __P((struct ecb *));
-/*static*/ void esp_msgin __P((struct esp_softc *));
-/*static*/ void esp_msgout __P((struct esp_softc *));
-/*static*/ int espintr __P((struct esp_softc *));
-/*static*/ void esp_timeout __P((void *arg));
-
-/* Linkup to the rest of the kernel */
+int esp_debug = ESP_SHOWPHASE|ESP_SHOWMISC|ESP_SHOWTRAC|ESP_SHOWCMDS; /**/
+
+#if 1
+#define ESPD(x)
+#else
+#define ESPD(x) printf x
+#endif
+
+void espattach __P((struct device *, struct device *, void *));
+int espmatch __P((struct device *, void *, void *));
+u_int esp_minphys __P((struct buf *));
+int espprint __P((void *, char *));
+void espreadregs __P((struct esp_softc *));
+int espgetbyte __P((struct esp_softc *, int));
+void espselect __P((struct esp_softc *));
+void esp_reset __P((struct esp_softc *));
+void esp_init __P((struct esp_softc *, int));
+int esp_scsi_cmd __P((struct scsi_xfer *));
+int esp_poll __P((struct esp_softc *, struct ecb *));
+int espphase __P((struct esp_softc *));
+void esp_sched __P((struct esp_softc *));
+void esp_done __P((struct ecb *));
+void esp_msgin __P((struct esp_softc *));
+void esp_makemsg __P((struct esp_softc *));
+void esp_msgout __P((struct esp_softc *));
+int espintr __P((struct esp_softc *));
+void esp_timeout __P((void *arg));
+
struct cfdriver espcd = {
- NULL, "esp", espmatch, espattach,
- DV_DULL, sizeof(struct esp_softc)
+ NULL, "esp", espmatch, espattach, DV_DULL, sizeof(struct esp_softc)
};
struct scsi_adapter esp_switch = {
- esp_scsi_cmd,
- minphys, /* no max at this level; handled by DMA code */
- NULL,
- NULL,
+ esp_scsi_cmd, esp_minphys, NULL, NULL
};
struct scsi_device esp_dev = {
- NULL, /* Use default error handler */
- NULL, /* have a queue, served by this */
- NULL, /* have no async handler */
- NULL, /* Use default 'done' routine */
+ NULL, NULL, NULL, NULL
};
/*
- * Read the ESP registers, and save their contents for later use.
- * ESP_STAT, ESP_STEP & ESP_INTR are mostly zeroed out when reading
- * ESP_INTR - so make sure it is the last read.
- *
- * I think that (from reading the docs) most bits in these registers
- * only make sense when he DMA CSR has an interrupt showing. So I have
- * coded this to not do anything if there is no interrupt or error
- * pending.
+ * Does anyone actually use this, and what for ?
*/
-void
-espreadregs(sc)
- struct esp_softc *sc;
-{
- volatile caddr_t esp = sc->sc_reg;
-
- /* they mean nothing if the is no pending interrupt ??? */
- if (!(DMA_ISINTR(sc->sc_dma)))
- return;
-
- /* Only the stepo bits are of interest */
- sc->sc_espstep = esp[ESP_STEP] & ESPSTEP_MASK;
- sc->sc_espstat = esp[ESP_STAT];
- sc->sc_espintr = esp[ESP_INTR];
-
- ESP_MISC(("regs[intr=%02x,stat=%02x,step=%02x] ", sc->sc_espintr,
- sc->sc_espstat, sc->sc_espstep));
-}
-
-/*
- * no error checking ouch
- */
-int
-espgetbyte(sc, v)
- struct esp_softc *sc;
- u_char *v;
-{
- volatile caddr_t esp = sc->sc_reg;
-
- if (!(esp[ESP_FFLAG] & ESPFIFO_FF)) {
-ESPCMD(sc, ESPCMD_FLUSH);
-DELAY(1);
- ESPCMD(sc, ESPCMD_TRANS);
- while (!DMA_ISINTR(sc->sc_dma))
- DELAY(1);
- /*
- * If we read something, then clear the outstanding
- * interrupts
- */
- espreadregs(sc);
- if (sc->sc_espintr & ESPINTR_ILL) /* Oh, why? */
- return -1;
- }
- if (!(esp[ESP_FFLAG] & ESPFIFO_FF)) {
- printf("error... ");
- return -1;
- }
- *v = esp[ESP_FIFO];
- return 0;
-}
-
-/*
- * Send a command to a target, set the driver state to ESP_SELECTING
- * and let the caller take care of the rest.
- *
- * Keeping this as a function allows me to say that this may be done
- * by DMA instead of programmed I/O soon.
- */
-void
-espselect(sc, target, lun, cmd, clen)
- struct esp_softc *sc;
- u_char target, lun;
- caddr_t cmd;
- u_char clen;
-{
- volatile caddr_t esp = sc->sc_reg;
- int i;
-
- /*
- * The docs say the target register is never reset, and I
- * can't think of a better place to set it
- */
- esp[ESP_SELID] = target;
- esp[ESP_SYNCOFF] = sc->sc_tinfo[target].offset;
- esp[ESP_SYNCTP] = 250 / sc->sc_tinfo[target].period;
-
- /*
- * Who am I. This is where we tell the target that we are
- * happy for it to disconnect etc.
- */
-#if 1
- if ((sc->sc_tinfo[target].flags & T_XXX) == 0)
-#endif
- esp[ESP_FIFO] = ESP_MSG_IDENTIFY(lun);
-
- /* Now the command into the FIFO */
- for (i = 0; i < clen; i++)
- esp[ESP_FIFO] = *cmd++;
-
- /* And get the targets attention */
-#if 1
- if ((sc->sc_tinfo[target].flags & T_XXX) == 0)
-#endif
- ESPCMD(sc, ESPCMD_SELATN);
-#if 1
- else
- ESPCMD(sc, ESPCMD_SELNATN);
-#endif
-
- /* new state ESP_SELECTING */
- sc->sc_state = ESP_SELECTING;
-}
-
int
espprint(aux, name)
void *aux;
char *name;
{
- if (name != NULL)
- printf("%s: scsibus ", name);
- return UNCONF;
+ return (UNCONF);
}
+
int
espmatch(parent, vcf, aux)
struct device *parent;
@@ -245,6 +141,7 @@ espmatch(parent, vcf, aux)
return (probeget(ra->ra_vaddr, 1) != -1);
}
+
/*
* Attach this instance, and then all the sub-devices
*/
@@ -255,7 +152,9 @@ espattach(parent, self, aux)
{
register struct confargs *ca = aux;
struct esp_softc *sc = (void *)self;
+ struct espregs *espr;
struct bootpath *bp;
+ int freq;
/*
* Make sure things are sane. I don't know if this is ever
@@ -274,11 +173,13 @@ espattach(parent, self, aux)
* address space.
*/
if (ca->ca_ra.ra_vaddr)
- sc->sc_reg = (volatile caddr_t) ca->ca_ra.ra_vaddr;
+ sc->sc_regs = (struct espregs *) ca->ca_ra.ra_vaddr;
else {
- sc->sc_reg = (volatile caddr_t)
- mapiodev(ca->ca_ra.ra_paddr, ca->ca_ra.ra_len, ca->ca_bustype);
+ sc->sc_regs = (struct espregs *)
+ mapiodev(ca->ca_ra.ra_paddr, ca->ca_ra.ra_len,
+ ca->ca_bustype);
}
+ espr = sc->sc_regs;
/* Other settings */
sc->sc_node = ca->ca_ra.ra_node;
@@ -293,85 +194,66 @@ espattach(parent, self, aux)
sc->sc_freq = ((struct sbus_softc *)
sc->sc_dev.dv_parent)->sc_clockfreq;
- /* gimme Mhz */
- sc->sc_freq /= 1000000;
+ freq = sc->sc_freq / 1000000; /* gimme Mhz */
/*
- * This is the value used to start sync negotiations
- * For a 25Mhz clock, this gives us 40, or 160nS, or
- * 6.25Mb/s. It is constant for each adapter.
- *
- * In turn, notice that the ESP register "SYNCTP" is
- * = (250 / the negotiated period). It works, try it
- * on paper.
+ * This is the value used to start sync negotiations. For a
+ * 25Mhz clock, this gives us 40, or 160nS, or 6.25Mb/s. It
+ * is constant for each adapter. In turn, notice that the ESP
+ * register "SYNCTP" is = (250 / the negotiated period).
*/
- sc->sc_minsync = 1000 / sc->sc_freq;
+ sc->sc_minsync = 1000 / freq;
/* 0 is actually 8, even though the register only has 3 bits */
- sc->sc_ccf = FREQTOCCF(sc->sc_freq) & 0x07;
+ sc->sc_ccf = FREQTOCCF(freq) & 0x07;
- /* The value *must not* be == 1. Make it 2 */
+ /* The value must not be 1 -- make it 2 */
if (sc->sc_ccf == 1)
sc->sc_ccf = 2;
/*
- * The recommended timeout is 250ms. This register is loaded
- * with a value calculated as follows, from the docs:
+ * The recommended timeout is 250ms (1/4 seconds). This
+ * register is loaded with a value calculated as follows:
*
* (timout period) x (CLK frequency)
* reg = -------------------------------------
* 8192 x (Clock Conversion Factor)
*
- * We have the CCF from above, so the sum is simple, and generally
- * gives us a constant of 153. Try working out a few and see.
+ * We have the CCF from above. For a 25MHz clock this gives
+ * a constant of 153 (we round up).
*/
- sc->sc_timeout = ESP_DEF_TIMEOUT;
+ sc->sc_timeout = (sc->sc_freq / 4 / 8192 / sc->sc_ccf) + 1;
/*
- * find the DMA by poking around the dma device structures
- *
- * What happens here is that if the dma driver has not been
- * configured, then this returns a NULL pointer. Then when the
- * dma actually gets configured, it does the opposing test, and
- * if the sc->sc_esp field in it's softc is NULL, then tries to
- * find the matching esp driver.
- *
+ * find the corresponding DMA controller.
*/
sc->sc_dma = ((struct dma_softc *)getdevunit("dma",
sc->sc_dev.dv_unit));
-
- /*
- * and a back pointer to us, for DMA
- */
if (sc->sc_dma)
sc->sc_dma->sc_esp = sc;
- /*
- * It is necessary to try to load the 2nd config register here,
- * to find out what rev the esp chip is, else the esp_reset
- * will not set up the defaults correctly.
- */
sc->sc_cfg1 = sc->sc_id | ESPCFG1_PARENB;
- sc->sc_cfg2 = ESPCFG2_SCSI2 | ESPCFG2_RPE;
- sc->sc_cfg3 = ESPCFG3_CDB;
- sc->sc_reg[ESP_CFG2] = sc->sc_cfg2;
+ sc->sc_cfg2 = 0;
+ sc->sc_cfg3 = 0;
- if ((sc->sc_reg[ESP_CFG2] & ~ESPCFG2_RSVD) != (ESPCFG2_SCSI2 | ESPCFG2_RPE)) {
+ /*
+ * The ESP100 only has a cfg1 register. The ESP100A has it, but
+ * lacks the cfg3 register. Thus, we can tell which chip we have.
+ * XXX: what about the FAS100A?
+ */
+ espr->espr_cfg2 = ESPCFG2_SCSI2 | ESPCFG2_RPE;
+ if ((espr->espr_cfg2 & ~ESPCFG2_RSVD) != (ESPCFG2_SCSI2 | ESPCFG2_RPE)) {
printf(": ESP100");
sc->sc_rev = ESP100;
} else {
- sc->sc_cfg2 = 0;
- sc->sc_reg[ESP_CFG2] = sc->sc_cfg2;
- sc->sc_cfg3 = 0;
- sc->sc_reg[ESP_CFG3] = sc->sc_cfg3;
- sc->sc_cfg3 = 5;
- sc->sc_reg[ESP_CFG3] = sc->sc_cfg3;
- if (sc->sc_reg[ESP_CFG3] != 5) {
+ espr->espr_cfg2 = 0;
+ espr->espr_cfg3 = 0;
+ espr->espr_cfg3 = ESPCFG3_CDB | ESPCFG3_FCLK;
+ if (espr->espr_cfg3 != (ESPCFG3_CDB | ESPCFG3_FCLK)) {
printf(": ESP100A");
sc->sc_rev = ESP100A;
} else {
- sc->sc_cfg3 = 0;
- sc->sc_reg[ESP_CFG3] = sc->sc_cfg3;
+ espr->espr_cfg3 = 0;
printf(": ESP200");
sc->sc_rev = ESP200;
}
@@ -380,16 +262,23 @@ espattach(parent, self, aux)
sc->sc_state = 0;
esp_init(sc, 1);
- printf(" %dMhz, target %d\n", sc->sc_freq, sc->sc_id);
+ printf(" %dMhz, target %d\n", freq, sc->sc_id);
- /* add me to the sbus structures */
- sc->sc_sd.sd_reset = (void *) esp_reset;
#if defined(SUN4C) || defined(SUN4M)
- if (ca->ca_bustype == BUS_SBUS)
+ if (ca->ca_bustype == BUS_SBUS) {
+ /* add to the sbus structures */
+ sc->sc_sd.sd_reset = (void *) esp_reset;
sbus_establish(&sc->sc_sd, &sc->sc_dev);
+
+ /*
+ * If the device is in an SBUS slave slot, bail now.
+ */
+ if (sbus_slavecheck(self, ca))
+ return;
+ }
#endif /* SUN4C || SUN4M */
- /* and the interuppts */
+ /* and the interupts */
sc->sc_ih.ih_fun = (void *) espintr;
sc->sc_ih.ih_arg = sc;
intr_establish(sc->sc_pri, &sc->sc_ih);
@@ -405,21 +294,23 @@ espattach(parent, self, aux)
sc->sc_link.openings = 2;
/*
- * If the boot path is "esp" at the moment and it's me, then
- * walk our pointer to the sub-device, ready for the config
- * below.
+ * If the boot path is "esp", and the numbers point to
+ * this controller, then advance the bootpath a step.
+ * XXX boot /sbus/esp/sd@3,0 passes in esp@0,0 -- which
+ * is almost always wrong, but match in that case anyways.
*/
bp = ca->ca_ra.ra_bp;
+ bootcomp = NULL;
switch (ca->ca_bustype) {
case BUS_SBUS:
if (bp != NULL && strcmp(bp->name, "esp") == 0 &&
- SAME_ESP(sc, bp, ca))
- bootpath_store(1, bp + 1);
+ ((bp->val[0]==ca->ca_slot &&
+ (bp->val[1]==ca->ca_offset || bp->val[1]==0)) ||
+ (bp->val[0]==-1 && bp->val[1]==sc->sc_dev.dv_unit)))
+ bootcomp = sc->sc_bp = bp + 1;
break;
default:
- if (bp != NULL && strcmp(bp->name, "esp") == 0 &&
- bp->val[0] == -1 && bp->val[1] == sc->sc_dev.dv_unit)
- bootpath_store(1, bp + 1);
+ bootcomp = sc->sc_bp = bp;
break;
}
@@ -427,66 +318,184 @@ espattach(parent, self, aux)
* Now try to attach all the sub-devices
*/
config_found(self, &sc->sc_link, espprint);
+}
- bootpath_store(1, NULL);
+
+/*
+ * Warning: This inserts two commands into the ESP command fifo. Because
+ * there are only 2 entries in that fifo, you may need to put a DELAY(1)
+ * after calling this, if you are going to issue another command soon.
+ */
+void
+espflush(sc)
+ struct esp_softc *sc;
+{
+ struct espregs *espr = sc->sc_regs;
+
+ if (espr->espr_fflag & ESPFIFO_FF) {
+ espr->espr_cmd = ESPCMD_FLUSH;
+ espr->espr_cmd = ESPCMD_NOP;
+ }
}
/*
- * 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
- * and resets the DMA.
+ * Read the ESP registers, and save their contents for later use.
+ * ESP_STAT, ESP_STEP & ESP_INTR are mostly zeroed out when reading
+ * ESP_INTR - so make sure it is the last read.
*
- * After reset, registers are loaded with the defaults from the attach
- * routine above.
+ * XXX: TDR: this logic seems unsound
+ * I think that (from reading the docs) most bits in these registers
+ * only make sense when the DMA CSR has an interrupt showing. So I have
+ * coded this to not do anything if there is no interrupt or error
+ * pending.
+ */
+void
+espreadregs(sc)
+ struct esp_softc *sc;
+{
+ struct espregs *espr = sc->sc_regs;
+
+ /* they mean nothing if the is no pending interrupt ??? */
+ if (!(dmapending(sc->sc_dma)))
+ return;
+
+ /* Only the stepo bits are of interest */
+ sc->sc_espstep = espr->espr_step & ESPSTEP_MASK;
+ sc->sc_espstat = espr->espr_stat;
+ sc->sc_espintr = espr->espr_intr;
+
+ ESP_MISC(("regs[intr=%02x,stat=%02x,step=%02x] ", sc->sc_espintr,
+ sc->sc_espstat, sc->sc_espstep));
+}
+
+/*
+ * returns -1 if no byte is available because fifo is empty.
+ */
+int
+espgetbyte(sc, dotrans)
+ struct esp_softc *sc;
+ int dotrans;
+{
+ struct espregs *espr = sc->sc_regs;
+
+ if (espr->espr_fflag & ESPFIFO_FF)
+ return espr->espr_fifo;
+ return -1;
+}
+
+/*
+ * Send a command to a target, as well as any required messages.
+ * There are three cases. The first two cases are fairly simple..
+ * 1) command alone
+ * load command into fifo, and use ESPCMD_SELNATN
+ * 2) MSG_IDENTIFY + command
+ * load message and command into fifo, and use ESPCMD_SELATN
+ * 3) a bunch of messages + command
+ * load first message byte into fifo. Use ESPCMD_SELATNS. When the
+ * next interrupt occurs, load the remainer of the message into
+ * the fifo and use ESPCMD_TRANS. When the device is ready to
+ * receive the command, it will switch into COMMAND_PHASE, and
+ * at that point we will feed it the command.
+ */
+void
+espselect(sc)
+ struct esp_softc *sc;
+{
+ struct espregs *espr = sc->sc_regs;
+ register struct ecb *ecb = sc->sc_nexus;
+ register struct scsi_link *sc_link = ecb->xs->sc_link;
+ struct esp_tinfo *ti = &sc->sc_tinfo[sc_link->target];
+ u_char *cmd = (u_char *)&ecb->cmd;
+ int loadcmd = 1;
+ int outcmd, i;
+
+ espr->espr_selid = sc_link->target;
+ espr->espr_syncoff = ti->offset;
+ espr->espr_synctp = ti->synctp;
+
+ sc->sc_state = ESPS_SELECTING;
+
+ if (ecb->xs->flags & SCSI_RESET)
+ sc->sc_msgpriq = SEND_DEV_RESET;
+ else if (ti->flags & DO_NEGOTIATE)
+ sc->sc_msgpriq = SEND_SDTR;
+ else
+ sc->sc_msgpriq = SEND_IDENTIFY;
+
+ if (sc->sc_msgpriq) {
+ esp_makemsg(sc);
+
+ ESPD(("OM["));
+ for (i = 0; i < sc->sc_omlen; i++)
+ ESPD(("%02x%c", sc->sc_omp[i],
+ (i == sc->sc_omlen-1) ? ']' : ' '));
+ ESPD((" "));
+
+ espr->espr_fifo = sc->sc_omp[0]; /* 1st msg byte only */
+ if (sc->sc_omlen == 1) {
+ outcmd = ESPCMD_SELATN;
+ } else {
+ outcmd = ESPCMD_SELATNS;
+ /* and this will will load the rest of the msg bytes */
+ sc->sc_state = ESPS_SELECTSTOP;
+ loadcmd = 0;
+ }
+ } else
+ outcmd = ESPCMD_SELNATN;
+
+ ESPD(("P%d/%02x/%d ", (ecb->xs->flags & SCSI_POLL) ? 1 : 0, outcmd, loadcmd));
+
+ if (loadcmd) {
+ ESPD(("CMD["));
+ for (i = 0; i < ecb->clen; i++)
+ ESPD(("%02x%c", cmd[i],
+ (i == ecb->clen-1) ? ']' : ' '));
+ ESPD((" "));
+
+ /* load the command into the FIFO */
+ for (i = 0; i < ecb->clen; i++)
+ espr->espr_fifo = cmd[i];
+ }
+
+ espr->espr_cmd = outcmd;
+ if (!(ecb->xs->flags & SCSI_POLL))
+ sc->sc_flags |= ESPF_MAYDISCON;
+}
+
+
+/*
+ * Reset the ESP and DMA chips. Stops any transactions dead in the water;
+ * does not cause an interrupt.
*/
void
esp_reset(sc)
struct esp_softc *sc;
{
- volatile caddr_t esp = sc->sc_reg;
+ struct espregs *espr = sc->sc_regs;
- /* reset DMA first */
- DMA_RESET(sc->sc_dma);
+ dmareset(sc->sc_dma); /* reset DMA first */
- ESPCMD(sc, ESPCMD_RSTCHIP); /* reset chip */
- ESPCMD(sc, ESPCMD_NOP);
- DELAY(500);
+ espr->espr_cmd = ESPCMD_RSTCHIP;
+ DELAY(5);
+ espr->espr_cmd = ESPCMD_NOP;
+ DELAY(5);
/* do these backwards, and fall through */
switch (sc->sc_rev) {
case ESP200:
- esp[ESP_CFG3] = sc->sc_cfg3;
+ espr->espr_cfg3 = sc->sc_cfg3;
case ESP100A:
- esp[ESP_CFG2] = sc->sc_cfg2;
+ espr->espr_cfg2 = sc->sc_cfg2;
case ESP100:
- esp[ESP_CFG1] = sc->sc_cfg1;
- esp[ESP_CCF] = sc->sc_ccf;
- esp[ESP_SYNCOFF] = 0;
- esp[ESP_TIMEOUT] = sc->sc_timeout;
+ espr->espr_cfg1 = sc->sc_cfg1;
+ espr->espr_ccf = sc->sc_ccf;
+ espr->espr_syncoff = 0;
+ espr->espr_timeout = sc->sc_timeout;
break;
- default:
- printf("%s: unknown revision code, assuming ESP100\n",
- sc->sc_dev.dv_xname);
- esp[ESP_CFG1] = sc->sc_cfg1;
- esp[ESP_CCF] = sc->sc_ccf;
- esp[ESP_SYNCOFF] = 0;
- esp[ESP_TIMEOUT] = sc->sc_timeout;
}
}
/*
- * Reset the SCSI bus, but not the chip
- */
-void
-esp_scsi_reset(sc)
- struct esp_softc *sc;
-{
- printf("esp: resetting SCSI bus\n");
- ESPCMD(sc, ESPCMD_RSTSCSI);
- DELAY(50);
-}
-
-/*
* Initialize esp state machine
*/
void
@@ -494,8 +503,9 @@ esp_init(sc, doreset)
struct esp_softc *sc;
int doreset;
{
+ struct espregs *espr = sc->sc_regs;
struct ecb *ecb;
- int r;
+ int i;
/*
* reset the chip to a known state
@@ -503,8 +513,8 @@ esp_init(sc, doreset)
esp_reset(sc);
if (doreset) {
- ESPCMD(sc, ESPCMD_RSTSCSI);
- DELAY(50);
+ espr->espr_cmd = ESPCMD_RSTSCSI;
+ DELAY(500);
/* cheat: we don't want the state machine to reset again.. */
esp_reset(sc);
}
@@ -516,13 +526,13 @@ esp_init(sc, doreset)
sc->sc_nexus = 0;
ecb = sc->sc_ecb;
bzero(ecb, sizeof(sc->sc_ecb));
- for (r = 0; r < sizeof(sc->sc_ecb) / sizeof(*ecb); r++) {
+ for (i = 0; i < sizeof(sc->sc_ecb) / sizeof(*ecb); i++) {
TAILQ_INSERT_TAIL(&sc->free_list, ecb, chain);
ecb++;
}
bzero(sc->sc_tinfo, sizeof(sc->sc_tinfo));
} else {
- sc->sc_state = ESP_IDLE;
+ sc->sc_state = ESPS_IDLE;
if (sc->sc_nexus != NULL) {
sc->sc_nexus->xs->error = XS_DRIVER_STUFFUP;
untimeout(esp_timeout, sc->sc_nexus);
@@ -536,26 +546,20 @@ esp_init(sc, doreset)
}
}
- sc->sc_phase = sc->sc_prevphase = INVALID_PHASE;
- for (r = 0; r < 8; r++) {
- struct esp_tinfo *tp = &sc->sc_tinfo[r];
+ for (i = 0; i < 8; i++) {
+ struct esp_tinfo *ti = &sc->sc_tinfo[i];
- tp->flags = DO_NEGOTIATE | NEED_TO_RESET;
- tp->period = sc->sc_minsync;
- tp->offset = ESP_SYNC_REQ_ACK_OFS;
+ ti->flags = DO_NEGOTIATE;
+ ti->period = sc->sc_minsync;
+ ti->synctp = 250 / ti->period;
+ ti->offset = ESP_SYNC_REQOFF;
}
- sc->sc_state = ESP_IDLE;
- return;
+ sc->sc_state = ESPS_IDLE;
}
/*
- * DRIVER FUNCTIONS CALLABLE FROM HIGHER LEVEL DRIVERS
- */
-
-/*
- * Start a SCSI-command
- * This function is called by the higher level SCSI-driver to queue/run
- * SCSI-commands.
+ * Start a SCSI-command: This function is called by the higher level
+ * SCSI-driver to queue/run SCSI-commands.
*/
int
esp_scsi_cmd(xs)
@@ -563,14 +567,13 @@ esp_scsi_cmd(xs)
{
struct scsi_link *sc_link = xs->sc_link;
struct esp_softc *sc = sc_link->adapter_softc;
- struct ecb *ecb;
- int s, flags;
+ struct ecb *ecb;
+ int s;
- ESP_TRACE(("esp_scsi_cmd\n"));
- ESP_CMDS(("[0x%x, %d]->%d ", (int)xs->cmd->opcode, xs->cmdlen,
- sc_link->target));
+ /*ESPD(("NS%08x/%08x/%d ", xs, xs->data, xs->datalen));*/
- flags = xs->flags;
+ /* XXX: set lun */
+ xs->cmd->bytes[0] |= (sc_link->lun << SCSI_CMD_LUN_SHIFT);
/* Get a esp command block */
s = splbio();
@@ -582,7 +585,6 @@ esp_scsi_cmd(xs)
if (ecb == NULL) {
xs->error = XS_DRIVER_STUFFUP;
- ESP_MISC(("TRY_AGAIN_LATER"));
return TRY_AGAIN_LATER;
}
@@ -597,21 +599,30 @@ esp_scsi_cmd(xs)
s = splbio();
TAILQ_INSERT_TAIL(&sc->ready_list, ecb, chain);
- timeout(esp_timeout, ecb, (xs->timeout*hz)/1000);
+ if ((xs->flags & SCSI_POLL) == 0)
+ timeout(esp_timeout, ecb, (xs->timeout * hz) / 1000);
- if (sc->sc_state == ESP_IDLE)
+ if (sc->sc_state == ESPS_IDLE)
esp_sched(sc);
-
splx(s);
- if (flags & SCSI_POLL) {
- /* Not allowed to use interrupts, use polling instead */
+ /* Not allowed to use interrupts, use polling instead */
+ if (xs->flags & SCSI_POLL)
return esp_poll(sc, ecb);
- }
-
- ESP_MISC(("SUCCESSFULLY_QUEUED"));
return SUCCESSFULLY_QUEUED;
+}
+/*
+ * Adjust transfer size in buffer structure
+ *
+ * We have no max transfer size, since the DMA driver will break it
+ * down into watever is needed.
+ */
+u_int
+esp_minphys(bp)
+ struct buf *bp;
+{
+ return (minphys(bp));
}
/*
@@ -623,40 +634,37 @@ esp_poll(sc, ecb)
struct ecb *ecb;
{
struct scsi_xfer *xs = ecb->xs;
- int count = xs->timeout * 10;
+ int count = xs->timeout * 1000;
ESP_TRACE(("esp_poll\n"));
while (count) {
- if (DMA_ISINTR(sc->sc_dma)) {
- espintr(sc);
+ if (dmapending(sc->sc_dma)) {
+ /*
+ * We decrement the interrupt event counter to
+ * repair it... because this isn't a real interrupt.
+ */
+ if (espintr(sc))
+ --sc->sc_intrcnt.ev_count;
+ continue;
}
if (xs->flags & ITSDONE)
break;
- DELAY(5);
- if (sc->sc_state == ESP_IDLE) {
- ESP_TRACE(("esp_poll: rescheduling"));
- esp_sched(sc);
- }
+ DELAY(1);
count--;
}
if (count == 0) {
- ESP_MISC(("esp_poll: timeout"));
- esp_timeout((caddr_t)ecb);
+ ecb->xs->error = XS_TIMEOUT;
+ sc_print_addr(ecb->xs->sc_link);
+ printf("timed out\n");
+ esp_reset(sc);
}
-
+ dmaenintr(sc->sc_dma);
return COMPLETE;
}
/*
- * LOW LEVEL SCSI UTILITIES
- */
-
-/*
- * Determine the SCSI bus phase, return either a real SCSI bus phase
- * or some pseudo phase we use to detect certain exceptions.
- *
* Notice that we do not read the live register on an ESP100. On the
* ESP100A and above the FE (Feature Enable) bit in config 2 latches
* the phase in the register so it is safe to read.
@@ -665,13 +673,9 @@ int
espphase(sc)
struct esp_softc *sc;
{
-
- if (sc->sc_espintr & ESPINTR_DIS) /* Disconnected */
- return BUSFREE_PHASE;
if (sc->sc_rev > ESP100)
- return (sc->sc_reg[ESP_STAT] & ESPSTAT_PHASE);
-
+ return (sc->sc_regs->espr_stat & ESPSTAT_PHASE);
return (sc->sc_espstat & ESPSTAT_PHASE);
}
@@ -680,7 +684,7 @@ espphase(sc)
* Schedule a scsi operation. This has now been pulled out of the interrupt
* handler so that we may call it from esp_scsi_cmd and esp_done. This may
* save us an unecessary interrupt just to get things going. Should only be
- * called when state == ESP_IDLE and at bio pl.
+ * called when state == ESPS_IDLE and at bio pl.
*/
void
esp_sched(sc)
@@ -701,14 +705,13 @@ esp_sched(sc)
sc_link = ecb->xs->sc_link;
t = sc_link->target;
if (!(sc->sc_tinfo[t].lubusy & (1 << sc_link->lun))) {
- struct esp_tinfo *ti = &sc->sc_tinfo[t];
+ struct esp_tinfo *ti = &sc->sc_tinfo[ecb->xs->sc_link->target];
TAILQ_REMOVE(&sc->ready_list, ecb, chain);
sc->sc_nexus = ecb;
sc->sc_flags = 0;
- sc->sc_prevphase = INVALID_PHASE;
sc_link = ecb->xs->sc_link;
- espselect(sc, t, sc_link->lun, cmd, ecb->clen);
+ espselect(sc);
ti = &sc->sc_tinfo[sc_link->target];
sc->sc_dp = ecb->daddr;
sc->sc_dleft = ecb->dleft;
@@ -740,9 +743,13 @@ esp_done(ecb)
* commands for this target/lunit, else we lose the sense info.
* We don't support chk sense conditions for the request sense cmd.
*/
- if (xs->error == XS_NOERROR && !(ecb->flags & ECB_CHKSENSE)) {
- if ((ecb->stat & ST_MASK)==SCSI_CHECK) {
+ if (xs->error == XS_NOERROR) {
+ if (ecb->flags & ECB_CHKSENSE)
+ xs->error = XS_SENSE;
+ else if ((ecb->stat & ST_MASK) == SCSI_CHECK) {
struct scsi_sense *ss = (void *)&ecb->cmd;
+
+ ESPD(("RS "));
ESP_MISC(("requesting sense "));
/* First, save the return values */
xs->resid = ecb->dleft;
@@ -750,7 +757,7 @@ esp_done(ecb)
/* Next, setup a request sense command block */
bzero(ss, sizeof(*ss));
ss->opcode = REQUEST_SENSE;
- ss->byte2 = sc_link->lun << 5;
+ ss->byte2 = sc_link->lun << SCSI_CMD_LUN_SHIFT;
ss->length = sizeof(struct scsi_sense_data);
ecb->clen = sizeof(*ss);
ecb->daddr = (char *)&xs->sense;
@@ -761,29 +768,24 @@ esp_done(ecb)
~(1<<sc_link->lun);
sc->sc_tinfo[sc_link->target].senses++;
/* found it */
- if (sc->sc_nexus == ecb) {
- sc->sc_nexus = NULL;
- sc->sc_state = ESP_IDLE;
- esp_sched(sc);
- }
+ if (sc->sc_nexus != ecb)
+ TAILQ_INSERT_HEAD(&sc->ready_list, ecb, chain);
+ sc->sc_state = ESPS_IDLE;
+ esp_sched(sc);
return;
- }
- }
-
- if (xs->error == XS_NOERROR && (ecb->flags & ECB_CHKSENSE)) {
- xs->error = XS_SENSE;
- } else {
- xs->resid = ecb->dleft;
+ } else
+ xs->resid = ecb->dleft;
}
+
xs->flags |= ITSDONE;
-#ifdef ESP_DEBUG
+#if ESP_DEBUG > 1
if (esp_debug & ESP_SHOWMISC) {
- printf("err=0x%02x ",xs->error);
+ printf("err=0x%02x ", xs->error);
if (xs->error == XS_SENSE)
- printf("sense=%2x\n", xs->sense.error_code);
+ printf("sense=%02x\n", xs->sense.error_code);
}
- if ((xs->resid || xs->error > XS_SENSE) && esp_debug & ESP_SHOWMISC) {
+ if ((xs->resid || xs->error > XS_SENSE) && (esp_debug & ESP_SHOWMISC)) {
if (xs->resid)
printf("esp_done: resid=%d\n", xs->resid);
if (xs->error)
@@ -799,24 +801,24 @@ esp_done(ecb)
* longer busy. This code is sickening, but it works.
*/
if (ecb == sc->sc_nexus) {
- sc->sc_state = ESP_IDLE;
sc->sc_tinfo[sc_link->target].lubusy &= ~(1<<sc_link->lun);
+ sc->sc_nexus = NULL;
+ sc->sc_state = ESPS_IDLE;
esp_sched(sc);
} else if (sc->ready_list.tqh_last == &ecb->chain.tqe_next) {
TAILQ_REMOVE(&sc->ready_list, ecb, chain);
} else {
register struct ecb *ecb2;
+
for (ecb2 = sc->nexus_list.tqh_first; ecb2;
ecb2 = ecb2->chain.tqe_next)
- if (ecb2 == ecb) {
- TAILQ_REMOVE(&sc->nexus_list, ecb, chain);
- sc->sc_tinfo[sc_link->target].lubusy
- &= ~(1<<sc_link->lun);
+ if (ecb2 == ecb)
break;
- }
- if (ecb2)
- ;
- else if (ecb->chain.tqe_next) {
+ if (ecb2) {
+ TAILQ_REMOVE(&sc->nexus_list, ecb, chain);
+ sc->sc_tinfo[sc_link->target].lubusy &=
+ ~(1<<sc_link->lun);
+ } else if (ecb->chain.tqe_next) {
TAILQ_REMOVE(&sc->ready_list, ecb, chain);
} else {
printf("%s: can't find matching ecb\n",
@@ -829,8 +831,8 @@ esp_done(ecb)
TAILQ_INSERT_HEAD(&sc->free_list, ecb, chain);
sc->sc_tinfo[sc_link->target].cmds++;
+ ESPD(("DONE%d ", xs->resid));
scsi_done(xs);
- return;
}
/*
@@ -843,13 +845,13 @@ esp_done(ecb)
* else there will be an illegal command interrupt.
*/
#define esp_sched_msgout(m) \
- do { \
- ESP_MISC(("esp_sched_msgout %d ", m)); \
- ESPCMD(sc, ESPCMD_SETATN); \
- sc->sc_msgpriq |= (m); \
+ do { \
+ sc->sc_regs->espr_cmd = ESPCMD_SETATN; \
+ sc->sc_msgpriq |= (m); \
+ ESPD(("Mq%02x ", sc->sc_msgpriq)); \
} while (0)
-#define IS1BYTEMSG(m) (((m) != 1 && (m) < 0x20) || (m) & 0x80)
+#define IS1BYTEMSG(m) (((m) != 1 && ((unsigned)(m)) < 0x20) || (m) & 0x80)
#define IS2BYTEMSG(m) (((m) & 0xf0) == 0x20)
#define ISEXTMSG(m) ((m) == 1)
@@ -863,8 +865,10 @@ void
esp_msgin(sc)
register struct esp_softc *sc;
{
- volatile caddr_t esp = sc->sc_reg;
+ struct espregs *espr = sc->sc_regs;
+ struct ecb *ecb;
int extlen;
+ int x, i;
ESP_TRACE(("esp_msgin "));
@@ -874,6 +878,9 @@ esp_msgin(sc)
return;
}
+ ESPD(("MSGIN:%d ", espr->espr_fflag & ESPFIFO_FF));
+
+#ifdef fixme
/*
* Prepare for a new message. A message should (according
* to the SCSI standard) be transmitted in one single
@@ -881,166 +888,191 @@ esp_msgin(sc)
* then this is a new message.
*/
if (sc->sc_prevphase != MESSAGE_IN_PHASE) {
- sc->sc_flags &= ~ESP_DROP_MSGI;
+ sc->sc_flags &= ~ESPF_DROP_MSGI;
+ if (sc->sc_imlen > 0) {
+ printf("%s: message type %02x",
+ sc->sc_dev.dv_xname, sc->sc_imess[0]);
+ if (!IS1BYTEMSG(sc->sc_imess[0]))
+ printf(" %02x", sc->sc_imess[1]);
+ printf(" was dropped\n");
+ }
sc->sc_imlen = 0;
}
+#endif
- if (sc->sc_state == ESP_RESELECTED && sc->sc_imlen == 0) {
- /*
- * Which target is reselecting us? (The ID bit really)
- */
- (void)espgetbyte(sc, &sc->sc_selid);
- sc->sc_selid &= ~(1<<sc->sc_id);
- ESP_MISC(("selid=0x%2x ", sc->sc_selid));
+ /*
+ * Which target is reselecting us? (The ID bit really)
+ * On a reselection, there should be the reselecting target's
+ * id and an identify message in the fifo.
+ */
+ if (sc->sc_state == ESPS_RESELECTED && sc->sc_imlen == 0) {
+ x = espgetbyte(sc, 0);
+ if (x == -1) {
+ printf("%s: msgin reselection found fifo empty\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+ ESPD(("ID=%02x ", x));
+ sc->sc_selid = (u_char)x & ~(1<<sc->sc_id);
+ ESP_MISC(("selid=0x%02x ", sc->sc_selid));
}
- for (;;) {
- /*
- * If parity errors just dump everything on the floor
- */
- if (sc->sc_espstat & ESPSTAT_PE) {
- esp_sched_msgout(SEND_PARITY_ERROR);
- sc->sc_flags |= ESP_DROP_MSGI;
- }
+ /*
+ * If parity errors just dump everything on the floor
+ */
+ if (sc->sc_espstat & ESPSTAT_PE) {
+ printf("%s: SCSI bus parity error\n",
+ sc->sc_dev.dv_xname);
+ esp_sched_msgout(SEND_PARITY_ERROR);
+ DELAY(1);
+ sc->sc_flags |= ESPF_DROP_MSGI;
+ }
- /*
- * 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 & ESP_DROP_MSGI) == 0) {
- if (espgetbyte(sc, &sc->sc_imess[sc->sc_imlen])) {
- /*
- * XXX - hack alert.
- * Apparently, the chip didn't grok a multibyte
- * message from the target; set a flag that
- * will cause selection w.o. ATN when we retry
- * (after a SCSI reset).
- * Set NOLUNS quirk as we won't be asking for
- * a lun to identify.
- */
- struct scsi_link *sc_link = sc->sc_nexus->xs->sc_link;
- printf("%s(%d,%d): "
- "MSGIN failed; trying alt selection\n",
- sc->sc_dev.dv_xname,
- sc_link->target, sc_link->lun);
- esp_sched_msgout(SEND_REJECT);
- sc->sc_tinfo[sc_link->target].flags |= T_XXX;
- sc_link->quirks |= SDEV_NOLUNS;
- if (sc->sc_state == ESP_HASNEXUS) {
- TAILQ_INSERT_HEAD(&sc->ready_list,
- sc->sc_nexus, chain);
- sc->sc_nexus = NULL;
- sc->sc_tinfo[sc_link->target].lubusy
- &= ~(1<<sc_link->lun);
- }
- esp_scsi_reset(sc);
- sc->sc_state = ESP_IDLE;
- return;
- }
- ESP_MISC(("0x%02x ", sc->sc_imess[sc->sc_imlen]));
- if (sc->sc_imlen >= ESP_MAX_MSG_LEN) {
- esp_sched_msgout(SEND_REJECT);
- sc->sc_flags |= ESP_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]))
- break;
- if (sc->sc_imlen == 2 && IS2BYTEMSG(sc->sc_imess[0]))
- break;
- if (sc->sc_imlen >= 3 && ISEXTMSG(sc->sc_imess[0]) &&
- sc->sc_imlen == sc->sc_imess[1] + 2)
- break;
- }
+ /*
+ * If we're going to reject the message, don't bother storing
+ * the incoming bytes. But still, we need to ACK them.
+ * XXX: the above comment might be true -- this is not being
+ * done though!
+ */
+ if ((sc->sc_flags & ESPF_DROP_MSGI) == 0) {
+ x = espgetbyte(sc, 1);
+ if (x == -1) {
+ printf("%s: msgin fifo empty at %d\n",
+ sc->sc_dev.dv_xname, sc->sc_imlen);
+ if (sc->sc_espintr & ESPINTR_BS) {
+ espr->espr_cmd = ESPCMD_TRANS;
+ ESPD(("MSI:y "));
+ } else
+ ESPD(("MSI:n "));
+ return;
+ }
+ ESPD(("{%02x} ", (u_char)x));
+ sc->sc_imess[sc->sc_imlen] = (u_char)x;
+ if (sc->sc_imlen >= ESP_MSGLEN_MAX) {
+ esp_sched_msgout(SEND_REJECT);
+ DELAY(1);
+ sc->sc_flags |= ESPF_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)
+ goto gotit;
}
}
+ goto done;
+
+gotit:
+ ESPD(("MP["));
+ for (i=0; i<sc->sc_imlen; i++)
+ ESPD(("%02x%c", sc->sc_imess[i],
+ (i==sc->sc_imlen-1) ? ']' : ' '));
+ ESPD((" "));
+
+ sc->sc_imlen = 0; /* message is fully received */
ESP_MISC(("gotmsg "));
/*
* Now we should have a complete message (1 byte, 2 byte
* and moderately long extended messages). We only handle
* extended messages which total length is shorter than
- * ESP_MAX_MSG_LEN. Longer messages will be amputated.
+ * ESP_MSGLEN_MAX. Longer messages will be amputated.
*/
- if (sc->sc_state == ESP_HASNEXUS) {
- struct ecb *ecb = sc->sc_nexus;
- struct esp_tinfo *ti = &sc->sc_tinfo[ecb->xs->sc_link->target];
+ if (sc->sc_state == ESPS_NEXUS ||
+ sc->sc_state == ESPS_DOINGMSGIN ||
+ sc->sc_state == ESPS_DOINGSTATUS) {
+ struct esp_tinfo *ti;
+
+ ecb = sc->sc_nexus;
+ ti = &sc->sc_tinfo[ecb->xs->sc_link->target];
+
+ sc->sc_state = ESPS_NEXUS; /* where we go after this */
switch (sc->sc_imess[0]) {
case MSG_CMDCOMPLETE:
ESP_MISC(("cmdcomplete "));
if (!ecb) {
esp_sched_msgout(SEND_ABORT);
+ DELAY(1);
printf("%s: CMDCOMPLETE but no command?\n",
sc->sc_dev.dv_xname);
break;
}
if (sc->sc_dleft < 0) {
- struct scsi_link *sc_link = ecb->xs->sc_link;
- printf("esp: %d extra bytes from %d:%d\n",
- -sc->sc_dleft, sc_link->target, sc_link->lun);
- sc->sc_dleft = 0;
+ sc_print_addr(ecb->xs->sc_link);
+ printf("%d extra bytes\n", -sc->sc_dleft);
+ ecb->dleft = 0;
}
- ESPCMD(sc, ESPCMD_MSGOK);
ecb->xs->resid = ecb->dleft = sc->sc_dleft;
- sc->sc_flags |= ESP_BUSFREE_OK;
- return;
-
+ ecb->flags |= ECB_DONE;
+ break;
case MSG_MESSAGE_REJECT:
if (esp_debug & ESP_SHOWMISC)
- printf("%s: our msg rejected by target\n",
- sc->sc_dev.dv_xname);
- if (sc->sc_flags & ESP_SYNCHNEGO) {
- ti->period = ti->offset = 0;
- sc->sc_flags &= ~ESP_SYNCHNEGO;
+ printf("%s targ %d: our msg rejected by target\n",
+ sc->sc_dev.dv_xname,
+ ecb->xs->sc_link->target);
+ if (sc->sc_flags & ESPF_SYNCHNEGO) {
+ /*
+ * Device doesn't even know what sync is
+ * (right?)
+ */
+ ti->offset = 0;
+ ti->period = sc->sc_minsync;
+ ti->synctp = 250 / ti->period;
+ sc->sc_flags &= ~ESPF_SYNCHNEGO;
ti->flags &= ~DO_NEGOTIATE;
}
/* Not all targets understand INITIATOR_DETECTED_ERR */
- if (sc->sc_msgout == SEND_INIT_DET_ERR)
+ if (sc->sc_msgout == SEND_INIT_DET_ERR) {
esp_sched_msgout(SEND_ABORT);
- ESPCMD(sc, ESPCMD_MSGOK);
+ DELAY(1);
+ }
break;
case MSG_NOOP:
- ESPCMD(sc, ESPCMD_MSGOK);
break;
case MSG_DISCONNECT:
if (!ecb) {
esp_sched_msgout(SEND_ABORT);
+ DELAY(1);
printf("%s: nothing to DISCONNECT\n",
sc->sc_dev.dv_xname);
break;
}
- ESPCMD(sc, ESPCMD_MSGOK);
ti->dconns++;
TAILQ_INSERT_HEAD(&sc->nexus_list, ecb, chain);
ecb = sc->sc_nexus = NULL;
- sc->sc_state = ESP_IDLE;
- sc->sc_flags |= ESP_BUSFREE_OK;
+ sc->sc_state = ESPS_EXPECTDISC;
+ sc->sc_flags |= ESPF_MAYDISCON;
break;
case MSG_SAVEDATAPOINTER:
if (!ecb) {
esp_sched_msgout(SEND_ABORT);
+ DELAY(1);
printf("%s: no DATAPOINTERs to save\n",
sc->sc_dev.dv_xname);
break;
}
- ESPCMD(sc, ESPCMD_MSGOK);
+ ESPD(("DL%d/%d ", sc->sc_dleft, ecb->dleft));
ecb->dleft = sc->sc_dleft;
ecb->daddr = sc->sc_dp;
break;
case MSG_RESTOREPOINTERS:
if (!ecb) {
esp_sched_msgout(SEND_ABORT);
+ DELAY(1);
printf("%s: no DATAPOINTERs to restore\n",
sc->sc_dev.dv_xname);
break;
}
- ESPCMD(sc, ESPCMD_MSGOK);
sc->sc_dp = ecb->daddr;
sc->sc_dleft = ecb->dleft;
break;
@@ -1049,39 +1081,41 @@ esp_msgin(sc)
case MSG_EXT_SDTR:
ti->period = sc->sc_imess[3];
ti->offset = sc->sc_imess[4];
- if (ti->offset == 0) {
- printf("%s: async\n", TARGETNAME(ecb));
- ti->offset = 0;
- } else if (ti->period > 124) {
- printf("%s: async\n", TARGETNAME(ecb));
+ sc->sc_flags &= ~ESPF_SYNCHNEGO;
+ ti->flags &= ~DO_NEGOTIATE;
+ if (ti->offset == 0 ||
+ ti->offset > ESP_SYNC_MAXOFF ||
+ ti->period < sc->sc_minsync) {
ti->offset = 0;
- esp_sched_msgout(SEND_SDTR);
- } else { /* we are sync */
- printf("%s: sync rate %2fMb/s\n",
- TARGETNAME(ecb),
- sc->sc_freq/ti->period);
+ ti->period = sc->sc_minsync;
+ ti->synctp = 250 / ti->period;
+ break;
}
+ printf("%s targ %d: sync, offset %d, period %dnsec\n",
+ sc->sc_dev.dv_xname, ecb->xs->sc_link->target,
+ ti->offset, ti->period * 4);
+ ti->synctp = 250 / ti->period;
break;
- default: /* Extended messages we don't handle */
- ESPCMD(sc, ESPCMD_SETATN);
+ default:
+ /* Extended messages we don't handle */
+ esp_sched_msgout(SEND_REJECT);
+ DELAY(1);
break;
}
- ESPCMD(sc, ESPCMD_MSGOK);
break;
default:
- /* thanks for that ident... */
- if (!ESP_MSG_ISIDENT(sc->sc_imess[0])) {
- ESP_MISC(("unknown "));
- ESPCMD(sc, ESPCMD_SETATN);
+ /* thanks for that ident... XXX: we should check it? */
+ if (!MSG_ISIDENTIFY(sc->sc_imess[0])) {
+ esp_sched_msgout(SEND_REJECT);
+ DELAY(1);
}
- ESPCMD(sc, ESPCMD_MSGOK);
break;
}
- } else if (sc->sc_state == ESP_RESELECTED) {
+ } else if (sc->sc_state == ESPS_RESELECTED) {
struct scsi_link *sc_link;
- struct ecb *ecb;
u_char lunit;
- if (ESP_MSG_ISIDENT(sc->sc_imess[0])) { /* Identify? */
+
+ if (MSG_ISIDENTIFY(sc->sc_imess[0])) { /* Identify? */
ESP_MISC(("searching "));
/*
* Search wait queue for disconnected cmd
@@ -1101,37 +1135,101 @@ esp_msgin(sc)
}
}
- if (!ecb) { /* Invalid reselection! */
+ if (!ecb) {
+ /* Invalid reselection! */
esp_sched_msgout(SEND_ABORT);
+ DELAY(1);
printf("esp: invalid reselect (idbit=0x%2x)\n",
sc->sc_selid);
- } else { /* Reestablish nexus */
+ } else {
/*
+ * Reestablish nexus:
* Setup driver data structures and
* do an implicit RESTORE POINTERS
*/
sc->sc_nexus = ecb;
sc->sc_dp = ecb->daddr;
sc->sc_dleft = ecb->dleft;
- sc->sc_tinfo[sc_link->target].lubusy
- |= (1<<sc_link->lun);
- esp[ESP_SYNCOFF] =
+ sc->sc_tinfo[sc_link->target].lubusy |=
+ (1<<sc_link->lun);
+ espr->espr_syncoff =
sc->sc_tinfo[sc_link->target].offset;
- esp[ESP_SYNCTP] =
- 250 / sc->sc_tinfo[sc_link->target].period;
+ espr->espr_synctp = 250 /
+ sc->sc_tinfo[sc_link->target].period;
ESP_MISC(("... found ecb"));
- sc->sc_state = ESP_HASNEXUS;
+ sc->sc_state = ESPS_NEXUS;
}
} else {
printf("%s: bogus reselect (no IDENTIFY) %0x2x\n",
sc->sc_dev.dv_xname, sc->sc_selid);
esp_sched_msgout(SEND_DEV_RESET);
+ DELAY(1);
}
} else { /* Neither ESP_HASNEXUS nor ESP_RESELECTED! */
printf("%s: unexpected message in; will send DEV_RESET\n",
sc->sc_dev.dv_xname);
esp_sched_msgout(SEND_DEV_RESET);
+ DELAY(1);
+ }
+done:
+ espr->espr_cmd = ESPCMD_MSGOK;
+ espr->espr_cmd = ESPCMD_NOP;
+}
+
+
+void
+esp_makemsg(sc)
+ register struct esp_softc *sc;
+{
+ struct esp_tinfo *ti;
+ struct ecb *ecb = sc->sc_nexus;
+ int i;
+
+ sc->sc_msgout = sc->sc_msgpriq & -sc->sc_msgpriq;
+ ESPD(("MQ%02x/%02x ", sc->sc_msgpriq, sc->sc_msgout));
+
+ sc->sc_omlen = 1; /* "Default" message len */
+ switch (sc->sc_msgout) {
+ case SEND_SDTR: /* implies an IDENTIFY message */
+ sc->sc_flags |= ESPF_SYNCHNEGO;
+ ti = &sc->sc_tinfo[ecb->xs->sc_link->target];
+ sc->sc_omess[0] = MSG_IDENTIFY(ecb->xs->sc_link->lun,
+ !(ecb->xs->flags & SCSI_POLL));
+ sc->sc_omess[1] = MSG_EXTENDED;
+ sc->sc_omess[2] = 3;
+ sc->sc_omess[3] = MSG_EXT_SDTR;
+ sc->sc_omess[4] = ti->period;
+ sc->sc_omess[5] = ti->offset;
+ sc->sc_omlen = 6;
+ /* Fallthrough! */
+ case SEND_IDENTIFY:
+ sc->sc_omess[0] = MSG_IDENTIFY(ecb->xs->sc_link->lun,
+ !(ecb->xs->flags & SCSI_POLL));
+ break;
+ case SEND_DEV_RESET:
+ sc->sc_omess[0] = MSG_BUS_DEV_RESET;
+ if (sc->sc_nexus)
+ sc->sc_nexus->flags |= ECB_DONE;
+ break;
+ case SEND_PARITY_ERROR:
+ sc->sc_omess[0] = MSG_PARITY_ERROR;
+ break;
+ case SEND_ABORT:
+ sc->sc_omess[0] = MSG_ABORT;
+ if (sc->sc_nexus)
+ sc->sc_nexus->flags |= ECB_DONE;
+ break;
+ case SEND_INIT_DET_ERR:
+ sc->sc_omess[0] = MSG_INITIATOR_DET_ERR;
+ break;
+ case SEND_REJECT:
+ sc->sc_omess[0] = MSG_MESSAGE_REJECT;
+ break;
+ default:
+ sc->sc_omess[0] = MSG_NOOP;
+ break;
}
+ sc->sc_omp = sc->sc_omess;
}
@@ -1142,434 +1240,620 @@ void
esp_msgout(sc)
register struct esp_softc *sc;
{
- volatile caddr_t esp = sc->sc_reg;
+ struct espregs *espr = sc->sc_regs;
struct esp_tinfo *ti;
struct ecb *ecb;
+ int i;
- if (sc->sc_prevphase != MESSAGE_OUT_PHASE) {
- /* Pick up highest priority message */
- sc->sc_msgout = sc->sc_msgpriq & -sc->sc_msgpriq;
- sc->sc_omlen = 1; /* "Default" message len */
- switch (sc->sc_msgout) {
- case SEND_SDTR: /* Also implies an IDENTIFY message */
- ecb = sc->sc_nexus;
- sc->sc_flags |= ESP_SYNCHNEGO;
- ti = &sc->sc_tinfo[ecb->xs->sc_link->target];
- sc->sc_omess[1] = MSG_EXTENDED;
- sc->sc_omess[2] = 3;
- sc->sc_omess[3] = MSG_EXT_SDTR;
- sc->sc_omess[4] = ti->period;
- sc->sc_omess[5] = ti->offset;
- sc->sc_omlen = 6;
- /* Fallthrough! */
- case SEND_IDENTIFY:
- if (sc->sc_state != ESP_HASNEXUS) {
- printf("esp at line %d: no nexus", __LINE__);
- Debugger();
- }
- ecb = sc->sc_nexus;
- sc->sc_omess[0] = ESP_MSG_IDENTIFY(ecb->xs->sc_link->lun);
- break;
- case SEND_DEV_RESET:
- sc->sc_omess[0] = MSG_BUS_DEV_RESET;
- sc->sc_flags |= ESP_BUSFREE_OK;
- break;
- case SEND_PARITY_ERROR:
- sc->sc_omess[0] = MSG_PARITY_ERR;
- break;
- case SEND_ABORT:
- sc->sc_omess[0] = MSG_ABORT;
- sc->sc_flags |= ESP_BUSFREE_OK;
- break;
- case SEND_INIT_DET_ERR:
- sc->sc_omess[0] = MSG_INITIATOR_DET_ERR;
- break;
- case SEND_REJECT:
- sc->sc_omess[0] = MSG_MESSAGE_REJECT;
- break;
- default:
- sc->sc_omess[0] = MSG_NOOP;
- break;
- }
- sc->sc_omp = sc->sc_omess;
- }
+ /* Pick up highest priority message */
+ sc->sc_msgout = sc->sc_msgpriq & -sc->sc_msgpriq;
+ esp_makemsg(sc);
- /* (re)send the message */
- DMA_START(sc->sc_dma, &sc->sc_omp, &sc->sc_omlen, 0);
+ for (i = 0; i < sc->sc_omlen; i++)
+ espr->espr_fifo = sc->sc_omp[i];
+ espr->espr_cmd = ESPCMD_TRANS;
+}
+
+
+void
+esp_timeout(arg)
+ void *arg;
+{
+ struct ecb *ecb = (struct ecb *)arg;
+ struct esp_softc *sc;
+
+ sc = ecb->xs->sc_link->adapter_softc;
+ sc_print_addr(ecb->xs->sc_link);
+ printf("timed out at %d %d\n", sc->sc_state, sc->sc_phase);
+
+ ecb->xs->error = XS_TIMEOUT;
+ esp_reset(sc);
+ esp_done(ecb);
+ printf("new ecb %08x\n", sc->sc_nexus);
}
/*
- * This is the most critical part of the driver, and has to know
- * how to deal with *all* error conditions and phases from the SCSI
- * bus. If there are no errors and the DMA was active, then call the
- * DMA pseudo-interrupt handler. If this returns 1, then that was it
- * and we can return from here without further processing.
- *
- * Most of this needs verifying.
+ * Whatever we do, we must generate an interrupt if we expect to go
+ * to the next state.
+ * Note: this increments the events even if called from esp_poll()
*/
int
espintr(sc)
register struct esp_softc *sc;
{
+ struct espregs *espr = sc->sc_regs;
register struct ecb *ecb = sc->sc_nexus;
register struct scsi_link *sc_link;
- volatile caddr_t esp = sc->sc_reg;
+ u_char *cmd;
struct esp_tinfo *ti;
- caddr_t cmd;
- int loop;
+ int dmaintrwas, i;
- ESP_TRACE(("espintr\n"));
-
/*
- * I have made some (maybe seriously flawed) assumptions here,
- * but basic testing (uncomment the printf() below), show that
- * certainly something happens when this loop is here.
- *
- * The idea is that many of the SCSI operations take very little
- * time, and going away and getting interrupted is too high an
- * overhead to pay. For example, selecting, sending a message
- * and command and then doing some work can be done in one "pass".
- *
- * The DELAY is not variable because I do not understand that the
- * DELAY loop should be fixed-time regardless of CPU speed, but
- * I am *assuming* that the faster SCSI processors get things done
- * quicker (sending a command byte etc), and so there is no
- * need to be too slow.
- *
- * This is a heuristic. It is 2 when at 20Mhz, 2 at 25Mhz and 1
- * at 40Mhz. This needs testing.
+ * Revision 1 DMA's must have their interrupts disabled,
+ * otherwise we can get bus timeouts while reading ESP
+ * registers.
*/
- for (loop = 0; 1;loop++, DELAY(50/sc->sc_freq)) {
- /* a feeling of deja-vu */
- if (!DMA_ISINTR(sc->sc_dma) && loop)
- return 1;
-#if 0
- if (loop)
- printf("*");
-#endif
+ if (sc->sc_dma->sc_rev == DMAREV_1)
+ dmaintrwas = dmadisintr(sc->sc_dma);
- /* and what do the registers say... */
- espreadregs(sc);
+ espreadregs(sc);
- if (sc->sc_state == ESP_IDLE) {
- printf("%s: stray interrupt\n", sc->sc_dev.dv_xname);
- return 0;
- }
+ /*
+ * If either of these two things is true, we caused an interrupt.
+ * If we didn't, let's get out of here.
+ */
+ if ((sc->sc_espintr | dmapending(sc->sc_dma)) == 0) {
+ if (sc->sc_dma->sc_rev == DMAREV_1 && dmaintrwas)
+ dmaenintr(sc->sc_dma);
+ return (0);
+ }
- sc->sc_intrcnt.ev_count++;
+ sc->sc_phase = espphase(sc);
+ ESPD(("I%02x/%02x/%02x ", sc->sc_espintr, sc->sc_state, sc->sc_phase));
+
+ if (sc->sc_espintr & ESPINTR_SBR) {
+ espflush(sc);
+ printf("%s: scsi bus reset\n", sc->sc_dev.dv_xname);
+ esp_init(sc, 0); /* Restart everything */
/*
- * What phase are we in when we *entered* the
- * interrupt handler ?
- *
- * On laster ESP chips (ESP236 and up) the FE (features
- * enable) bit in config 2 latches the phase bits
- * at each "command completion".
+ * No interrupt expected, since there are now no
+ * ecb's in progress.
*/
- sc->sc_phase = espphase(sc);
+ goto done;
+ }
+
+ if (sc->sc_espintr & ESPINTR_ILL) {
+ printf("%s: illegal command %02x\n",
+ sc->sc_dev.dv_xname, espr->espr_cmd);
+ if (sc->sc_state == ESPS_NEXUS) {
+ ecb->xs->error = XS_DRIVER_STUFFUP;
+ untimeout(esp_timeout, ecb);
+ esp_done(ecb);
+ }
+ esp_reset(sc);
+ esp_sched(sc); /* start next command */
+ goto done;
+ }
+
+ if (sc->sc_espstat & ESPSTAT_GE) {
+ /* no target ? */
+ espflush(sc);
+ printf("%s: gross error\n", sc->sc_dev.dv_xname);
+ if (sc->sc_state == ESPS_NEXUS) {
+ ecb->xs->error = XS_DRIVER_STUFFUP;
+ untimeout(esp_timeout, ecb);
+ esp_done(ecb);
+ }
+ esp_reset(sc);
+ esp_sched(sc);
+ /* started next command, which will interrupt us */
+ goto done;
+ }
+
+ /*
+ * The `bad interrupts' have already been checked.
+ */
+states:
+ switch (sc->sc_state) {
+ case ESPS_IDLE:
+ ESPD(("I "));
+
+ sc->sc_nexus = NULL;
+
+ if (sc->sc_espintr & ESPINTR_RESEL) {
+ sc->sc_state = ESPS_RESELECTED;
+ goto states;
+ }
+ esp_sched(sc);
/*
- * At the moment, only a SCSI Bus Reset or Illegal
- * Command are classed as errors. A diconnect is a
- * valid condition, and we let the code check is the
- * "ESP_BUSFREE_OK" flag was set before declaring it
- * and error.
- *
- * Also, the status register tells us about "Gross
- * Errors" and "Parity errors". Only the Gross Error
- * is really bad, and the parity errors are dealt
- * with later
- *
- * TODO
- * If there are too many parity error, go to slow
- * cable mode ?
+ * We will get an interrupt if esp_sched() queues
+ * anything to the scsi bus.
*/
-#define ESPINTR_ERR (ESPINTR_SBR|ESPINTR_ILL)
-
- if (sc->sc_espintr & ESPINTR_ERR ||
- sc->sc_espstat & ESPSTAT_GE) {
- /* SCSI Reset */
- if (sc->sc_espintr & ESPINTR_SBR) {
- if (esp[ESP_FFLAG] & ESPFIFO_FF) {
- ESPCMD(sc, ESPCMD_FLUSH);
- DELAY(1);
- }
- printf("%s: SCSI bus reset\n",
- sc->sc_dev.dv_xname);
- esp_init(sc, 0); /* Restart everything */
- return 1;
+ goto done;
+ case ESPS_SELECTING:
+ /*
+ * For a simple select, we sent either
+ * ESPCMD_SELATN or ESPCMD_SELNATN
+ */
+ ESPD(("S "));
+
+ if (sc->sc_espintr & ESPINTR_DIS) {
+ sc->sc_state = ESPS_NEXUS; /* will cleanup */
+ goto states;
+ }
+
+ if (sc->sc_espintr & ESPINTR_RESEL) {
+ /*
+ * If we were trying to select a target,
+ * push our command back into the ready list.
+ */
+ if (sc->sc_state == ESPS_SELECTING) {
+ TAILQ_INSERT_HEAD(&sc->ready_list,
+ sc->sc_nexus, chain);
+ ecb = sc->sc_nexus = NULL;
}
+ sc->sc_state = ESPS_RESELECTED;
+ goto states;
+ }
- if (sc->sc_espstat & ESPSTAT_GE) {
- /* no target ? */
- if (esp[ESP_FFLAG] & ESPFIFO_FF) {
- ESPCMD(sc, ESPCMD_FLUSH);
- DELAY(1);
+ if (sc->sc_espintr == (ESPINTR_FC|ESPINTR_BS)) {
+ ESPD(("STEP%d ", sc->sc_espstep));
+ if (sc->sc_espstep == 0) {
+ /* ATN: cannot happen */
+ /* NATN: target did not assert message phase */
+ sc->sc_state = ESPS_IDLE;
+ goto states;
+ } else if (sc->sc_espstep == 2) {
+ /* BOTH: target did not assert command phase */
+ sc->sc_state = ESPS_IDLE;
+ goto states;
+ } else if (sc->sc_espstep == 3) {
+ /* BOTH: changed phase during cmd transfer */
+ ESPD(("FF%d ", espr->espr_fflag & ESPFIFO_FF));
+ if (espr->espr_fflag & ESPFIFO_FF) {
+ sc->sc_state = ESPS_IDLE;
+ goto states;
}
+ }
+
+ espr->espr_cmd = ESPCMD_NOP; /* unlatch fifo counter */
+ if (sc->sc_phase != DATA_IN_PHASE &&
+ sc->sc_tinfo[ecb->xs->sc_link->target].offset == 0) {
DELAY(1);
- if (sc->sc_state == ESP_HASNEXUS) {
- ecb->xs->error = XS_DRIVER_STUFFUP;
- untimeout(esp_timeout, ecb);
- espreadregs(sc);
- esp_done(ecb);
- }
- return 1;
+ espflush(sc);
}
- if (sc->sc_espintr & ESPINTR_ILL) {
- /* illegal command, out of sync ? */
- printf("%s: illegal command ",
- sc->sc_dev.dv_xname);
- if (esp[ESP_FFLAG] & ESPFIFO_FF) {
- ESPCMD(sc, ESPCMD_FLUSH);
- DELAY(1);
- }
- if (sc->sc_state == ESP_HASNEXUS) {
- ecb->xs->error = XS_DRIVER_STUFFUP;
- untimeout(esp_timeout, ecb);
- esp_done(ecb);
- }
- esp_reset(sc); /* so start again */
- return 1;
+ ecb = sc->sc_nexus;
+ if (!ecb)
+ panic("esp: not nexus at sc->sc_nexus");
+ sc_link = ecb->xs->sc_link;
+ ti = &sc->sc_tinfo[sc_link->target];
+ sc->sc_flags = 0;
+
+ /* Clearly we succeeded at sending the message */
+ sc->sc_msgpriq &= ~sc->sc_msgout;
+ sc->sc_msgout = 0;
+ if (sc->sc_msgpriq)
+ espr->espr_cmd = ESPCMD_SETATN;
+
+ sc->sc_dp = ecb->daddr; /* implicit RESTOREDATAPOINTERS */
+ sc->sc_dleft = ecb->dleft;
+ ti->lubusy |= (1<<sc_link->lun);
+
+ sc->sc_state = ESPS_NEXUS;
+ }
+ break; /* handle the phase */
+ case ESPS_SELECTSTOP:
+ /*
+ * We wanted to select with multiple message bytes.
+ * As a result, we needed to use SELATNS. One
+ * message byte has been sent at this point. We need
+ * to send the other bytes, then the target will either
+ * switch to command phase to fetch the command or
+ * send us a message.
+ */
+ ESPD(("SS%d ", sc->sc_espstep));
+
+ if (sc->sc_espintr & ESPINTR_DIS) {
+ sc->sc_state = ESPS_NEXUS;
+ goto states;
+ }
+
+ if (sc->sc_espintr & ESPINTR_RESEL) {
+ /*
+ * If we were trying to select a target,
+ * push our command back into the ready list.
+ */
+ if (sc->sc_state == ESPS_SELECTING) {
+ TAILQ_INSERT_HEAD(&sc->ready_list,
+ sc->sc_nexus, chain);
+ ecb = sc->sc_nexus = NULL;
}
+ sc->sc_state = ESPS_RESELECTED;
+ goto states;
+ }
+
+ if (sc->sc_espintr & (ESPINTR_BS|ESPINTR_FC)) {
+ int i;
+
+ /*
+ * Shove the remainder of the message bytes
+ * into the fifo. After we send them, command
+ * phase will request the command...
+ */
+ espflush(sc);
+ for (i = 1; i < sc->sc_omlen; i++)
+ espr->espr_fifo = sc->sc_omp[i];
+ espr->espr_cmd = ESPCMD_TRANS;
+ sc->sc_state = ESPS_MULTMSGEND;
+ goto done;
+ }
+ goto done;
+ case ESPS_MULTMSGEND:
+ ESPD(("MME "));
+ if (sc->sc_espintr & ESPINTR_DIS) {
+ sc->sc_state = ESPS_NEXUS;
+ goto states;
+ }
+
+ sc->sc_msgpriq &= ~sc->sc_msgout;
+ sc->sc_msgout = 0;
+
+ ESPD(("F%d ", espr->espr_fflag & ESPFIFO_FF));
+ if (espr->espr_fflag & ESPFIFO_FF) {
+ espflush(sc);
+ DELAY(1);
}
/*
- * Call if DMA is active.
- *
- * If DMA_INTR returns true, then maybe go 'round the loop
- * again in case there is no more DMA queued, but a phase
- * change is expected.
+ * If there are more messages, re-assert
+ * ATN so that we will enter MSGOUT phase
+ * again. Also, pause till that interrupt is
+ * done to see if the phase changes.
*/
- if (sc->sc_dma->sc_active && DMA_INTR(sc->sc_dma)) {
- /* If DMA active here, then go back to work... */
- if (sc->sc_dma->sc_active)
- return 1;
- DELAY(50/sc->sc_freq);
- continue;
+ if (sc->sc_msgpriq)
+ espr->espr_cmd = ESPCMD_SETATN;
+ sc->sc_state = ESPS_NEXUS;
+ goto states;
+ case ESPS_RESELECTED:
+ ESPD(("RS "));
+
+ if (sc->sc_phase != MESSAGE_IN_PHASE) {
+ printf("%s: target didn't identify\n",
+ sc->sc_dev.dv_xname);
+ esp_init(sc, 1);
+ goto done;
+ }
+ esp_msgin(sc);
+ if (sc->sc_state != ESPS_NEXUS) {
+ /* IDENTIFY failed?! */
+ printf("%s: identify failed\n", sc->sc_dev.dv_xname);
+ esp_init(sc, 1);
+ goto done;
}
/*
- * check for less serious errors
+ * We will get an interrupt because of the ESPCMD_MSGOK
+ * interrupt inside esp_msgin()
+ */
+ goto done;
+ case ESPS_DOINGDMA:
+ /*
+ * Call if DMA is still active. If dmaintr() has finished,
+ * delay a while to let things settle, and then re-read the
+ * phase.
+ *
+ * XXX: should check ESPINTR_DIS
*/
if (sc->sc_espstat & ESPSTAT_PE) {
- printf("esp: SCSI bus parity error\n");
- if (sc->sc_prevphase == MESSAGE_IN_PHASE)
- esp_sched_msgout(SEND_PARITY_ERROR);
- else
- esp_sched_msgout(SEND_INIT_DET_ERR);
+ printf("%s: SCSI bus parity error\n",
+ sc->sc_dev.dv_xname);
+ esp_sched_msgout(SEND_INIT_DET_ERR);
+ /* XXX: anything else to do? */
}
- if (sc->sc_espintr & ESPINTR_DIS) {
- ESP_MISC(("disc "));
- if (esp[ESP_FFLAG] & ESPFIFO_FF) {
- ESPCMD(sc, ESPCMD_FLUSH);
- DELAY(1);
- }
+ if (sc->sc_espintr & ESPINTR_BS) {
/*
- * This command must (apparently) be issued within
- * 250mS of a disconnect. So here you are...
+ * ESP says we have changed phase, or have
+ * finished transferring all the bytes. It
+ * doesn't matter which case, either way we
+ * will let the phase determine what happens
+ * next.
*/
- ESPCMD(sc, ESPCMD_ENSEL);
- if (sc->sc_state != ESP_IDLE) {
- /* it may be OK to disconnect */
- if (!(sc->sc_flags & ESP_BUSFREE_OK))
- ecb->xs->error = XS_TIMEOUT;
- untimeout(esp_timeout, ecb);
- esp_done(ecb);
- return 1;
+ ESPD(("DOINGDMA:BS:%02x/%02x ", sc->sc_espstat,
+ espr->espr_tcl));
+ if (sc->sc_dma->sc_active) {
+ dmaintr(sc->sc_dma, 0);
+ if (sc->sc_dma->sc_active)
+ goto done;
+ }
+ } else {
+ ESPD(("DOINGDMA:cont "));
+ if (sc->sc_dma->sc_active) {
+ dmaintr(sc->sc_dma, 1);
+ if (sc->sc_dma->sc_active)
+ goto done;
}
}
- /* did a message go out OK ? This must be broken */
- if (sc->sc_prevphase == MESSAGE_OUT_PHASE &&
- sc->sc_phase != MESSAGE_OUT_PHASE) {
- /* we have sent it */
+ /* finished a DMA transaction */
+ sc->sc_flags = 0;
+
+ ecb->daddr = sc->sc_dp; /* implicit SAVEDATAPOINTERS */
+ ecb->dleft = sc->sc_dleft;
+
+ /*
+ * ESP100 strangeness -- need to fetch a new phase --
+ * why?
+ */
+ if (sc->sc_rev==ESP100 &&
+ sc->sc_tinfo[ecb->xs->sc_link->target].offset) {
+ sc->sc_espstat = espr->espr_stat & ESPSTAT_PHASE;
+ sc->sc_phase = espphase(sc);
+ /* XXX should check if sync under/overran */
+ }
+ sc->sc_state = ESPS_NEXUS;
+ break; /* handle the phase */
+ case ESPS_DOINGMSGOUT:
+ ESPD(("DMO "));
+ if (sc->sc_espintr & ESPINTR_DIS) {
+ sc->sc_state = ESPS_NEXUS; /* will cleanup */
+ goto states;
+ }
+
+ if (sc->sc_espintr & ESPINTR_BS) {
sc->sc_msgpriq &= ~sc->sc_msgout;
sc->sc_msgout = 0;
- }
- switch (sc->sc_state) {
+ ESPD(("F%d ", espr->espr_fflag & ESPFIFO_FF));
+ if (espr->espr_fflag & ESPFIFO_FF) {
+ espflush(sc);
+ DELAY(1);
+ }
- case ESP_RESELECTED:
/*
- * we must be continuing a message ?
+ * If there are more messages, re-assert
+ * ATN so that we will enter MSGOUT phase
+ * again. Also, pause till that interrupt is
+ * done to see if the phase changes.
*/
- if (sc->sc_phase != MESSAGE_IN_PHASE) {
- printf("%s: target didn't identify\n",
- sc->sc_dev.dv_xname);
- esp_init(sc, 1);
- return 1;
- }
- esp_msgin(sc);
- if (sc->sc_state != ESP_HASNEXUS) {
- /* IDENTIFY fail?! */
- printf("%s: identify failed\n",
- sc->sc_dev.dv_xname);
- esp_init(sc, 1);
- return 1;
- }
+ if (sc->sc_msgpriq)
+ espr->espr_cmd = ESPCMD_SETATN;
break;
+ }
- case ESP_IDLE:
- case ESP_SELECTING:
+#if 0
+ if (sc->sc_espintr & ESPINTR_FC) {
+ /*
+ * XXX: the book claims that an ESPINTR_BS is
+ * generated, not ESPINTR_FC. Check if that is
+ * true.
+ */
+ }
+#endif
+ break; /* handle the phase */
+ case ESPS_DOINGMSGIN:
+ /*
+ * We receive two interrupts per message byte here: one for
+ * the MSGOK command in esp_msgin(), and one for the TRANS
+ * command below.
+ */
+ ESPD(("DMI "));
- if (sc->sc_espintr & ESPINTR_RESEL) {
+ if (sc->sc_espintr & ESPINTR_DIS) {
+ sc->sc_state = ESPS_NEXUS; /* will cleanup */
+ goto states;
+ }
+
+ if (sc->sc_espstat & ESPSTAT_PE) {
+ printf("%s: SCSI bus parity error\n",
+ sc->sc_dev.dv_xname);
+ esp_sched_msgout(SEND_PARITY_ERROR);
+ espflush(sc);
+ sc->sc_state = ESPS_NEXUS;
+ /* XXX: are we missing some fifo handling? */
+ goto done; /* will be interrupted for MSGOUT */
+ }
+
+ /*
+ * The two interrupts we receive per byte will alternate
+ * between entering the following two if() statements.
+ * XXX: just incase, are these two if()'s in the correct
+ * order?
+ */
+ if (sc->sc_espintr & ESPINTR_BS) {
+ /*
+ * interrupted by ESPCMD_MSGOK in esp_msgin()
+ */
+ if (sc->sc_phase == MESSAGE_IN_PHASE)
+ espr->espr_cmd = ESPCMD_TRANS;
+ else {
+ ESPD(("PC "));
/*
- * If we're trying to select a
- * target ourselves, push our command
- * back into the ready list.
+ * XXX: The phase has been changed on us.
+ * This should lead us to discard the
+ * current (incomplete) message, perhaps
+ * assert an error(?), and go handle whatever
+ * new phase we are now in.
*/
- if (sc->sc_state == ESP_SELECTING) {
- ESP_MISC(("backoff selector "));
- TAILQ_INSERT_HEAD(&sc->ready_list,
- sc->sc_nexus, chain);
- sc->sc_tinfo[sc->sc_nexus->xs->sc_link->target].lubusy
- &= ~(1<<sc_link->lun);
- sc->sc_nexus = NULL;
- }
- sc->sc_state = ESP_RESELECTED;
- if (sc->sc_phase != MESSAGE_IN_PHASE) {
- /*
- * Things are seriously fucked up.
- * Pull the brakes, i.e. reset
- */
- printf("%s: target didn't identify\n",
- sc->sc_dev.dv_xname);
- esp_init(sc, 1);
- return 1;
- }
- esp_msgin(sc); /* Handle identify message */
- if (sc->sc_state != ESP_HASNEXUS) {
- /* IDENTIFY fail?! */
- printf("%s: identify failed\n",
- sc->sc_dev.dv_xname);
- esp_init(sc, 1);
- return 1;
- }
break;
}
+ goto done;
+ }
+ if (sc->sc_espintr & ESPINTR_FC) {
+ /*
+ * interrupted by ESPCMD_TRANS a few lines above.
+ */
+ esp_msgin(sc);
+ goto done;
+ }
+ goto done;
+ case ESPS_DOINGSTATUS:
+ if (sc->sc_espintr & ESPINTR_DIS) {
+ sc->sc_state = ESPS_NEXUS; /* will cleanup */
+ goto states;
+ }
-#define ESPINTR_DONE (ESPINTR_FC|ESPINTR_BS)
- if ((sc->sc_espintr & ESPINTR_DONE) == ESPINTR_DONE) {
- ecb = sc->sc_nexus;
- if (!ecb)
- panic("esp: not nexus at sc->sc_nexus");
- sc_link = ecb->xs->sc_link;
- ti = &sc->sc_tinfo[sc_link->target];
- if (ecb->xs->flags & SCSI_RESET)
- sc->sc_msgpriq = SEND_DEV_RESET;
- else if (ti->flags & DO_NEGOTIATE)
- sc->sc_msgpriq =
- SEND_IDENTIFY | SEND_SDTR;
- else
- sc->sc_msgpriq = SEND_IDENTIFY;
- sc->sc_state = ESP_HASNEXUS;
- sc->sc_flags = 0;
- sc->sc_prevphase = INVALID_PHASE;
- sc->sc_dp = ecb->daddr;
- sc->sc_dleft = ecb->dleft;
- ti->lubusy |= (1<<sc_link->lun);
- break;
- } else if (sc->sc_espintr & ESPINTR_FC) {
- if (sc->sc_espstep != ESPSTEP_DONE)
- if (esp[ESP_FFLAG] & ESPFIFO_FF) {
- ESPCMD(sc, ESPCMD_FLUSH);
- DELAY(1);
- }
+ if (sc->sc_espintr & (ESPINTR_FC|ESPINTR_BS)) {
+ i = espgetbyte(sc, 0);
+ if (i == -1) {
+ printf("%s: ESPS_DOINGSTATUS fifo empty\n",
+ sc->sc_dev.dv_xname);
+ /* XXX: how do we cleanup from this error? */
+ goto done;
}
- /* We aren't done yet, but expect to be soon */
- DELAY(50/sc->sc_freq);
- continue;
+ if (sc->sc_espstat & ESPSTAT_PE) {
+ printf("%s: SCSI bus parity error\n",
+ sc->sc_dev.dv_xname);
+ /*
+ * Can't tell what the real status should
+ * be, so force a later check.
+ */
+ i = SCSI_CHECK;
+ }
+ ecb->stat = (u_char)i;
+ ESPD(("status=%02x ", (u_char)i));
- case ESP_HASNEXUS:
- break;
- default:
- panic("esp unknown state");
+ if (sc->sc_espintr & ESPINTR_FC) {
+ /*
+ * XXX assumes a single byte message --
+ * and what about parity errors and other
+ * possible botches inside esp_msgin?
+ */
+ esp_msgin(sc);
+ sc->sc_state = ESPS_NEXUS;
+ } else
+ sc->sc_state = ESPS_DOINGMSGIN;
+ goto done;
}
-
+ goto done;
+ case ESPS_EXPECTDISC:
/*
- * Driver is now in state ESP_HASNEXUS, i.e. we
- * have a current command working the SCSI bus.
+ * We were told to expect a disconnection interrupt.
+ * When we get it, we can go back to other work.
*/
- cmd = (caddr_t) &ecb->cmd;
- if (sc->sc_state != ESP_HASNEXUS || ecb == NULL) {
- panic("esp no nexus");
+ ESPD(("ED "));
+ if (sc->sc_espintr & ESPINTR_DIS) {
+ espflush(sc);
+ DELAY(1);
+ espr->espr_cmd = ESPCMD_ENSEL; /* within 250ms of disconnect */
+ sc->sc_state = ESPS_IDLE;
}
+ goto states;
+ case ESPS_NEXUS:
+ ESPD(("NX "));
- switch (sc->sc_phase) {
- case MESSAGE_OUT_PHASE:
- ESP_PHASE(("MESSAGE_OUT_PHASE "));
- esp_msgout(sc);
- sc->sc_prevphase = MESSAGE_OUT_PHASE;
- break;
- case MESSAGE_IN_PHASE:
- ESP_PHASE(("MESSAGE_IN_PHASE "));
- esp_msgin(sc);
- sc->sc_prevphase = MESSAGE_IN_PHASE;
- break;
- case COMMAND_PHASE:
- /* well, this means send the command again */
- ESP_PHASE(("COMMAND_PHASE 0x%02x (%d) ",
- ecb->cmd.opcode, ecb->clen));
- if (esp[ESP_FFLAG] & ESPFIFO_FF) {
- ESPCMD(sc, ESPCMD_FLUSH);
- DELAY(1);
- }
- espselect(sc, ecb->xs->sc_link->target,
- ecb->xs->sc_link->lun, (caddr_t)&ecb->cmd,
- ecb->clen);
- sc->sc_prevphase = COMMAND_PHASE;
- break;
- case DATA_OUT_PHASE:
- ESP_PHASE(("DATA_OUT_PHASE [%d] ", sc->sc_dleft));
- ESPCMD(sc, ESPCMD_FLUSH);
- DMA_START(sc->sc_dma, &sc->sc_dp, &sc->sc_dleft, 0);
- sc->sc_prevphase = DATA_OUT_PHASE;
- break;
- case DATA_IN_PHASE:
- ESP_PHASE(("DATA_IN_PHASE "));
- ESPCMD(sc, ESPCMD_FLUSH);
- DMA_DRAIN(sc->sc_dma);
- DMA_START(sc->sc_dma, &sc->sc_dp, &sc->sc_dleft,
- D_WRITE);
- sc->sc_prevphase = DATA_IN_PHASE;
- break;
- case STATUS_PHASE:
- ESP_PHASE(("STATUS_PHASE "));
- ESPCMD(sc, ESPCMD_ICCS);
- (void)espgetbyte(sc, &ecb->stat);
- ESP_PHASE(("0x%02x ", ecb->stat));
- sc->sc_prevphase = STATUS_PHASE;
- break;
- case INVALID_PHASE:
- break;
- case BUSFREE_PHASE:
- if (sc->sc_flags & ESP_BUSFREE_OK) {
- /*It's fun the 1st time.. */
- sc->sc_flags &= ~ESP_BUSFREE_OK;
+ if (sc->sc_espintr & ESPINTR_DIS) {
+ espflush(sc);
+ DELAY(1);
+ espr->espr_cmd = ESPCMD_ENSEL; /* within 250ms of disconnect */
+ sc->sc_msgpriq = sc->sc_msgout = 0;
+
+ ESPD(("DD:"));
+ if (ecb->flags & ECB_DONE) {
+ ESPD(("D "));
+ /* successfully finished a transaction */
+ untimeout(esp_timeout, ecb);
+ esp_done(ecb);
+ } else if (sc->sc_flags & ESPF_MAYDISCON) {
+ ESPD(("M "));
+ /* legal discon/recon is happening */
+ TAILQ_INSERT_HEAD(&sc->nexus_list,
+ ecb, chain);
+ sc->sc_nexus = NULL;
+ sc->sc_state = ESPS_IDLE;
+ sc->sc_flags &= ~ESPF_MAYDISCON;
+ goto states;
+ } else {
+ ESPD(("U "));
+ /* unexpected disconnection! fail. */
+ ecb->xs->error = XS_TIMEOUT;
+ untimeout(esp_timeout, ecb);
+ esp_done(ecb);
}
- break;
- default:
- panic("esp: bogus bus phase\n");
+ goto done;
}
+ break; /* handle the phase */
+ default:
+ printf("%s: unknown state %d\n",
+ sc->sc_dev.dv_xname, sc->sc_state);
+ panic("cannot continue");
}
-}
-void
-esp_timeout(arg)
- void *arg;
-{
- int s = splbio();
- struct ecb *ecb = (struct ecb *)arg;
- struct esp_softc *sc;
+ ecb = sc->sc_nexus;
- sc = ecb->xs->sc_link->adapter_softc;
- sc_print_addr(ecb->xs->sc_link);
- ecb->xs->error = XS_TIMEOUT;
- printf("timed out\n");
+ switch (sc->sc_phase) {
+ case MESSAGE_OUT_PHASE:
+ /*
+ * ATN was asserted, and the bus changed into MSGOUT phase.
+ * Send a message.
+ */
+ esp_msgout(sc); /* cause intr */
+ sc->sc_state = ESPS_DOINGMSGOUT;
+ break;
+ case COMMAND_PHASE:
+ /*
+ * Apparently we need to repeat the previous command.
+ */
+ espflush(sc);
+ DELAY(1);
+ cmd = (u_char *)&ecb->cmd;
+
+ ESPD(("CMD["));
+ for (i = 0; i < ecb->clen; i++)
+ ESPD(("%02x%c", cmd[i],
+ (i == ecb->clen-1) ? ']' : ' '));
+ ESPD((" "));
+
+ /* Now the command into the FIFO */
+ for (i = 0; i < ecb->clen; i++)
+ espr->espr_fifo = cmd[i];
+
+ espr->espr_cmd = ESPCMD_TRANS; /* cause intr */
+ sc->sc_state = ESPS_NEXUS;
+ break;
+ case DATA_OUT_PHASE:
+ /*
+ * We can feed the device the data now.
+ */
+ espflush(sc);
+ DELAY(1);
+ dmastart(sc->sc_dma, &sc->sc_dp, &sc->sc_dleft, 0,
+ ecb->xs->flags & SCSI_POLL); /* cause intr */
+ sc->sc_state = ESPS_DOINGDMA;
+ break;
+ case DATA_IN_PHASE:
+ /*
+ * The device is ready to give us the data.
+ */
+ dmadrain(sc->sc_dma);
+ dmastart(sc->sc_dma, &sc->sc_dp, &sc->sc_dleft, 1,
+ ecb->xs->flags & SCSI_POLL); /* cause intr */
+ sc->sc_state = ESPS_DOINGDMA;
+ break;
+ case MESSAGE_IN_PHASE:
+ espflush(sc);
+ DELAY(1);
+ espr->espr_cmd = ESPCMD_TRANS; /* cause intr */
+ sc->sc_state = ESPS_DOINGMSGIN;
+ break;
+ case STATUS_PHASE:
+ espflush(sc);
+ DELAY(1);
+ espr->espr_cmd = ESPCMD_ICCS; /* cause intr */
+ sc->sc_state = ESPS_DOINGSTATUS;
+ break;
+ default:
+ printf("%s: bogus bus phase %d\n",
+ sc->sc_dev.dv_xname);
+ break;
+ }
- esp_done(ecb);
- esp_reset(sc);
- splx(s);
+done:
+ if (sc->sc_dma->sc_rev == DMAREV_1 && dmaintrwas)
+ dmaenintr(sc->sc_dma);
+ sc->sc_intrcnt.ev_count++;
+ return (1);
}