Keep track of CRC errors in Ultra-DMA mode. If we noticed a CRC error and we
need to downgrade, downgrade to PIO, as it has been shown if we got CRC errors in Ultra-DMA mode, we will have silent data corruption in multiword DMA mode (isn't IDE wonderfull ? :). Set timeout to 1s for "normal" ata I/O, to minimise the effects of missed interrupts.
This commit is contained in:
parent
c8126918cb
commit
ba74f49e60
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: ata_wdc.c,v 1.15 1999/02/21 00:52:04 hubertf Exp $ */
|
||||
/* $NetBSD: ata_wdc.c,v 1.16 1999/03/07 14:02:53 bouyer Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Manuel Bouyer.
|
||||
|
@ -114,13 +114,14 @@ int wdcdebug_wd_mask = 0;
|
|||
#define WDCDEBUG_PRINT(args, level)
|
||||
#endif
|
||||
|
||||
#define ATA_DELAY 10000 /* 10s for a drive I/O */
|
||||
#define ATA_DELAY_NORMAL 1000 /* 1s for a normal drive I/O */
|
||||
#define ATA_DELAY_RECOVERY 10000 /* 10s for setup or recovery drive I/O */
|
||||
|
||||
void wdc_ata_bio_start __P((struct channel_softc *,struct wdc_xfer *));
|
||||
int wdc_ata_bio_intr __P((struct channel_softc *, struct wdc_xfer *));
|
||||
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 wdc_ata_err __P((struct channel_softc *, struct ata_bio *));
|
||||
int wdc_ata_err __P((struct ata_drive_datas *, struct ata_bio *));
|
||||
#define WDC_ATA_NOERR 0x00 /* Drive doesn't report an error */
|
||||
#define WDC_ATA_RECOV 0x01 /* There was a recovered error */
|
||||
#define WDC_ATA_ERR 0x02 /* Drive reports an error */
|
||||
|
@ -165,6 +166,7 @@ wdc_ata_bio_start(chp, xfer)
|
|||
u_int16_t cyl;
|
||||
u_int8_t head, sect, cmd = 0;
|
||||
int nblks;
|
||||
int ata_delay;
|
||||
int dma_flags = 0;
|
||||
|
||||
WDCDEBUG_PRINT(("wdc_ata_bio_start %s:%d:%d\n",
|
||||
|
@ -188,13 +190,14 @@ wdc_ata_bio_start(chp, xfer)
|
|||
xfer->c_intr = wdc_ata_ctrl_intr;
|
||||
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
|
||||
WDSD_IBM | (xfer->drive << 4));
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY) != 0)
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY_RECOVERY) != 0)
|
||||
goto timeout;
|
||||
wdccommandshort(chp, xfer->drive, WDCC_RECAL);
|
||||
drvp->state = RECAL_WAIT;
|
||||
if ((ata_bio->flags & ATA_POLL) == 0) {
|
||||
chp->ch_flags |= WDCF_IRQ_WAIT;
|
||||
timeout(wdctimeout, chp, ATA_DELAY / 1000 * hz);
|
||||
timeout(wdctimeout, chp,
|
||||
ATA_DELAY_RECOVERY / 1000 * hz);
|
||||
} else {
|
||||
/* Wait for at last 400ns for status bit to be valid */
|
||||
delay(1);
|
||||
|
@ -207,6 +210,10 @@ wdc_ata_bio_start(chp, xfer)
|
|||
dma_flags = (ata_bio->flags & ATA_READ) ? WDC_DMA_READ : 0;
|
||||
dma_flags |= (ata_bio->flags & ATA_POLL) ? WDC_DMA_POLL : 0;
|
||||
}
|
||||
if (ata_bio->flags & ATA_SINGLE)
|
||||
ata_delay = ATA_DELAY_RECOVERY;
|
||||
else
|
||||
ata_delay = ATA_DELAY_NORMAL;
|
||||
again:
|
||||
/*
|
||||
*
|
||||
|
@ -276,7 +283,7 @@ again:
|
|||
/* Initiate command */
|
||||
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
|
||||
WDSD_IBM | (xfer->drive << 4));
|
||||
if (wait_for_ready(chp, ATA_DELAY) < 0)
|
||||
if (wait_for_ready(chp, ata_delay) < 0)
|
||||
goto timeout;
|
||||
wdccommand(chp, xfer->drive, cmd, cyl,
|
||||
head, sect, nblks, 0);
|
||||
|
@ -298,7 +305,7 @@ again:
|
|||
/* Initiate command! */
|
||||
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
|
||||
WDSD_IBM | (xfer->drive << 4));
|
||||
if (wait_for_ready(chp, ATA_DELAY) < 0)
|
||||
if (wait_for_ready(chp, ata_delay) < 0)
|
||||
goto timeout;
|
||||
wdccommand(chp, xfer->drive, cmd, cyl,
|
||||
head, sect, nblks,
|
||||
|
@ -314,17 +321,17 @@ again:
|
|||
}
|
||||
/* If this was a write and not using DMA, push the data. */
|
||||
if ((ata_bio->flags & ATA_READ) == 0) {
|
||||
if (wait_for_drq(chp, ATA_DELAY) != 0) {
|
||||
if (wait_for_drq(chp, ata_delay) != 0) {
|
||||
printf("%s:%d:%d: timeout waiting for DRQ, "
|
||||
"st=0x%02x, err=0x%02x\n",
|
||||
chp->wdc->sc_dev.dv_xname, chp->channel,
|
||||
xfer->drive, chp->ch_status, chp->ch_error);
|
||||
if (wdc_ata_err(chp, ata_bio) != WDC_ATA_ERR)
|
||||
if (wdc_ata_err(drvp, ata_bio) != WDC_ATA_ERR)
|
||||
ata_bio->error = TIMEOUT;
|
||||
wdc_ata_bio_done(chp, xfer);
|
||||
return;
|
||||
}
|
||||
if (wdc_ata_err(chp, ata_bio) == WDC_ATA_ERR) {
|
||||
if (wdc_ata_err(drvp, ata_bio) == WDC_ATA_ERR) {
|
||||
wdc_ata_bio_done(chp, xfer);
|
||||
return;
|
||||
}
|
||||
|
@ -362,7 +369,7 @@ 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);
|
||||
timeout(wdctimeout, chp, ata_delay / 1000 * hz);
|
||||
} else {
|
||||
/* Wait for at last 400ns for status bit to be valid */
|
||||
delay(1);
|
||||
|
@ -375,7 +382,7 @@ timeout:
|
|||
printf("%s:%d:%d: not ready, st=0x%02x, err=0x%02x\n",
|
||||
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
|
||||
chp->ch_status, chp->ch_error);
|
||||
if (wdc_ata_err(chp, ata_bio) != WDC_ATA_ERR)
|
||||
if (wdc_ata_err(drvp, ata_bio) != WDC_ATA_ERR)
|
||||
ata_bio->error = TIMEOUT;
|
||||
wdc_ata_bio_done(chp, xfer);
|
||||
return;
|
||||
|
@ -410,7 +417,7 @@ wdc_ata_bio_intr(chp, xfer)
|
|||
}
|
||||
|
||||
/* Ack interrupt done by wait_for_unbusy */
|
||||
if (wait_for_unbusy(chp, ATA_DELAY) < 0) {
|
||||
if (wait_for_unbusy(chp, ATA_DELAY_NORMAL) < 0) {
|
||||
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);
|
||||
|
@ -425,7 +432,7 @@ wdc_ata_bio_intr(chp, xfer)
|
|||
return 1;
|
||||
}
|
||||
|
||||
drv_err = wdc_ata_err(chp, ata_bio);
|
||||
drv_err = wdc_ata_err(drvp, ata_bio);
|
||||
|
||||
/* If we were using DMA, Turn off the DMA channel and check for error */
|
||||
if (xfer->c_flags & C_DMA) {
|
||||
|
@ -437,7 +444,7 @@ wdc_ata_bio_intr(chp, xfer)
|
|||
* asserted for DMA transfers, so poll for DRDY.
|
||||
*/
|
||||
if (wdcwait(chp, WDCS_DRDY | WDCS_DRQ, WDCS_DRDY,
|
||||
ATA_DELAY) < 0) {
|
||||
ATA_DELAY_NORMAL) < 0) {
|
||||
printf("%s:%d:%d: polled transfer timed out "
|
||||
"(st=0x%x)\n", chp->wdc->sc_dev.dv_xname,
|
||||
chp->channel, xfer->drive, chp->ch_status);
|
||||
|
@ -593,7 +600,7 @@ again:
|
|||
|
||||
case RECAL_WAIT:
|
||||
errstring = "recal";
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY))
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY_RECOVERY))
|
||||
goto timeout;
|
||||
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
|
||||
goto error;
|
||||
|
@ -613,7 +620,7 @@ again:
|
|||
|
||||
case PIOMODE_WAIT:
|
||||
errstring = "piomode";
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY))
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY_RECOVERY))
|
||||
goto timeout;
|
||||
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
|
||||
goto error;
|
||||
|
@ -633,7 +640,7 @@ again:
|
|||
break;
|
||||
case DMAMODE_WAIT:
|
||||
errstring = "dmamode";
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY))
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY_RECOVERY))
|
||||
goto timeout;
|
||||
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
|
||||
goto error;
|
||||
|
@ -653,7 +660,7 @@ again:
|
|||
|
||||
case GEOMETRY_WAIT:
|
||||
errstring = "geometry";
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY))
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY_RECOVERY))
|
||||
goto timeout;
|
||||
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
|
||||
goto error;
|
||||
|
@ -670,7 +677,7 @@ again:
|
|||
|
||||
case MULTIMODE_WAIT:
|
||||
errstring = "setmulti";
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY))
|
||||
if (wdcwait(chp, WDCS_DRDY, WDCS_DRDY, ATA_DELAY_RECOVERY))
|
||||
goto timeout;
|
||||
if (chp->ch_status & (WDCS_ERR | WDCS_DWF))
|
||||
goto error;
|
||||
|
@ -689,7 +696,7 @@ again:
|
|||
|
||||
if ((ata_bio->flags & ATA_POLL) == 0) {
|
||||
chp->ch_flags |= WDCF_IRQ_WAIT;
|
||||
timeout(wdctimeout, chp, ATA_DELAY / 1000 * hz);
|
||||
timeout(wdctimeout, chp, ATA_DELAY_RECOVERY / 1000 * hz);
|
||||
} else {
|
||||
goto again;
|
||||
}
|
||||
|
@ -723,10 +730,11 @@ error:
|
|||
}
|
||||
|
||||
int
|
||||
wdc_ata_err(chp, ata_bio)
|
||||
struct channel_softc *chp;
|
||||
wdc_ata_err(drvp, ata_bio)
|
||||
struct ata_drive_datas *drvp;
|
||||
struct ata_bio *ata_bio;
|
||||
{
|
||||
struct channel_softc *chp = drvp->chnl_softc;
|
||||
ata_bio->error = 0;
|
||||
if (chp->ch_status & WDCS_BSY) {
|
||||
ata_bio->error = TIMEOUT;
|
||||
|
@ -741,6 +749,14 @@ wdc_ata_err(chp, ata_bio)
|
|||
if (chp->ch_status & WDCS_ERR) {
|
||||
ata_bio->error = ERROR;
|
||||
ata_bio->r_error = chp->ch_error;
|
||||
if (drvp->drive_flags & DRIVE_UDMA &&
|
||||
(ata_bio->r_error & WDCE_CRC)) {
|
||||
/*
|
||||
* Record the CRC error, to avoid downgrading to
|
||||
* multiword DMA
|
||||
*/
|
||||
drvp->drive_flags |= DRIVE_DMAERR;
|
||||
}
|
||||
if (ata_bio->r_error & (WDCE_BBK | WDCE_UNC | WDCE_IDNF |
|
||||
WDCE_ABRT | WDCE_TK0NF | WDCE_AMNF))
|
||||
return WDC_ATA_ERR;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: atavar.h,v 1.11 1999/01/29 11:36:20 bouyer Exp $ */
|
||||
/* $NetBSD: atavar.h,v 1.12 1999/03/07 14:02:53 bouyer Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998 Manuel Bouyer.
|
||||
|
@ -48,6 +48,7 @@ struct ata_drive_datas {
|
|||
#define DRIVE_UDMA 0x10
|
||||
#define DRIVE_MODE 0x20 /* the drive reported its mode */
|
||||
#define DRIVE_RESET 0x40 /* reset the drive state at next xfer */
|
||||
#define DRIVE_DMAERR 0x80 /* Udma transfer had crc error, don't try DMA */
|
||||
/*
|
||||
* Current setting of drive's PIO, DMA and UDMA modes.
|
||||
* Is initialised by the disks drivers at attach time, and may be
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: wdc.c,v 1.60 1999/02/21 02:07:52 abs Exp $ */
|
||||
/* $NetBSD: wdc.c,v 1.61 1999/03/07 14:02:54 bouyer Exp $ */
|
||||
|
||||
|
||||
/*
|
||||
|
@ -943,14 +943,22 @@ wdc_downgrade_mode(drvp)
|
|||
(cf_flags & ATA_CONFIG_UDMA_SET))
|
||||
return 0;
|
||||
|
||||
if (drvp->drive_flags & DRIVE_UDMA) {
|
||||
/*
|
||||
* If we were using ultra-DMA, don't downgrade to multiword DMA
|
||||
* if we noticed a CRC error. It has been noticed that CRC errors
|
||||
* in ultra-DMA lead to silent data corruption in multiword DMA.
|
||||
* Data corruption is less likely to occur in PIO mode.
|
||||
*/
|
||||
|
||||
if ((drvp->drive_flags & DRIVE_UDMA) &&
|
||||
(drvp->drive_flags & DRIVE_DMAERR) == 0) {
|
||||
drvp->drive_flags &= ~DRIVE_UDMA;
|
||||
drvp->drive_flags |= DRIVE_DMA;
|
||||
drvp->DMA_mode = drvp->DMA_cap;
|
||||
printf("%s: transfer error, downgrading to DMA mode %d\n",
|
||||
drv_dev->dv_xname, drvp->DMA_mode);
|
||||
} else if (drvp->drive_flags & DRIVE_DMA) {
|
||||
drvp->drive_flags &= ~DRIVE_DMA;
|
||||
} else if (drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) {
|
||||
drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA);
|
||||
drvp->PIO_mode = drvp->PIO_cap;
|
||||
printf("%s: transfer error, downgrading to PIO mode %d\n",
|
||||
drv_dev->dv_xname, drvp->PIO_mode);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: wdcreg.h,v 1.21 1999/01/18 20:06:25 bouyer Exp $ */
|
||||
/* $NetBSD: wdcreg.h,v 1.22 1999/03/07 14:02:54 bouyer Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1991 The Regents of the University of California.
|
||||
|
@ -83,6 +83,7 @@
|
|||
* Error bits.
|
||||
*/
|
||||
#define WDCE_BBK 0x80 /* bad block detected */
|
||||
#define WDCE_CRC 0x80 /* CRC error (Ultra-DMA only) */
|
||||
#define WDCE_UNC 0x40 /* uncorrectable data error */
|
||||
#define WDCE_MC 0x20 /* media changed */
|
||||
#define WDCE_IDNF 0x10 /* id not found */
|
||||
|
@ -90,7 +91,6 @@
|
|||
#define WDCE_ABRT 0x04 /* aborted command */
|
||||
#define WDCE_TK0NF 0x02 /* track 0 not found */
|
||||
#define WDCE_AMNF 0x01 /* address mark not found */
|
||||
#define WDERR_BITS "\020\010bbk\007unc\006mc\005idnf\004mcr\003abrt\002tk0nf\001amnf"
|
||||
|
||||
/*
|
||||
* Commands for Disk Controller.
|
||||
|
|
Loading…
Reference in New Issue