- When starting an ATA or ATAPI transfer, handle the case where (*dma_init)()
returns EINVAL, indicating that DMA cannot be done for this transfer. Fall back to PIO in this case. - Add a geodeide_dma_init() routine that checks to make sure that transfers start on a 16 byte boundary, returning EINVAL if not. Works around a chip bug that causes a hard system hang. Problem reported and patch tested by Erik Fair.
This commit is contained in:
parent
053b017df8
commit
527d62e0a2
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ata_wdc.c,v 1.81 2005/06/07 13:45:11 peter Exp $ */
|
||||
/* $NetBSD: ata_wdc.c,v 1.82 2005/07/06 01:46:52 thorpej Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998, 2001, 2003 Manuel Bouyer.
|
||||
@ -66,7 +66,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ata_wdc.c,v 1.81 2005/06/07 13:45:11 peter Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: ata_wdc.c,v 1.82 2005/07/06 01:46:52 thorpej Exp $");
|
||||
|
||||
#ifndef ATADEBUG
|
||||
#define ATADEBUG
|
||||
@ -323,7 +323,7 @@ _wdc_ata_bio_start(struct ata_channel *chp, struct ata_xfer *xfer)
|
||||
int wait_flags = (xfer->c_flags & C_POLL) ? AT_POLL : 0;
|
||||
u_int16_t cyl;
|
||||
u_int8_t head, sect, cmd = 0;
|
||||
int nblks;
|
||||
int nblks, error;
|
||||
int dma_flags = 0;
|
||||
|
||||
ATADEBUG_PRINT(("_wdc_ata_bio_start %s:%d:%d\n",
|
||||
@ -397,10 +397,21 @@ again:
|
||||
cmd = (ata_bio->flags & ATA_READ) ?
|
||||
WDCC_READDMA : WDCC_WRITEDMA;
|
||||
/* Init the DMA channel. */
|
||||
if ((*wdc->dma_init)(wdc->dma_arg,
|
||||
error = (*wdc->dma_init)(wdc->dma_arg,
|
||||
chp->ch_channel, xfer->c_drive,
|
||||
(char *)xfer->c_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);
|
||||
@ -437,6 +448,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);
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: geodeide.c,v 1.9 2005/06/25 05:04:01 fair Exp $ */
|
||||
/* $NetBSD: geodeide.c,v 1.10 2005/07/06 01:46:52 thorpej Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004 Manuel Bouyer.
|
||||
@ -37,7 +37,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: geodeide.c,v 1.9 2005/06/25 05:04:01 fair Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: geodeide.c,v 1.10 2005/07/06 01:46:52 thorpej Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
@ -54,6 +54,7 @@ __KERNEL_RCSID(0, "$NetBSD: geodeide.c,v 1.9 2005/06/25 05:04:01 fair Exp $");
|
||||
static void geodeide_chip_map(struct pciide_softc *,
|
||||
struct pci_attach_args *);
|
||||
static void geodeide_setup_channel(struct ata_channel *);
|
||||
static int geodeide_dma_init(void *, int, int, void *, size_t, int);
|
||||
|
||||
static int geodeide_match(struct device *, struct cfdata *, void *);
|
||||
static void geodeide_attach(struct device *, struct device *, void *);
|
||||
@ -120,6 +121,11 @@ geodeide_chip_map(struct pciide_softc *sc, struct pci_attach_args *pa)
|
||||
if (sc->sc_dma_ok) {
|
||||
sc->sc_wdcdev.sc_atac.atac_cap = ATAC_CAP_DMA | ATAC_CAP_UDMA;
|
||||
sc->sc_wdcdev.irqack = pciide_irqack;
|
||||
/*
|
||||
* XXXJRT What chip revisions actually need the DMA
|
||||
* alignment work-around?
|
||||
*/
|
||||
sc->sc_wdcdev.dma_init = geodeide_dma_init;
|
||||
}
|
||||
sc->sc_wdcdev.sc_atac.atac_pio_cap = 4;
|
||||
sc->sc_wdcdev.sc_atac.atac_dma_cap = 2;
|
||||
@ -260,3 +266,18 @@ geodeide_setup_channel(struct ata_channel *chp)
|
||||
idedma_ctl);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
geodeide_dma_init(void *v, int channel, int drive, void *databuf,
|
||||
size_t datalen, int flags)
|
||||
{
|
||||
|
||||
/*
|
||||
* If the buffer is not properly aligned, we can't allow DMA
|
||||
* and need to fall back to PIO.
|
||||
*/
|
||||
if (((uintptr_t)databuf) & 0xf)
|
||||
return (EINVAL);
|
||||
|
||||
return (pciide_dma_init(v, channel, drive, databuf, datalen, flags));
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: atapi_wdc.c,v 1.94 2005/06/07 13:45:11 peter Exp $ */
|
||||
/* $NetBSD: atapi_wdc.c,v 1.95 2005/07/06 01:46:52 thorpej Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1998, 2001 Manuel Bouyer.
|
||||
@ -30,7 +30,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.94 2005/06/07 13:45:11 peter Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.95 2005/07/06 01:46:52 thorpej Exp $");
|
||||
|
||||
#ifndef ATADEBUG
|
||||
#define ATADEBUG
|
||||
@ -611,7 +611,7 @@ wdc_atapi_intr(struct ata_channel *chp, struct ata_xfer *xfer, int irq)
|
||||
struct scsipi_xfer *sc_xfer = xfer->c_cmd;
|
||||
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive];
|
||||
int len, phase, i, retries=0;
|
||||
int ire;
|
||||
int ire, error;
|
||||
int dma_flags = 0;
|
||||
void *cmd;
|
||||
|
||||
@ -692,11 +692,22 @@ again:
|
||||
ATADEBUG_PRINT(("PHASE_CMDOUT\n"), DEBUG_INTR);
|
||||
/* Init the DMA channel if necessary */
|
||||
if (xfer->c_flags & C_DMA) {
|
||||
if ((*wdc->dma_init)(wdc->dma_arg,
|
||||
error = (*wdc->dma_init)(wdc->dma_arg,
|
||||
chp->ch_channel, xfer->c_drive,
|
||||
xfer->c_databuf, xfer->c_bcount, dma_flags) != 0) {
|
||||
sc_xfer->error = XS_DRIVER_STUFFUP;
|
||||
break;
|
||||
xfer->c_databuf, xfer->c_bcount, 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;
|
||||
} else {
|
||||
sc_xfer->error = XS_DRIVER_STUFFUP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user