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:
bouyer 1999-03-07 14:02:53 +00:00
parent c8126918cb
commit ba74f49e60
4 changed files with 55 additions and 30 deletions

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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.