diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c index 3baa3576d5c7..ebe0885a2e17 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.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; diff --git a/sys/dev/ata/atavar.h b/sys/dev/ata/atavar.h index 23aa5a5bf74d..095d7c8af099 100644 --- a/sys/dev/ata/atavar.h +++ b/sys/dev/ata/atavar.h @@ -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 diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c index 3ca8d44a888f..15b8c22e87e0 100644 --- a/sys/dev/ic/wdc.c +++ b/sys/dev/ic/wdc.c @@ -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); diff --git a/sys/dev/ic/wdcreg.h b/sys/dev/ic/wdcreg.h index aae1105e770c..502f8e6118bc 100644 --- a/sys/dev/ic/wdcreg.h +++ b/sys/dev/ic/wdcreg.h @@ -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.