summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/mvme88k/dev/vs.c118
1 files changed, 72 insertions, 46 deletions
diff --git a/sys/arch/mvme88k/dev/vs.c b/sys/arch/mvme88k/dev/vs.c
index d6e1a748407..91eb77ddf23 100644
--- a/sys/arch/mvme88k/dev/vs.c
+++ b/sys/arch/mvme88k/dev/vs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: vs.c,v 1.59 2005/12/27 22:45:28 miod Exp $ */
+/* $OpenBSD: vs.c,v 1.60 2005/12/27 22:48:01 miod Exp $ */
/*
* Copyright (c) 2004, Miodrag Vallat.
@@ -97,7 +97,6 @@ M328_SG vs_build_memory_structure(struct vs_softc *, struct scsi_xfer *,
void vs_chksense(struct scsi_xfer *);
void vs_dealloc_scatter_gather(M328_SG);
int vs_eintr(void *);
-void vs_free(struct vs_cb *);
bus_addr_t vs_getcqe(struct vs_softc *);
bus_addr_t vs_getiopb(struct vs_softc *);
int vs_initialize(struct vs_softc *);
@@ -107,11 +106,12 @@ void vs_link_sg_list(sg_list_element_t *, vaddr_t, int);
int vs_nintr(void *);
int vs_poll(struct vs_softc *, struct vs_cb *);
void vs_print_addr(struct vs_softc *, struct scsi_xfer *);
-int vs_queue_number(struct scsi_link *, struct vs_softc *);
+struct vs_cb *vs_find_queue(struct scsi_link *, struct vs_softc *);
void vs_reset(struct vs_softc *, int);
void vs_resync(struct vs_softc *);
-void vs_scsidone(struct vs_softc *, struct scsi_xfer *);
+void vs_scsidone(struct vs_softc *, struct vs_cb *);
+static __inline__ void vs_free(struct vs_cb *);
static __inline__ void vs_clear_return_info(struct vs_softc *);
static __inline__ paddr_t kvtop(vaddr_t);
@@ -274,15 +274,16 @@ do_vspoll(struct vs_softc *sc, struct scsi_xfer *xs, int canreset)
}
int
-vs_poll(struct vs_softc *sc, struct vs_cb *cmd)
+vs_poll(struct vs_softc *sc, struct vs_cb *cb)
{
struct scsi_xfer *xs;
+ int s;
int rc;
- xs = cmd->cb_xs;
+ xs = cb->cb_xs;
rc = do_vspoll(sc, xs, 1);
- vs_free(cmd);
+ s = splbio();
if (rc != 0) {
xs->error = XS_SELTIMEOUT;
xs->status = -1;
@@ -290,8 +291,10 @@ vs_poll(struct vs_softc *sc, struct vs_cb *cmd)
#if 0
scsi_done(xs);
#endif
+ vs_free(cb);
} else
- vs_scsidone(sc, xs);
+ vs_scsidone(sc, cb);
+ splx(s);
if (CRSW & M_CRSW_ER)
CRB_CLR_ER;
@@ -321,11 +324,11 @@ thaw_all_queues(struct vs_softc *sc)
}
void
-vs_scsidone(struct vs_softc *sc, struct scsi_xfer *xs)
+vs_scsidone(struct vs_softc *sc, struct vs_cb *cb)
{
+ struct scsi_xfer *xs = cb->cb_xs;
u_int32_t len;
int error;
- int tgt;
len = vs_read(4, sh_RET_IOPB + IOPB_LENGTH);
xs->resid = xs->datalen - len;
@@ -337,16 +340,16 @@ vs_scsidone(struct vs_softc *sc, struct scsi_xfer *xs)
} else
xs->status = error >> 8;
- tgt = vs_queue_number(xs->sc_link, sc);
while (xs->status == SCSI_CHECK) {
vs_chksense(xs);
- thaw_queue(sc, tgt);
}
xs->flags |= ITSDONE;
- thaw_queue(sc, tgt);
+ thaw_queue(sc, cb->cb_q);
scsi_done(xs);
+
+ vs_free(cb);
}
int
@@ -359,29 +362,50 @@ vs_scsicmd(struct scsi_xfer *xs)
bus_addr_t cqep, iopb;
struct vs_cb *cb;
u_int queue;
+ int s;
flags = xs->flags;
-
- queue = flags & SCSI_POLL ? 0 : vs_queue_number(slp, sc);
- cb = &sc->sc_cb[queue];
- if (cb->cb_xs != NULL)
- return (TRY_AGAIN_LATER);
-
if (flags & SCSI_POLL) {
+ cb = &sc->sc_cb[0];
cqep = sh_MCE;
iopb = sh_MCE_IOPB;
+#ifdef VS_DEBUG
+ if (mce_read(2, CQE_QECR) & M_QECR_GO)
+ printf("%s: master command queue busy\n",
+ sc->sc_dev.dv_xname);
+#endif
/* Wait until we can use the command queue entry. */
while (mce_read(2, CQE_QECR) & M_QECR_GO)
;
+#ifdef VS_DEBUG
+ if (cb->cb_xs != NULL) {
+ printf("%s: master command not idle\n",
+ sc->sc_dev.dv_xname);
+ return (TRY_AGAIN_LATER);
+ }
+#endif
+ s = splbio();
} else {
+ s = splbio();
+ cb = vs_find_queue(slp, sc);
+ if (cb == NULL) {
+ splx(s);
+#ifdef VS_DEBUG
+ printf("%s: no free queues\n", sc->sc_dev.dv_xname);
+#endif
+ return (TRY_AGAIN_LATER);
+ }
cqep = vs_getcqe(sc);
if (cqep == 0) {
+ splx(s);
return (TRY_AGAIN_LATER);
}
iopb = vs_getiopb(sc);
}
+ queue = cb->cb_q;
+
vs_bzero(iopb, IOPB_LONG_SIZE);
/*
@@ -435,6 +459,8 @@ vs_scsicmd(struct scsi_xfer *xs)
vs_write(1, cqep + CQE_WORK_QUEUE, queue);
cb->cb_xs = xs;
+ splx(s);
+
if (xs->datalen != 0)
cb->cb_sg = vs_build_memory_structure(sc, xs, iopb);
else
@@ -550,6 +576,9 @@ vs_initialize(struct vs_softc *sc)
{
int i, msr, dbid;
+ for (i = 0; i < NUM_WQ; i++)
+ sc->sc_cb[i].cb_q = i;
+
/*
* Reset the board, and wait for it to get ready.
* The reset signal is applied for 70 usec, and the board status
@@ -767,7 +796,8 @@ vs_reset(struct vs_softc *sc, int bus)
splx(s);
}
-void
+/* free a cb; invoked at splbio */
+static __inline__ void
vs_free(struct vs_cb *cb)
{
if (cb->cb_sg != NULL) {
@@ -783,7 +813,6 @@ vs_nintr(void *vsc)
{
struct vs_softc *sc = (struct vs_softc *)vsc;
struct vs_cb *cb;
- struct scsi_xfer *xs;
int s;
if ((CRSW & CONTROLLER_ERROR) == CONTROLLER_ERROR)
@@ -799,12 +828,8 @@ vs_nintr(void *vsc)
* to point to address 0. But then, we should have caught
* the controller error above.
*/
- if (cb != NULL) {
- xs = cb->cb_xs;
- vs_free(cb);
-
- vs_scsidone(sc, xs);
- }
+ if (cb != NULL)
+ vs_scsidone(sc, cb);
/* ack the interrupt */
if (CRSW & M_CRSW_ER)
@@ -893,30 +918,31 @@ vs_clear_return_info(struct vs_softc *sc)
}
/*
- * Choose the work queue number for a specific target.
- *
- * Targets on the primary channel are mapped to queues 1-7, while targets
- * on the secondary channel are mapped to queues 8-14.
- * To do so, we assign each target the queue matching its own number,
- * plus eight on the secondary bus, except for target 0 on the first channel
- * and 7 on the secondary channel which gets assigned to the queue matching
- * the controller id.
+ * Choose the first available work queue (invoked at splbio).
+ * We used a simple round-robin mechanism which is faster than rescanning
+ * from the beginning if we have more than one target on the bus.
*/
-int
-vs_queue_number(struct scsi_link *sl, struct vs_softc *sc)
+struct vs_cb *
+vs_find_queue(struct scsi_link *sl, struct vs_softc *sc)
{
- int bus, target;
-
- bus = !!(sl->flags & SDEV_2NDBUS);
- target = sl->target;
+ struct vs_cb *cb;
+ static u_int last = 0;
+ u_int q;
- if (target == sc->sc_id[bus])
- return 0;
+ q = last;
+ for (;;) {
+ if (++q == NUM_WQ)
+ q = 1;
+ if (q == last)
+ break;
- if (target > sc->sc_id[bus])
- target--;
+ if ((cb = &sc->sc_cb[q])->cb_xs == NULL) {
+ last = q;
+ return (cb);
+ }
+ }
- return (bus == 0 ? 1 : 8) + target;
+ return (NULL);
}
/*