summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-08 08:13:31 +0000
committerChristopher Pascoe <pascoe@cvs.openbsd.org>2007-04-08 08:13:31 +0000
commitc63dc2cd28bc7c6086012d11ab0560e76e203098 (patch)
treed72df95dae3b72dcf673559cd2b48a571e050efb /sys
parent21f76d57f9c5ee3319ff1ad5cb910cd5a1b2e090 (diff)
Change the API to sili_port_intr so it can timeout a command and reuse the
existing reset/requeue infrastructure, and implement command timeouts. sili_port_intr now always processes all pending CCBs, so switch to use the auto-clearing interrupt status reads.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ic/sili.c52
1 files changed, 37 insertions, 15 deletions
diff --git a/sys/dev/ic/sili.c b/sys/dev/ic/sili.c
index 0ffa5ba784d..24c8fdda643 100644
--- a/sys/dev/ic/sili.c
+++ b/sys/dev/ic/sili.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sili.c,v 1.29 2007/04/08 06:51:25 pascoe Exp $ */
+/* $OpenBSD: sili.c,v 1.30 2007/04/08 08:13:30 pascoe Exp $ */
/*
* Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
@@ -152,7 +152,7 @@ void sili_start(struct sili_port *, struct sili_ccb *);
int sili_read_ncq_error(struct sili_port *, int *);
/* port interrupt handler */
-u_int32_t sili_port_intr(struct sili_port *, u_int32_t);
+u_int32_t sili_port_intr(struct sili_port *, int);
/* atascsi interface */
int sili_ata_probe(void *, int);
@@ -206,7 +206,7 @@ sili_detach(struct sili_softc *sc, int flags)
}
u_int32_t
-sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
+sili_port_intr(struct sili_port *sp, int timeout_slot)
{
u_int32_t is, pss_saved, pss_masked;
u_int32_t processed = 0, need_restart = 0;
@@ -214,11 +214,7 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
struct sili_ccb *ccb;
is = sili_pread(sp, SILI_PREG_IS);
- pss_saved = sili_pread(sp, SILI_PREG_PSS);
-
- /* Ack port interrupt only if checking all command slots. */
- if (slotmask == SILI_PREG_PSS_ALL_SLOTS)
- sili_pwrite(sp, SILI_PREG_IS, is);
+ pss_saved = sili_pread(sp, SILI_PREG_PSS); /* reading acks CMDCOMP */
#ifdef SILI_DEBUG
if ((pss_saved & SILI_PREG_PSS_ALL_SLOTS) != sp->sp_active ||
@@ -237,6 +233,7 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
u_int32_t sactive = 0;
bus_size_t r;
+ sili_pwrite(sp, SILI_PREG_IS, SILI_PREG_IS_CMDERR);
err_slot = SILI_PREG_PCS_ACTIVE(sili_pread(sp, SILI_PREG_PCS));
err_code = sili_pread(sp, SILI_PREG_CE);
@@ -298,6 +295,22 @@ sili_port_intr(struct sili_port *sp, u_int32_t slotmask)
}
fatal:
+ /* Process command timeout request only if command is still active. */
+ if (timeout_slot >= 0 && (pss_saved & (1 << timeout_slot))) {
+ DPRINTF(SILI_D_VERBOSE, "%s: timing out slot %d, active %08x\n",
+ PORTNAME(sp), timeout_slot, sp->sp_active);
+
+ /* Clear the failed commmand in saved PSS so cmd_done runs. */
+ pss_saved &= ~(1 << timeout_slot);
+
+ ccb = &sp->sp_ccbs[timeout_slot];
+ KASSERT(ccb->ccb_xa.state == ATA_S_ONCHIP);
+ ccb->ccb_xa.state = ATA_S_TIMEOUT;
+
+ /* Reset device to abort all commands (including this one). */
+ need_restart = SILI_PREG_PCS_DEVRESET;
+ }
+
/* Command slot is complete if its bit in PSS is 0 but 1 in active. */
pss_masked = ~pss_saved & sp->sp_active;
while (pss_masked) {
@@ -307,7 +320,8 @@ fatal:
DPRINTF(SILI_D_INTR, "%s: slot %d is complete%s\n",
PORTNAME(sp), slot, ccb->ccb_xa.state == ATA_S_ERROR ?
- " (error)" : "");
+ " (error)" : (ccb->ccb_xa.state == ATA_S_TIMEOUT ?
+ " (timeout)" : ""));
sili_ata_cmd_done(ccb, need_restart);
@@ -351,7 +365,8 @@ fatal:
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.state == ATA_S_ERROR ||
+ ccb->ccb_xa.state == ATA_S_TIMEOUT);
ccb->ccb_xa.complete(&ccb->ccb_xa);
}
}
@@ -374,7 +389,7 @@ sili_intr(void *arg)
while (is & SILI_REG_GIS_PIS_MASK) {
port = ffs(is) - 1;
- sili_port_intr(&sc->sc_ports[port], SILI_PREG_PSS_ALL_SLOTS);
+ sili_port_intr(&sc->sc_ports[port], -1);
is &= ~(1 << port);
}
@@ -709,7 +724,6 @@ sili_ata_probe(void *xsc, int port)
sili_pwrite(sp, SILI_PREG_PCC, SILI_PREG_PCC_PORTRESET);
sili_pwrite(sp, SILI_PREG_PCC, SILI_PREG_PCC_A32B);
- sili_pwrite(sp, SILI_PREG_PCS, SILI_PREG_PCS_NOINTCLR);
if (!sili_pwait_eq(sp, SILI_PREG_SSTS, SATA_SStatus_DET,
SATA_SStatus_DET_DEV, 1000))
@@ -853,14 +867,23 @@ sili_ata_cmd_done(struct sili_ccb *ccb, int defer_completion)
TAILQ_INSERT_TAIL(&sp->sp_deferred_ccbs, ccb, ccb_entry);
else if (xa->state == ATA_S_COMPLETE)
xa->complete(xa);
+#ifdef DIAGNOSTIC
+ else
+ printf("%s: completion not deferred, but xa->state is %02x?\n",
+ PORTNAME(sp), xa->state);
+#endif
}
void
sili_ata_cmd_timeout(void *xccb)
{
struct sili_ccb *ccb = xccb;
+ struct sili_port *sp = ccb->ccb_port;
+ int s;
- printf("%s: ccb %p timed out\n", PORTNAME(ccb->ccb_port), ccb);
+ s = splbio();
+ sili_port_intr(sp, ccb->ccb_xa.tag);
+ splx(s);
}
int
@@ -956,8 +979,7 @@ sili_poll(struct sili_ccb *ccb, int timeout, void (*timeout_fn)(void *))
s = splbio();
sili_start(sp, ccb);
do {
- if (ISSET(sili_port_intr(sp, SILI_PREG_PSS_ALL_SLOTS),
- 1 << ccb->ccb_xa.tag)) {
+ if (sili_port_intr(sp, -1) & (1 << ccb->ccb_xa.tag)) {
splx(s);
return (0);
}