from netbsd:
authorderaadt <deraadt@openbsd.org>
Sun, 19 Nov 1995 13:51:25 +0000 (13:51 +0000)
committerderaadt <deraadt@openbsd.org>
Sun, 19 Nov 1995 13:51:25 +0000 (13:51 +0000)
Latest, greatest version of the new NCR driver.
DMA works!  Interrupts work!  Disconnect/reselect works!
To be paranoid, leave that all disabled for now...

sys/arch/sun3/dev/ncr5380reg.h
sys/arch/sun3/dev/ncr5380sbc.c
sys/arch/sun3/dev/ncr5380var.h
sys/arch/sun3/dev/ncr_si.c
sys/arch/sun3/dev/ncr_sireg.h

index 0901fd1..4a1e871 100644 (file)
@@ -1,4 +1,4 @@
-/*     $NetBSD: ncr5380reg.h,v 1.1 1995/10/29 21:19:08 gwr Exp $       */
+/*     $NetBSD: ncr5380reg.h,v 1.1.2.1 1995/11/18 07:08:23 gwr Exp $   */
 
 /* 
  * Mach Operating System
  */
 
 /*
- * Register map
+ * Register map:  Note not declared here anymore!
+ * All the 5380 registers are accessed through individual
+ * pointers initialized by MD code.  This allows the 5380
+ * MI functions to be shared between MD drivers that have
+ * different padding between the registers (i.e. amiga).
  */
-typedef struct {
-       volatile unsigned char  sci_data;       /* r:  Current data */
-#define                        sci_odata sci_data      /* w:  Out data */
-       volatile unsigned char  sci_icmd;       /* rw: Initiator command */
-       volatile unsigned char  sci_mode;       /* rw: Mode */
-       volatile unsigned char  sci_tcmd;       /* rw: Target command */
-       volatile unsigned char  sci_bus_csr;    /* r:  Bus Status */
-#define                        sci_sel_enb sci_bus_csr /* w:  Select enable */
-       volatile unsigned char  sci_csr;        /* r:  Status */
-#define                        sci_dma_send sci_csr    /* w:  Start dma send data */
-       volatile unsigned char  sci_idata;      /* r:  Input data */
-#define                        sci_trecv sci_idata     /* w:  Start dma receive, target */
-       volatile unsigned char  sci_iack;       /* r:  Interrupt Acknowledge  */
-#define                        sci_irecv sci_iack      /* w:  Start dma receive, initiator */
-} sci_regmap_t;
+#if 0  /* example only */
+struct ncr5380regs {
+       volatile u_char sci_r0;
+       volatile u_char sci_r1;
+       volatile u_char sci_r2;
+       volatile u_char sci_r3;
+       volatile u_char sci_r4;
+       volatile u_char sci_r5;
+       volatile u_char sci_r6;
+       volatile u_char sci_r7;
+};
+#endif
+
+/*
+ * Machine-independent code uses these names:
+ */
+#define        sci_data        sci_r0  /* r:  Current data */
+#define        sci_odata       sci_r0  /* w:  Out data */
+
+#define        sci_icmd        sci_r1  /* rw: Initiator command */
+#define        sci_mode        sci_r2  /* rw: Mode */
+#define        sci_tcmd        sci_r3  /* rw: Target command */
+
+#define        sci_bus_csr     sci_r4  /* r:  Bus Status */
+#define sci_sel_enb sci_r4     /* w:  Select enable */
+
+#define        sci_csr          sci_r5 /* r:  Status */
+#define sci_dma_send sci_r5    /* w:  Start dma send data */
+
+#define        sci_idata       sci_r6  /* r:  Input data */
+#define        sci_trecv       sci_r6  /* w:  Start dma receive, target */
+
+#define        sci_iack        sci_r7  /* r:  Interrupt Acknowledge  */
+#define sci_irecv      sci_r7  /* w:  Start dma receive, initiator */
 
 
 /*
- * Initiator command register
+ * R1: Initiator command register
  */
 #define SCI_ICMD_DATA          0x01            /* rw: Assert data bus   */
 #define SCI_ICMD_ATN           0x02            /* rw: Assert ATN signal */
