summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ic/sili.c69
1 files changed, 51 insertions, 18 deletions
diff --git a/sys/dev/ic/sili.c b/sys/dev/ic/sili.c
index 505b4d4a78f..941f6280cec 100644
--- a/sys/dev/ic/sili.c
+++ b/sys/dev/ic/sili.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sili.c,v 1.27 2007/04/08 00:47:50 pascoe Exp $ */
+/* $OpenBSD: sili.c,v 1.28 2007/04/08 04:45:40 pascoe Exp $ */
/*
* Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
@@ -77,6 +77,8 @@ struct sili_port {
TAILQ_HEAD(, sili_ccb) sp_free_ccbs;
volatile u_int32_t sp_active;
+ TAILQ_HEAD(, sili_ccb) sp_active_ccbs;
+ TAILQ_HEAD(, sili_ccb) sp_deferred_ccbs;
#ifdef SILI_DEBUG
char sp_name[16];
@@ -160,7 +162,7 @@ struct atascsi_methods sili_atascsi_methods = {
};
/* completion paths */
-void sili_ata_cmd_done(struct sili_ccb *);
+void sili_ata_cmd_done(struct sili_ccb *, int);
void sili_ata_cmd_timeout(void *);
int
@@ -233,13 +235,13 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
ccb = &sp->sp_ccbs[err_slot];
- DPRINTF(SILI_D_VERBOSE, "%s: error, code %d, slot %d\n",
- PORTNAME(sp), sili_pread(sp, SILI_PREG_CE), err_slot);
+ DPRINTF(SILI_D_VERBOSE, "%s: error, code %d, slot %d, "
+ "active %08x\n", PORTNAME(sp), sili_pread(sp, SILI_PREG_CE),
+ err_slot, sp->sp_active);
switch (sili_pread(sp, SILI_PREG_CE)) {
case SILI_PREG_CE_DEVICEERROR:
case SILI_PREG_CE_DATAFISERROR:
- need_restart = 1;
/* Extract error from command slot in LRAM. */
r = SILI_PREG_SLOT(err_slot) + 8;
bus_space_barrier(sp->sp_sc->sc_iot_port, sp->sp_ioh,
@@ -259,11 +261,13 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
break;
}
- /* Clear the failed commmand in saved PSS so completion runs. */
+ /* Clear the failed commmand in saved PSS so cmd_done runs. */
pss_saved &= ~(1 << err_slot);
KASSERT(ccb->ccb_xa.state == ATA_S_ONCHIP);
ccb->ccb_xa.state = ATA_S_ERROR;
+
+ need_restart = 1;
}
/* Command slot is complete if its bit in PSS is 0 but 1 in active. */
@@ -277,14 +281,13 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
PORTNAME(sp), slot, ccb->ccb_xa.state == ATA_S_ERROR ?
" (error)" : "");
- sp->sp_active &= ~(1 << slot);
- sili_ata_cmd_done(ccb);
+ sili_ata_cmd_done(ccb, need_restart);
processed |= 1 << slot;
}
- /* Restart port and reissue outstanding commands. */
if (need_restart) {
+ /* Re-enable transfers on port. */
sili_pwrite(sp, SILI_PREG_PCS, SILI_PREG_PCS_PORTINIT);
if (!sili_pwait_eq(sp, SILI_PREG_PCS, SILI_PREG_PCS_PORTRDY,
SILI_PREG_PCS_PORTRDY, 1000)) {
@@ -292,16 +295,37 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
PORTNAME(sp));
}
- while (pss_saved) {
- slot = ffs(pss_saved) - 1;
- ccb = &sp->sp_ccbs[slot];
- pss_saved &= ~(1 << slot);
-
+ /* Restart CCBs in the order they were originally queued. */
+ pss_masked = pss_saved;
+ TAILQ_FOREACH(ccb, &sp->sp_active_ccbs, ccb_entry) {
DPRINTF(SILI_D_VERBOSE, "%s: restarting slot %d "
- "after error\n", PORTNAME(sp), slot);
+ "after error, state %02x\n", PORTNAME(sp),
+ ccb->ccb_xa.tag, ccb->ccb_xa.state);
+ if (!(pss_masked & (1 << ccb->ccb_xa.tag))) {
+ panic("sili_intr: slot %d not active in "
+ "pss_masked: %08x, state %02x",
+ ccb->ccb_xa.tag, pss_masked,
+ ccb->ccb_xa.state);
+ }
+ pss_masked &= ~(1 << ccb->ccb_xa.tag);
+
KASSERT(ccb->ccb_xa.state == ATA_S_ONCHIP);
sili_post_indirect(sp, ccb);
}
+ KASSERT(pss_masked == 0);
+
+ /*
+ * Finally, run atascsi completion for any finished CCBs. If
+ * we had run these during cmd_done above, any ccbs that their
+ * completion generated would have been activated out of order.
+ */
+ while ((ccb = TAILQ_FIRST(&sp->sp_deferred_ccbs)) != NULL) {
+ TAILQ_REMOVE(&sp->sp_deferred_ccbs, ccb, ccb_entry);
+
+ KASSERT(ccb->ccb_xa.state == ATA_S_COMPLETE ||
+ ccb->ccb_xa.state == ATA_S_ERROR);
+ ccb->ccb_xa.complete(&ccb->ccb_xa);
+ }
}
return processed;
@@ -391,6 +415,8 @@ sili_ccb_alloc(struct sili_port *sp)
int i;
TAILQ_INIT(&sp->sp_free_ccbs);
+ TAILQ_INIT(&sp->sp_active_ccbs);
+ TAILQ_INIT(&sp->sp_deferred_ccbs);
sp->sp_ccbs = malloc(sizeof(struct sili_ccb) * SILI_MAX_CMDS,
M_DEVBUF, M_WAITOK);
@@ -620,7 +646,6 @@ sili_post_direct(struct sili_port *sp, u_int slot, void *buf, size_t buflen)
void
sili_post_indirect(struct sili_port *sp, struct sili_ccb *ccb)
{
- ccb->ccb_xa.state = ATA_S_ONCHIP;
sili_pwrite(sp, SILI_PREG_CAR_LO(ccb->ccb_xa.tag),
(u_int32_t)ccb->ccb_cmd_dva);
sili_pwrite(sp, SILI_PREG_CAR_HI(ccb->ccb_xa.tag),
@@ -766,7 +791,7 @@ failcmd:
}
void
-sili_ata_cmd_done(struct sili_ccb *ccb)
+sili_ata_cmd_done(struct sili_ccb *ccb, int defer_completion)
{
struct sili_port *sp = ccb->ccb_port;
struct sili_softc *sc = sp->sp_sc;
@@ -781,6 +806,9 @@ sili_ata_cmd_done(struct sili_ccb *ccb)
sili_unload(ccb);
+ TAILQ_REMOVE(&sp->sp_active_ccbs, ccb, ccb_entry);
+ sp->sp_active &= ~(1 << xa->tag);
+
if (xa->state == ATA_S_ONCHIP)
xa->state = ATA_S_COMPLETE;
#ifdef DIAGNOSTIC
@@ -788,7 +816,9 @@ sili_ata_cmd_done(struct sili_ccb *ccb)
printf("%s: invalid ata_xfer state %02x in sili_ata_cmd_done, "
"slot %d\n", PORTNAME(sp), xa->state, xa->tag);
#endif
- if (xa->state != ATA_S_TIMEOUT)
+ if (defer_completion)
+ TAILQ_INSERT_TAIL(&sp->sp_deferred_ccbs, ccb, ccb_entry);
+ else if (xa->state == ATA_S_COMPLETE)
xa->complete(xa);
}
@@ -919,7 +949,10 @@ sili_start(struct sili_port *sp, struct sili_ccb *ccb)
splassert(IPL_BIO);
KASSERT(ccb->ccb_xa.state == ATA_S_PENDING);
+ TAILQ_INSERT_TAIL(&sp->sp_active_ccbs, ccb, ccb_entry);
sp->sp_active |= 1 << slot;
+ ccb->ccb_xa.state = ATA_S_ONCHIP;
+
sili_post_indirect(sp, ccb);
}