summaryrefslogtreecommitdiff
path: root/sys/dev/atapiscsi/atapiscsi.c
diff options
context:
space:
mode:
authorConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>2001-07-31 06:14:06 +0000
committerConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>2001-07-31 06:14:06 +0000
commit14a2cd20d08133deb909c4b5f5efc3d5230ec84b (patch)
tree03e3ed814ca11fe1c285b938253a53af889b79b9 /sys/dev/atapiscsi/atapiscsi.c
parent7b7c3f33cbbf0a158f7555eecbdbc09f48d46008 (diff)
On DMA timeouts, stop busmaster PCIIDE and reset channel. Avoid reading
device registers as this may cause lock-ups on some chipsets Clean-up in atapiscsi. Get rid of claim_irq. Instead, atapiscsi always returns -1 (maybe) from the interrupt handler.
Diffstat (limited to 'sys/dev/atapiscsi/atapiscsi.c')
-rw-r--r--sys/dev/atapiscsi/atapiscsi.c205
1 files changed, 122 insertions, 83 deletions
diff --git a/sys/dev/atapiscsi/atapiscsi.c b/sys/dev/atapiscsi/atapiscsi.c
index 27581c27e23..2a1a8c586a8 100644
--- a/sys/dev/atapiscsi/atapiscsi.c
+++ b/sys/dev/atapiscsi/atapiscsi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: atapiscsi.c,v 1.49 2001/07/21 09:08:49 csapuntz Exp $ */
+/* $OpenBSD: atapiscsi.c,v 1.50 2001/07/31 06:14:05 csapuntz Exp $ */
/*
* This code is derived from code with the copyright below.
@@ -92,10 +92,10 @@ enum atapi_drive_states {
#define DEBUG_ERRORS 0x80 /* Debug error handling code */
#if defined(WDCDEBUG)
-int wdcdebug_atapi_mask = 0x0;
+int wdcdebug_atapi_mask = 0;
#define WDCDEBUG_PRINT(args, level) \
if (wdcdebug_atapi_mask & (level)) \
- printf args
+ lprintf args
#else
#define WDCDEBUG_PRINT(args, level)
#endif
@@ -123,7 +123,7 @@ void wdc_atapi_intr_data __P((struct channel_softc *, struct wdc_xfer *,
int, struct atapi_return_args *));
void wdc_atapi_intr_complete __P((struct channel_softc *, struct wdc_xfer *,
int, struct atapi_return_args *));
-void wdc_atapi_intr_for_us __P((struct channel_softc *, struct wdc_xfer *,
+void wdc_atapi_pio_intr __P((struct channel_softc *, struct wdc_xfer *,
int, struct atapi_return_args *));
void wdc_atapi_send_packet __P((struct channel_softc *, struct wdc_xfer *,
int, struct atapi_return_args *));
@@ -155,7 +155,7 @@ struct atapiscsi_softc {
struct device sc_dev;
struct scsi_link sc_adapterlink;
struct channel_softc *chp;
- enum atapi_state { as_none, as_cmdout, as_data, as_completed };
+ enum atapi_state { as_none, as_data, as_completed };
enum atapi_state protocol_phase;
int drive;
@@ -315,8 +315,9 @@ wdc_atapi_send_cmd(sc_xfer)
struct ata_drive_datas *drvp = &chp->ch_drive[as->drive];
struct wdc_xfer *xfer;
int s, ret;
+ int idx;
- WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d\n",
+ WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d start\n",
chp->wdc->sc_dev.dv_xname, chp->channel, as->drive), DEBUG_XFERS);
if (sc_xfer->sc_link->target != 0) {
@@ -341,6 +342,17 @@ wdc_atapi_send_cmd(sc_xfer)
timeout_set(&xfer->atapi_poll_to, wdc_atapi_timer_handler, chp);
+ WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d ",
+ chp->wdc->sc_dev.dv_xname, chp->channel, as->drive),
+ DEBUG_XFERS | DEBUG_ERRORS);
+
+ for (idx = 0; idx < sc_xfer->cmdlen; idx++) {
+ WDCDEBUG_PRINT((" %02x",
+ ((unsigned char *)sc_xfer->cmd)[idx]),
+ DEBUG_XFERS | DEBUG_ERRORS);
+ }
+ WDCDEBUG_PRINT(("\n"), DEBUG_XFERS | DEBUG_ERRORS);
+
s = splbio();
if (drvp->atapi_cap & ACAP_DSC) {
@@ -499,10 +511,10 @@ enum atapi_context {
ctxt_interrupt = 2
};
-int wdc_atapi_the_machine __P((struct channel_softc *, struct wdc_xfer *,
+void wdc_atapi_the_machine __P((struct channel_softc *, struct wdc_xfer *,
enum atapi_context));
-int wdc_atapi_the_poll_machine __P((struct channel_softc *, struct wdc_xfer *));
+void wdc_atapi_the_poll_machine __P((struct channel_softc *, struct wdc_xfer *));
void
wdc_atapi_start(chp, xfer)
@@ -549,22 +561,24 @@ wdc_atapi_intr(chp, xfer, irq)
/* XXX we should consider an alternate signaling regime here */
if (xfer->c_flags & C_TIMEOU) {
xfer->c_flags &= ~C_TIMEOU;
- return (wdc_atapi_the_machine(chp, xfer, ctxt_timer));
+ wdc_atapi_the_machine(chp, xfer, ctxt_timer);
+ return (0);
}
- return (wdc_atapi_the_machine(chp, xfer, ctxt_interrupt));
+ wdc_atapi_the_machine(chp, xfer, ctxt_interrupt);
+
+ return (-1);
}
struct atapi_return_args {
int timeout;
int delay;
int expect_irq;
- int claim_irq;
};
-#define ARGS_INIT {-1, 0, 0, -1}
+#define ARGS_INIT {-1, 0, 0}
-int
+void
wdc_atapi_the_poll_machine(chp, xfer)
struct channel_softc *chp;
struct wdc_xfer *xfer;
@@ -583,7 +597,7 @@ wdc_atapi_the_poll_machine(chp, xfer)
if (xfer->next == NULL) {
wdc_free_xfer(chp, xfer);
wdcstart(chp);
- return (0);
+ return;
}
if (retargs.timeout != -1) {
@@ -601,14 +615,13 @@ wdc_atapi_the_poll_machine(chp, xfer)
}
-int
+void
wdc_atapi_the_machine(chp, xfer, ctxt)
struct channel_softc *chp;
struct wdc_xfer *xfer;
enum atapi_context ctxt;
{
int idx = 0;
- int claim_irq = 0;
extern int ticks;
int timeout_delay = hz / 10;
@@ -617,11 +630,11 @@ wdc_atapi_the_machine(chp, xfer, ctxt)
if (ctxt == ctxt_interrupt)
xfer->endticks = 1;
- return (0);
+ return;
}
wdc_atapi_the_poll_machine(chp, xfer);
- return (0);
+ return;
}
/* Don't go through more than 50 state machine steps
@@ -642,12 +655,6 @@ wdc_atapi_the_machine(chp, xfer, ctxt)
xfer->endticks =
max((retargs.timeout * hz) / 1000, 1) + 1 + ticks;
- if (retargs.claim_irq) {
- claim_irq = retargs.claim_irq;
- if (chp->wdc->cap & WDC_CAPABILITY_IRQACK)
- chp->wdc->irqack(chp);
- }
-
if (xfer->next == NULL) {
if (xfer->c_flags & C_POLL_MACHINE)
timeout_del(&xfer->atapi_poll_to);
@@ -655,13 +662,13 @@ wdc_atapi_the_machine(chp, xfer, ctxt)
wdc_free_xfer(chp, xfer);
wdcstart(chp);
- return (claim_irq);
+ return;
}
if (retargs.expect_irq) {
chp->ch_flags |= WDCF_IRQ_WAIT;
timeout_add(&chp->ch_timo, xfer->endticks - ticks);
- return (claim_irq);
+ return;
}
if (retargs.delay != 0) {
@@ -675,7 +682,7 @@ wdc_atapi_the_machine(chp, xfer, ctxt)
timeout_add(&xfer->atapi_poll_to, timeout_delay);
xfer->c_flags |= C_POLL_MACHINE;
- return (claim_irq);
+ return;
}
@@ -709,10 +716,6 @@ wdc_atapi_real_start(chp, xfer, timeout, ret)
#endif
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
- WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x\n",
- chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive,
- sc_xfer->flags), DEBUG_XFERS);
-
/*
* Only set the DMA flag if the transfer is reasonably large.
* At least one older drive failed to complete a 4 byte DMA transfer.
@@ -720,7 +723,7 @@ wdc_atapi_real_start(chp, xfer, timeout, ret)
/* Turn off DMA flag on REQUEST SENSE */
- if (!(xfer->c_flags & (C_POLL | C_SENSE)) &&
+ if (!(xfer->c_flags & (C_POLL | C_SENSE | C_MEDIA_ACCESS)) &&
(drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) &&
(xfer->c_bcount > 100))
xfer->c_flags |= C_DMA;
@@ -735,6 +738,11 @@ wdc_atapi_real_start(chp, xfer, timeout, ret)
xfer->next = wdc_atapi_real_start_2;
ret->timeout = ATAPI_DELAY;
+ WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x, ATA flags 0x%x\n",
+ chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive,
+ sc_xfer->flags, xfer->c_flags), DEBUG_XFERS);
+
+
return;
}
@@ -785,6 +793,8 @@ wdc_atapi_send_packet(chp, xfer, timeout, ret)
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc;
+ wdc_enable_intr(chp);
+
/*
* Even with WDCS_ERR, the device should accept a command packet
* Limit length to what can be stuffed into the cylinder register
@@ -799,18 +809,21 @@ wdc_atapi_send_packet(chp, xfer, timeout, ret)
0, 0, 0,
(xfer->c_flags & C_DMA) ? ATAPI_PKT_CMD_FTRE_DMA : 0);
- as->protocol_phase = as_cmdout;
as->retries = 0;
DELAY(1);
- xfer->next = wdc_atapi_intr_for_us;
+ xfer->next = wdc_atapi_intr_command;
ret->timeout = sc_xfer->timeout;
if ((drvp->atapi_cap & ATAPI_CFG_DRQ_MASK) == ATAPI_CFG_IRQ_DRQ) {
/* We expect an IRQ to tell us of the next state */
ret->expect_irq = 1;
}
+
+ WDCDEBUG_PRINT(("wdc_atapi_send_packet %s:%d:%d command sent\n",
+ chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive
+ ), DEBUG_XFERS);
return;
}
@@ -830,6 +843,18 @@ wdc_atapi_intr_command(chp, xfer, timeout, ret)
int cmdlen = (drvp->atapi_cap & ACAP_LEN) ? 16 : 12;
int dma_flags = ((sc_xfer->flags & SCSI_DATA_IN) ||
(xfer->c_flags & C_SENSE)) ? WDC_DMA_READ : 0;
+
+ wdc_atapi_update_status(chp);
+
+ if ((chp->ch_status & WDCS_BSY) || !(chp->ch_status & WDCS_DRQ)) {
+ if (timeout)
+ goto timeout;
+
+ return;
+ }
+
+ if (chp->wdc->cap & WDC_CAPABILITY_IRQACK)
+ chp->wdc->irqack(chp);
bzero(cmd, sizeof(cmd));
@@ -851,7 +876,7 @@ wdc_atapi_intr_command(chp, xfer, timeout, ret)
xfer->c_bcount, dma_flags) != 0) {
sc_xfer->error = XS_DRIVER_STUFFUP;
- xfer->next = wdc_atapi_done;
+ xfer->next = wdc_atapi_reset;
return;
}
}
@@ -862,12 +887,15 @@ wdc_atapi_intr_command(chp, xfer, timeout, ret)
if (xfer->c_flags & C_DMA) {
(*chp->wdc->dma_start)(chp->wdc->dma_arg,
chp->channel, xfer->drive);
- }
+ xfer->next = wdc_atapi_intr_complete;
+ } else {
+ if (xfer->c_bcount == 0)
+ as->protocol_phase = as_completed;
+ else
+ as->protocol_phase = as_data;
- if (xfer->c_bcount == 0 || (xfer->c_flags & C_DMA))
- as->protocol_phase = as_completed;
- else
- as->protocol_phase = as_data;
+ xfer->next = wdc_atapi_pio_intr;
+ }
ret->expect_irq = 1;
@@ -891,6 +919,14 @@ wdc_atapi_intr_command(chp, xfer, timeout, ret)
}
return;
+
+ timeout:
+ printf ("%s:%d:%d: device timeout waiting to send SCSI packet\n",
+ chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive);
+
+ sc_xfer->error = XS_TIMEOUT;
+ xfer->next = wdc_atapi_reset;
+ return;
}
@@ -968,6 +1004,9 @@ wdc_atapi_intr_data(chp, xfer, timeout, ret)
Give the drive 100ms to get its house in order
before we try again. */
+ WDCDEBUG_PRINT(("wdc_atapi_intr: %s\n", message),
+ DEBUG_ERRORS);
+
if (!timeout) {
ret->delay = 100;
return;
@@ -1020,12 +1059,11 @@ wdc_atapi_intr_data(chp, xfer, timeout, ret)
}
ret->expect_irq = 1;
- xfer->next = wdc_atapi_intr_for_us;
+ xfer->next = wdc_atapi_pio_intr;
return;
}
-
void
wdc_atapi_intr_complete(chp, xfer, timeout, ret)
struct channel_softc *chp;
@@ -1040,14 +1078,41 @@ wdc_atapi_intr_complete(chp, xfer, timeout, ret)
WDCDEBUG_PRINT(("PHASE_COMPLETED\n"), DEBUG_INTR);
if (xfer->c_flags & C_DMA) {
+ int retry;
+
+ if (timeout) {
+ chp->wdc->dma_status =
+ (*chp->wdc->dma_finish)
+ (chp->wdc->dma_arg, chp->channel,
+ xfer->drive);
+
+ sc_xfer->error = XS_TIMEOUT;
+ drvp->n_dmaerrs++;
+
+ xfer->next = wdc_atapi_reset;
+ return;
+ }
+
+ for (retry = 5; retry > 0; retry--) {
+ wdc_atapi_update_status(chp);
+ if ((chp->ch_status & (WDCS_BSY | WDCS_DRQ)) == 0)
+ break;
+ DELAY(5);
+ }
+ if (retry == 0) {
+ ret->expect_irq = 1;
+ return;
+ }
+
chp->wdc->dma_status =
- (*chp->wdc->dma_finish)(chp->wdc->dma_arg, chp->channel,
+ (*chp->wdc->dma_finish)
+ (chp->wdc->dma_arg, chp->channel,
xfer->drive);
if (chp->wdc->dma_status & WDC_DMAST_UNDER)
xfer->c_bcount = 1;
else
- xfer->c_bcount = 0;
+ xfer->c_bcount = 0;
}
as->protocol_phase = as_none;
@@ -1068,7 +1133,7 @@ wdc_atapi_intr_complete(chp, xfer, timeout, ret)
*/
sc_xfer->error = XS_SHORTSENSE;
} else if (xfer->c_bcount < sizeof(sc_xfer->sense)) {
- /* use the sense we just read */
+ /* use the sense we just read */
sc_xfer->error = XS_SENSE;
} else {
/*
@@ -1133,19 +1198,16 @@ wdc_atapi_intr_complete(chp, xfer, timeout, ret)
}
void
-wdc_atapi_intr_for_us(chp, xfer, timeout, ret)
+wdc_atapi_pio_intr(chp, xfer, timeout, ret)
struct channel_softc *chp;
struct wdc_xfer *xfer;
int timeout;
struct atapi_return_args *ret;
{
struct scsi_xfer *sc_xfer = xfer->cmd;
- struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
struct atapiscsi_softc *as = sc_xfer->sc_link->adapter_softc;
u_int8_t ireason;
-#if 0
- WDCDEBUG_PRINT(("ATAPI_INTR\n"), DEBUG_INTR);
-#endif
+
wdc_atapi_update_status(chp);
if (chp->ch_status & WDCS_BSY) {
@@ -1157,6 +1219,7 @@ wdc_atapi_intr_for_us(chp, xfer, timeout, ret)
if (!wdc_atapi_drive_selected(chp, xfer->drive))
{
+ WDCDEBUG_PRINT(("wdc_atapi_intr_for_us: wrong drive selected\n"), DEBUG_INTR);
CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (xfer->drive << 4));
delay (1);
@@ -1164,8 +1227,7 @@ wdc_atapi_intr_for_us(chp, xfer, timeout, ret)
return;
}
- if (as->protocol_phase != as_cmdout &&
- (xfer->c_flags & C_MEDIA_ACCESS) &&
+ if ((xfer->c_flags & C_MEDIA_ACCESS) &&
!(chp->ch_status & (WDCS_DSC | WDCS_DRQ))) {
if (timeout)
goto timeout;
@@ -1174,23 +1236,13 @@ wdc_atapi_intr_for_us(chp, xfer, timeout, ret)
return;
}
- ret->claim_irq = -1;
+ if (chp->wdc->cap & WDC_CAPABILITY_IRQACK)
+ chp->wdc->irqack(chp);
ireason = CHP_READ_REG(chp, wdr_ireason);
- WDCDEBUG_PRINT(("(%x, %x) ", chp->ch_status, ireason), DEBUG_INTR );
+ WDCDEBUG_PRINT(("Phase %d, (%x, %x) ", as->protocol_phase, chp->ch_status, ireason), DEBUG_INTR );
switch (as->protocol_phase) {
- case as_cmdout:
- if (timeout)
- goto timeout;
-
- if (!(chp->ch_status & WDCS_DRQ)) {
- return;
- }
-
- wdc_atapi_intr_command(chp, xfer, timeout, ret);
- return;
-
case as_data:
if ((chp->ch_status & WDCS_DRQ) ||
(ireason & 3) != 3) {
@@ -1223,23 +1275,12 @@ wdc_atapi_intr_for_us(chp, xfer, timeout, ret)
return;
timeout:
- printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d\n",
- chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
- xfer->c_bcount, xfer->c_skip);
-
- if (xfer->c_flags & C_DMA) {
- ireason = CHP_READ_REG(chp, wdr_ireason);
+ ireason = CHP_READ_REG(chp, wdr_ireason);
- chp->wdc->dma_status =
- (*chp->wdc->dma_finish)(chp->wdc->dma_arg,
- chp->channel, xfer->drive);
-
- WDCDEBUG_PRINT(("Drive status: %02x %02x %02x\n",
- chp->ch_status, chp->ch_error, ireason),
- DEBUG_ERRORS);
-
- drvp->n_dmaerrs++;
- }
+ printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d, "
+ "status=%02x, ireason=%02x\n",
+ chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
+ xfer->c_bcount, xfer->c_skip, chp->ch_status, ireason);
sc_xfer->error = XS_TIMEOUT;
xfer->next = wdc_atapi_reset;
@@ -1325,8 +1366,6 @@ wdc_atapi_ctrl(chp, xfer, timeout, ret)
chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive, drvp->state),
DEBUG_INTR | DEBUG_FUNCS);
- ret->claim_irq = -1;
-
switch (drvp->state) {
/* My ATAPI slave device likes to assert DASP-/PDIAG- until
it is DEVICE RESET. This causes the LED to stay on.