Many older revisions of the Acer Labs M5229 UDMA IDE controller have a hardware bug that makes DMA fail for LBA48 commands fail. As a result accessing data on disks bigger than 137 GB beyond the 137 GB boundary will fail. A workaround for this is to fall back to PIO, but this is quite a bit slower than DMA (3 MB/s vs. 15 MB/s on my setup). A much better approach would be to use DMA for everything below the 137 GB boundary and PIO for everything above. I never found an elegant way to do that though.
Recently, Takeshi Nakayama did post a diff to the NetBSD sparc64 mailing list that has a fairly elegant way to fix this: http://mail-index.netbsd.org/port-sparc64/2010/10/26/msg001398.html Here's the equivalent diff for our tree. With this diff, I can access everything on a 160 GB disk. Accessing the tail end is quite a bit slower than the start of the disk. For that reason NetBSD prints a warning in dmesg. I chose not to do that for now, but I can probably be convinced to add a printf here. ok? Index: ata/ata_wdc.c =================================================================== RCS file: /cvs/src/sys/dev/ata/ata_wdc.c,v retrieving revision 1.34 diff -u -p -r1.34 ata_wdc.c --- ata/ata_wdc.c 23 Jul 2010 07:47:12 -0000 1.34 +++ ata/ata_wdc.c 5 Nov 2010 22:18:48 -0000 @@ -167,7 +167,7 @@ _wdc_ata_bio_start(struct channel_softc 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); Index: pci/pciide.c =================================================================== RCS file: /cvs/src/sys/dev/pci/pciide.c,v retrieving revision 1.321 diff -u -p -r1.321 pciide.c --- pci/pciide.c 31 Aug 2010 17:13:44 -0000 1.321 +++ pci/pciide.c 5 Nov 2010 22:18:51 -0000 @@ -215,6 +215,7 @@ void ns_scx200_setup_channel(struct chan void acer_chip_map(struct pciide_softc *, struct pci_attach_args *); void acer_setup_channel(struct channel_softc *); int acer_pci_intr(void *); +int acer_dma_init(void *, int, int, void *, size_t, int); void pdc202xx_chip_map(struct pciide_softc *, struct pci_attach_args *); void pdc202xx_setup_channel(struct channel_softc *); @@ -5629,6 +5630,8 @@ acer_chip_map(struct pciide_softc *sc, s } sc->sc_wdcdev.cap |= WDC_CAPABILITY_IRQACK; sc->sc_wdcdev.irqack = pciide_irqack; + if (rev <= 0xC4) + sc->sc_wdcdev.dma_init = acer_dma_init; } sc->sc_wdcdev.PIO_cap = 4; @@ -5821,6 +5824,17 @@ acer_pci_intr(void *arg) } } return (rv); +} + +int +acer_dma_init(void *v, int channel, int drive, void *databuf, + size_t datalen, int flags) +{ + /* Use PIO for LBA48 transfers. */ + if (flags & WDC_DMA_LBA48) + return (EINVAL); + + return (pciide_dma_init(v, channel, drive, databuf, datalen, flags)); } void
