summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2010-11-06 16:53:16 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2010-11-06 16:53:16 +0000
commit45d3e364b6be21e55782f40158b077add47f1e3f (patch)
tree71dac2d5550c76615586cad3845a5a606d060be0 /sys/dev
parent5c1102b77df12dbb075ff378bfff2b9dd0c7d942 (diff)
If the dma_init callback function returns with EINVAL, fall back to PIO.
This will be used to work around bugs in certain pciide(4) controllers. From NetBSD. ok jsg@, krw@, deraadt@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ata/ata_wdc.c20
1 files changed, 16 insertions, 4 deletions
diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c
index e4134e7114c..f44acc279a5 100644
--- a/sys/dev/ata/ata_wdc.c
+++ b/sys/dev/ata/ata_wdc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ata_wdc.c,v 1.34 2010/07/23 07:47:12 jsg Exp $ */
+/* $OpenBSD: ata_wdc.c,v 1.35 2010/11/06 16:53:15 kettenis Exp $ */
/* $NetBSD: ata_wdc.c,v 1.21 1999/08/09 09:43:11 bouyer Exp $ */
/*
@@ -167,7 +167,7 @@ _wdc_ata_bio_start(struct channel_softc *chp, struct wdc_xfer *xfer)
u_int8_t head, sect, cmd = 0;
int nblks;
int ata_delay;
- int dma_flags = 0;
+ int error, dma_flags = 0;
WDCDEBUG_PRINT(("_wdc_ata_bio_start %s:%d:%d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive),
@@ -249,10 +249,21 @@ again:
cmd = (ata_bio->flags & ATA_READ) ?
WDCC_READDMA : WDCC_WRITEDMA;
/* Init the DMA channel. */
- if ((*chp->wdc->dma_init)(chp->wdc->dma_arg,
+ error = (*chp->wdc->dma_init)(chp->wdc->dma_arg,
chp->channel, xfer->drive,
(char *)xfer->databuf + xfer->c_skip,
- ata_bio->nbytes, dma_flags) != 0) {
+ ata_bio->nbytes, dma_flags);
+ if (error) {
+ if (error == EINVAL) {
+ /*
+ * We can't do DMA on this transfer
+ * for some reason. Fall back to
+ * PIO.
+ */
+ xfer->c_flags &= ~C_DMA;
+ error = 0;
+ goto do_pio;
+ }
ata_bio->error = ERR_DMA;
ata_bio->r_error = 0;
wdc_ata_bio_done(chp, xfer);
@@ -276,6 +287,7 @@ again:
/* wait for irq */
goto intr;
} /* else not DMA */
+ do_pio:
ata_bio->nblks = min(nblks, ata_bio->multi);
ata_bio->nbytes = ata_bio->nblks * ata_bio->lp->d_secsize;
KASSERT(nblks == 1 || (ata_bio->flags & ATA_SINGLE) == 0);