summaryrefslogtreecommitdiff
path: root/sys/dev/ata
diff options
context:
space:
mode:
authorbouyer <bouyer@NetBSD.org>1999-08-06 12:00:23 +0000
committerbouyer <bouyer@NetBSD.org>1999-08-06 12:00:23 +0000
commitfae9929734603cdb453786d8d06e44e1d0674fc2 (patch)
tree4688ce17e79e267016ba10e055802dde29999973 /sys/dev/ata
parent54f2532106ef57336a675b068398fe8a3b64904a (diff)
- Add some debug printf (WDCPROBE) in _wdcreset_wait(), I've needed these
2 times in the past - Set up timeout per xfer instead of per interrupt. This helps with PIO transfer (we would call timeout()/untimout() several times for a transfer). - If we missed an interrupt for a PIO transfer, reset and restart it immedialy, don't try to recover and continue. If we missed an interrupt we may have lost a read/write cycle on the IDE bus. If this happens 1) we corrupt data and 2) we enter an interrupt loop at the end of the xfer, as the drive has some more data to read/write, but the host thinks the xfer is done. This last change fix the (or at last some of the) 'lookup after lost interrupt' some peoples have been experiencing.
Diffstat (limited to 'sys/dev/ata')
-rw-r--r--sys/dev/ata/ata_wdc.c49
1 files changed, 36 insertions, 13 deletions
diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c
index 14e74ad4eca..79a39234d1c 100644
--- a/sys/dev/ata/ata_wdc.c
+++ b/sys/dev/ata/ata_wdc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ata_wdc.c,v 1.19 1999/04/01 21:46:28 bouyer Exp $ */
+/* $NetBSD: ata_wdc.c,v 1.20 1999/08/06 12:00:23 bouyer Exp $ */
/*
* Copyright (c) 1998 Manuel Bouyer.
@@ -117,6 +117,7 @@ int wdcdebug_wd_mask = 0;
#define ATA_DELAY 10000 /* 10s for a drive I/O */
void wdc_ata_bio_start __P((struct channel_softc *,struct wdc_xfer *));
+void _wdc_ata_bio_start __P((struct channel_softc *,struct wdc_xfer *));
int wdc_ata_bio_intr __P((struct channel_softc *, struct wdc_xfer *, int));
void wdc_ata_bio_done __P((struct channel_softc *, struct wdc_xfer *));
int wdc_ata_ctrl_intr __P((struct channel_softc *, struct wdc_xfer *, int));
@@ -161,6 +162,22 @@ wdc_ata_bio_start(chp, xfer)
struct wdc_xfer *xfer;
{
struct ata_bio *ata_bio = xfer->cmd;
+ WDCDEBUG_PRINT(("wdc_ata_bio_start %s:%d:%d\n",
+ chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive),
+ DEBUG_XFERS);
+
+ /* start timeout machinery */
+ if ((ata_bio->flags & ATA_POLL) == 0)
+ timeout(wdctimeout, chp, ATA_DELAY / 1000 * hz);
+ _wdc_ata_bio_start(chp, xfer);
+}
+
+void
+_wdc_ata_bio_start(chp, xfer)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+{
+ struct ata_bio *ata_bio = xfer->cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
u_int16_t cyl;
u_int8_t head, sect, cmd = 0;
@@ -168,10 +185,9 @@ wdc_ata_bio_start(chp, xfer)
int ata_delay;
int dma_flags = 0;
- WDCDEBUG_PRINT(("wdc_ata_bio_start %s:%d:%d\n",
+ WDCDEBUG_PRINT(("_wdc_ata_bio_start %s:%d:%d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive),
- DEBUG_XFERS);
-
+ DEBUG_INTR | DEBUG_XFERS);
/* Do control operations specially. */
if (drvp->state < READY) {
/*
@@ -181,10 +197,10 @@ wdc_ata_bio_start(chp, xfer)
*/
/* at this point, we should only be in RECAL state */
if (drvp->state != RECAL) {
- printf("%s:%d:%d: bad state %d in wdc_ata_bio_start\n",
+ printf("%s:%d:%d: bad state %d in _wdc_ata_bio_start\n",
chp->wdc->sc_dev.dv_xname, chp->channel,
xfer->drive, drvp->state);
- panic("wdc_ata_bio_start: bad state");
+ panic("_wdc_ata_bio_start: bad state");
}
xfer->c_intr = wdc_ata_ctrl_intr;
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
@@ -195,8 +211,6 @@ wdc_ata_bio_start(chp, xfer)
drvp->state = RECAL_WAIT;
if ((ata_bio->flags & ATA_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp,
- ATA_DELAY / 1000 * hz);
} else {
/* Wait for at last 400ns for status bit to be valid */
DELAY(1);
@@ -368,7 +382,6 @@ again:
intr: /* Wait for IRQ (either real or polled) */
if ((ata_bio->flags & ATA_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, ata_delay / 1000 * hz);
} else {
/* Wait for at last 400ns for status bit to be valid */
delay(1);
@@ -416,6 +429,16 @@ wdc_ata_bio_intr(chp, xfer, irq)
dma_flags |= (ata_bio->flags & ATA_POLL) ? WDC_DMA_POLL : 0;
}
+ /*
+ * if we missed an interrupt in a PIO transfer, reset and restart.
+ * Don't try to continue transfer, we may have missed cycles.
+ */
+ if ((xfer->c_flags & (C_TIMEOU | C_DMA)) == C_TIMEOU) {
+ ata_bio->error = TIMEOUT;
+ wdc_ata_bio_done(chp, xfer);
+ return 1;
+ }
+
/* Ack interrupt done by wait_for_unbusy */
if (wait_for_unbusy(chp,
(irq == 0) ? ATA_DELAY : 0) < 0) {
@@ -532,9 +555,9 @@ end:
if (xfer->c_bcount > 0) {
if ((ata_bio->flags & ATA_POLL) == 0) {
/* Start the next operation */
- wdc_ata_bio_start(chp, xfer);
+ _wdc_ata_bio_start(chp, xfer);
} else {
- /* Let wdc_ata_bio_start do the loop */
+ /* Let _wdc_ata_bio_start do the loop */
return 1;
}
} else { /* Done with this transfer */
@@ -559,6 +582,7 @@ wdc_ata_bio_done(chp, xfer)
(u_int)xfer->c_flags),
DEBUG_XFERS);
+ untimeout(wdctimeout, chp);
if (ata_bio->error == NOERROR)
drvp->n_dmaerrs = 0;
else if (drvp->n_dmaerrs >= NERRS_MAX) {
@@ -696,13 +720,12 @@ again:
* The drive is usable now
*/
xfer->c_intr = wdc_ata_bio_intr;
- wdc_ata_bio_start(chp, xfer);
+ _wdc_ata_bio_start(chp, xfer);
return 1;
}
if ((ata_bio->flags & ATA_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, ATA_DELAY / 1000 * hz);
} else {
goto again;
}