-/* $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
*
* 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>
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 *));
#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
#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",
"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
*****************************************************************/
{
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_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. */
*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;
}
{
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. */
*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;
}
{
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);
+ }
}
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! */
/*
* 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);
}
* 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
*****************************************************************/
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;
/*
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;
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;
}
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);
/* 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. */
}
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) {
}
/*
- * 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);
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
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. */
/* 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);
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;
}
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);
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;
}
* 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
*(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;
}
* 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
* 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. */
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;
}
/* XXX - Make parity checking optional? */
*sc->sci_mode = (SCI_MODE_MONBSY | SCI_MODE_PAR_CHK);
- NCR_MISC(("ncr5380_select: success\n"));
return XS_NOERROR;
}
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;
* 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
/* 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++;
*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? */
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:
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();
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;
}
*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;
}
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;
* 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;
}
}
/* 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);
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);
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);
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;
* 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;
}
/* 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;
}
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;
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;
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 {
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);
}
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");
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) {
act_flags = ncr5380_data_xfer(sc, phase);
break;
- case PHASE_CMD:
+ case PHASE_COMMAND:
act_flags = ncr5380_command(sc);
break;
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) {
*/
*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 */
-/* $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
* 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>
#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
/*
* 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;
};
* 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) */
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 *));
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
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;
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:
/*
* 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 = ®s->sci.sci_data;
- sc->ncr.sci_icmd = ®s->sci.sci_icmd;
- sc->ncr.sci_mode = ®s->sci.sci_mode;
- sc->ncr.sci_tcmd = ®s->sci.sci_tcmd;
- sc->ncr.sci_bus_csr = ®s->sci.sci_bus_csr;
- sc->ncr.sci_csr = ®s->sci.sci_csr;
- sc->ncr.sci_idata = ®s->sci.sci_idata;
- sc->ncr.sci_iack = ®s->sci.sci_iack;
+ ncr_sc->sci_r0 = ®s->sci.sci_r0;
+ ncr_sc->sci_r1 = ®s->sci.sci_r1;
+ ncr_sc->sci_r2 = ®s->sci.sci_r2;
+ ncr_sc->sci_r3 = ®s->sci.sci_r3;
+ ncr_sc->sci_r4 = ®s->sci.sci_r4;
+ ncr_sc->sci_r5 = ®s->sci.sci_r5;
+ ncr_sc->sci_r6 = ®s->sci.sci_r6;
+ ncr_sc->sci_r7 = ®s->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);
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");
#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
si->fifo_cnt_hi = 0;
}
- SCI_CLR_INTR(ncr);
+ SCI_CLR_INTR(ncr_sc);
}
* 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. */
* 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
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 |
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);
****************************************************************/
+/*
+ * 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 */
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
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
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;
}
}
+/*
+ * 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;
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 */
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
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);
}
}
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