@@ -85,7 +108,7 @@ typedef struct {
 
 
 /*
- * Mode register
+ * R2: Mode register
  */
 #define SCI_MODE_ARB           0x01            /* rw: Start arbitration */
 #define SCI_MODE_DMA           0x02            /* rw: Enable DMA xfers */
@@ -94,11 +117,11 @@ typedef struct {
 #define SCI_MODE_PERR_IE       0x10            /* rw: Interrupt on parity errors */
 #define SCI_MODE_PAR_CHK       0x20            /* rw: Check parity */
 #define SCI_MODE_TARGET                0x40            /* rw: Target mode (Initiator if 0) */
-#define SCI_MODE_BLOCKDMA      0x80            /* rw: Block-mode DMA handshake (MBZ) */
+#define SCI_MODE_BLOCKDMA      0x80            /* rw: Block-mode DMA handshake */
 
 
 /*
- * Target command register
+ * R3: Target command register
  */
 #define SCI_TCMD_IO            0x01            /* rw: Assert I/O signal */
 #define SCI_TCMD_CD            0x02            /* rw: Assert C/D signal */
@@ -108,10 +131,10 @@ typedef struct {
 #define        SCI_TCMD_LAST_SENT      0x80            /* ro: Last byte was xferred
                                                 *     (not on 5380/1) */
 
-#define        SCI_PHASE(x)            ((x)&0x7)
+#define        SCI_TCMD_PHASE(x)               ((x) & 0x7)
 
 /*
- * Current (SCSI) Bus status (.sci_bus_csr)
+ * R4: Current (SCSI) Bus status (.sci_bus_csr)
  */
 #define SCI_BUS_DBP            0x01            /* r:  Data Bus parity */
 #define SCI_BUS_SEL            0x02            /* r:  SEL signal */
@@ -122,10 +145,10 @@ typedef struct {
 #define SCI_BUS_BSY            0x40            /* r:  BSY signal */
 #define SCI_BUS_RST            0x80            /* r:  RST signal */
 
-#define        SCI_CUR_PHASE(x)        SCI_PHASE((x)>>2)
+#define        SCI_BUS_PHASE(x)        (((x) >> 2) & 7)
 
 /*
- * Bus and Status register (.sci_csr)
+ * R5: Bus and Status register (.sci_csr)
  */
 #define SCI_CSR_ACK            0x01            /* r:  ACK signal */
 #define SCI_CSR_ATN            0x02            /* r:  ATN signal */
index 76e600c..18900d6 100644 (file)
@@ -1,4 +1,4 @@
-/*     $NetBSD: ncr5380sbc.c,v 1.1 1995/10/29 21:19:09 gwr Exp $       */
+/*     $NetBSD: ncr5380sbc.c,v 1.1.2.1 1995/11/18 07:08:34 gwr Exp $   */
 
 /*
  * Copyright (c) 1995 David Jones, Gordon W. Ross
@@ -50,7 +50,8 @@
  *
  * Gordon Ross integrated the message phase code, added lots of
  * comments about what happens when and why (re. SCSI spec.),
- * and debugged some reentrance problems.
+ * debugged some reentrance problems, and added several new
+ * "hooks" needed for the Sun3 "si" adapters.
  *
  * The message in/out code was taken nearly verbatim from
  * the aic6360 driver by Jarle Greipsland.
  */
 
 #include <sys/types.h>
-#include <sys/malloc.h>
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/kernel.h>
 #include <sys/errno.h>
+#include <sys/device.h>
 #include <sys/buf.h>
 #include <sys/proc.h>
 #include <sys/user.h>
-#include <sys/device.h>
 
 #include <scsi/scsi_all.h>
 #include <scsi/scsi_debug.h>
 #include <scsi/scsi_message.h>
 #include <scsi/scsiconf.h>
 
+#define DEBUG XXX
+
 #if 0  /* XXX - not yet... */
 #include <dev/ic/ncr5380reg.h>
 #include <dev/ic/ncr5380var.h>
@@ -95,7 +98,7 @@ static void   ncr5380_sched __P((struct ncr5380_softc *));
 static void    ncr5380_done __P((struct ncr5380_softc *));
 
 static int     ncr5380_select
-       __P((struct ncr5380_softc *, struct scsi_xfer *));
+       __P((struct ncr5380_softc *, struct sci_req *));
 static void    ncr5380_reselect __P((struct ncr5380_softc *));
 
 static int     ncr5380_msg_in __P((struct ncr5380_softc *));
@@ -112,9 +115,8 @@ static void ncr5380_machine __P((struct ncr5380_softc *));
 #define ACT_CONTINUE   0x00    /* No flags: expect another phase */
 #define ACT_DISCONNECT 0x01    /* Target is disconnecting */
 #define ACT_CMD_DONE   0x02    /* Need to call scsi_done() */
-#define ACT_ABORT_CMD  0x04    /* Need to forcibly disconnect it */
-#define ACT_RESET_BUS  0x08    /* Need bus reset (cmd timeout) */
-#define ACT_WAIT_INTR  0x10    /* Wait for interrupt (DMA) */
+#define ACT_RESET_BUS  0x04    /* Need bus reset (cmd timeout) */
+#define ACT_WAIT_DMA   0x10    /* Wait for DMA to complete */
 
 /*****************************************************************
  * Debugging stuff
@@ -127,31 +129,21 @@ int Debugger();
 #define Debugger() printf("Debug: ncr5380.c:%d\n", __LINE__)
 #endif
 
-#define DEBUG XXX
-
 #ifdef DEBUG
-int ncr5380_debug = 0;
-/* extern struct scsi_link *thescsilink; */
-
-#define        NCR_BREAK()     Debugger()
-#define        NCR_PRINT(b, s) \
-       do {if ((ncr5380_debug & (b)) != 0) printf s;} while (0)
 
+#define        NCR_DBG_BREAK   1
+#define        NCR_DBG_CMDS    2
+int ncr5380_debug = NCR_DBG_BREAK;
+#define        NCR_BREAK() \
+       do { if (ncr5380_debug & NCR_DBG_BREAK) Debugger(); } while (0)
+static void ncr5380_show_scsi_cmd __P((struct scsi_xfer *));
+static void ncr5380_show_sense __P((struct scsi_xfer *));
 #else  /* DEBUG */
-
-#define        NCR_BREAK()     /* nah */
-#define        NCR_PRINT(b, s)
-
+#define        NCR_BREAK()             /* nada */
+#define ncr5380_show_scsi_cmd(xs) /* nada */
+#define ncr5380_show_sense(xs) /* nada */
 #endif /* DEBUG */
 
-#define NCR_MISC(s)    NCR_PRINT(0x01, s)
-#define NCR_MSGS(s)    NCR_PRINT(0x02, s)
-#define NCR_CMDS(s)    NCR_PRINT(0x04, s)
-#define NCR_TRACE(s)   NCR_PRINT(0x10, s)
-#define NCR_START(s)   NCR_PRINT(0x20, s)
-
-#ifdef DEBUG
-
 static char *
 phase_names[8] = {
        "DATA_OUT",
@@ -164,47 +156,6 @@ phase_names[8] = {
        "MSG_IN",
 };
 
-static int
-ncr5380_show_scsi_cmd(xs)
-       struct scsi_xfer *xs;
-{
-       u_char  *b = (u_char *) xs->cmd;
-       int     i  = 0;
-
-       if ( ! ( xs->flags & SCSI_RESET ) ) {
-               printf("si(%d:%d:%d)-",
-                          xs->sc_link->scsibus,
-                          xs->sc_link->target,
-                          xs->sc_link->lun);
-               while (i < xs->cmdlen) {
-                       if (i) printf(",");
-                       printf("%x",b[i++]);
-               }
-               printf("-\n");
-       } else {
-               printf("si(%d:%d:%d)-RESET-\n",
-                          xs->sc_link->scsibus,
-                          xs->sc_link->target,
-                          xs->sc_link->lun);
-       }
-}
-
-
-static void
-ncr5380_show_sense(xs)
-       struct scsi_xfer *xs;
-{
-       u_char  *b = (u_char *)&xs->sense;
-       int     i;
-
-       printf("sense:");
-       for (i = 0; i < sizeof(xs->sense); i++)
-               printf(" %02x", b[i]);
-       printf("\n");
-}
-#endif
-
-
 /*****************************************************************
  * Actual chip control
  *****************************************************************/
@@ -279,6 +230,7 @@ ncr5380_pio_out(sc, phase, count, data)
 {
        register u_char         icmd;
        register int            resid;
+       register int            error;
 
        icmd = *(sc->sci_icmd) & SCI_ICMD_RMASK;
 
@@ -288,14 +240,14 @@ ncr5380_pio_out(sc, phase, count, data)
        resid = count;
        while (resid > 0) {
                if (!SCI_BUSY(sc)) {
-                       NCR_MISC(("ncr5380_pio_out: lost BSY\n"));
+                       NCR_TRACE("pio_out: lost BSY, resid=%d\n", resid);
                        break;
                }
                if (ncr5380_wait_req(sc)) {
-                       NCR_MISC(("ncr5380_pio_out: no REQ\n"));
+                       NCR_TRACE("pio_out: no REQ, resid=%d\n", resid);
                        break;
                }
-               if (SCI_CUR_PHASE(*sc->sci_bus_csr) != phase)
+               if (SCI_BUS_PHASE(*sc->sci_bus_csr) != phase)
                        break;
 
                /* Put the data on the bus. */
@@ -306,15 +258,17 @@ ncr5380_pio_out(sc, phase, count, data)
                *sc->sci_icmd = icmd;
 
                /* Wait for target to get it. */
-               if (ncr5380_wait_not_req(sc)) {
-                       NCR_MISC(("ncr5380_pio_out: stuck REQ\n"));
-                       break;
-               }
+               error = ncr5380_wait_not_req(sc);
 
-               /* OK, it's got it. */
+               /* OK, it's got it (or we gave up waiting). */
                icmd &= ~SCI_ICMD_ACK;
                *sc->sci_icmd = icmd;
 
+               if (error) {
+                       NCR_TRACE("pio_out: stuck REQ, resid=%d\n", resid);
+                       break;
+               }
+
                --resid;
        }
 
@@ -334,21 +288,22 @@ ncr5380_pio_in(sc, phase, count, data)
 {
        register u_char         icmd;
        register int            resid;
+       register int            error;
 
        icmd = *(sc->sci_icmd) & SCI_ICMD_RMASK;
 
        resid = count;
        while (resid > 0) {
                if (!SCI_BUSY(sc)) {
-                       NCR_MISC(("ncr5380_pio_in: lost BSY\n"));
+                       NCR_TRACE("pio_in: lost BSY, resid=%d\n", resid);
                        break;
                }
                if (ncr5380_wait_req(sc)) {
-                       NCR_MISC(("ncr5380_pio_in: no REQ\n"));
+                       NCR_TRACE("pio_in: no REQ, resid=%d\n", resid);
                        break;
                }
                /* A phase change is not valid until AFTER REQ rises! */
-               if (SCI_CUR_PHASE(*sc->sci_bus_csr) != phase)
+               if (SCI_BUS_PHASE(*sc->sci_bus_csr) != phase)
                        break;
 
                /* Read the data bus. */
@@ -359,15 +314,17 @@ ncr5380_pio_in(sc, phase, count, data)
                *sc->sci_icmd = icmd;
 
                /* Wait for target to drop REQ... */
-               if (ncr5380_wait_not_req(sc)) {
-                       NCR_MISC(("ncr5380_pio_in: stuck REQ\n"));
-                       break;
-               }
+               error = ncr5380_wait_not_req(sc);
 
                /* OK, we can drop ACK. */
                icmd &= ~SCI_ICMD_ACK;
                *sc->sci_icmd = icmd;
 
+               if (error) {
+                       NCR_TRACE("pio_in: stuck REQ, resid=%d\n", resid);
+                       break;
+               }
+
                --resid;
        }
 
@@ -381,18 +338,34 @@ ncr5380_init(sc)
 {
        int i, j;
 
+#ifdef DEBUG
+       ncr5380_debug_sc = sc;
+#endif
+
        for (i = 0; i < SCI_OPENINGS; i++)
                sc->sc_ring[i].sr_xs = NULL;
        for (i = 0; i < 8; i++)
                for (j = 0; j < 8; j++)
                        sc->sc_matrix[i][j] = NULL;
 
-       sc->sc_link.openings = SCI_OPENINGS;
+       sc->sc_link.openings = 2;       /* XXX - Not SCI_OPENINGS */
        sc->sc_prevphase = PHASE_INVALID;
-       sc->sc_msg_flags = 0;   /* XXX */
+       sc->sc_state = NCR_IDLE;
 
-       /* XXX: Reselect interrupts... */
+       *sc->sci_tcmd = PHASE_INVALID;
+       *sc->sci_icmd = 0;
+       *sc->sci_mode = 0;
+       *sc->sci_sel_enb = 0;
+       SCI_CLR_INTR(sc);
+
+       /* XXX: Enable reselect interrupts... */
        *sc->sci_sel_enb = 0x80;
+
+       /* Another hack (Er.. hook!) for the sun3 si: */
+       if (sc->sc_intr_on) {
+               NCR_TRACE("init: intr ON\n", 0);
+               sc->sc_intr_on(sc);
+       }
 }
 
 
@@ -401,14 +374,15 @@ ncr5380_reset_scsibus(sc)
        struct ncr5380_softc *sc;
 {
 
-       NCR_MISC(("ncr5380_reset_scsibus\n"));
+       NCR_TRACE("reset_scsibus, cur=0x%x\n",
+                         (long) sc->sc_current);
 
        *sc->sci_icmd = SCI_ICMD_RST;
        delay(500);
        *sc->sci_icmd = 0;
 
        *sc->sci_mode = 0;
-       *sc->sci_tcmd = 0;
+       *sc->sci_tcmd = PHASE_INVALID;
 
        SCI_CLR_INTR(sc);
        /* XXX - Need long delay here! */
@@ -420,40 +394,34 @@ ncr5380_reset_scsibus(sc)
 
 /*
  * Interrupt handler for the SCSI Bus Controller (SBC)
- * This is also called by a timeout handler after it
- * raises to the appropriate SPL.
+ * This may also called for a DMA timeout (at splbio).
  */
 int
-ncr5380_sbc_intr(sc)
+ncr5380_intr(sc)
        struct ncr5380_softc *sc;
 {
        int claimed = 0;
-       u_char st;
-
-       if (sc->sc_dma_flags & DMA5380_INPROGRESS) {
-               /* Stop DMA to prevent register conflicts */
-               NCR_MISC(("ncr5380_sbc_intr: call DMA stop\n"));
-               (*sc->sc_dma_stop)(sc);
-               /* Will check for error in ncr5380_machine */
-               /* We know the nexus is busy during DMA. */
-       }
-
-       /* OK, now we can look at the SBC. */
-       st = *sc->sci_csr;
-       SCI_CLR_INTR(sc);
 
-       NCR_MISC(("ncr5380_sbc_intr: st=0x%x\n", st));
-
-       /* XXX - There may be a better place to check... */
-       if (st & SCI_CSR_PERR) {
-               printf("ncr5380_sbc_intr: parity error!\n");
-       }
+       /*
+        * Do not touch SBC regs here unless sc_current == NULL
+        * or it will complain about "register conflict" errors.
+        * Instead, just let ncr5380_machine() deal with it.
+        */
+       NCR_TRACE("intr: top, state=%d\n", sc->sc_state);
 
-       if (sc->sc_current == NULL) {
+       if (sc->sc_state == NCR_IDLE) {
                /*
-                *  Might be reselect.  ncr5380_reselect() will check, and
-                *  set up the connection if so.
+                * Might be reselect.  ncr5380_reselect() will check,
+                * and set up the connection if so.  This will verify
+                * that sc_current == NULL at the beginning...
                 */
+
+               /* Another hack (Er.. hook!) for the sun3 si: */
+               if (sc->sc_intr_off) {
+                       NCR_TRACE("intr: for reselect, intr off\n", 0);
+                   sc->sc_intr_off(sc);
+               }
+
                ncr5380_reselect(sc);
        }
 
@@ -462,23 +430,135 @@ ncr5380_sbc_intr(sc)
         * disconnect.  In addition, the sunsi controller may produce a state
         * where SCI_CSR_DONE is false, yet DMA is complete.
         *
-        * The procedure in all these cases is to let ncr5380_machine() figure
-        * out what to do next.
+        * The procedure in all these cases is to let ncr5380_machine()
+        * figure out what to do next.
         */
-       if (sc->sc_current) {
+       if (sc->sc_state & NCR_WORKING) {
+               NCR_TRACE("intr: call machine, cur=0x%x\n",
+                                 (long) sc->sc_current);
                /* This will usually free-up the nexus. */
                ncr5380_machine(sc);
+               NCR_TRACE("intr: machine done, cur=0x%x\n",
+                                 (long) sc->sc_current);
                claimed = 1;
        }
 
-       /* Maybe we can start another command now... */
-       if (sc->sc_current == NULL)
+       /* Maybe we can run some commands now... */
+       if (sc->sc_state == NCR_IDLE) {
+               NCR_TRACE("intr: call sched, cur=0x%x\n",
+                                 (long) sc->sc_current);
                ncr5380_sched(sc);
+               NCR_TRACE("intr: sched done, cur=0x%x\n",
+                                 (long) sc->sc_current);
+       }
 
        return claimed;
 }
 
 
+/*
+ * Abort the current command (i.e. due to timeout)
+ */
+void
+ncr5380_abort(sc)
+       struct ncr5380_softc *sc;
+{
+
+       /*
+        * Finish it now.  If DMA is in progress, we
+        * can not call ncr_sched_msgout() because
+        * that hits the SBC (avoid DMA conflict).
+        */
+
+       /* Another hack (Er.. hook!) for the sun3 si: */
+       if (sc->sc_intr_off) {
+               NCR_TRACE("abort: intr off\n", 0);
+               sc->sc_intr_off(sc);
+       }
+
+       sc->sc_state |= NCR_ABORTING;
+       if ((sc->sc_state & NCR_DOINGDMA) == 0) {
+               ncr_sched_msgout(sc, SEND_ABORT);
+       }
+       NCR_TRACE("abort: call machine, cur=0x%x\n",
+                         (long) sc->sc_current);
+       ncr5380_machine(sc);
+       NCR_TRACE("abort: machine done, cur=0x%x\n",
+                         (long) sc->sc_current);
+
+       /* Another hack (Er.. hook!) for the sun3 si: */
+       if (sc->sc_intr_on) {
+               NCR_TRACE("abort: intr ON\n", 0);
+           sc->sc_intr_on(sc);
+       }
+}
+
+/*
+ * Timeout handler, scheduled for each SCSI command.
+ */
+void
+ncr5380_cmd_timeout(arg)
+       void *arg;
+{
+       struct sci_req *sr = arg;
+       struct scsi_xfer *xs;
+       struct scsi_link *sc_link;
+       struct ncr5380_softc *sc;
+       int s;
+
+       s = splbio();
+
+       /* Get all our variables... */
+       xs = sr->sr_xs;
+       if (xs == NULL) {
+               printf("ncr5380_cmd_timeout: no scsi_xfer\n");
+               goto out;
+       }
+       sc_link = xs->sc_link;
+       sc = sc_link->adapter_softc;
+
+       printf("%s: cmd timeout, targ=%d, lun=%d\n",
+                  sc->sc_dev.dv_xname,
+                  sr->sr_target, sr->sr_lun);
+
+       /*
+        * Mark the overdue job as failed, and arrange for
+        * ncr5380_machine to terminate it.  If the victim
+        * is the current job, call ncr5380_machine() now.
+        * Otherwise arrange for ncr5380_sched() to do it.
+        */
+       sr->sr_flags |= SR_OVERDUE;
+       if (sc->sc_current == sr) {
+               NCR_TRACE("cmd_tmo: call abort, sr=0x%x\n", (long) sr);
+               ncr5380_abort(sc);
+       } else {
+               /*
+                * The driver may be idle, or busy with another job.
+                * Arrange for ncr5380_sched() to do the deed.
+                */
+               NCR_TRACE("cmd_tmo: clear matrix, t/l=0x%02x\n",
+                                 (sr->sr_target << 4) | sr->sr_lun);
+               sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL;
+       }
+
+       /*
+        * We may have aborted the current job, or may have
+        * already been idle. In either case, we should now
+        * be idle, so try to start another job.
+        */
+       if (sc->sc_state == NCR_IDLE) {
+               NCR_TRACE("cmd_tmo: call sched, cur=0x%x\n",
+                                 (long) sc->sc_current);
+               ncr5380_sched(sc);
+               NCR_TRACE("cmd_tmo: sched done, cur=0x%x\n",
+                                 (long) sc->sc_current);
+       }
+
+out:
+       splx(s);
+}
+
+
 /*****************************************************************
  * Interface to higher level
  *****************************************************************/
@@ -495,11 +575,12 @@ int
 ncr5380_scsi_cmd(xs)
        struct scsi_xfer *xs;
 {
-       struct  ncr5380_softc *sc = xs->sc_link->adapter_softc;
-       int s, r, i, flags;
+       struct  ncr5380_softc *sc;
+       struct sci_req  *sr;
+       int s, rv, i, flags;
        extern int cold;                /* XXX */
 
-       /* thescsilink = xs->sc_link; */
+       sc = xs->sc_link->adapter_softc;
 
        flags = xs->flags;
        /*
@@ -510,88 +591,257 @@ ncr5380_scsi_cmd(xs)
        if (cold)
                flags |= SCSI_POLL;
 
+       if (sc->sc_flags & NCR5380_FORCE_POLLING)
+               flags |= SCSI_POLL;
+
        if (flags & SCSI_DATA_UIO)
                panic("ncr5380: scsi data uio requested");
 
-       if (sc->sc_current && (flags & SCSI_POLL))
-               panic("ncr5380_scsi_cmd: can't poll while busy");
-
        s = splbio();
 
-       /* Find lowest empty slot in ring buffer. */
+       if (flags & SCSI_POLL) {
+               /* Terminate any current command. */
+               sr = sc->sc_current;
+               if (sr) {
+                       printf("%s: polled request aborting %d/%d\n",
+                                  sc->sc_dev.dv_xname,
+                                  sr->sr_target, sr->sr_lun);
+                       ncr5380_abort(sc);
+               }
+               if (sc->sc_state != NCR_IDLE) {
+                       panic("ncr5380_scsi_cmd: polled request, abort failed");
+               }
+       }
+
+       /*
+        * Find lowest empty slot in ring buffer.
+        * XXX: What about "fairness" and cmd order?
+        */
        for (i = 0; i < SCI_OPENINGS; i++)
                if (sc->sc_ring[i].sr_xs == NULL)
-                       break;
+                       goto new;
 
-       if (i == SCI_OPENINGS) {
-               splx(s);
-               return TRY_AGAIN_LATER;
-       }
+       rv = TRY_AGAIN_LATER;
+       NCR_TRACE("scsi_cmd: no openings, rv=%d\n", rv);
+       goto out;
 
+new:
        /* Create queue entry */
-       sc->sc_ring[i].sr_xs = xs;
-       sc->sc_ring[i].sr_target = xs->sc_link->target;
-       sc->sc_ring[i].sr_lun = xs->sc_link->lun;
-       sc->sc_ring[i].sr_data = xs->data;
-       sc->sc_ring[i].sr_datalen = xs->datalen;
-       sc->sc_ring[i].sr_dma_hand = NULL;
-       sc->sc_ring[i].sr_flags = (flags & SCSI_POLL) ? SR_IMMED : 0;
-       sc->sc_ring[i].sr_status = -1;  /* no value */
+       sr = &sc->sc_ring[i];
+       sr->sr_xs = xs;
+       sr->sr_target = xs->sc_link->target;
+       sr->sr_lun = xs->sc_link->lun;
+       sr->sr_dma_hand = NULL;
+       sr->sr_dataptr = xs->data;
+       sr->sr_datalen = xs->datalen;
+       sr->sr_flags = (flags & SCSI_POLL) ? SR_IMMED : 0;
+       sr->sr_status = -1;     /* no value */
        sc->sc_ncmds++;
+       rv = SUCCESSFULLY_QUEUED;
+
+       NCR_TRACE("scsi_cmd: new sr=0x%x\n", (long)sr);
+
+       if (flags & SCSI_POLL) {
+               /* Force this new command to be next. */
+               sc->sc_rr = i;
+       }
 
        /*
-        * Consider starting another command if the bus is idle.
-        * To stop recursion, this is non-NULL when scsi_done()
-        * calls back here to issue the next command.
+        * If we were idle, run some commands...
         */
-       if (sc->sc_current == NULL)
+       if (sc->sc_state == NCR_IDLE) {
+               NCR_TRACE("scsi_cmd: call sched, cur=0x%x\n",
+                                 (long) sc->sc_current);
                ncr5380_sched(sc);
+               NCR_TRACE("scsi_cmd: sched done, cur=0x%x\n",
+                                 (long) sc->sc_current);
+       }
 
+       if (flags & SCSI_POLL) {
+               /* Make sure ncr5380_sched() finished it. */
+               if ((xs->flags & ITSDONE) == 0)
+                       panic("ncr5380_scsi_cmd: poll didn't finish");
+               rv = COMPLETE;
+       }
+
+out:
        splx(s);
+       return (rv);
+}
+
+
+/*
+ * POST PROCESSING OF SCSI_CMD (usually current)
+ * Called by ncr5380_sched(), ncr5380_machine()
+ */
+static void
+ncr5380_done(sc)
+       struct ncr5380_softc *sc;
+{
+       struct  sci_req *sr;
+       struct  scsi_xfer *xs;
+
+#ifdef DIAGNOSTIC
+       if ((getsr() & PSL_IPL) < PSL_IPL2)
+               panic("ncr5380_done: bad spl");
+       if (sc->sc_state == NCR_IDLE)
+               panic("ncr5380_done: state=idle");
+       if (sc->sc_current == NULL)
+               panic("ncr5380_done: current=0");
+#endif
+
+       sr = sc->sc_current;
+       xs = sr->sr_xs;
+
+       NCR_TRACE("done: top, cur=0x%x\n", (long) sc->sc_current);
 
-       /* The call to ncr5380_sched() may have finished it. */
-       if (xs->flags & ITSDONE) {
-               return COMPLETE;
+       /*
+        * Clean up DMA resources for this command.
+        */
+       if (sr->sr_dma_hand) {
+               NCR_TRACE("done: dma_free, dh=0x%x\n",
+                                 (long) sr->sr_dma_hand);
+               (*sc->sc_dma_free)(sc);
+       }
+#ifdef DIAGNOSTIC
+       if (sr->sr_dma_hand)
+               panic("ncr5380_done: dma free did not");
+#endif
+
+       if (sc->sc_state & NCR_ABORTING) {
+               NCR_TRACE("done: aborting, error=%d\n", xs->error);
+               if (xs->error == XS_NOERROR)
+                       xs->error = XS_TIMEOUT;
        }
 
-       if (flags & SCSI_POLL)
-               panic("ncr5380_scsi_cmd: poll didn't finish");
+       NCR_TRACE("done: check error=%d\n", (long) xs->error);
+
+       /* If error is already set, ignore sr_status value. */
+       if (xs->error != XS_NOERROR)
+               goto finish;
+
+       NCR_TRACE("done: check status=%d\n", sr->sr_status);
+
+       switch (sr->sr_status) {
+       case SCSI_OK:   /* 0 */
+               if (sr->sr_flags & SR_SENSE) {
+                       if (ncr5380_debug & NCR_DBG_CMDS) {
+                               ncr5380_show_sense(xs);
+                       }
+                       xs->error = XS_SENSE;
+               }
+               break;
+
+       case SCSI_CHECK:
+               if (sr->sr_flags & SR_SENSE) {
+                       /* Sense command also asked for sense? */
+                       printf("ncr5380_done: sense asked for sense\n");
+                       NCR_BREAK();
+                       xs->error = XS_DRIVER_STUFFUP;
+                       break;
+               }
+               sr->sr_flags |= SR_SENSE;
+               NCR_TRACE("done: get sense, sr=0x%x\n", (long) sr);
+               /*
+                * Leave queued, but clear sc_current so we start over
+                * with selection.  Guaranteed to get the same request.
+                */
+               sc->sc_state = NCR_IDLE;
+               sc->sc_current = NULL;
+               sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL;
+               return;         /* XXX */
+
+       case SCSI_BUSY:
+               xs->error = XS_BUSY;
+               break;
 
-       return SUCCESSFULLY_QUEUED;
+       case -1:
+               /* This is our "impossible" initial value. */
+               /* fallthrough */
+       default:
+               printf("%s: target %d, bad status=%d\n",
+                       sc->sc_dev.dv_xname, sr->sr_target, sr->sr_status);
+               xs->error = XS_DRIVER_STUFFUP;
+               break;
+       }
+
+finish:
+
+       NCR_TRACE("done: finish, error=%d\n", xs->error);
+
+       /*
+        * Dequeue the finished command, but don't clear sc_state until
+        * after the call to scsi_done(), because that may call back to
+        * ncr5380_scsi_cmd() - unwanted recursion!
+        *
+        * Keeping sc->sc_state != idle terminates the recursion.
+        */
+#ifdef DIAGNOSTIC
+       if ((sc->sc_state & NCR_WORKING) == 0)
+               panic("ncr5380_done: bad state");
+#endif
+
+       /* Clear our pointers to the request. */
+       sc->sc_current = NULL;
+       sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL;
+       untimeout(ncr5380_cmd_timeout, sr);
+
+       /* Make the request free. */
+       sr->sr_xs = NULL;
+       sc->sc_ncmds--;
+
+       /* Tell common SCSI code it is done. */
+       xs->flags |= ITSDONE;
+       scsi_done(xs);
+
+       sc->sc_state = NCR_IDLE;
+       /* Now ncr5380_sched() may be called again. */
 }
 
 
 /*
  * Schedule a SCSI operation.  This routine should return
  * only after it achieves one of the following conditions:
- *     Bus is busy (sc->sc_current != NULL)
- *     All targets are busy (i.e. disconnected)
+ *     Busy (sc->sc_state != NCR_IDLE)
+ *     No more work can be started.
  */
 static void
 ncr5380_sched(sc)
        struct  ncr5380_softc *sc;
 {
-       struct scsi_xfer *xs;
        struct sci_req  *sr;
+       struct scsi_xfer *xs;
        int     target, lun;
        int     error, i;
 
 #ifdef DIAGNOSTIC
        if ((getsr() & PSL_IPL) < PSL_IPL2)
                panic("ncr5380_sched: bad spl");
+#endif
 
+       /* Another hack (Er.. hook!) for the sun3 si: */
+       if (sc->sc_intr_off) {
+               NCR_TRACE("sched: top, intr off\n", 0);
+           sc->sc_intr_off(sc);
+       }
+
+next_job:
+       /*
+        * Grab the next job from queue.  Must be idle.
+        */
+#ifdef DIAGNOSTIC
+       if (sc->sc_state != NCR_IDLE)
+               panic("ncr5380_sched: not idle");
        if (sc->sc_current)
-               panic("ncr5380_sched: bus already busy");
+               panic("ncr5380_sched: current set");
 #endif
 
-next_job:
        /*
-        * Grab the next job from queue.
-        *
         * Always start the search where we last looked.
         * The REQUEST_SENSE logic depends on this to
         * choose the same job as was last picked, so it
         * can just clear sc_current and reschedule.
+        * (Avoids loss of "contingent allegiance".)
         */
        i = sc->sc_rr;
        sr = NULL;
@@ -600,7 +850,8 @@ next_job:
                        target = sc->sc_ring[i].sr_target;
                        lun = sc->sc_ring[i].sr_lun;
                        if (sc->sc_matrix[target][lun] == NULL) {
-                           sc->sc_matrix[target][lun] = sr = &sc->sc_ring[i];
+                           sc->sc_matrix[target][lun] =
+                                       sr = &sc->sc_ring[i];
                            sc->sc_rr = i;
                            break;
                        }
@@ -610,37 +861,61 @@ next_job:
                        i = 0;
        } while (i != sc->sc_rr);
 
-       if (sr == NULL)
+       if (sr == NULL) {
+               NCR_TRACE("sched: no work, cur=0x%x\n",
+                                 (long) sc->sc_current);
+
+               /* Another hack (Er.. hook!) for the sun3 si: */
+               if (sc->sc_intr_on) {
+                       NCR_TRACE("sched: ret, intr ON\n", 0);
+                   sc->sc_intr_on(sc);
+               }
+
                return;         /* No more work to do. */
+       }
 
-       xs = sr->sr_xs;
-       error = ncr5380_select(sc, xs);
+       NCR_TRACE("sched: select for t/l=0x%02x\n",
+                         (sr->sr_target << 4) | sr->sr_lun);
+
+       sc->sc_state = NCR_WORKING;
+       error = ncr5380_select(sc, sr);
        if (sc->sc_current) {
                /* Lost the race!  reselected out from under us! */
+               /* Work with the reselected job. */
                if (sr->sr_flags & SR_IMMED) {
-                       printf("%s: reselected while polling (reset)\n",
+                       printf("%s: reselected while polling (abort)\n",
                                   sc->sc_dev.dv_xname);
-                       error = XS_BUSY;
-                       goto reset;
+                       /* Abort the reselected job. */
+                       sc->sc_state |= NCR_ABORTING;
+                       sc->sc_msgpriq |= SEND_ABORT;
                }
-               /* Work with the reselected target. */
                sr = sc->sc_current;
                xs = sr->sr_xs;
+               NCR_TRACE("sched: reselect, new sr=0x%x\n", (long)sr);
                goto have_nexus;
        }
+
+       /* Normal selection result */
        sc->sc_current = sr;    /* connected */
+       xs = sr->sr_xs;
 
-       /* Initialize pointers, etc. */
-       sc->sc_dataptr  = sr->sr_data;
+       /*
+        * Initialize pointers, etc. for this job
+        */
+       sc->sc_dataptr  = sr->sr_dataptr;
        sc->sc_datalen  = sr->sr_datalen;
-       sc->sc_dma_hand = sr->sr_dma_hand;
+       sc->sc_prevphase = PHASE_INVALID;
+       sc->sc_msgpriq = SEND_IDENTIFY;
+       sc->sc_msgoutq = 0;
+       sc->sc_msgout  = 0;
+
+       NCR_TRACE("sched: select rv=%d\n", error);
 
        switch (error) {
        case XS_NOERROR:
                break;
 
        case XS_BUSY:
-       reset:
                /* XXX - Reset and try again. */
                printf("%s: SCSI bus busy, resetting...\n",
                           sc->sc_dev.dv_xname);
@@ -648,141 +923,119 @@ next_job:
                /* fallthrough */
        case XS_SELTIMEOUT:
        default:
-               NCR_MISC(("ncr5380_sched: select error=%d\n", error));
-               xs->error = error;
+               xs->error = error;      /* from select */
+               NCR_TRACE("sched: call done, sr=0x%x\n", (long)sr);
                ncr5380_done(sc);
+
+               /* Paranoia: clear everything. */
+               sc->sc_dataptr = NULL;
+               sc->sc_datalen = 0;
+               sc->sc_prevphase = PHASE_INVALID;
+               sc->sc_msgpriq = 0;
+               sc->sc_msgoutq = 0;
+               sc->sc_msgout  = 0;
+
                goto next_job;
        }
 
        /*
-        * Selection was successful.
-        * We now have a new command.
+        * Selection was successful.  Normally, this means
+        * we are starting a new command.  However, this
+        * might be the termination of an overdue job.
         */
-       NCR_CMDS(("next cmd: ",
-                         ncr5380_show_scsi_cmd(xs) ));
-       sc->sc_prevphase = PHASE_MSG_IN;
-       if (xs->flags & SCSI_RESET) {
-               sc->sc_msgpriq = SEND_DEV_RESET;
+       if (sr->sr_flags & SR_OVERDUE) {
+               NCR_TRACE("sched: overdue, sr=0x%x\n", (long)sr);
+               sc->sc_state |= NCR_ABORTING;
+               sc->sc_msgpriq |= SEND_ABORT;
                goto have_nexus;
        }
-       sc->sc_msgpriq = SEND_IDENTIFY;
-       if (sc->sc_dataptr && sc->sc_dma_alloc &&
-               (sc->sc_datalen >= sc->sc_min_dma_len))
-       {
-               /* Allocate DMA space */
-               sc->sc_dma_flags = 0;
-               if (xs->flags & SCSI_DATA_OUT)
-                       sc->sc_dma_flags |= DMA5380_WRITE;
-               if (xs->bp && (xs->bp->b_flags & B_PHYS))
-                       sc->sc_dma_flags |= DMA5380_PHYS;
-               if (sr->sr_flags & SR_IMMED)
-                       sc->sc_dma_flags |= DMA5380_POLL;
-               /* This may also set DMA5380_POLL */
-               (*sc->sc_dma_alloc)(sc);
-               /* Now, _maybe_ sc_dma_hand is set. */
-       }
-       sr->sr_dma_hand = sc->sc_dma_hand;
-
-have_nexus:
-       ncr5380_machine(sc);
 
        /*
-        * What state did ncr5380_machine() leave us in?
-        * Hopefully it sometimes completes a job...
+        * This may be the continuation of some job that
+        * completed with a "check condition" code.
         */
-       if (sc->sc_current)
-               return;         /* Have work in progress... */
-
-       goto next_job;
-}
-
-
-/*
- * POST PROCESSING OF SCSI_CMD (usually current)
- * Called by ncr5380_sched(), ncr5380_machine()
- */
-static void
-ncr5380_done(sc)
-       struct ncr5380_softc *sc;
-{
-       struct  sci_req *sr = sc->sc_current;
-       struct  scsi_xfer *xs = sr->sr_xs;
-
-       /* Clean up DMA associated with this command */
-       if (sc->sc_dma_hand) {
-               (*sc->sc_dma_free)(sc);
-               sc->sc_dma_hand = NULL;
+       if (sr->sr_flags & SR_SENSE) {
+               NCR_TRACE("sched: get sense, sr=0x%x\n", (long)sr);
+               /* Do not allocate DMA, nor set timeout. */
+               goto have_nexus;
        }
 
-       if (xs->error)
-               goto finish;
-
-       NCR_MISC(("ncr5380_done, sr_status=%d\n", sr->sr_status));
-
-       switch (sr->sr_status) {
-       case SCSI_OK:   /* 0 */
-               if (sr->sr_flags & SR_SENSE) {
-                       /* ncr5380_show_sense(xs); */
-                       xs->error = XS_SENSE;
-               }
-               break;
+       /*
+        * OK, we are starting a new command.
+        * Initialize and allocate resources for the new command.
+        * Device reset is special (only uses MSG_OUT phase).
+        * Normal commands start in MSG_OUT phase where we will
+        * send and IDENDIFY message, and then expect CMD phase.
+        */
+       if (ncr5380_debug & NCR_DBG_CMDS) {
+               printf("ncr5380_sched: begin, target=%d, LUN=%d\n",
+                          xs->sc_link->target, xs->sc_link->lun);
+               ncr5380_show_scsi_cmd(xs);
+       }
+       if (xs->flags & SCSI_RESET) {
+               NCR_TRACE("sched: cmd=reset, sr=0x%x\n", (long)sr);
+               /* Not an error, so do not set NCR_ABORTING */
+               sc->sc_msgpriq |= SEND_DEV_RESET;
+               goto have_nexus;
+       }
 
-       case SCSI_CHECK:
-               if (sr->sr_flags & SR_SENSE) {
-                       /* Sense command also asked for sense? */
-                       printf("ncr5380_done: sense asked for sense\n");
+#ifdef DIAGNOSTIC
+       if ((xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) == 0) {
+               if (sc->sc_dataptr) {
+                       printf("%s: ptr but no data in/out flags?\n");
                        NCR_BREAK();
-                       xs->error = XS_DRIVER_STUFFUP;
-                       break;
+                       sc->sc_dataptr = NULL;
                }
-               sr->sr_flags |= SR_SENSE;
-               NCR_MISC(("ncr5380_done: start request sense\n"));
-               NCR_CMDS(("cmd: ", ncr5380_show_scsi_cmd(xs) ));
-               /*
-                * Leave queued, but clear sc_current so we start over
-                * with selection.  Guaranteed to get the same request.
-                */
-               sc->sc_current = NULL;
-               sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL;
-               return;
-
-       case SCSI_BUSY:
-               xs->error = XS_BUSY;
-               break;
-
-       case -1:
-               /* This is our "impossible" initial value. */
-               /* fallthrough */
-       default:
-               printf("%s: target %d, bad status=%d\n",
-                       sc->sc_dev.dv_xname, sr->sr_target, sr->sr_status);
-               xs->error = XS_DRIVER_STUFFUP;
-               break;
        }
+#endif
 
-finish:
+       /* Allocate DMA space (maybe) */
+       if (sc->sc_dataptr && sc->sc_dma_alloc &&
+               (sc->sc_datalen >= sc->sc_min_dma_len))
+       {
+               NCR_TRACE("sched: dma_alloc, len=%d\n", sc->sc_datalen);
+               (*sc->sc_dma_alloc)(sc);
+       }
 
-       NCR_MISC(("ncr5380_done: finished target=%d, LUN=%d\n",
-                         sr->sr_target, sr->sr_lun));
+       /*
+        * Initialization hook called just after select,
+        * at the beginning of COMMAND phase.
+        * (but AFTER the DMA allocation is done)
+        *
+        * The evil Sun "si" adapter (OBIO variant) needs some
+        * setup done to the DMA engine BEFORE the target puts
+        * the SCSI bus into any DATA phase.
+        */
+       if (sr->sr_dma_hand && sc->sc_dma_setup) {
+               NCR_TRACE("sched: dma_setup, dh=0x%x\n",
+                                 (long) sr->sr_dma_hand);
+           sc->sc_dma_setup(sc);
+       }
 
        /*
-        * Dequeue the finished command
+        * Schedule a timeout for the job we are starting.
         */
-       sc->sc_matrix[sr->sr_target][sr->sr_lun] = NULL;
-       sc->sc_ncmds--;
-       sr->sr_xs = NULL;
+       if ((sr->sr_flags & SR_IMMED) == 0) {
+               i = (xs->timeout * hz) / 1000;
+               NCR_TRACE("sched: set timeout=%d\n", i);
+               timeout(ncr5380_cmd_timeout, sr, i);
+       }
+
+have_nexus:
+       NCR_TRACE("sched: call machine, cur=0x%x\n",
+                         (long) sc->sc_current);
+       ncr5380_machine(sc);
+       NCR_TRACE("sched: machine done, cur=0x%x\n",
+                         (long) sc->sc_current);
 
        /*
-        * Do not clear sc_current quite yet.  The call to scsi_done()
-        * below may recursively call ncr5380_scsi_cmd() and leaving
-        * sc->sc_current != NULL ends the recursion after the new
-        * command is enqueued.
+        * What state did ncr5380_machine() leave us in?
+        * Hopefully it sometimes completes a job...
         */
-       xs->flags |= ITSDONE;
-       scsi_done(xs);
+       if (sc->sc_state == NCR_IDLE)
+               goto next_job;
 
-       /* Allow ncr5380_sched() to be called again. */
-       sc->sc_current = NULL;  /* idle */
+       return;         /* Have work in progress. */
 }
 
 
@@ -799,17 +1052,23 @@ void
 ncr5380_reselect(sc)
        struct ncr5380_softc *sc;
 {
+       struct sci_req *sr;
        int target, lun, phase, timo;
        u_char bus, data, icmd, msg;
 
 #ifdef DIAGNOSTIC
+       /*
+        * Note: sc_state will be "idle" when ncr5380_intr()
+        * calls, or "working" when ncr5380_select() calls.
+        * (So don't test that in this DIAGNOSTIC)
+        */
        if (sc->sc_current)
-               panic("ncr5380_reselect: already have nexus!");
+               panic("ncr5380_reselect: current set");
 #endif
 
        /*
         * First, check the select line.
-        * (That has to be set by now.)
+        * (That has to be set first.)
         */
        bus = *(sc->sci_bus_csr);
        if ((bus & SCI_BUS_SEL) == 0) {
@@ -818,12 +1077,16 @@ ncr5380_reselect(sc)
        }
 
        /*
-        * Now wait for: SEL==1, BSY==0
-        * before reading the data bus.
+        * The target will assert BSY first (for bus arbitration),
+        * then raise SEL, and finally drop BSY.  Only then is the
+        * data bus required to have valid selection ID bits set.
+        * Wait for: SEL==1, BSY==0 before reading the data bus.
         */
-       for (timo = 25000;;) {
+       timo = ncr5380_wait_nrq_timo;
+       for (;;) {
                if ((bus & SCI_BUS_BSY) == 0)
                        break;
+               /* Probably never get here... */
                if (--timo <= 0) {
                        printf("%s: reselect, BSY stuck, bus=0x%x\n",
                                sc->sc_dev.dv_xname, bus);
@@ -838,6 +1101,8 @@ ncr5380_reselect(sc)
                        return;
                /* Still have SEL, check BSY. */
        }
+       NCR_TRACE("reselect, valid data after %d loops\n",
+                         ncr5380_wait_nrq_timo - timo);
 
        /*
         * Good.  We have SEL=1 and BSY=0.  Now wait for a
@@ -873,24 +1138,25 @@ ncr5380_reselect(sc)
                return;
        }
 
-       NCR_MISC(("ncr5380_reselect: data=0x%x\n", data));
+       NCR_TRACE("reselect: target=0x%x\n", target);
 
        /* Raise BSY to acknowledge target reselection. */
        *(sc->sci_icmd) = SCI_ICMD_BSY;
 
        /* Wait for target to drop SEL. */
-       for (timo = 1000;;) {
+       timo = ncr5380_wait_nrq_timo;
+       for (;;) {
                bus = *(sc->sci_bus_csr);
                if ((bus & SCI_BUS_SEL) == 0)
                        break;  /* success */
                if (--timo <= 0) {
                        printf("%s: reselect, SEL stuck, bus=0x%x\n",
                                sc->sc_dev.dv_xname, bus);
-                       /* Not much we can do. Reset the bus. */
-                       ncr5380_reset_scsibus(sc);
-                       return;
+                       NCR_BREAK();
+                       /* assume connected (fail later if not) */
+                       break;
                }
-               delay(5);
+               delay(2);
        }
 
        /* Now we drop BSY, and we are connected. */
@@ -911,15 +1177,15 @@ ncr5380_reselect(sc)
                /* Try to send an ABORT message. */
                goto abort;
        }
-       phase = SCI_CUR_PHASE(*sc->sci_bus_csr);
+       phase = SCI_BUS_PHASE(*sc->sci_bus_csr);
        if (phase != PHASE_MSG_IN) {
                printf("%s: reselect, phase=%d\n",
                           sc->sc_dev.dv_xname, phase);
                goto abort;
        }
 
-       /* Ack. the new phase. */
-       *(sc->sci_tcmd) = phase;
+       /* Ack. the change to PHASE_MSG_IN */
+       *(sc->sci_tcmd) = PHASE_MSG_IN;
 
        /* Peek at the message byte without consuming it! */
        msg = *(sc->sci_data);
@@ -931,15 +1197,35 @@ ncr5380_reselect(sc)
        lun = msg & 7;
        
        /* We now know target/LUN.  Do we have the request? */
-       sc->sc_current = sc->sc_matrix[target][lun];
-       if (sc->sc_current) {
+       sr = sc->sc_matrix[target][lun];
+       if (sr) {
+               /* We now have a nexus. */
+               sc->sc_state |= NCR_WORKING;
+               sc->sc_current = sr;
+               NCR_TRACE("reselect: resume sr=0x%x\n", (long)sr);
+
+               /* Implicit restore pointers message */
+               sc->sc_dataptr = sr->sr_dataptr;
+               sc->sc_datalen = sr->sr_datalen;
+
+               sc->sc_prevphase = PHASE_INVALID;
+               sc->sc_msgpriq = 0;
+               sc->sc_msgoutq = 0;
+               sc->sc_msgout  = 0;
+
+               /*
+                * Another hack for the Sun3 "si", which needs
+                * some setup done to its DMA engine before the
+                * target puts the SCSI bus into any DATA phase.
+                */
+               if (sr->sr_dma_hand && sc->sc_dma_setup) {
+                       NCR_TRACE("reselect: call DMA setup, dh=0x%x\n",
+                                         (long) sr->sr_dma_hand);
+                   sc->sc_dma_setup(sc);
+               }
+
                /* Now consume the IDENTIFY message. */
                ncr5380_pio_in(sc, PHASE_MSG_IN, 1, &msg);
-               /* Implicit restore pointers message */
-               sc->sc_dataptr = sc->sc_current->sr_data;
-               sc->sc_datalen = sc->sc_current->sr_datalen;
-               sc->sc_dma_hand = sc->sc_current->sr_dma_hand;
-               /* We now have a nexus. */
                return;
        }
 
@@ -947,9 +1233,12 @@ ncr5380_reselect(sc)
                   sc->sc_dev.dv_xname, target, lun);
 abort:
        /*
-        * Try to send an ABORT message.
-        * Raise ATN, then raise ACK...
+        * Try to send an ABORT message.  This makes us
+        * temporarily busy, but no current command...
         */
+       sc->sc_state |= NCR_ABORTING;
+
+       /* Raise ATN, delay, raise ACK... */
        icmd = SCI_ICMD_ATN;
        *sc->sci_icmd = icmd;
        delay(2);
@@ -958,12 +1247,16 @@ abort:
        ncr5380_pio_in(sc, PHASE_MSG_IN, 1, &msg);
 
        /* Finally try to send the ABORT. */
-       sc->sc_prevphase = PHASE_MSG_IN;
+       sc->sc_prevphase = PHASE_INVALID;
        sc->sc_msgpriq = SEND_ABORT;
-       *(sc->sci_tcmd) = PHASE_MSG_OUT;
        ncr5380_msg_out(sc);
 
+       *(sc->sci_tcmd) = PHASE_INVALID;
+       *sc->sci_sel_enb = 0;
+       SCI_CLR_INTR(sc);
        *sc->sci_sel_enb = 0x80;
+
+       sc->sc_state &= ~NCR_ABORTING;
 }
 
 
@@ -978,32 +1271,28 @@ abort:
  *     XS_SELTIMEOUT           ==> no response to selection
  */
 static int
-ncr5380_select(sc, xs)
+ncr5380_select(sc, sr)
        struct ncr5380_softc *sc;
-       struct scsi_xfer *xs;
+       struct sci_req *sr;
 {
        int timo;
        u_char bus, data, icmd;
 
-       NCR_MISC(("ncr5380_select: begin, target=%d\n",
-                         xs->sc_link->target));
-
        /* Check for reselect */
        ncr5380_reselect(sc);
        if (sc->sc_current) {
-               NCR_MISC(("ncr5380_select: reselected!\n"));
-               return XS_NOERROR;      /* reselected */
+               NCR_TRACE("select: reselect, cur=0x%x\n",
+                                 (long) sc->sc_current);
+               return XS_BUSY; /* reselected */
        }
 
        /*
         * Set phase bits to 0, otherwise the 5380 won't drive the bus during
         * selection.
         */
+       *sc->sci_tcmd = PHASE_DATA_OUT;
        *sc->sci_icmd = icmd = 0;
        *sc->sci_mode = 0;
-       *sc->sci_tcmd = 0;
-
-       NCR_MISC(("ncr5380_select: arbitration\n"));
 
        /*
         * Arbitrate for the bus.  The 5380 takes care of the
@@ -1021,25 +1310,30 @@ ncr5380_select(sc, xs)
        *(sc->sci_mode) = SCI_MODE_ARB;
 
        /* Wait for ICMD_AIP. */
-       for (timo = 50000;;) {
+       timo = ncr5380_wait_req_timo;
+       for (;;) {
                if (*(sc->sci_icmd) & SCI_ICMD_AIP)
                        break;
                if (--timo <= 0) {
                        /* Did not see any "bus free" period. */
-                       NCR_MISC(("ncr5380_select: failed, bus busy\n"));
                        *sc->sci_mode = 0;
+                       NCR_TRACE("select: bus busy, rc=%d\n", XS_BUSY);
                        return XS_BUSY;
                }
                delay(2);
        }
+       NCR_TRACE("select: have AIP after %d loops\n",
+                         ncr5380_wait_req_timo - timo);
+
        /* Got AIP.  Wait one arbitration delay (2.2 uS.) */
        delay(3);
 
        /* Check for ICMD_LST */
        if (*(sc->sci_icmd) & SCI_ICMD_LST) {
                /* Some other target asserted SEL. */
-               NCR_MISC(("ncr5380_select: lost arbitration\n"));
                *sc->sci_mode = 0;
+               NCR_TRACE("select: lost one, rc=%d\n", XS_BUSY);
+               ncr5380_reselect(sc);   /* XXX */
                return XS_BUSY;
        }
 
@@ -1070,9 +1364,9 @@ ncr5380_select(sc, xs)
         * XXX: did win arbitration.  (too paranoid?)
         */
        if (*(sc->sci_icmd) & SCI_ICMD_LST) {
-               NCR_MISC(("ncr5380_select: lost arb. after SEL\n"));
                *sc->sci_icmd = 0;
                *sc->sci_mode = 0;
+               NCR_TRACE("select: lost two, rc=%d\n", XS_BUSY);
                return XS_BUSY;
        }
 #endif
@@ -1086,9 +1380,8 @@ ncr5380_select(sc, xs)
         * the host and target.  Also set ATN now, to
         * ask the target for a messgae out phase.
         */
-       NCR_MISC(("ncr5380_select: selection\n"));
-       data = (1 << xs->sc_link->target);
-       *(sc->sci_odata) = 0x80 | data;
+       data = 0x80 | (1 << sr->sr_target);
+       *(sc->sci_odata) = data;
        icmd |= (SCI_ICMD_DATA | SCI_ICMD_ATN);
        *(sc->sci_icmd) = icmd;
        delay(2);       /* two deskew delays. */
@@ -1123,10 +1416,13 @@ ncr5380_select(sc, xs)
        delay(201);
        if ((*sc->sci_bus_csr & SCI_BUS_BSY) == 0) {
                /* Really no device on bus */
+               *sc->sci_tcmd = PHASE_INVALID;
                *sc->sci_icmd = 0;
                *sc->sci_mode = 0;
+               *sc->sci_sel_enb = 0;
+               SCI_CLR_INTR(sc);
                *sc->sci_sel_enb = 0x80;
-               NCR_MISC(("ncr5380_select: device down\n"));
+               NCR_TRACE("select: device down, rc=%d\n", XS_SELTIMEOUT);
                return XS_SELTIMEOUT;
        }
 
@@ -1142,7 +1438,6 @@ success:
        /* XXX - Make parity checking optional? */
        *sc->sci_mode = (SCI_MODE_MONBSY | SCI_MODE_PAR_CHK);
 
-       NCR_MISC(("ncr5380_select: success\n"));
        return XS_NOERROR;
 }
 
@@ -1199,17 +1494,21 @@ ncr5380_msg_in(sc)
        int act_flags;
        register u_char icmd;
 
+       /* acknowledge phase change */
+       *sc->sci_tcmd = PHASE_MSG_IN;
+
        act_flags = ACT_CONTINUE;
        icmd = *sc->sci_icmd & SCI_ICMD_RMASK;
 
        if (sc->sc_prevphase == PHASE_MSG_IN) {
                /* This is a continuation of the previous message. */
                n = sc->sc_imp - sc->sc_imess;
+               NCR_TRACE("msg_in: continuation, n=%d\n", n);
                goto nextbyte;
        }
 
        /* This is a new MESSAGE IN phase.  Clean up our state. */
-       sc->sc_msg_flags &= ~NCR_DROP_MSGIN;
+       sc->sc_state &= ~NCR_DROP_MSGIN;
 
 nextmsg:
        n = 0;
@@ -1227,18 +1526,17 @@ nextbyte:
                 * First, check BSY, REQ, phase...
                 */
                if (!SCI_BUSY(sc)) {
-                       NCR_MISC(("ncr5380_msg_in: lost BSY\n"));
+                       NCR_TRACE("msg_in: lost BSY, n=%d\n", n);
                        /* XXX - Assume the command completed? */
                        act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE);
                        return (act_flags);
                }
                if (ncr5380_wait_req(sc)) {
-                       NCR_MISC(("ncr5380_msg_in: BSY but no REQ\n"));
-                       /* XXX - Try asserting ATN...? */
-                       act_flags |= ACT_RESET_BUS;
+                       NCR_TRACE("msg_in: BSY but no REQ, n=%d\n", n);
+                       /* Just let ncr5380_machine() handle it... */
                        return (act_flags);
                }
-               phase = SCI_CUR_PHASE(*sc->sci_bus_csr);
+               phase = SCI_BUS_PHASE(*sc->sci_bus_csr);
                if (phase != PHASE_MSG_IN) {
                        /*
                         * Target left MESSAGE IN, probably because it
@@ -1250,14 +1548,14 @@ nextbyte:
                /* Still in MESSAGE IN phase, and REQ is asserted. */
                if (*sc->sci_csr & SCI_CSR_PERR) {
                        ncr_sched_msgout(sc, SEND_PARITY_ERROR);
-                       sc->sc_msg_flags |= NCR_DROP_MSGIN;
+                       sc->sc_state |= NCR_DROP_MSGIN;
                }
 
                /* Gather incoming message bytes if needed. */
-               if ((sc->sc_msg_flags & NCR_DROP_MSGIN) == 0) {
+               if ((sc->sc_state & NCR_DROP_MSGIN) == 0) {
                        if (n >= NCR_MAX_MSG_LEN) {
                                ncr_sched_msgout(sc, SEND_REJECT);
-                               sc->sc_msg_flags |= NCR_DROP_MSGIN;
+                               sc->sc_state |= NCR_DROP_MSGIN;
                        } else {
                                *sc->sc_imp++ = *sc->sci_data;
                                n++;
@@ -1288,58 +1586,44 @@ nextbyte:
                *sc->sci_icmd = icmd;
 
                if (ncr5380_wait_not_req(sc)) {
-                       NCR_MISC(("ncr5380_msg_in: stuck REQ\n"));
+                       NCR_TRACE("msg_in: drop, stuck REQ, n=%d\n", n);
                        act_flags |= ACT_RESET_BUS;
-                       return (act_flags);
                }
 
                icmd &= ~SCI_ICMD_ACK;
                *sc->sci_icmd = icmd;
+
+               if (act_flags != ACT_CONTINUE)
+                       return (act_flags);
+
                /* back to nextbyte */
        }
 
 have_msg:
        /* We now have a complete message.  Parse it. */
-       NCR_MSGS(("n=%d imess=0x%02x  ", n, sc->sc_imess[0]));
-
-       /* Make sure we are connected. */
-       if (sc->sc_current == NULL) {
-               printf("%s: unexpected MESSAGE IN; sending DEVICE RESET\n",
-                       sc->sc_dev.dv_xname);
-               NCR_BREAK();
-               goto reset;
-       }
-       sr = sc->sc_current;
 
        switch (sc->sc_imess[0]) {
        case MSG_CMDCOMPLETE:
-               /* sc->sc_state = NCR_CMDCOMPLETE; */
-               if (sc->sc_datalen < 0) {
-                       printf("%s: %d extra bytes from %d:%d\n",
-                                  sc->sc_dev.dv_xname, -sc->sc_datalen,
-                                  sr->sr_target, sr->sr_lun);
-                       sc->sc_datalen = 0;
-               }
+               NCR_TRACE("msg_in: CMDCOMPLETE\n", 0);
                /* Target is about to disconnect. */
-               NCR_MISC(("ncr5380_msg_in: cmdcomplete\n"));
                act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE);
                break;
 
        case MSG_DISCONNECT:
-               /* sc->sc_state = NCR_DISCONNECT; */
+               NCR_TRACE("msg_in: DISCONNECT\n", 0);
                /* Target is about to disconnect. */
-               NCR_MISC(("ncr5380_msg_in: disconnect\n"));
                act_flags |= ACT_DISCONNECT;
                break;
 
        case MSG_PARITY_ERROR:
+               NCR_TRACE("msg_in: PARITY_ERROR\n", 0);
                /* Resend the last message. */
                ncr_sched_msgout(sc, sc->sc_msgout);
                break;
 
        case MSG_MESSAGE_REJECT:
                /* The target rejects the last message we sent. */
-               NCR_MSGS(("ncr5380_msg_in: reject\n"));
+               NCR_TRACE("msg_in: got reject for 0x%x\n", sc->sc_msgout);
                switch (sc->sc_msgout) {
                case SEND_IDENTIFY:
                        /* Really old target controller? */
@@ -1351,18 +1635,19 @@ have_msg:
                break;
 
        case MSG_NOOP:
+               NCR_TRACE("msg_in: NOOP\n", 0);
                break;
 
        case MSG_SAVEDATAPOINTER:
-               sr->sr_data = sc->sc_dataptr;
+               NCR_TRACE("msg_in: SAVE_PTRS\n", 0);
+               sr->sr_dataptr = sc->sc_dataptr;
                sr->sr_datalen = sc->sc_datalen;
-               sr->sr_dma_hand = sc->sc_dma_hand;
                break;
 
        case MSG_RESTOREPOINTERS:
-               sc->sc_dataptr = sr->sr_data;
+               NCR_TRACE("msg_in: RESTORE_PTRS\n", 0);
+               sc->sc_dataptr = sr->sr_dataptr;
                sc->sc_datalen = sr->sr_datalen;
-               sc->sc_dma_hand = sr->sr_dma_hand;
                break;
 
        case MSG_EXTENDED:
@@ -1380,6 +1665,7 @@ have_msg:
                break;
 
        default:
+               NCR_TRACE("msg_in: eh? imsg=0x%x\n", sc->sc_imess[0]);
                printf("%s: unrecognized MESSAGE; sending REJECT\n",
                           sc->sc_dev.dv_xname);
                NCR_BREAK();
@@ -1388,13 +1674,8 @@ have_msg:
                ncr_sched_msgout(sc, SEND_REJECT);
                break;
 
-       reset:
-               sc->sc_msg_flags |= NCR_ABORTING;
-               ncr_sched_msgout(sc, SEND_DEV_RESET);
-               break;
-
        abort:
-               sc->sc_msg_flags |= NCR_ABORTING;
+               sc->sc_state |= NCR_ABORTING;
                ncr_sched_msgout(sc, SEND_ABORT);
                break;
        }
@@ -1404,7 +1685,7 @@ have_msg:
        *sc->sci_icmd = icmd;
 
        if (ncr5380_wait_not_req(sc)) {
-               NCR_MISC(("ncr5380_msg_in: stuck REQ\n"));
+               NCR_TRACE("msg_in: last, stuck REQ, n=%d\n", n);
                act_flags |= ACT_RESET_BUS;
        }
 
@@ -1447,16 +1728,17 @@ ncr5380_msg_out(sc)
        int progress, act_flags;
        register u_char icmd;
 
-       NCR_TRACE(("ncr_msgout  "));
+       /* acknowledge phase change */
+       *sc->sci_tcmd = PHASE_MSG_OUT;
 
        progress = 0;   /* did we send any messages? */
        act_flags = ACT_CONTINUE;
-       icmd = *sc->sci_icmd & SCI_ICMD_RMASK;
 
        /*
         * Set ATN.  If we're just sending a trivial 1-byte message,
         * we'll clear ATN later on anyway.  Also drive the data bus.
         */
+       icmd = *sc->sci_icmd & SCI_ICMD_RMASK;
        icmd |= (SCI_ICMD_ATN | SCI_ICMD_DATA);
        *sc->sci_icmd = icmd;
 
@@ -1473,11 +1755,12 @@ ncr5380_msg_out(sc)
                         * start again from the top.  Our simple priority
                         * scheme keeps the messages in the right order.
                         */
-                       NCR_MSGS(("ncr5380_msg_out: retransmit\n"));
                        sc->sc_msgpriq |= sc->sc_msgoutq;
+                       NCR_TRACE("msg_out: retrans priq=0x%x\n", sc->sc_msgpriq);
                } else {
                        /* This is a continuation of the previous message. */
                        n = sc->sc_omp - sc->sc_omess;
+                       NCR_TRACE("msg_out: continuation, n=%d\n", n);
                        goto nextbyte;
                }
        }
@@ -1494,7 +1777,7 @@ nextmsg:
        /* Build the outgoing message data. */
        switch (sc->sc_msgout) {
        case SEND_IDENTIFY:
-               /* (sc->sc_state != NCR_CONNECTED) */
+               NCR_TRACE("msg_out: SEND_IDENTIFY\n", 0);
                if (sr == NULL) {
                        printf("%s: SEND_IDENTIFY while not connected; sending NOOP\n",
                                sc->sc_dev.dv_xname);
@@ -1508,6 +1791,7 @@ nextmsg:
                break;
 
        case SEND_DEV_RESET:
+               NCR_TRACE("msg_out: SEND_DEV_RESET\n", 0);
                /* Expect disconnect after this! */
                /* XXX: Kill jobs for this target? */
                act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE);
@@ -1516,21 +1800,25 @@ nextmsg:
                break;
 
        case SEND_REJECT:
+               NCR_TRACE("msg_out: SEND_REJECT\n", 0);
                sc->sc_omess[0] = MSG_MESSAGE_REJECT;
                n = 1;
                break;
 
        case SEND_PARITY_ERROR:
+               NCR_TRACE("msg_out: SEND_PARITY_ERROR\n", 0);
                sc->sc_omess[0] = MSG_PARITY_ERROR;
                n = 1;
                break;
 
        case SEND_INIT_DET_ERR:
+               NCR_TRACE("msg_out: SEND_INIT_DET_ERR\n", 0);
                sc->sc_omess[0] = MSG_INITIATOR_DET_ERR;
                n = 1;
                break;
 
        case SEND_ABORT:
+               NCR_TRACE("msg_out: SEND_ABORT\n", 0);
                /* Expect disconnect after this! */
                /* XXX: Set error flag? */
                act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE);
@@ -1543,6 +1831,7 @@ nextmsg:
                        sc->sc_dev.dv_xname);
                NCR_BREAK();
        noop:
+               NCR_TRACE("msg_out: send NOOP\n", 0);
                sc->sc_omess[0] = MSG_NOOP;
                n = 1;
                break;
@@ -1563,19 +1852,20 @@ nextbyte:
                 * First check BSY, REQ, phase...
                 */
                if (!SCI_BUSY(sc)) {
-                       NCR_MISC(("ncr5380_msg_out: lost BSY\n"));
+                       NCR_TRACE("msg_out: lost BSY, n=%d\n", n);
                        goto out;
                }
                if (ncr5380_wait_req(sc)) {
-                       NCR_MISC(("ncr5380_msg_out: no REQ\n"));
+                       NCR_TRACE("msg_out: no REQ, n=%d\n", n);
                        goto out;
                }
-               phase = SCI_CUR_PHASE(*sc->sci_bus_csr);
+               phase = SCI_BUS_PHASE(*sc->sci_bus_csr);
                if (phase != PHASE_MSG_OUT) {
                        /*
                         * Target left MESSAGE OUT, possibly to reject
                         * our message.
                         */
+                       NCR_TRACE("msg_out: new phase=%d\n", phase);
                        goto out;
                }
 
@@ -1599,19 +1889,23 @@ nextbyte:
 
                /* Wait for REQ to be negated. */
                if (ncr5380_wait_not_req(sc)) {
-                       NCR_MISC(("ncr5380_msg_out: stuck REQ\n"));
+                       NCR_TRACE("msg_out: stuck REQ, n=%d\n", n);
                        act_flags |= ACT_RESET_BUS;
-                       goto out;
                }
 
                /* Finally, drop ACK. */
                icmd &= ~SCI_ICMD_ACK;
                *sc->sci_icmd = icmd;
+
+               /* Stuck bus or something... */
+               if (act_flags & ACT_RESET_BUS)
+                       goto out;
+
        }
        progress++;
 
        /* We get here only if the entire message has been transmitted. */
-       if ((sc->sc_msgpriq != 0) && (act_flags == ACT_CONTINUE)) {
+       if (sc->sc_msgpriq != 0) {
                /* There are more outgoing messages. */
                goto nextmsg;
        }
@@ -1643,10 +1937,13 @@ static int
 ncr5380_command(sc)
        struct ncr5380_softc *sc;
 {
-       int len;
-       struct scsi_sense rqs;
        struct sci_req *sr = sc->sc_current;
        struct scsi_xfer *xs = sr->sr_xs;
+       struct scsi_sense rqs;
+       int len;
+
+       /* acknowledge phase change */
+       *sc->sci_tcmd = PHASE_COMMAND;
 
        if (sr->sr_flags & SR_SENSE) {
                rqs.opcode = REQUEST_SENSE;
@@ -1654,22 +1951,29 @@ ncr5380_command(sc)
                rqs.length = sizeof(xs->sense);
 
                rqs.unused[0] = rqs.unused[1] = rqs.control = 0;
-               len = ncr5380_pio_out(sc, PHASE_CMD, sizeof(rqs),
+               len = ncr5380_pio_out(sc, PHASE_COMMAND, sizeof(rqs),
                        (u_char *)&rqs);
        }
        else {
                /* Assume command can be sent in one go. */
                /* XXX: Do this using DMA, and get a phase change intr? */
-               len = ncr5380_pio_out(sc, PHASE_CMD, xs->cmdlen,
+               len = ncr5380_pio_out(sc, PHASE_COMMAND, xs->cmdlen,
                        (u_char *)xs->cmd);
        }
 
        if (len != xs->cmdlen) {
+#ifdef DEBUG
                printf("ncr5380_command: short transfer: wanted %d got %d.\n",
                           xs->cmdlen, len);
+               ncr5380_show_scsi_cmd(xs);
                NCR_BREAK();
-               if (len < 6)
-                       return ACT_RESET_BUS;
+#endif
+               if (len < 6) {
+                       xs->error = XS_DRIVER_STUFFUP;
+                       sc->sc_state |= NCR_ABORTING;
+                       ncr_sched_msgout(sc, SEND_ABORT);
+               }
+
        }
 
        return ACT_CONTINUE;
@@ -1689,69 +1993,67 @@ ncr5380_data_xfer(sc, phase)
        int expected_phase;
        int i, len;
 
-       if (sc->sc_msg_flags & NCR_ABORTING) {
-               sc->sc_msg_flags &= ~NCR_ABORTING;
-               printf("%s: data phase after abort\n",
-                          sc->sc_dev.dv_xname);
-               return ACT_RESET_BUS;
-       }
-
        if (sr->sr_flags & SR_SENSE) {
+               NCR_TRACE("data_xfer: get sense, sr=0x%x\n", (long)sr);
                if (phase != PHASE_DATA_IN) {
                        printf("%s: sense phase error\n", sc->sc_dev.dv_xname);
-                       return (ACT_ABORT_CMD);
+                       goto abort;
                }
-               NCR_MISC(("ncr5380_data_xfer: reading sense data\n"));
+               /* acknowledge phase change */
+               *sc->sci_tcmd = PHASE_DATA_IN;
                len = ncr5380_pio_in(sc, phase, sizeof(xs->sense),
                                (u_char *)&xs->sense);
-               return ACT_CONTINUE;    /* XXX */
+               return ACT_CONTINUE;
+       }
+
+       /*
+        * When aborting a command, disallow any data phase.
+        */
+       if (sc->sc_state & NCR_ABORTING) {
+               printf("%s: aborting, but phase=%s (reset)\n",
+                      sc->sc_dev.dv_xname,
+                          phase_names[phase & 7]);
+               return ACT_RESET_BUS;   /* XXX */
        }
 
        /* Validate expected phase (data_in or data_out) */
        expected_phase = (xs->flags & SCSI_DATA_OUT) ?
                PHASE_DATA_OUT : PHASE_DATA_IN;
        if (phase != expected_phase) {
-               printf("%s: data phase error\n", sc->sc_dev.dv_xname);
-               return (ACT_ABORT_CMD);
+               printf("%s: data phase error\n",
+                          sc->sc_dev.dv_xname);
+               goto abort;
        }
 
        /* Make sure we have some data to move. */
        if (sc->sc_datalen <= 0) {
-               printf("%s: can not transfer more data\n");
-               return (ACT_ABORT_CMD);
+               printf("%s: can not transfer more data\n",
+                          sc->sc_dev.dv_xname);
+               goto abort;
        }
-       NCR_MISC(("ncr5380_data_xfer: todo=%d\n", sc->sc_datalen));
 
        /*
         * Attempt DMA only if dma_alloc gave us a DMA handle AND
         * there is enough left to transfer so DMA is worth while.
-        * Note that we can come back here after a DMA transfer moves
-        * all but the last few bytes of a request, in which case
-        * we should finish the request using PIO.  In this case
-        * there WILL be a dma_handle, but we should not use it.
-        * Data alignment was checked in DMA alloc.
         */
-       if (sc->sc_dma_hand &&
+       if (sr->sr_dma_hand &&
                (sc->sc_datalen >= sc->sc_min_dma_len))
        {
-               /* OK, really do DMA. */
+               /*
+                * OK, really start DMA.  Note, the MI start function
+                * is responsible for setting the TCMD register, etc.
+                * (Acknowledge the phase change there, not here.)
+                */
+               NCR_TRACE("data_xfer: dma_start, dh=0x%x\n",
+                         (long) sr->sr_dma_hand);
                (*sc->sc_dma_start)(sc);
-               if (sc->sc_dma_flags & DMA5380_POLL) {
-                       (*sc->sc_dma_poll)(sc);
-                       (*sc->sc_dma_stop)(sc);
-               }
-               if (sc->sc_dma_flags & DMA5380_INPROGRESS) {
-                       /* Interrupt will let us continue */
-                       NCR_MISC(("ncr5380_data_xfer: expect DMA intr.\n"));
-                       return ACT_WAIT_INTR;
-               }
-               /* Must have done polled DMA. */
-               SCI_CLR_INTR(sc);       /* XXX */
-               /* Let _machine call us again... */
-               return ACT_CONTINUE;
+               return ACT_WAIT_DMA;
        }
 
-       NCR_MISC(("ncr5380_data_xfer: doing PIO, len=%d\n", sc->sc_datalen));
+       NCR_TRACE("data_xfer: doing PIO, len=%d\n", sc->sc_datalen);
+
+       /* acknowledge phase change */
+       *sc->sci_tcmd = phase;
        if (phase == PHASE_DATA_OUT) {
                len = ncr5380_pio_out(sc, phase, sc->sc_datalen, sc->sc_dataptr);
        } else {
@@ -1760,7 +2062,12 @@ ncr5380_data_xfer(sc, phase)
        sc->sc_dataptr += len;
        sc->sc_datalen -= len;
 
-       NCR_MISC(("ncr5380_data_xfer: did PIO, resid=%d\n", sc->sc_datalen));
+       NCR_TRACE("data_xfer: did PIO, resid=%d\n", sc->sc_datalen);
+       return (ACT_CONTINUE);
+
+abort:
+       sc->sc_state |= NCR_ABORTING;
+       ncr_sched_msgout(sc, SEND_ABORT);
        return (ACT_CONTINUE);
 }
 
@@ -1774,59 +2081,62 @@ ncr5380_status(sc)
        struct sci_req *sr = sc->sc_current;
        struct scsi_xfer *xs = sr->sr_xs;
 
-       len = ncr5380_pio_in(sc, PHASE_STATUS, 1, &status);
+       /* acknowledge phase change */
+       *sc->sci_tcmd = PHASE_STATUS;
 
+       len = ncr5380_pio_in(sc, PHASE_STATUS, 1, &status);
        if (len) {
                sr->sr_status = status;
        } else {
                printf("ncr5380_status: none?\n");
        }
+
        return ACT_CONTINUE;
 }
 
 
 /*
- *  This is the big state machine that follows SCSI phase changes.
- *  This is somewhat like a co-routine.  It will do a SCSI command,
- *  and exit if the command is complete, or if it must wait (usually
- *  for DMA).
+ * This is the big state machine that follows SCSI phase changes.
+ * This is somewhat like a co-routine.  It will do a SCSI command,
+ * and exit if the command is complete, or if it must wait, i.e.
+ * for DMA to complete or for reselect to resume the job.
  *
- *  The bus must be selected, and we need to know which command is
- *  being undertaken.
+ * The bus must be selected, and we need to know which command is
+ * being undertaken.
  */
 static void
 ncr5380_machine(sc)
        struct ncr5380_softc *sc;
 {
-       struct sci_req *sr = sc->sc_current;
-       struct scsi_xfer *xs = sr->sr_xs;
-       int phase, act_flags, timo;
+       struct sci_req *sr;
+       struct scsi_xfer *xs;
+       int act_flags, phase, timo;
 
 #ifdef DIAGNOSTIC
        if ((getsr() & PSL_IPL) < PSL_IPL2)
                panic("ncr5380_machine: bad spl");
+       if (sc->sc_state == NCR_IDLE)
+               panic("ncr5380_machine: state=idle");
+       if (sc->sc_current == NULL)
+               panic("ncr5380_machine: no current cmd");
 #endif
 
+       sr = sc->sc_current;
+       xs = sr->sr_xs;
        act_flags = ACT_CONTINUE;
 
-next_phase:
        /*
-        * We can arrive back here due to a DMA interrupt
-        * or DMA timeout error.  On error, reset it.
-        * If DMA failed, we probably can't talk to it.
+        * This will be called by ncr5380_intr() when DMA is
+        * complete.  Must stop DMA before touching the 5380 or
+        * there will be "register conflict" errors.
         */
-       if (sc->sc_dma_flags & DMA5380_ERROR) {
-               printf("ncr5380: DMA error\n");
-               NCR_BREAK();
-               /* Make sure we don't keep trying it... */
-               if (sc->sc_dma_hand) {
-                       (*sc->sc_dma_free)(sc);
-                       sc->sc_dma_hand = NULL;
-               }
-               act_flags |= ACT_RESET_BUS;
-               goto do_actions;
+       if (sc->sc_state & NCR_DOINGDMA) {
+               /* Pick-up where where we left off... */
+               goto dma_done;
        }
 
+next_phase:
+
        if (!SCI_BUSY(sc)) {
                /* Unexpected disconnect */
                printf("ncr5380_machine: unexpected disconnect.\n");
@@ -1845,24 +2155,38 @@ next_phase:
                if (*sc->sci_bus_csr & SCI_BUS_REQ)
                        break;
                if (--timo <= 0) {
-                       printf("%s: no REQ for next phase, reset\n",
-                                  sc->sc_dev.dv_xname);
-                       /* Reset implies disconnect, cmd fail. */
-                       act_flags |= ACT_RESET_BUS;
-                       goto do_actions;
+                       if (sc->sc_state & NCR_ABORTING) {
+                               printf("%s: no REQ while aborting, reset\n",
+                                      sc->sc_dev.dv_xname);
+                               act_flags |= ACT_RESET_BUS;
+                               goto do_actions;
+                       }
+                       printf("%s: no REQ for next phase, abort\n",
+                              sc->sc_dev.dv_xname);
+                       sc->sc_state |= NCR_ABORTING;
+                       ncr_sched_msgout(sc, SEND_ABORT);
+                       goto next_phase;
                }
                delay(100);
        }
 
-       phase = SCI_CUR_PHASE(*sc->sci_bus_csr);
-       NCR_MISC(("ncr5380_machine: phase=%s\n",
-                         phase_names[phase & 7]));
+       phase = SCI_BUS_PHASE(*sc->sci_bus_csr);
+       NCR_TRACE("machine: phase=%s\n",
+                         (long) phase_names[phase & 7]);
+
+       /*
+        * We assume that the device knows what it's doing,
+        * so any phase is good.
+        */
 
+#if 0
        /*
-        *  We make the assumption that the device knows what it's
-        *  doing, so any phase is good.
+        * XXX: Do not ACK the phase yet! do it later...
+        * XXX: ... each phase routine does that itself.
+        * In particular, DMA needs it done LATER.
         */
-       *sc->sci_tcmd = phase;  /* acknowledge */
+       *sc->sci_tcmd = phase;  /* acknowledge phase change */
+#endif
 
        switch (phase) {
 
@@ -1871,7 +2195,7 @@ next_phase:
                act_flags = ncr5380_data_xfer(sc, phase);
                break;
 
-       case PHASE_CMD:
+       case PHASE_COMMAND:
                act_flags = ncr5380_command(sc);
                break;
 
@@ -1889,61 +2213,89 @@ next_phase:
 
        default:
                printf("ncr5380_machine: Unexpected phase 0x%x\n", phase);
-               act_flags = ACT_ABORT_CMD;
-               break;
+               sc->sc_state |= NCR_ABORTING;
+               ncr_sched_msgout(sc, SEND_ABORT);
+               goto next_phase;
 
        } /* switch */
        sc->sc_prevphase = phase;
 
-       /* short-cut */
-       if (act_flags == ACT_CONTINUE)
-               goto next_phase;
-
 do_actions:
        __asm("_ncr5380_actions:");
 
-       NCR_MISC(("ncr5380_machine: act_flags=0x%x\n", act_flags));
+       if (act_flags & ACT_WAIT_DMA) {
+               act_flags &= ~ACT_WAIT_DMA;
+               /* Wait for DMA to complete (polling, or interrupt). */
+               if ((sr->sr_flags & SR_IMMED) == 0) {
+                       NCR_TRACE("machine: wait for DMA intr.\n", 0);
+                       return;         /* will resume at dma_done */
+               }
+               /* Busy-wait for it to finish. */
+               NCR_TRACE("machine: dma_poll, dh=0x%x\n",
+                                 (long) sr->sr_dma_hand);
+               (*sc->sc_dma_poll)(sc);
+       dma_done:
+               /* Return here after interrupt. */
+               if (sr->sr_flags & SR_OVERDUE)
+                       sc->sc_state |= NCR_ABORTING;
+               NCR_TRACE("machine: dma_stop, dh=0x%x\n",
+                                 (long) sr->sr_dma_hand);
+               (*sc->sc_dma_stop)(sc);
+               SCI_CLR_INTR(sc);       /* XXX */
+               /*
+                * While DMA is running we can not touch the SBC,
+                * so various places just set NCR_ABORTING and
+                * expect us the "kick it" when DMA is done.
+                */
+               if (sc->sc_state & NCR_ABORTING) {
+                       ncr_sched_msgout(sc, SEND_ABORT);
+               }
+       }
 
-       if (act_flags & ACT_WAIT_INTR) {
-               /* Wait for DMA complete interrupt (or timeout). */
-               NCR_MISC(("ncr5380_machine: wait for dma\n"));
-               return;
+       /*
+        * Check for parity error.
+        * XXX - better place to check?
+        */
+       if (*(sc->sci_csr) & SCI_CSR_PERR) {
+               printf("%s: parity error!\n",
+                          sc->sc_dev.dv_xname);
+               /* XXX: sc->sc_state |= NCR_ABORTING; */
+               ncr_sched_msgout(sc, SEND_PARITY_ERROR);
        }
 
+       if (act_flags == ACT_CONTINUE)
+               goto next_phase;
+       /* All other actions "break" from the loop. */
+
+       NCR_TRACE("machine: act_flags=0x%x\n", act_flags);
+
        if (act_flags & ACT_RESET_BUS) {
+               act_flags |= ACT_CMD_DONE;
                /*
                 * Reset the SCSI bus, usually due to a timeout.
                 * The error code XS_TIMEOUT allows retries.
                 */
-               NCR_MISC(("ncr5380_machine: reset scsi bus\n"));
+               sc->sc_state |= NCR_ABORTING;
+               printf("%s: reset SCSI bus for TID=%d LUN=%d\n",
+                          sc->sc_dev.dv_xname,
+                          sr->sr_target, sr->sr_lun);
                ncr5380_reset_scsibus(sc);
-               xs->error = XS_TIMEOUT;
-               act_flags = (ACT_DISCONNECT | ACT_CMD_DONE);
-       }
-
-       /* Try ONCE to send an ABORT message. */
-       if (act_flags & ACT_ABORT_CMD) {
-               act_flags &= ~ACT_ABORT_CMD;
-               /* Try to send MSG_ABORT to the target. */
-               NCR_MISC(("ncr5380_machine: send abort\n"));
-               sc->sc_msg_flags |= NCR_ABORTING;
-               ncr_sched_msgout(sc, SEND_ABORT);
-               goto next_phase;
-       }
-
-       if (sc->sc_msg_flags & NCR_ABORTING) {
-               sc->sc_msg_flags &= ~NCR_ABORTING;
-               printf("ncr5380_machine: command aborted\n");
-               xs->error = XS_DRIVER_STUFFUP;
-               act_flags |= (ACT_DISCONNECT | ACT_CMD_DONE);
        }
 
        if (act_flags & ACT_CMD_DONE) {
+               act_flags |= ACT_DISCONNECT;
                /* Need to call scsi_done() */
+               /* XXX: from the aic6360 driver, but why? */
+               if (sc->sc_datalen < 0) {
+                       printf("%s: %d extra bytes from %d:%d\n",
+                                  sc->sc_dev.dv_xname, -sc->sc_datalen,
+                                  sr->sr_target, sr->sr_lun);
+                       sc->sc_datalen = 0;
+               }
                xs->resid = sc->sc_datalen;
                /* Note: this will clear sc_current */
+               NCR_TRACE("machine: call done, cur=0x%x\n", (long)sr);
                ncr5380_done(sc);
-               act_flags |= ACT_DISCONNECT;
        }
 
        if (act_flags & ACT_DISCONNECT) {
@@ -1953,17 +2305,204 @@ do_actions:
                 */
                *sc->sci_icmd = 0;
                *sc->sci_mode = 0;
-               *sc->sci_tcmd = 0;
+               *sc->sci_tcmd = PHASE_INVALID;
+               *sc->sci_sel_enb = 0;
                SCI_CLR_INTR(sc);
-
                *sc->sci_sel_enb = 0x80;
 
+               if ((act_flags & ACT_CMD_DONE) == 0) {
+                       __asm("_ncr5380_disconnected:");
+                       NCR_TRACE("machine: discon, cur=0x%x\n", (long)sr);
+               }
+
                /*
                 * We may be here due to a disconnect message,
                 * in which case we did NOT call ncr5380_done,
-                * so we need to clear sc_current.
+                * and we need to clear sc_current.
                 */
+               sc->sc_state = NCR_IDLE;
                sc->sc_current = NULL;
+
+               /* Paranoia: clear everything. */
+               sc->sc_dataptr = NULL;
+               sc->sc_datalen = 0;
+               sc->sc_prevphase = PHASE_INVALID;
+               sc->sc_msgpriq = 0;
+               sc->sc_msgoutq = 0;
+               sc->sc_msgout  = 0;
+
+               /* Our caller will re-enable interrupts. */
+       }
+}
+
+
+#ifdef DEBUG
+
+static void
+ncr5380_show_scsi_cmd(xs)
+       struct scsi_xfer *xs;
+{
+       u_char  *b = (u_char *) xs->cmd;
+       int     i  = 0;
+
+       if ( ! ( xs->flags & SCSI_RESET ) ) {
+               printf("si(%d:%d:%d)-",
+                          xs->sc_link->scsibus,
+                          xs->sc_link->target,
+                          xs->sc_link->lun);
+               while (i < xs->cmdlen) {
+                       if (i) printf(",");
+                       printf("%x",b[i++]);
+               }
+               printf("-\n");
+       } else {
+               printf("si(%d:%d:%d)-RESET-\n",
+                          xs->sc_link->scsibus,
+                          xs->sc_link->target,
+                          xs->sc_link->lun);
+       }
+}
+
+
+static void
+ncr5380_show_sense(xs)
+       struct scsi_xfer *xs;
+{
+       u_char  *b = (u_char *)&xs->sense;
+       int     i;
+
+       printf("sense:");
+       for (i = 0; i < sizeof(xs->sense); i++)
+               printf(" %02x", b[i]);
+       printf("\n");
+}
+
+int ncr5380_traceidx = 0;
+
+#define        TRACE_MAX       1024
+struct trace_ent {
+       char *msg;
+       long  val;
+} ncr5380_tracebuf[TRACE_MAX];
+
+void
+ncr5380_trace(msg, val)
+       char *msg;
+       long  val;
+{
+       register struct trace_ent *tr;
+       register int s;
+
+       s = splhigh();
+
+       tr = &ncr5380_tracebuf[ncr5380_traceidx];
+
+       ncr5380_traceidx++;
+       if (ncr5380_traceidx >= TRACE_MAX)
+               ncr5380_traceidx = 0;
+
+       tr->msg = msg;
+       tr->val = val;
+
+       splx(s);
+}
+
+#ifdef DDB
+void
+ncr5380_clear_trace()
+{
+       ncr5380_traceidx = 0;
+       bzero((char*) ncr5380_tracebuf, sizeof(ncr5380_tracebuf));
+}
+
+void
+ncr5380_show_trace()
+{
+       struct trace_ent *tr;
+       int idx;
+
+       idx = ncr5380_traceidx;
+       do {
+               tr = &ncr5380_tracebuf[idx];
+               idx++;
+               if (idx >= TRACE_MAX)
+                       idx = 0;
+               if (tr->msg)
+                       db_printf(tr->msg, tr->val);
+       } while (idx != ncr5380_traceidx);
+}
+
+void
+ncr5380_show_req(sr)
+       struct sci_req *sr;
+{
+       struct scsi_xfer *xs = sr->sr_xs;
+
+       db_printf("TID=%d ",    sr->sr_target);
+       db_printf("LUN=%d ",    sr->sr_lun);
+       db_printf("dh=0x%x ",   sr->sr_dma_hand);
+       db_printf("dptr=0x%x ", sr->sr_dataptr);
+       db_printf("dlen=0x%x ", sr->sr_datalen);
+       db_printf("flags=%d ",  sr->sr_flags);
+       db_printf("stat=%d ",   sr->sr_status);
+
+       if (xs == NULL) {
+               db_printf("(xs=NULL)\n");
+               return;
+       }
+       db_printf("\n");
+#ifdef SCSIDEBUG
+       show_scsi_xs(xs);
+#else
+       db_printf("xs=0x%x\n", xs);
+#endif
+}
+
+void
+ncr5380_show_state()
+{
+       struct ncr5380_softc *sc;
+       struct sci_req *sr;
+       int i, j, k;
+
+       sc = ncr5380_debug_sc;
+
+       if (sc == NULL) {
+               db_printf("ncr5380_debug_sc == NULL\n");
                return;
        }
+
+       db_printf("sc_ncmds=%d\n",      sc->sc_ncmds);
+       k = -1; /* which is current? */
+       for (i = 0; i < SCI_OPENINGS; i++) {
+               sr = &sc->sc_ring[i];
+               if (sr->sr_xs) {
+                       if (sr == sc->sc_current)
+                               k = i;
+                       db_printf("req %d: (sr=0x%x)", i, (long)sr);
+                       ncr5380_show_req(sr);
+               }
+       }
+       db_printf("sc_rr=%d, current=%d\n", sc->sc_rr, k);
+
+       db_printf("Active request matrix:\n");
+       for(i = 0; i < 8; i++) {                /* targets */
+               for (j = 0; j < 8; j++) {       /* LUN */
+                       sr = sc->sc_matrix[i][j];
+                       if (sr) {
+                               db_printf("TID=%d LUN=%d sr=0x%x\n", i, j, (long)sr);
+                       }
+               }
+       }
+
+       db_printf("sc_state=0x%x\n",    sc->sc_state);
+       db_printf("sc_current=0x%x\n",  sc->sc_current);
+       db_printf("sc_dataptr=0x%x\n",  sc->sc_dataptr);
+       db_printf("sc_datalen=0x%x\n",  sc->sc_datalen);
+
+       db_printf("sc_prevphase=%d\n",  sc->sc_prevphase);
+       db_printf("sc_msgpriq=0x%x\n",  sc->sc_msgpriq);
 }
+
+#endif /* DDB */
+#endif /* DEBUG */
index 1c27ecc..1ae35b4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $NetBSD: ncr5380var.h,v 1.1 1995/10/29 21:19:10 gwr Exp $       */
+/*     $NetBSD: ncr5380var.h,v 1.1.2.1 1995/11/18 07:08:41 gwr Exp $   */
 
 /*
  * Copyright (c) 1995 David Jones, Gordon W. Ross
 #define SCI_CLR_INTR(sc)       (*(sc)->sci_iack)
 #define        SCI_BUSY(sc)            (*sc->sci_bus_csr & SCI_BUS_BSY)
 
+/* These are NOT artibtrary, but map to bits in sci_tcmd */
 #define PHASE_DATA_OUT 0x0
 #define PHASE_DATA_IN  0x1
-#define PHASE_CMD      0x2
+#define PHASE_COMMAND  0x2
 #define PHASE_STATUS   0x3
 #define PHASE_UNSPEC1  0x4
 #define PHASE_UNSPEC2  0x5
 #define PHASE_MSG_OUT  0x6
 #define PHASE_MSG_IN   0x7
 
-#define PHASE_INVALID  -1
+/*
+ * This illegal phase is used to prevent the 5380 from having
+ * a phase-match condition when we don't want one, such as
+ * when setting up the DMA engine or whatever...
+ */
+#define PHASE_INVALID  PHASE_UNSPEC1
 
-#define SCSI_PHASE(x)  ((x)&0x7)
 
 /* Per-request state.  This is required in order to support reselection. */
 struct sci_req {
        struct          scsi_xfer *sr_xs;       /* Pointer to xfer struct, NULL=unused */
        int             sr_target, sr_lun;      /* For fast access */
        void            *sr_dma_hand;           /* Current DMA hnadle */
-       u_char          *sr_data;               /* Saved data pointer */
+       u_char          *sr_dataptr;            /* Saved data pointer */
        int             sr_datalen;
        int             sr_flags;               /* Internal error code */
 #define        SR_IMMED                        1       /* Immediate command */
 #define        SR_SENSE                        2       /* We are getting sense */
-#define        SR_ERROR                        4       /* Error occurred */
+#define        SR_OVERDUE                      4       /* Timeout while not current */
+#define        SR_ERROR                        8       /* Error occurred */
        int             sr_status;              /* Status code from last cmd */
 };
-#define        SCI_OPENINGS    4               /* Up to 4 commands at once */
+#define        SCI_OPENINGS    16              /* How many commands we can enqueue. */
 
 
 struct ncr5380_softc {
        struct device   sc_dev;
        struct          scsi_link sc_link;
 
-       /* Pointers to 5380 registers.  MD code must set these up. */
-       volatile u_char *sci_data;
-       volatile u_char *sci_icmd;
-       volatile u_char *sci_mode;
-       volatile u_char *sci_tcmd;
-       volatile u_char *sci_bus_csr;
-       volatile u_char *sci_csr;
-       volatile u_char *sci_idata;
-       volatile u_char *sci_iack;
+       /* Pointers to 5380 registers.  See ncr5380reg.h */
+       volatile u_char *sci_r0;
+       volatile u_char *sci_r1;
+       volatile u_char *sci_r2;
+       volatile u_char *sci_r3;
+       volatile u_char *sci_r4;
+       volatile u_char *sci_r5;
+       volatile u_char *sci_r6;
+       volatile u_char *sci_r7;
 
        /* Functions set from MD code */
        int             (*sc_pio_out) __P((struct ncr5380_softc *,
@@ -90,35 +96,40 @@ struct ncr5380_softc {
                                          int, int, u_char *));
        void            (*sc_dma_alloc) __P((struct ncr5380_softc *));
        void            (*sc_dma_free) __P((struct ncr5380_softc *));
+
+       void            (*sc_dma_setup) __P((struct ncr5380_softc *));
        void            (*sc_dma_start) __P((struct ncr5380_softc *));
        void            (*sc_dma_poll) __P((struct ncr5380_softc *));
        void            (*sc_dma_eop) __P((struct ncr5380_softc *));
        void            (*sc_dma_stop) __P((struct ncr5380_softc *));
 
+       void            (*sc_intr_on) __P((struct ncr5380_softc *));
+       void            (*sc_intr_off) __P((struct ncr5380_softc *));
+
        int             sc_flags;       /* Misc. flags and capabilities */
 #define        NCR5380_PERMIT_RESELECT         1  /* Allow disconnect/reselect */
+#define        NCR5380_FORCE_POLLING           2  /* Do not use interrupts. */
 
        int     sc_min_dma_len; /* Smaller than this is done with PIO */
 
        /* Begin MI shared data */
 
-       /* Active data pointer for current SCSI process */
+       int             sc_state;
+#define        NCR_IDLE                   0    /* Ready for new work. */
+#define NCR_WORKING    0x01    /* Some command is in progress. */
+#define        NCR_ABORTING    0x02    /* Bailing out */
+#define NCR_DOINGDMA   0x04    /* The FIFO data path is active! */
+#define NCR_DROP_MSGIN 0x10    /* Discard all msgs (parity err detected) */
+
+       /* The request that has the bus now. */
+       struct          sci_req *sc_current;
+
+       /* Active data pointer for current SCSI command. */
        u_char          *sc_dataptr;
        int             sc_datalen;
 
-       void            *sc_dma_hand;   /* DMA handle */
-       u_int           sc_dma_flags;
-#define        DMA5380_INPROGRESS              1       /* MD: DMA is curently in progress */
-#define        DMA5380_WRITE                   2       /* MI: DMA is to output to SCSI */
-#define        DMA5380_POLL                    4       /* MI: Poll for DMA completion */
-#define        DMA5380_ERROR                   8       /* MD: DMA operation failed */
-#define        DMA5380_PHYS                    16      /* MI: Buffer has B_PHYS set */
-
        /* Begin MI private data */
 
-       /* The request that has the bus now. */
-       struct          sci_req *sc_current;
-
        /* The number of operations in progress on the bus */
        volatile int    sc_ncmds;
 
@@ -131,10 +142,7 @@ struct ncr5380_softc {
 
        /* Message stuff */
        int     sc_prevphase;
-       int     sc_msg_flags;
-#define NCR_DROP_MSGIN 1
-#define NCR_ABORTING   2
-#define NCR_NEED_RESET 4
+
        u_int   sc_msgpriq;     /* Messages we want to send */
        u_int   sc_msgoutq;     /* Messages sent during last MESSAGE OUT */
        u_int   sc_msgout;      /* Message last transmitted */
@@ -156,8 +164,15 @@ struct ncr5380_softc {
 
 void   ncr5380_init __P((struct ncr5380_softc *));
 void   ncr5380_reset_scsibus __P((struct ncr5380_softc *));
-int    ncr5380_sbc_intr __P((struct ncr5380_softc *));
+int    ncr5380_intr __P((struct ncr5380_softc *));
 int    ncr5380_scsi_cmd __P((struct scsi_xfer *));
 int    ncr5380_pio_in __P((struct ncr5380_softc *, int, int, u_char *));
 int    ncr5380_pio_out __P((struct ncr5380_softc *, int, int, u_char *));
 
+#ifdef DEBUG
+struct ncr5380_softc *ncr5380_debug_sc;
+void ncr5380_trace __P((char *msg, long val));
+#define        NCR_TRACE(msg, val) ncr5380_trace(msg, val)
+#else
+#define        NCR_TRACE(msg, val)     /* nada */
+#endif
index 951e373..07119e0 100644 (file)
@@ -1,8 +1,8 @@
-/*     $NetBSD: ncr_si.c,v 1.1 1995/10/29 21:19:11 gwr Exp $   */
+/*     $NetBSD: ncr_si.c,v 1.1.2.1 1995/11/18 07:08:48 gwr Exp $       */
 
 /*
- * Copyright (c) 1995 David Jones
- * Copyright (c) 1994 Adam Glass, Gordon W. Ross
+ * Copyright (c) 1995 David Jones, Gordon W. Ross
+ * Copyright (c) 1994 Adam Glass
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -18,7 +18,7 @@
  * 4. All advertising materials mentioning features or use of this software
  *    must display the following acknowledgement:
  *      This product includes software developed by
- *      Adam Glass, David Jones and Gordon Ross
+ *      Adam Glass, David Jones, and Gordon Ross
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * This file contains only the machine-dependent parts of the
  * Sun3 SCSI driver.  (Autoconfig stuff and DMA functions.)
  * The machine-independent parts are in ncr5380sbc.c
+ *
+ * Supported hardware includes:
+ * Sun SCSI-3 on OBIO (Sun3/50,Sun3/60)
+ * Sun SCSI-3 on VME (Sun3/160,Sun3/260)
+ *
+ * Could be made to support the Sun3/E if someone wanted to.
+ *
+ * Note:  Both supported variants of the Sun SCSI-3 adapter have
+ * some really unusual "features" for this driver to deal with,
+ * generally related to the DMA engine.  The OBIO variant will
+ * ignore any attempt to write the FIFO count register while the
+ * SCSI bus is in DATA_IN or DATA_OUT phase.  This is dealt with
+ * by setting the FIFO count early in COMMAND or MSG_IN phase.
+ *
+ * The VME variant has a bit to enable or disable the DMA engine,
+ * but that bit also gates the interrupt line from the NCR5380!
+ * Therefore, in order to get any interrupt from the 5380, (i.e.
+ * for reselect) one must clear the DMA engine transfer count and
+ * then enable DMA.  This has the further complication that you
+ * CAN NOT touch the NCR5380 while the DMA enable bit is set, so
+ * we have to turn DMA back off before we even look at the 5380.
+ *
+ * What wonderfully whacky hardware this is!
+ *
+ * Credits, history:
+ *
+ * David Jones wrote the initial version of this module, which
+ * included support for the VME adapter only. (no reselection).
+ *
+ * Gordon Ross added support for the OBIO adapter, and re-worked
+ * both the VME and OBIO code to support disconnect/reselect.
+ * (Required figuring out the hardware "features" noted above.)
+ *
+ * The autoconfiguration boilerplate came from Adam Glass.
  */
 
 #include <sys/types.h>
-#include <sys/malloc.h>
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/kernel.h>
 #include <sys/errno.h>
+#include <sys/device.h>
 #include <sys/buf.h>
 #include <sys/proc.h>
 #include <sys/user.h>
-#include <sys/device.h>
 
 #include <scsi/scsi_all.h>
 #include <scsi/scsi_debug.h>
@@ -57,6 +91,8 @@
 #include <machine/obio.h>
 #include <machine/dvma.h>
 
+#define DEBUG XXX
+
 #if 0  /* XXX - not yet... */
 #include <dev/ic/ncr5380reg.h>
 #include <dev/ic/ncr5380var.h>
 #define        MIN_DMA_LEN 128
 
 /*
- * Transfers lager than 63K need to be broken up
- * because some of the device counters are 16 bits.
+ * Transfers lager than 65535 bytes need to be split-up.
+ * (Some of the FIFO logic has only 16 bits counters.)
+ * Make the size an integer multiple of the page size
+ * to avoid buf/cluster remap problems.  (paranoid?)
  */
-#define        MAX_DMA_LEN 0xFC00
+#define        MAX_DMA_LEN 0xE000
 
 /*
- * How many uS. to delay after touching the am9616 UDC.
+ * How many uS. to delay after touching the am9516 UDC.
  */
 #define UDC_WAIT_USEC 5
 
-#define DEBUG XXX
-
 #ifdef DEBUG
 int si_debug = 0;
-static int si_flags = 0 /* | SDEV_DB2 */ ;
-static int Seq = 0;
-static int Nwrite = 0;
-struct si_softc *TheSoftC;
-volatile struct si_regs *TheRegs;
-void log_intr(void);
-void log_start(void);
-void si_printregs(void);
+static int si_link_flags = 0 /* | SDEV_DB2 */ ;
 #endif
 
 /*
@@ -105,12 +134,12 @@ void si_printregs(void);
  * the array of these has to be in DVMA space.
  */
 struct si_dma_handle {
-       u_long          dh_addr;        /* KVA of start of buffer */
-       int             dh_len;         /* Original data length */
-       u_long          dh_dvma;        /* VA of buffer in DVMA space */
        int             dh_flags;
 #define        SIDH_BUSY       1               /* This DH is in use */
 #define        SIDH_OUT        2               /* DMA does data out (write) */
+       u_char *        dh_addr;        /* KVA of start of buffer */
+       int             dh_maplen;      /* Length of KVA mapping. */
+       long            dh_dvma;        /* VA of buffer in DVMA space */
        /* DMA command block for the OBIO controller. */
        struct udc_table dh_cmd;
 };
@@ -120,22 +149,19 @@ struct si_dma_handle {
  * so we can just cast to go back and fourth between them.
  */
 struct si_softc {
-       struct ncr5380_softc    ncr;
+       struct ncr5380_softc    ncr_sc;
        volatile struct si_regs *sc_regs;
        int             sc_adapter_type;
        int             sc_adapter_iv_am; /* int. vec + address modifier */
-       int             sc_timo;
        struct si_dma_handle *sc_dma;
+       int     sc_xlen;                /* length of current DMA segment. */
 };
 
-/*
- * XXX: Note that reselect CAN NOT WORK given the current need
- * to set the damn FIFO count logic during dma_alloc, because
- * the DMA might be need by another target in the mean time.
- * (It works when there is only one target/lun though...)
- */
-int si_permit_reselect = 0;    /* XXX: Do not set this yet. */
-int si_polled_dma = 0; /* Set if interrupts don't work */
+/* Options.  Interesting values are: 1,3,7 */
+int si_options = 0;
+#define SI_ENABLE_DMA  1       /* Use DMA (maybe polled) */
+#define SI_DMA_INTR    2       /* DMA completion interrupts */
+#define        SI_DO_RESELECT  4       /* Allow disconnect/reselect */
 
 /* How long to wait for DMA before declaring an error. */
 int si_dma_intr_timo = 500;    /* ticks (sec. X 100) */
@@ -151,10 +177,15 @@ void si_dma_alloc __P((struct ncr5380_softc *));
 void si_dma_free __P((struct ncr5380_softc *));
 void si_dma_poll __P((struct ncr5380_softc *));
 
+void si_vme_dma_setup __P((struct ncr5380_softc *));
 void si_vme_dma_start __P((struct ncr5380_softc *));
 void si_vme_dma_eop __P((struct ncr5380_softc *));
 void si_vme_dma_stop __P((struct ncr5380_softc *));
 
+void si_vme_intr_on  __P((struct ncr5380_softc *));
+void si_vme_intr_off __P((struct ncr5380_softc *));
+
+void si_obio_dma_setup __P((struct ncr5380_softc *));
 void si_obio_dma_start __P((struct ncr5380_softc *));
 void si_obio_dma_eop __P((struct ncr5380_softc *));
 void si_obio_dma_stop __P((struct ncr5380_softc *));
@@ -178,8 +209,8 @@ static struct scsi_device si_dev = {
 
 
 struct cfdriver ncr_sicd = {
-       NULL, si_name, si_match, si_attach, DV_DULL,
-       sizeof(struct si_softc), NULL, 0,
+       NULL, si_name, si_match, si_attach,
+       DV_DULL, sizeof(struct si_softc), NULL, 0,
 };
 
 static int
@@ -250,6 +281,7 @@ si_attach(parent, self, args)
        void            *args;
 {
        struct si_softc *sc = (struct si_softc *) self;
+       struct ncr5380_softc *ncr_sc = (struct ncr5380_softc *)sc;
        volatile struct si_regs *regs;
        struct confargs *ca = args;
        int i;
@@ -259,17 +291,11 @@ si_attach(parent, self, args)
        case BUS_OBIO:
                regs = (struct si_regs *)
                        obio_alloc(ca->ca_paddr, sizeof(*regs));
-               isr_add_autovect(si_intr, (void *)sc,
-                                                ca->ca_intpri);
                break;
 
        case BUS_VME16:
                regs = (struct si_regs *)
                        bus_mapin(ca->ca_bustype, ca->ca_paddr, sizeof(*regs));
-               isr_add_vectored(si_intr, (void *)sc,
-                                                ca->ca_intpri, ca->ca_intvec);
-               sc->sc_adapter_iv_am =
-                       VME_SUPV_DATA_24 | (ca->ca_intvec & 0xFF);
                break;
 
        default:
@@ -281,51 +307,62 @@ si_attach(parent, self, args)
        /*
         * Fill in the prototype scsi_link.
         */
-       sc->ncr.sc_link.adapter_softc = sc;
-       sc->ncr.sc_link.adapter_target = 7;
-       sc->ncr.sc_link.adapter = &si_ops;
-       sc->ncr.sc_link.device = &si_dev;
+       ncr_sc->sc_link.adapter_softc = sc;
+       ncr_sc->sc_link.adapter_target = 7;
+       ncr_sc->sc_link.adapter = &si_ops;
+       ncr_sc->sc_link.device = &si_dev;
 
        /*
         * Initialize fields used by the MI code
         */
-       sc->ncr.sci_data = &regs->sci.sci_data;
-       sc->ncr.sci_icmd = &regs->sci.sci_icmd;
-       sc->ncr.sci_mode = &regs->sci.sci_mode;
-       sc->ncr.sci_tcmd = &regs->sci.sci_tcmd;
-       sc->ncr.sci_bus_csr = &regs->sci.sci_bus_csr;
-       sc->ncr.sci_csr = &regs->sci.sci_csr;
-       sc->ncr.sci_idata = &regs->sci.sci_idata;
-       sc->ncr.sci_iack = &regs->sci.sci_iack;
+       ncr_sc->sci_r0 = &regs->sci.sci_r0;
+       ncr_sc->sci_r1 = &regs->sci.sci_r1;
+       ncr_sc->sci_r2 = &regs->sci.sci_r2;
+       ncr_sc->sci_r3 = &regs->sci.sci_r3;
+       ncr_sc->sci_r4 = &regs->sci.sci_r4;
+       ncr_sc->sci_r5 = &regs->sci.sci_r5;
+       ncr_sc->sci_r6 = &regs->sci.sci_r6;
+       ncr_sc->sci_r7 = &regs->sci.sci_r7;
 
        /*
         * MD function pointers used by the MI code.
         */
-       sc->ncr.sc_pio_out = ncr5380_pio_out;
-       sc->ncr.sc_pio_in =  ncr5380_pio_in;
-       sc->ncr.sc_dma_alloc = si_dma_alloc;
-       sc->ncr.sc_dma_free  = si_dma_free;
-       sc->ncr.sc_dma_poll  = si_dma_poll;
+       ncr_sc->sc_pio_out = ncr5380_pio_out;
+       ncr_sc->sc_pio_in =  ncr5380_pio_in;
+       ncr_sc->sc_dma_alloc = si_dma_alloc;
+       ncr_sc->sc_dma_free  = si_dma_free;
+       ncr_sc->sc_dma_poll  = si_dma_poll;
+       ncr_sc->sc_intr_on   = NULL;
+       ncr_sc->sc_intr_off  = NULL;
        if (ca->ca_bustype == BUS_VME16) {
-               sc->ncr.sc_dma_start = si_vme_dma_start;
-               sc->ncr.sc_dma_eop   = si_vme_dma_stop;
-               sc->ncr.sc_dma_stop  = si_vme_dma_stop;
+               ncr_sc->sc_dma_setup = si_vme_dma_setup;
+               ncr_sc->sc_dma_start = si_vme_dma_start;
+               ncr_sc->sc_dma_eop   = si_vme_dma_stop;
+               ncr_sc->sc_dma_stop  = si_vme_dma_stop;
+               if (si_options & SI_DO_RESELECT) {
+                       /*
+                        * Need to enable interrupts (and DMA!)
+                        * on this H/W for reselect to work.
+                        */
+                       ncr_sc->sc_intr_on   = si_vme_intr_on;
+                       ncr_sc->sc_intr_off  = si_vme_intr_off;
+               }
        } else {
-               sc->ncr.sc_dma_start = si_obio_dma_start;
-               sc->ncr.sc_dma_eop   = si_obio_dma_stop;
-               sc->ncr.sc_dma_stop  = si_obio_dma_stop;
+               ncr_sc->sc_dma_setup = si_obio_dma_setup;
+               ncr_sc->sc_dma_start = si_obio_dma_start;
+               ncr_sc->sc_dma_eop   = si_obio_dma_stop;
+               ncr_sc->sc_dma_stop  = si_obio_dma_stop;
        }
-       sc->ncr.sc_flags = (si_permit_reselect) ?
-               NCR5380_PERMIT_RESELECT : 0;
-       sc->ncr.sc_min_dma_len = MIN_DMA_LEN;
+       ncr_sc->sc_flags = 0;
+       if (si_options & SI_DO_RESELECT)
+               ncr_sc->sc_flags |= NCR5380_PERMIT_RESELECT;
+       if ((si_options & SI_DMA_INTR) == 0)
+               ncr_sc->sc_flags |= NCR5380_FORCE_POLLING;
+       ncr_sc->sc_min_dma_len = MIN_DMA_LEN;
 
        /*
         * Initialize fields used only here in the MD code.
         */
-       sc->sc_regs = regs;
-       sc->sc_adapter_type = ca->ca_bustype;
-       /*  sc_adapter_iv_am = (was set above) */
-       sc->sc_timo = 0;        /* no timeout armed. */
 
        /* Need DVMA-capable memory for the UDC command blocks. */
        i = SCI_OPENINGS * sizeof(struct si_dma_handle);
@@ -335,51 +372,86 @@ si_attach(parent, self, args)
        for (i = 0; i < SCI_OPENINGS; i++)
                sc->sc_dma[i].dh_flags = 0;
 
+       sc->sc_regs = regs;
+       sc->sc_adapter_type = ca->ca_bustype;
+
+       /* Now ready for interrupts. */
+       if (ca->ca_bustype == BUS_OBIO) {
+               isr_add_autovect(si_intr, (void *)sc,
+                                ca->ca_intpri);
+       } else {
+               isr_add_vectored(si_intr, (void *)sc,
+                                ca->ca_intpri, ca->ca_intvec);
+               sc->sc_adapter_iv_am =
+                       VME_SUPV_DATA_24 | (ca->ca_intvec & 0xFF);
+       }
+
 #ifdef DEBUG
        if (si_debug)
-               printf("Set TheSoftC=%x TheRegs=%x\n", sc, regs);
-       TheSoftC = sc;
-       TheRegs = regs;
-       sc->ncr.sc_link.flags |= si_flags;
+               printf("si: Set TheSoftC=%x TheRegs=%x\n", sc, regs);
+       ncr_sc->sc_link.flags |= si_link_flags;
 #endif
 
        /*
         *  Initialize si board itself.
         */
-       si_reset_adapter(&sc->ncr);
-       ncr5380_init(&sc->ncr);
-       ncr5380_reset_scsibus(&sc->ncr);
-       config_found(self, &(sc->ncr.sc_link), si_print);
+       si_reset_adapter(ncr_sc);
+       ncr5380_init(ncr_sc);
+       ncr5380_reset_scsibus(ncr_sc);
+       config_found(self, &(ncr_sc->sc_link), si_print);
 }
 
 static void
 si_minphys(struct buf *bp)
 {
        if (bp->b_bcount > MAX_DMA_LEN) {
-               printf("si_minphys len = %x.\n", MAX_DMA_LEN);
+#ifdef DEBUG
+               if (si_debug) {
+                       printf("si_minphys len = 0x%x.\n", bp->b_bcount);
+                       Debugger();
+               }
+#endif
                bp->b_bcount = MAX_DMA_LEN;
        }
        return (minphys(bp));
 }
 
 
+#define CSR_WANT (SI_CSR_SBC_IP | SI_CSR_DMA_IP | \
+       SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR )
+
 static int
 si_intr(void *arg)
 {
        struct si_softc *sc = arg;
        volatile struct si_regs *si = sc->sc_regs;
-       int claimed, rv = 0;
+       int dma_error, claimed;
+       u_short csr;
+
+       claimed = 0;
+       dma_error = 0;
+
+       /* SBC interrupt? DMA interrupt? */
+       csr = si->si_csr;
+       NCR_TRACE("si_intr: csr=0x%x\n", csr);
 
-       /* DMA interrupt? */
-       if (si->si_csr & SI_CSR_DMA_IP) {
-               rv |= SI_CSR_DMA_IP;
-               (*sc->ncr.sc_dma_stop)(&sc->ncr);
+       if (csr & SI_CSR_DMA_CONFLICT) {
+               dma_error |= SI_CSR_DMA_CONFLICT;
+               printf("si_intr: DMA conflict\n");
+       }
+       if (csr & SI_CSR_DMA_BUS_ERR) {
+               dma_error |= SI_CSR_DMA_BUS_ERR;
+               printf("si_intr: DMA bus error\n");
+       }
+       if (dma_error) {
+               if (sc->ncr_sc.sc_state & NCR_DOINGDMA)
+                       sc->ncr_sc.sc_state |= NCR_ABORTING;
+               /* Make sure we will call the main isr. */
+               csr |= SI_CSR_DMA_IP;
        }
 
-       /* SBC interrupt? */
-       if (si->si_csr & SI_CSR_SBC_IP) {
-               rv |= SI_CSR_SBC_IP;
-               claimed = ncr5380_sbc_intr(&sc->ncr);
+       if (csr & (SI_CSR_SBC_IP | SI_CSR_DMA_IP)) {
+               claimed = ncr5380_intr(&sc->ncr_sc);
 #ifdef DEBUG
                if (!claimed) {
                        printf("si_intr: spurious from SBC\n");
@@ -390,37 +462,14 @@ si_intr(void *arg)
 #endif
        }
 
-       return (rv);
-}
-
-
-static void
-si_dma_timeout(arg)
-       void *arg;
-{
-       struct si_softc *sc = arg;
-       int s;
-
-       s = splbio();
-
-       sc->sc_timo = 0;
-
-       /* Timeout during DMA transfer? */
-       if (sc->ncr.sc_dma_flags & DMA5380_INPROGRESS) {
-               sc->ncr.sc_dma_flags |= DMA5380_ERROR;
-               printf("si: DMA timeout (resetting)\n");
-               si_reset_adapter(&sc->ncr);
-               ncr5380_sbc_intr(&sc->ncr);
-       }
-
-       splx(s);
+       return (claimed);
 }
 
 
 static void
-si_reset_adapter(struct ncr5380_softc *ncr)
+si_reset_adapter(struct ncr5380_softc *ncr_sc)
 {
-       struct si_softc *sc = (struct si_softc *)ncr;
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
        volatile struct si_regs *si = sc->sc_regs;
 
 #ifdef DEBUG
@@ -450,7 +499,7 @@ si_reset_adapter(struct ncr5380_softc *ncr)
                si->fifo_cnt_hi = 0;
        }
 
-       SCI_CLR_INTR(ncr);
+       SCI_CLR_INTR(ncr_sc);
 }
 
 
@@ -464,28 +513,34 @@ si_reset_adapter(struct ncr5380_softc *ncr)
  * into DVMA space.  dvma_mapin() flushes the cache for us.
  */
 void
-si_dma_alloc(ncr)
-       struct ncr5380_softc *ncr;
+si_dma_alloc(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
 {
-       struct si_softc *sc = (struct si_softc *)ncr;
-       volatile struct si_regs *si = sc->sc_regs;
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       struct sci_req *sr = ncr_sc->sc_current;
+       struct scsi_xfer *xs = sr->sr_xs;
        struct si_dma_handle *dh;
        int i, xlen;
        u_long addr;
 
-#if 1
-       /* XXX - In case we don't trust interrupts... */
-       if (si_polled_dma)
-               sc->ncr.sc_dma_flags |= DMA5380_POLL;
+#ifdef DIAGNOSTIC
+       if (sr->sr_dma_hand != NULL)
+               panic("si_dma_alloc: already have DMA handle");
+#endif
+
+#if 1  /* XXX - Temporary */
+       /* XXX - In case we think DMA is completely broken... */
+       if ((si_options & SI_ENABLE_DMA) == 0)
+               return;
 #endif
 
-       addr = (u_long) sc->ncr.sc_dataptr;
-       xlen = sc->ncr.sc_datalen;
+       addr = (u_long) ncr_sc->sc_dataptr;
+       xlen = ncr_sc->sc_datalen;
 
        /* If the DMA start addr is misaligned then do PIO */
        if ((addr & 1) || (xlen & 1)) {
                printf("si_dma_alloc: misaligned.\n");
-               goto no_dma;
+               return;
        }
 
        /* Make sure our caller checked sc_min_dma_len. */
@@ -496,11 +551,12 @@ si_dma_alloc(ncr)
         * Never attempt single transfers of more than 63k, because
         * our count register may be only 16 bits (an OBIO adapter).
         * This should never happen since already bounded by minphys().
+        * XXX - Should just segment these...
         */
        if (xlen > MAX_DMA_LEN) {
                printf("si_dma_alloc: excessive xlen=0x%x\n", xlen);
                Debugger();
-               xlen = MAX_DMA_LEN;
+               ncr_sc->sc_datalen = xlen = MAX_DMA_LEN;
        }
 
        /* Find free DMA handle.  Guaranteed to find one since we have
@@ -513,97 +569,87 @@ si_dma_alloc(ncr)
 found:
 
        dh = &sc->sc_dma[i];
-       sc->ncr.sc_dma_hand = dh;
-       dh->dh_addr = addr;
-       dh->dh_len = xlen;
        dh->dh_flags = SIDH_BUSY;
+       dh->dh_addr = (u_char*) addr;
+       dh->dh_maplen  = xlen;
+       dh->dh_dvma = 0;
 
        /* Copy the "write" flag for convenience. */
-       if (sc->ncr.sc_dma_flags & DMA5380_WRITE)
+       if (xs->flags & SCSI_DATA_OUT)
                dh->dh_flags |= SIDH_OUT;
 
+#if 0
        /*
-        * We don't care about (sc_dma_flags & DMA5380_PHYS)
-        * because we always have to dup mappings anyway.
+        * Some machines might not need to remap B_PHYS buffers.
+        * The sun3 does not map B_PHYS buffers into DVMA space,
+        * (they are mapped into normal KV space) so on the sun3
+        * we must always remap to a DVMA address here. Re-map is
+        * cheap anyway, because it's done by segments, not pages.
         */
+       if (xs->bp && (xs->bp->b_flags & B_PHYS))
+               dh->dh_flags |= SIDH_PHYS;
+#endif
+
        dh->dh_dvma = (u_long) dvma_mapin((char *)addr, xlen);
        if (!dh->dh_dvma) {
                /* Can't remap segment */
                printf("si_dma_alloc: can't remap %x/%x\n",
-                       dh->dh_addr, dh->dh_len);
+                       dh->dh_addr, dh->dh_maplen);
                dh->dh_flags = 0;
-               goto no_dma;
-       }
-
-       /*
-        * Note:  We have to initialize the FIFO logic NOW,
-        * (just after selection, before talking on the bus)
-        * because after this point, (much to my surprise)
-        * writes to the fifo_count register ARE IGNORED!!!
-        */
-       si->fifo_count = 0;             /* also hits dma_count */
-       if (dh->dh_flags & SIDH_OUT) {
-               si->si_csr |= SI_CSR_SEND;
-       } else {
-               si->si_csr &= ~SI_CSR_SEND;
-       }
-       si->si_csr &= ~SI_CSR_FIFO_RES;         /* active low */
-       delay(10);
-       si->si_csr |= SI_CSR_FIFO_RES;
-       delay(10);
-       si->fifo_count = xlen;
-       if (sc->sc_adapter_type == BUS_VME16)
-               si->fifo_cnt_hi = 0;
-
-#ifdef DEBUG
-       if ((si->fifo_count > xlen) || (si->fifo_count < (xlen - 1))) {
-               printf("si_dma_alloc: fifo_count=0x%x, xlen=0x%x\n",
-                          si->fifo_count, xlen);
-               Debugger();
+               return;
        }
-#endif
 
-       return; /* success */
+       /* success */
+       sr->sr_dma_hand = dh;
 
-no_dma:
-       sc->ncr.sc_dma_hand = NULL;
+       return;
 }
 
 
 void
-si_dma_free(ncr)
-       struct ncr5380_softc *ncr;
+si_dma_free(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
 {
-       struct si_dma_handle *dh = ncr->sc_dma_hand;
+       struct sci_req *sr = ncr_sc->sc_current;
+       struct si_dma_handle *dh = sr->sr_dma_hand;
+
+#ifdef DIAGNOSTIC
+       if (dh == NULL)
+               panic("si_dma_free: no DMA handle");
+#endif
 
-       if (ncr->sc_dma_flags & DMA5380_INPROGRESS)
+       if (ncr_sc->sc_state & NCR_DOINGDMA)
                panic("si_dma_free: free while in progress");
 
        if (dh->dh_flags & SIDH_BUSY) {
                /* XXX - Should separate allocation and mapping. */
                /* Give back the DVMA space. */
-               dvma_mapout((caddr_t)dh->dh_dvma, dh->dh_len);
+               dvma_mapout((caddr_t)dh->dh_dvma, dh->dh_maplen);
                dh->dh_dvma = 0;
                dh->dh_flags = 0;
        }
+       sr->sr_dma_hand = NULL;
 }
 
 
 /*
  * Poll (spin-wait) for DMA completion.
+ * Called right after xx_dma_start(), and
+ * xx_dma_stop() will be called next.
  * Same for either VME or OBIO.
  */
 void
-si_dma_poll(ncr)
-       struct ncr5380_softc *ncr;
+si_dma_poll(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
 {
-       struct si_softc *sc = (struct si_softc *)ncr;
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       struct sci_req *sr = ncr_sc->sc_current;
+       struct si_dma_handle *dh = sr->sr_dma_hand;
        volatile struct si_regs *si = sc->sc_regs;
        int tmo, csr_mask;
 
-       /* Make sure the DMA actually started... */
-       /* XXX - Check DMA5380_INPROGRESS instead? */
-       if (sc->ncr.sc_dma_flags & DMA5380_ERROR)
+       /* Make sure DMA started successfully. */
+       if (ncr_sc->sc_state & NCR_ABORTING)
                return;
 
        csr_mask = SI_CSR_SBC_IP | SI_CSR_DMA_IP |
@@ -614,9 +660,9 @@ si_dma_poll(ncr)
                if (si->si_csr & csr_mask)
                        break;
                if (--tmo <= 0) {
-                       printf("si: DMA timeout\n");
-                       sc->ncr.sc_dma_flags |= DMA5380_ERROR;
-                       si_reset_adapter(&sc->ncr);
+                       printf("si: DMA timeout (while polling)\n");
+                       /* Indicate timeout as MI code would. */
+                       sr->sr_flags |= SR_OVERDUE;
                        break;
                }
                delay(100);
@@ -635,97 +681,182 @@ si_dma_poll(ncr)
  ****************************************************************/
 
 
+/*
+ * This is called when the bus is going idle,
+ * so we want to enable the SBC interrupts.
+ * That is controlled by the DMA enable!
+ * Who would have guessed!
+ * What a NASTY trick!
+ */
+void
+si_vme_intr_on(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
+{
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       volatile struct si_regs *si = sc->sc_regs;
+
+       si_vme_dma_setup(ncr_sc);
+       si->si_csr |= SI_CSR_DMA_EN;
+}
+
+/*
+ * This is called when the bus is idle and we are
+ * about to start playing with the SBC chip.
+ */
+void
+si_vme_intr_off(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
+{
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       volatile struct si_regs *si = sc->sc_regs;
+
+       si->si_csr &= ~SI_CSR_DMA_EN;
+}
+
+/*
+ * This function is called during the COMMAND or MSG_IN phase
+ * that preceeds a DATA_IN or DATA_OUT phase, in case we need
+ * to setup the DMA engine before the bus enters a DATA phase.
+ *
+ * XXX: The VME adapter appears to suppress SBC interrupts
+ * when the FIFO is not empty or the FIFO count is non-zero!
+ *
+ * On the VME version we just clear the DMA count and address
+ * here (to make sure it stays idle) and do the real setup
+ * later, in dma_start.
+ */
+void
+si_vme_dma_setup(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
+{
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       volatile struct si_regs *si = sc->sc_regs;
+
+       /* Reset the FIFO */
+       si->si_csr &= ~SI_CSR_FIFO_RES;         /* active low */
+       si->si_csr |= SI_CSR_FIFO_RES;
+
+       /* Set direction (assume recv here) */
+       si->si_csr &= ~SI_CSR_SEND;
+       /* Assume worst alignment */
+       si->si_csr |= SI_CSR_BPCON;
+
+       si->dma_addrh = 0;
+       si->dma_addrl = 0;
+
+       si->dma_counth = 0;
+       si->dma_countl = 0;
+
+       /* Clear FIFO counter. (also hits dma_count) */
+       si->fifo_cnt_hi = 0;
+       si->fifo_count = 0;             
+}
+
+
 void
-si_vme_dma_start(ncr)
-       struct ncr5380_softc *ncr;
+si_vme_dma_start(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
 {
-       struct si_softc *sc = (struct si_softc *)ncr;
-       struct si_dma_handle *dh = sc->ncr.sc_dma_hand;
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       struct sci_req *sr = ncr_sc->sc_current;
+       struct si_dma_handle *dh = sr->sr_dma_hand;
        volatile struct si_regs *si = sc->sc_regs;
-       u_long data_pa;
+       long data_pa;
+       int xlen;
 
        /*
         * Get the DVMA mapping for this segment.
         * XXX - Should separate allocation and mapin.
         */
-       data_pa = dvma_kvtopa((long)dh->dh_dvma, sc->sc_adapter_type) +
-               ((u_long)sc->ncr.sc_dataptr - dh->dh_addr);
+       data_pa = dvma_kvtopa(dh->dh_dvma, sc->sc_adapter_type);
+       data_pa += (ncr_sc->sc_dataptr - dh->dh_addr);
        if (data_pa & 1)
                panic("si_dma_start: bad pa=0x%x", data_pa);
+       xlen = ncr_sc->sc_datalen;
+       xlen &= ~1;
+       sc->sc_xlen = xlen;     /* XXX: or less... */
 
 #ifdef DEBUG
        if (si_debug & 2) {
                printf("si_dma_start: dh=0x%x, pa=0x%x, xlen=%d\n",
-                          dh, data_pa, dh->dh_len);
-       }
-#endif
-
-       /* Already setup FIFO in si_dma_alloc() */
-
-#ifdef DEBUG
-       if ((si->fifo_count > dh->dh_len) ||
-               (si->fifo_count < (dh->dh_len - 1)))
-       {
-               printf("si_dma_start: fifo_count=0x%x, xlen=0x%x\n",
-                          si->fifo_count, dh->dh_len);
-               Debugger();
+                          dh, data_pa, xlen);
        }
 #endif
 
        /*
         * Set up the DMA controller.
-        * Note that (dh-dh_len < sc_datalen)
         */
+       si->si_csr &= ~SI_CSR_FIFO_RES;         /* active low */
+       si->si_csr |= SI_CSR_FIFO_RES;
+
+       /* Set direction (send/recv) */
+       if (dh->dh_flags & SIDH_OUT) {
+               si->si_csr |= SI_CSR_SEND;
+       } else {
+               si->si_csr &= ~SI_CSR_SEND;
+       }
+
        if (data_pa & 2) {
                si->si_csr |= SI_CSR_BPCON;
        } else {
                si->si_csr &= ~SI_CSR_BPCON;
        }
-       si->dma_addrh = data_pa >> 16;
-       si->dma_addrl = data_pa & 0xFFFF;
-       si->dma_counth = dh->dh_len >> 16;
-       si->dma_countl = dh->dh_len & 0xFFFF;
 
-#if 0  /* XXX: Whack the FIFO again? */
-       si->si_csr &= ~SI_CSR_FIFO_RES;
-       delay(10);
-       si->si_csr |= SI_CSR_FIFO_RES;
-       delay(10);
+       si->dma_addrh = (ushort)(data_pa >> 16);
+       si->dma_addrl = (ushort)(data_pa & 0xFFFF);
+
+       si->dma_counth = (ushort)(xlen >> 16);
+       si->dma_countl = (ushort)(xlen & 0xFFFF);
+
+#if 1
+       /* Set it anyway, even though dma_count hits it? */
+       si->fifo_cnt_hi = (ushort)(xlen >> 16);
+       si->fifo_count  = (ushort)(xlen & 0xFFFF);
+#endif
+
+#ifdef DEBUG
+       if (si->fifo_count != xlen) {
+               printf("si_dma_start: fifo_count=0x%x, xlen=0x%x\n",
+                          si->fifo_count, xlen);
+               Debugger();
+       }
 #endif
 
        /*
-        * Put the SBIC into DMA mode and start the transfer.
+        * Acknowledge the phase change.  (After DMA setup!)
+        * Put the SBIC into DMA mode, and start the transfer.
         */
-       *sc->ncr.sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
        if (dh->dh_flags & SIDH_OUT) {
-               *sc->ncr.sci_icmd = SCI_ICMD_DATA;
-               *sc->ncr.sci_dma_send = 0;      /* start it */
+               *ncr_sc->sci_tcmd = PHASE_DATA_OUT;
+               SCI_CLR_INTR(ncr_sc);
+               *ncr_sc->sci_icmd = SCI_ICMD_DATA;
+               *ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
+               *ncr_sc->sci_dma_send = 0;      /* start it */
        } else {
-               *sc->ncr.sci_icmd = 0;
-               *sc->ncr.sci_irecv = 0; /* start it */
+               *ncr_sc->sci_tcmd = PHASE_DATA_IN;
+               SCI_CLR_INTR(ncr_sc);
+               *ncr_sc->sci_icmd = 0;
+               *ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
+               *ncr_sc->sci_irecv = 0; /* start it */
        }
-       si->si_csr |= SI_CSR_DMA_EN;    /* vme only */
 
-       sc->ncr.sc_dma_flags |= DMA5380_INPROGRESS;
+       /* Let'er rip! */
+       si->si_csr |= SI_CSR_DMA_EN;
 
-       if ((sc->ncr.sc_dma_flags & DMA5380_POLL) == 0) {
-               /* Expect an interrupt when DMA completes. */
-               sc->sc_timo = si_dma_intr_timo;
-               timeout(si_dma_timeout, sc, sc->sc_timo);
-       }
+       ncr_sc->sc_state |= NCR_DOINGDMA;
 
 #ifdef DEBUG
        if (si_debug & 2) {
                printf("si_dma_start: started, flags=0x%x\n",
-                          sc->ncr.sc_dma_flags);
+                          ncr_sc->sc_state);
        }
 #endif
 }
 
 
 void
-si_vme_dma_eop(ncr)
-       struct ncr5380_softc *ncr;
+si_vme_dma_eop(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
 {
 
        /* Not needed - DMA was stopped prior to examining sci_csr */
@@ -733,69 +864,71 @@ si_vme_dma_eop(ncr)
 
 
 void
-si_vme_dma_stop(ncr)
-       struct ncr5380_softc *ncr;
+si_vme_dma_stop(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
 {
-       struct si_softc *sc = (struct si_softc *)ncr;
-       struct si_dma_handle *dh = sc->ncr.sc_dma_hand;
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       struct sci_req *sr = ncr_sc->sc_current;
+       struct si_dma_handle *dh = sr->sr_dma_hand;
        volatile struct si_regs *si = sc->sc_regs;
-       int resid, ntrans, si_csr;
+       int resid, ntrans;
 
-       if ((sc->ncr.sc_dma_flags & DMA5380_INPROGRESS) == 0) {
+       if ((ncr_sc->sc_state & NCR_DOINGDMA) == 0) {
 #ifdef DEBUG
                printf("si_dma_stop: dma not running\n");
 #endif
                return;
        }
-       sc->ncr.sc_dma_flags &= ~DMA5380_INPROGRESS;
-       if (sc->sc_timo) {
-               sc->sc_timo = 0;
-               untimeout(si_dma_timeout, sc);
-       }
+       ncr_sc->sc_state &= ~NCR_DOINGDMA;
 
-#ifdef DEBUG
-       log_intr();
-#endif
+       /* First, halt the DMA engine. */
+       si->si_csr &= ~SI_CSR_DMA_EN;   /* VME only */
 
-       /* First, save csr bits and halt DMA. */
-       si_csr = si->si_csr;
-       si_csr &= ~SI_CSR_DMA_EN;       /* VME only */
-       si->si_csr = si_csr;
-
-       if (si_csr & (SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR)) {
-               printf("si: DMA error, csr=0x%x, reset\n", si_csr);
-               sc->ncr.sc_dma_flags |= DMA5380_ERROR;
-               si_reset_adapter(&sc->ncr);
+       if (si->si_csr & (SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR)) {
+               printf("si: DMA error, csr=0x%x, reset\n", si->si_csr);
+               sr->sr_xs->error = XS_DRIVER_STUFFUP;
+               ncr_sc->sc_state |= NCR_ABORTING;
+               si_reset_adapter(ncr_sc);
        }
 
        /* Note that timeout may have set the error flag. */
-       if (sc->ncr.sc_dma_flags & DMA5380_ERROR)
+       if (ncr_sc->sc_state & NCR_ABORTING)
                goto out;
 
-       resid = si->fifo_count;
+       /*
+        * Now try to figure out how much actually transferred
+        *
+        * The fifo_count does not reflect how many bytes were
+        * actually transferred for VME.
+        *
+        * SCSI-3 VME interface is a little funny on writes:
+        * if we have a disconnect, the dma has overshot by
+        * one byte and needs to be incremented.  This is
+        * true if we have not transferred either all data
+        * or no data.  XXX - from Matt Jacob
+        */
+
+       resid = si->fifo_count & 0xFFFF;
+       ntrans = sc->sc_xlen - resid;
+
 #ifdef DEBUG
-       if (resid != 0) {
-               printf("si_dma_stop: fifo resid=%d (ok?)\n", resid);
-               Debugger();
+       if (si_debug & 2) {
+               printf("si_dma_stop: resid=0x%x ntrans=0x%x\n",
+                      resid, ntrans);
        }
 #endif
-       /* XXX - Was getting (resid==-1), fixed now. */
-       if (resid & ~3) {
+
+       if (ntrans < MIN_DMA_LEN) {
                printf("si: fifo count: 0x%x\n", resid);
-               sc->ncr.sc_dma_flags |= DMA5380_ERROR;
+               ncr_sc->sc_state |= NCR_ABORTING;
                goto out;
        }
+       if (ntrans > ncr_sc->sc_datalen)
+               panic("si_dma_stop: excess transfer");
 
        /* Adjust data pointer */
-       ntrans = dh->dh_len - resid;
-       sc->ncr.sc_dataptr += ntrans;
-       sc->ncr.sc_datalen -= ntrans;
-
-#ifdef DEBUG
-       if (si_debug & 2) {
-               printf("si_dma_stop: ntrans=0x%x\n", ntrans);
-       }
-#endif
+       ncr_sc->sc_dataptr += ntrans;
+       ncr_sc->sc_datalen -= ntrans;
 
        /*
         * After a read, we may need to clean-up
@@ -804,7 +937,7 @@ si_vme_dma_stop(ncr)
        if (((dh->dh_flags & SIDH_OUT) == 0) &&
                ((si->si_csr & SI_CSR_LOB) != 0))
        {
-               char *cp = sc->ncr.sc_dataptr;
+               char *cp = ncr_sc->sc_dataptr;
 #ifdef DEBUG
                printf("si: Got Left-over bytes!\n");
 #endif
@@ -833,9 +966,15 @@ out:
        si->dma_addrh = 0;
        si->dma_addrl = 0;
 
+       si->dma_counth = 0;
+       si->dma_countl = 0;
+
+       si->fifo_cnt_hi = 0;
+       si->fifo_count  = 0;
+
        /* Put SBIC back in PIO mode. */
-       *sc->ncr.sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE);
-       *sc->ncr.sci_icmd = 0;
+       *ncr_sc->sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE);
+       *ncr_sc->sci_icmd = 0;
 }
 
 
@@ -867,58 +1006,118 @@ si_obio_udc_read(si, regnum)
 }
 
 
+/*
+ * This function is called during the COMMAND or MSG_IN phase
+ * that preceeds a DATA_IN or DATA_OUT phase, in case we need
+ * to setup the DMA engine before the bus enters a DATA phase.
+ *
+ * The OBIO "si" IGNORES any attempt to set the FIFO count
+ * register after the SCSI bus goes into any DATA phase, so
+ * this function has to setup the evil FIFO logic.
+ */
 void
-si_obio_dma_start(ncr)
-       struct ncr5380_softc *ncr;
+si_obio_dma_setup(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
 {
-       struct si_softc *sc = (struct si_softc *)ncr;
-       struct si_dma_handle *dh = sc->ncr.sc_dma_hand;
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       volatile struct si_regs *si = sc->sc_regs;
+       struct sci_req *sr;
+       struct si_dma_handle *dh;
+       int send = 0;
+       int xlen = 0;
+
+       /* Let this work even without a dma hand, for testing... */
+       if ((sr = ncr_sc->sc_current) != NULL) {
+               if ((dh = sr->sr_dma_hand) != NULL) {
+                       send = dh->dh_flags & SIDH_OUT;
+                       xlen = ncr_sc->sc_datalen;
+                       xlen &= ~1;
+               }
+       }
+
+#ifdef DEBUG
+       if (si_debug) {
+               printf("si_dma_setup: send=%d xlen=%d\n", send, xlen);
+       }
+#endif
+
+       /* Reset the FIFO */
+       si->si_csr &= ~SI_CSR_FIFO_RES;         /* active low */
+       si->si_csr |= SI_CSR_FIFO_RES;
+
+       /* Set direction (send/recv) */
+       if (send) {
+               si->si_csr |= SI_CSR_SEND;
+       } else {
+               si->si_csr &= ~SI_CSR_SEND;
+       }
+
+       /* Set the FIFO counter. */
+       si->fifo_count = xlen;
+
+#ifdef DEBUG
+       if ((si->fifo_count > xlen) || (si->fifo_count < (xlen - 1))) {
+               printf("si_dma_setup: fifo_count=0x%x, xlen=0x%x\n",
+                          si->fifo_count, xlen);
+               Debugger();
+       }
+#endif
+}
+
+
+void
+si_obio_dma_start(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
+{
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       struct sci_req *sr = ncr_sc->sc_current;
+       struct si_dma_handle *dh = sr->sr_dma_hand;
        volatile struct si_regs *si = sc->sc_regs;
        struct udc_table *cmd;
-       u_long data_pa, cmd_pa;
+       long data_pa, cmd_pa;
+       int xlen;
 
        /*
         * Get the DVMA mapping for this segment.
         * XXX - Should separate allocation and mapin.
         */
-       data_pa = dvma_kvtopa((long)dh->dh_dvma, sc->sc_adapter_type) +
-               ((u_long)sc->ncr.sc_dataptr - dh->dh_addr);
+       data_pa = dvma_kvtopa(dh->dh_dvma, sc->sc_adapter_type);
+       data_pa += (ncr_sc->sc_dataptr - dh->dh_addr);
        if (data_pa & 1)
                panic("si_dma_start: bad pa=0x%x", data_pa);
-       if (dh->dh_len & 1)
-               panic("si_dma_start: bad len=0x%x", dh->dh_len);
+       xlen = ncr_sc->sc_datalen;
+       xlen &= ~1;
+       sc->sc_xlen = xlen;     /* XXX: or less... */
 
 #ifdef DEBUG
        if (si_debug & 2) {
                printf("si_dma_start: dh=0x%x, pa=0x%x, xlen=%d\n",
-                          dh, data_pa, dh->dh_len);
+                          dh, data_pa, xlen);
        }
 #endif
 
-       /* Already setup FIFO in si_dma_alloc() */
+       /*
+        * Set up the DMA controller.
+        * Already set FIFO count in dma_setup.
+        */
 
 #ifdef DEBUG
-       if ((si->fifo_count > dh->dh_len) ||
-               (si->fifo_count < (dh->dh_len - 1)))
+       if ((si->fifo_count > xlen) ||
+               (si->fifo_count < (xlen - 1)))
        {
                printf("si_dma_start: fifo_count=0x%x, xlen=0x%x\n",
-                          si->fifo_count, dh->dh_len);
+                          si->fifo_count, xlen);
                Debugger();
        }
 #endif
 
-       /*
-        * Set up the DMA controller.
-        * Note that (dh-dh_len < sc_datalen)
-        */
-
        /*
         * The OBIO controller needs a command block.
         */
        cmd = &dh->dh_cmd;
        cmd->addrh = ((data_pa & 0xFF0000) >> 8) | UDC_ADDR_INFO;
        cmd->addrl = data_pa & 0xFFFF;
-       cmd->count = dh->dh_len / 2;    /* bytes -> words */
+       cmd->count = xlen / 2;  /* bytes -> words */
        cmd->cmrh = UDC_CMR_HIGH;
        if (dh->dh_flags & SIDH_OUT) {
                cmd->cmrl = UDC_CMR_LSEND;
@@ -945,37 +1144,37 @@ si_obio_dma_start(ncr)
        si_obio_udc_write(si, UDC_ADR_COMMAND, UDC_CMD_STRT_CHN);
 
        /*
-        * Put the SBIC into DMA mode and start the transfer.
+        * Acknowledge the phase change.  (After DMA setup!)
+        * Put the SBIC into DMA mode, and start the transfer.
         */
-       *sc->ncr.sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
        if (dh->dh_flags & SIDH_OUT) {
-               *sc->ncr.sci_icmd = SCI_ICMD_DATA;
-               *sc->ncr.sci_dma_send = 0;      /* start it */
+               *ncr_sc->sci_tcmd = PHASE_DATA_OUT;
+               SCI_CLR_INTR(ncr_sc);
+               *ncr_sc->sci_icmd = SCI_ICMD_DATA;
+               *ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
+               *ncr_sc->sci_dma_send = 0;      /* start it */
        } else {
-               *sc->ncr.sci_icmd = 0;
-               *sc->ncr.sci_irecv = 0; /* start it */
+               *ncr_sc->sci_tcmd = PHASE_DATA_IN;
+               SCI_CLR_INTR(ncr_sc);
+               *ncr_sc->sci_icmd = 0;
+               *ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
+               *ncr_sc->sci_irecv = 0; /* start it */
        }
 
-       sc->ncr.sc_dma_flags |= DMA5380_INPROGRESS;
-
-       if ((sc->ncr.sc_dma_flags & DMA5380_POLL) == 0) {
-               /* Expect an interrupt when DMA completes. */
-               sc->sc_timo = si_dma_intr_timo;
-               timeout(si_dma_timeout, sc, sc->sc_timo);
-       }
+       ncr_sc->sc_state |= NCR_DOINGDMA;
 
 #ifdef DEBUG
        if (si_debug & 2) {
                printf("si_dma_start: started, flags=0x%x\n",
-                          sc->ncr.sc_dma_flags);
+                          ncr_sc->sc_state);
        }
 #endif
 }
 
 
 void
-si_obio_dma_eop(ncr)
-       struct ncr5380_softc *ncr;
+si_obio_dma_eop(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
 {
 
        /* Not needed - DMA was stopped prior to examining sci_csr */
@@ -983,78 +1182,81 @@ si_obio_dma_eop(ncr)
 
 
 void
-si_obio_dma_stop(ncr)
-       struct ncr5380_softc *ncr;
+si_obio_dma_stop(ncr_sc)
+       struct ncr5380_softc *ncr_sc;
 {
-       struct si_softc *sc = (struct si_softc *)ncr;
-       struct si_dma_handle *dh = sc->ncr.sc_dma_hand;
+       struct si_softc *sc = (struct si_softc *)ncr_sc;
+       struct sci_req *sr = ncr_sc->sc_current;
+       struct si_dma_handle *dh = sr->sr_dma_hand;
        volatile struct si_regs *si = sc->sc_regs;
        int resid, ntrans, tmo, udc_cnt;
 
-       if ((sc->ncr.sc_dma_flags & DMA5380_INPROGRESS) == 0) {
+       if ((ncr_sc->sc_state & NCR_DOINGDMA) == 0) {
 #ifdef DEBUG
                printf("si_dma_stop: dma not running\n");
 #endif
                return;
        }
-       sc->ncr.sc_dma_flags &= ~DMA5380_INPROGRESS;
-       if (sc->sc_timo) {
-               sc->sc_timo = 0;
-               untimeout(si_dma_timeout, sc);
-       }
+       ncr_sc->sc_state &= ~NCR_DOINGDMA;
 
        if (si->si_csr & (SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR)) {
                printf("si: DMA error, csr=0x%x, reset\n", si->si_csr);
-               sc->ncr.sc_dma_flags |= DMA5380_ERROR;
-               si_reset_adapter(&sc->ncr);
+               sr->sr_xs->error = XS_DRIVER_STUFFUP;
+               ncr_sc->sc_state |= NCR_ABORTING;
+               si_reset_adapter(ncr_sc);
        }
 
        /* Note that timeout may have set the error flag. */
-       if (sc->ncr.sc_dma_flags & DMA5380_ERROR)
+       if (ncr_sc->sc_state & NCR_ABORTING)
                goto out;
 
-       /* After a read, wait for the FIFO to empty. */
+       /*
+        * After a read, wait for the FIFO to empty.
+        * Note: this only works on the OBIO version.
+        */
        if ((dh->dh_flags & SIDH_OUT) == 0) {
-               /* XXX: This bit is not reliable.  Beware! -dej */
                tmo = 200000;   /* X10 = 2 sec. */
                for (;;) {
                        if (si->si_csr & SI_CSR_FIFO_EMPTY)
                                break;
                        if (--tmo <= 0) {
                                printf("si: dma fifo did not empty, reset\n");
-                               sc->ncr.sc_dma_flags |= DMA5380_ERROR;
-                               si_reset_adapter(&sc->ncr);
+                               ncr_sc->sc_state |= NCR_ABORTING;
+                               /* si_reset_adapter(ncr_sc); */
                                goto out;
                        }
                        delay(10);
                }
        }
 
+       /*
+        * Now try to figure out how much actually transferred
+        *
+        * The fifo_count might not reflect how many bytes were
+        * actually transferred for VME.
+        */
+
+       resid = si->fifo_count & 0xFFFF;
+       ntrans = sc->sc_xlen - resid;
 
-       resid = si->fifo_count;
 #ifdef DEBUG
-       if (resid != 0) {
-               printf("si_dma_stop: fifo resid=%d (ok?)\n", resid);
-               Debugger();
+       if (si_debug & 2) {
+               printf("si_dma_stop: resid=0x%x ntrans=0x%x\n",
+                      resid, ntrans);
        }
 #endif
-       /* XXX - Was getting (resid==-1), fixed now. */
-       if (resid & ~3) {
+
+       if (ntrans < MIN_DMA_LEN) {
                printf("si: fifo count: 0x%x\n", resid);
-               sc->ncr.sc_dma_flags |= DMA5380_ERROR;
+               ncr_sc->sc_state |= NCR_ABORTING;
                goto out;
        }
+       if (ntrans > ncr_sc->sc_datalen)
+               panic("si_dma_stop: excess transfer");
 
        /* Adjust data pointer */
-       ntrans = dh->dh_len - resid;
-       sc->ncr.sc_dataptr += ntrans;
-       sc->ncr.sc_datalen -= ntrans;
-
-#ifdef DEBUG
-       if (si_debug & 2) {
-               printf("si_dma_stop: ntrans=0x%x\n", ntrans);
-       }
-#endif
+       ncr_sc->sc_dataptr += ntrans;
+       ncr_sc->sc_datalen -= ntrans;
 
        /*
         * After a read, we may need to clean-up
@@ -1063,16 +1265,16 @@ si_obio_dma_stop(ncr)
        if ((dh->dh_flags & SIDH_OUT) == 0) {
                /* If odd transfer count, grab last byte by hand. */
                if (ntrans & 1) {
-                       sc->ncr.sc_dataptr[-1] =
+                       ncr_sc->sc_dataptr[-1] =
                                (si->fifo_data & 0xff00) >> 8;
                        goto out;
                }
                /* UDC might not have transfered the last word. */
                udc_cnt = si_obio_udc_read(si, UDC_ADR_COUNT);
-               if (((udc_cnt * 2) - si->fifo_count) == 2) {
-                       sc->ncr.sc_dataptr[-2] =
+               if (((udc_cnt * 2) - resid) == 2) {
+                       ncr_sc->sc_dataptr[-2] =
                                (si->fifo_data & 0xff00) >> 8;
-                       sc->ncr.sc_dataptr[-1] =
+                       ncr_sc->sc_dataptr[-1] =
                                (si->fifo_data & 0x00ff);
                }
        }
@@ -1080,191 +1282,10 @@ si_obio_dma_stop(ncr)
 out:
        /* Reset the UDC. */
        si_obio_udc_write(si, UDC_ADR_COMMAND, UDC_CMD_RESET);
+       si->fifo_count = 0;
 
        /* Put SBIC back in PIO mode. */
-       *sc->ncr.sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE);
-       *sc->ncr.sci_icmd = 0;
-}
-
-
-#if 0
-int hexdigit(char c)
-{
-
-       if (c >= '0' && c <= '9') return c - '0';
-       else return c - ('a' - 10);
-}
-
-
-struct scsi_link *thescsilink;
-
-void sicmdloop(void)
-{
-char hexbuf[40];
-int c, i, pos;
-u_char cmdbuf[6];
-
-       while (1) {
-               pos = 0;
-               while (1) {
-                       c = cngetc();
-                       if ((c == 0x7F || c == 0x08) && pos) {
-                               pos--;
-                               cnputc(0x08);
-                               cnputc(' ');
-                               cnputc(0x08);
-                       }
-                       else if (c == '\r' || c == '\n') {
-                               hexbuf[pos] = 0;
-                               break;
-                       }
-                       else {
-                               hexbuf[pos++] = c;
-                               cnputc(c);
-                       }
-               }
-
-               pos = 0;
-               for (i = 0; i < 6; i++) {
-                   while (hexbuf[pos] == ' ') pos++;
-                   cmdbuf[i] = 16 * hexdigit(hexbuf[pos++]) + hexdigit(hexbuf[pos++]);
-               }
-
-               scsi_scsi_cmd(thescsilink, (struct scsi_generic *)cmdbuf, 6,
-                       0, 0, 1, 1000, 0, SCSI_POLL);
-       }
+       *ncr_sc->sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE);
+       *ncr_sc->sci_icmd = 0;
 }
-#endif
-
-
-#ifdef DEBUG
-
-#define NQENT 10
-static int Qptr = 0;
-static struct qent {
-    int seq;
-    int si_csr;
-    int sci_csr;
-    int sci_bus_csr;
-    long dma_addr, dma_count, fifo_len;
-    long start_addr, start_count, start_len;
-} Qents[NQENT];
 
-
-void log_start(void)
-{
-struct si_softc *sc = TheSoftC;
-volatile struct si_regs *si = sc->sc_regs;
-
-    Qents[Qptr].start_addr = (si->dma_addrh << 16) + si->dma_addrl;
-    Qents[Qptr].start_count = (si->dma_counth << 16) + si->dma_countl;
-    Qents[Qptr].start_len = si->fifo_count;
-    Qents[Qptr].seq = Seq++;
-}
-
-
-void log_intr(void)
-{
-struct si_softc *sc = TheSoftC;
-volatile struct si_regs *si = sc->sc_regs;
-
-    Qents[Qptr].si_csr = si->si_csr;
-    while (!(si->si_csr & SI_CSR_FIFO_EMPTY)) {
-       printf("log_intr: FIFO not empty before DMA disable at %d\n", Seq);
-    }
-
-    if (sc->sc_adapter_type == BUS_VME16)
-           si->si_csr &= ~SI_CSR_DMA_EN;
-    if (!(si->si_csr & SI_CSR_FIFO_EMPTY)) {
-       printf("log_intr: FIFO not empty after DMA disable at %d\n", Seq);
-    }
-    Qents[Qptr].dma_addr = (si->dma_addrh << 16) + si->dma_addrl;
-
-    Qents[Qptr].dma_count = (si->dma_counth << 16) + si->dma_countl;
-    Qents[Qptr].fifo_len = si->fifo_count;
-    Qents[Qptr].sci_csr = *sc->ncr.sci_csr;
-    Qents[Qptr].sci_bus_csr = *sc->ncr.sci_bus_csr;
-    Qptr++;
-    if (Qptr == NQENT) Qptr = 0;
-}
-
-
-void si_printregs(void)
-{
-struct si_softc *sc = TheSoftC;
-volatile struct si_regs *si = TheRegs;
-struct sci_req *sr;
-int i;
-int a, b, c, d;
-
-    if (!TheRegs) {
-       printf("TheRegs == NULL, please re-set!\n");
-       return;
-    }
-
-    c = si->si_csr;
-    printf("si->si_csr=%04x\n", c);
-    si->si_csr &= ~SI_CSR_DMA_EN;
-
-    a = si->dma_addrh; b = si->dma_addrl;
-    printf("si->dma_addr=%04x%04x\n", a, b);
-
-    /* printf("si->dma_addr=%04x%04x\n", si->dma_addrh, si->dma_addrl); */
-    printf("si->dma_count=%04x%04x\n", si->dma_counth, si->dma_countl);
-    printf("si->fifo_count=%04x\n", si->fifo_count);
-    printf("sci_icmd=%02x\n", si->sci.sci_icmd);
-    printf("sci_mode=%02x\n", si->sci.sci_mode);
-    printf("sci_tcmd=%02x\n", si->sci.sci_tcmd);
-    printf("sci_bus_csr=%02x\n", si->sci.sci_bus_csr);
-    printf("sci_csr=%02x\n", si->sci.sci_csr);
-    printf("sci_data=%02x\n\n", si->sci.sci_data);
-
-    if (!TheSoftC) {
-       printf("TheSoftC == NULL, can't continue.\n");
-       return;
-    }
-
-    printf("DMA handles:\n");
-    for (i = 0; i < SCI_OPENINGS; i++) {
-       if (sc->sc_dma[i].dh_flags & SIDH_BUSY) {
-           printf("%d: %x/%x => %x/%d %s\n", i,
-               sc->sc_dma[i].dh_addr,
-               sc->sc_dma[i].dh_len,
-               sc->sc_dma[i].dh_dvma,
-               sc->sc_dma[i].dh_flags,
-               (&sc->sc_dma[i] == sc->ncr.sc_dma_hand) ? "(active)" : "");
-       }
-       else {
-           printf("%d: idle\n", i);
-       }
-    }
-
-    printf("i\nsci_req queue:\n");
-    for (i = 0; i < SCI_OPENINGS; i++) {
-       sr = &sc->ncr.sc_ring[i];
-       printf("%d: %d/%d %x/%x => %x\n", i, sr->sr_target, sr->sr_lun,
-           sr->sr_data, sr->sr_datalen,
-           sr->sr_dma_hand);
-    }
-    printf("Total commands (sc_ncmds): %d\n", sc->ncr.sc_ncmds);
-    printf("\nCurrent SCSI data pointer: %x/%x\n", sc->ncr.sc_dataptr,
-       sc->ncr.sc_datalen);
-}
-
-void print_qent(void)
-{
-int i;
-
-    i = Qptr;
-    do {
-       printf("%d: si_csr=%04x csr=%02x bus_csr=%02x addr=%08x count=%08x fifo=%08x\n",
-           Qents[i].seq, Qents[i].si_csr, Qents[i].sci_csr, Qents[i].sci_bus_csr,
-           Qents[i].dma_addr, Qents[i].dma_count, Qents[i].fifo_len);
-       printf("    from addr=%08x count=%08x fifo=%08x\n",
-           Qents[i].start_addr, Qents[i].start_count, Qents[i].start_len);
-       i++;
-       if (i == NQENT) i = 0;
-    } while (i != Qptr);
-}
-
-#endif
index d617024..450ed65 100644 (file)
@@ -1,4 +1,4 @@
-/*     $NetBSD: ncr_sireg.h,v 1.1 1995/10/29 21:19:12 gwr Exp $        */
+/*     $NetBSD: ncr_sireg.h,v 1.1.2.1 1995/11/18 07:08:55 gwr Exp $    */
 
 /*
  * Register map for the Sun3 SCSI Interface (si)
  * vis versa.
  */
 
+/*
+ * Am5380 Register map (no padding)
+ */
+struct ncr5380regs {
+       volatile u_char sci_r0;
+       volatile u_char sci_r1;
+       volatile u_char sci_r2;
+       volatile u_char sci_r3;
+       volatile u_char sci_r4;
+       volatile u_char sci_r5;
+       volatile u_char sci_r6;
+       volatile u_char sci_r7;
+};
+
 struct si_regs {
-       sci_regmap_t sci;       /* See scsi_5380.h */
+       struct ncr5380regs sci;
+
        /* DMA controller registers */
        u_short                 dma_addrh;      /* dma address (VME only) */
        u_short                 dma_addrl;      /* (high word, low word)  */
@@ -80,7 +95,7 @@ struct si_regs {
 #define SI_CSR_LOB_ONE         0x0040  /* (r,v) one leftover byte */
 #define SI_CSR_BPCON           0x0020  /* (rw,v) byte packing control */
                                        /* dma is in 0=longwords, 1=words */
-#define SI_CSR_DMA_EN          0x0010  /* (rw,v) dma enable */
+#define SI_CSR_DMA_EN          0x0010  /* (rw,v) dma/interrupt enable */
 #define SI_CSR_SEND            0x0008  /* (rw,b) dma dir, 1=to device */
 #define SI_CSR_INTR_EN         0x0004  /* (rw,b) interrupts enable */
 #define SI_CSR_FIFO_RES                0x0002  /* (rw,b) inits fifo, 0=reset */