diff options
| author | bouyer <bouyer@NetBSD.org> | 1999-08-06 12:00:23 +0000 |
|---|---|---|
| committer | bouyer <bouyer@NetBSD.org> | 1999-08-06 12:00:23 +0000 |
| commit | fae9929734603cdb453786d8d06e44e1d0674fc2 (patch) | |
| tree | 4688ce17e79e267016ba10e055802dde29999973 /sys/dev | |
| parent | 54f2532106ef57336a675b068398fe8a3b64904a (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')
| -rw-r--r-- | sys/dev/ata/ata_wdc.c | 49 | ||||
| -rw-r--r-- | sys/dev/ic/wdc.c | 35 | ||||
| -rw-r--r-- | sys/dev/scsipi/atapi_wdc.c | 22 |
3 files changed, 85 insertions, 21 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; } diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c index 2dc11adc4f3..b858ee32164 100644 --- a/sys/dev/ic/wdc.c +++ b/sys/dev/ic/wdc.c @@ -1,4 +1,4 @@ -/* $NetBSD: wdc.c,v 1.69 1999/07/30 14:59:10 bouyer Exp $ */ +/* $NetBSD: wdc.c,v 1.70 1999/08/06 12:00:25 bouyer Exp $ */ /* @@ -533,7 +533,6 @@ wdcintr(arg) } WDCDEBUG_PRINT(("wdcintr\n"), DEBUG_INTR); - untimeout(wdctimeout, chp); chp->ch_flags &= ~WDCF_IRQ_WAIT; xfer = chp->ch_queue->sc_xfer.tqh_first; return xfer->c_intr(chp, xfer, 1); @@ -595,16 +594,32 @@ __wdcwait_reset(chp, drv_mask) { int timeout; u_int8_t st0, st1; +#ifdef WDCDEBUG + u_int8_t sc0, sn0, cl0, ch0; + u_int8_t sc1, sn1, cl1, ch1; +#endif /* wait for BSY to deassert */ for (timeout = 0; timeout < WDCNDELAY_RST;timeout++) { bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, WDSD_IBM); /* master */ delay(10); st0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); +#ifdef WDCDEBUG + sc0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt); + sn0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_sector); + cl0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo); + ch0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi); +#endif bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, WDSD_IBM | 0x10); /* slave */ delay(10); st1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status); +#ifdef WDCDEBUG + sc1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt); + sn1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_sector); + cl1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo); + ch1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi); +#endif if ((drv_mask & 0x01) == 0) { /* no master */ @@ -632,6 +647,15 @@ __wdcwait_reset(chp, drv_mask) if (st1 & WDCS_BSY) drv_mask &= ~0x02; end: + WDCDEBUG_PRINT(("%s:%d:0: after reset, sc=0x%x sn=0x%x " + "cl=0x%x ch=0x%x\n", + chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", + chp->channel, sc0, sn0, cl0, ch0), DEBUG_PROBE); + WDCDEBUG_PRINT(("%s:%d:1: after reset, sc=0x%x sn=0x%x " + "cl=0x%x ch=0x%x\n", + chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", + chp->channel, sc1, sn1, cl1, ch1), DEBUG_PROBE); + WDCDEBUG_PRINT(("%s:%d: wdcwait_reset() end, st0=0x%x, st1=0x%x\n", chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel, st0, st1), DEBUG_PROBE); @@ -717,7 +741,11 @@ wdctimeout(arg) * Call the interrupt routine. If we just missed and interrupt, * it will do what's needed. Else, it will take the needed * action (reset the device). + * Before that we need to reinstall the timeout callback, + * in case it will miss another irq while in this transfer + * We arbitray chose it to be 1s */ + timeout(wdctimeout, chp, hz); xfer->c_flags |= C_TIMEOU; chp->ch_flags &= ~WDCF_IRQ_WAIT; xfer->c_intr(chp, xfer, 1); @@ -1121,6 +1149,9 @@ __wdccommand_done(chp, xfer) WDCDEBUG_PRINT(("__wdccommand_done %s:%d:%d\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_FUNCS); + + untimeout(wdctimeout, chp); + if (chp->ch_status & WDCS_DWF) wdc_c->flags |= AT_DF; if (chp->ch_status & WDCS_ERR) { diff --git a/sys/dev/scsipi/atapi_wdc.c b/sys/dev/scsipi/atapi_wdc.c index 0db4d23ea55..e118708cd05 100644 --- a/sys/dev/scsipi/atapi_wdc.c +++ b/sys/dev/scsipi/atapi_wdc.c @@ -1,4 +1,4 @@ -/* $NetBSD: atapi_wdc.c,v 1.23 1999/05/05 21:35:15 perry Exp $ */ +/* $NetBSD: atapi_wdc.c,v 1.24 1999/08/06 12:00:26 bouyer Exp $ */ /* * Copyright (c) 1998 Manuel Bouyer. @@ -238,6 +238,9 @@ wdc_atapi_start(chp, xfer) xfer->c_flags |= C_DMA; else xfer->c_flags &= ~C_DMA; + /* start timeout machinery */ + if ((sc_xfer->flags & SCSI_POLL) == 0) + timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); /* Do control operations specially. */ if (drvp->state < READY) { if (drvp->state != PIOMODE) { @@ -285,7 +288,6 @@ wdc_atapi_start(chp, xfer) wdc_atapi_intr(chp, xfer, 0); } else { chp->ch_flags |= WDCF_IRQ_WAIT; - timeout(wdctimeout, chp, hz); } if (sc_xfer->flags & SCSI_POLL) { while ((sc_xfer->flags & ITSDONE) == 0) { @@ -322,6 +324,15 @@ wdc_atapi_intr(chp, xfer, irq) drvp->state); panic("wdc_atapi_intr: bad state\n"); } + /* + * 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) { + sc_xfer->error = XS_TIMEOUT; + wdc_atapi_reset(chp, xfer); + } + /* Ack interrupt done in wait_for_unbusy */ bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh, WDSD_IBM | (xfer->drive << 4)); @@ -425,7 +436,6 @@ again: if ((sc_xfer->flags & SCSI_POLL) == 0) { chp->ch_flags |= WDCF_IRQ_WAIT; - timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); } return 1; @@ -503,7 +513,6 @@ again: } if ((sc_xfer->flags & SCSI_POLL) == 0) { chp->ch_flags |= WDCF_IRQ_WAIT; - timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); } return 1; @@ -580,7 +589,6 @@ again: } if ((sc_xfer->flags & SCSI_POLL) == 0) { chp->ch_flags |= WDCF_IRQ_WAIT; - timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); } return 1; @@ -636,6 +644,7 @@ again: sizeof(sc_xfer->sense.scsi_sense); xfer->c_skip = 0; xfer->c_flags |= C_SENSE; + untimeout(wdctimeout, chp); wdc_atapi_start(chp, xfer); return 1; } @@ -753,13 +762,13 @@ piomode: ready: drvp->state = READY; xfer->c_intr = wdc_atapi_intr; + untimeout(wdctimeout, chp); wdc_atapi_start(chp, xfer); return 1; } if ((sc_xfer->flags & SCSI_POLL) == 0) { chp->ch_flags |= WDCF_IRQ_WAIT; xfer->c_intr = wdc_atapi_ctrl; - timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000); } else { goto again; } @@ -797,6 +806,7 @@ wdc_atapi_done(chp, xfer) WDCDEBUG_PRINT(("wdc_atapi_done %s:%d:%d: flags 0x%x\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, (u_int)xfer->c_flags), DEBUG_XFERS); + untimeout(wdctimeout, chp); /* remove this command from xfer queue */ wdc_free_xfer(chp, xfer); sc_xfer->flags |= ITSDONE; |
