diff options
author | Alexander Yurchenko <grange@cvs.openbsd.org> | 2003-10-17 08:14:10 +0000 |
---|---|---|
committer | Alexander Yurchenko <grange@cvs.openbsd.org> | 2003-10-17 08:14:10 +0000 |
commit | 6dbc421deb21d25ce2935f847921dc56a8399cbb (patch) | |
tree | dc0980203c1dd3c84fe617bd090ac4259903d662 /sys | |
parent | dc1014eb963fbe3c6148ff2c44c5843259f66b55 (diff) |
Merge an old fix from NetBSD:
- do not stop/unload current DMA operation if an IRQ was not detected
by DMA engine unless the force flag was given, fixes DMA problems
in shared IRQ setups;
- ack interrupt before entering DMA codepath
Tested by many.
Work by niklas@ but he doesn't want to commit it for some reason.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/arch/macppc/dev/wdc_obio.c | 8 | ||||
-rw-r--r-- | sys/dev/ata/ata_wdc.c | 22 | ||||
-rw-r--r-- | sys/dev/atapiscsi/atapiscsi.c | 9 | ||||
-rw-r--r-- | sys/dev/ic/wdc.c | 42 | ||||
-rw-r--r-- | sys/dev/ic/wdcvar.h | 5 | ||||
-rw-r--r-- | sys/dev/isa/wdc_isa.c | 7 | ||||
-rw-r--r-- | sys/dev/pci/pciide.c | 16 |
7 files changed, 73 insertions, 36 deletions
diff --git a/sys/arch/macppc/dev/wdc_obio.c b/sys/arch/macppc/dev/wdc_obio.c index 90ab87f9c35..648eb20bbb5 100644 --- a/sys/arch/macppc/dev/wdc_obio.c +++ b/sys/arch/macppc/dev/wdc_obio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wdc_obio.c,v 1.14 2003/07/19 14:45:41 drahn Exp $ */ +/* $OpenBSD: wdc_obio.c,v 1.15 2003/10/17 08:14:09 grange Exp $ */ /* $NetBSD: wdc_obio.c,v 1.15 2001/07/25 20:26:33 bouyer Exp $ */ /*- @@ -103,7 +103,7 @@ struct cfattach wdc_obio_ca = { int wdc_obio_dma_init(void *, int, int, void *, size_t, int); void wdc_obio_dma_start(void *, int, int); -int wdc_obio_dma_finish(void *, int, int); +int wdc_obio_dma_finish(void *, int, int, int); void wdc_obio_adjust_timing(struct channel_softc *); void wdc_obio_ata4_adjust_timing(struct channel_softc *); void wdc_obio_ata6_adjust_timing(struct channel_softc *); @@ -582,9 +582,9 @@ wdc_obio_dma_start(v, channel, drive) } int -wdc_obio_dma_finish(v, channel, drive) +wdc_obio_dma_finish(v, channel, drive, force) void *v; - int channel, drive; + int channel, drive, force; { struct wdc_obio_softc *sc = v; diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c index 10d934b217c..9ea56043b7a 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.23 2003/10/16 11:30:00 grange Exp $ */ +/* $OpenBSD: ata_wdc.c,v 1.24 2003/10/17 08:14:09 grange Exp $ */ /* $NetBSD: ata_wdc.c,v 1.21 1999/08/09 09:43:11 bouyer Exp $ */ /* @@ -315,6 +315,7 @@ again: /* start the DMA channel */ (*chp->wdc->dma_start)(chp->wdc->dma_arg, chp->channel, xfer->drive); + chp->ch_flags |= WDCF_DMA_WAIT; /* wait for irq */ goto intr; } /* else not DMA */ @@ -382,6 +383,10 @@ intr: /* Wait for IRQ (either real or polled) */ } else { /* Wait for at last 400ns for status bit to be valid */ delay(1); + if (chp->ch_flags & WDCF_DMA_WAIT) { + wdc_dmawait(chp, xfer, ATA_DELAY); + chp->ch_flags &= ~WDCF_DMA_WAIT; + } wdc_ata_bio_intr(chp, xfer, 0); if ((ata_bio->flags & ATA_ITSDONE) == 0) goto again; @@ -438,14 +443,12 @@ wdc_ata_bio_intr(chp, xfer, irq) goto timeout; } + if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) + chp->wdc->irqack(chp); drv_err = wdc_ata_err(drvp, ata_bio); if (xfer->c_flags & C_DMA) { - chp->wdc->dma_status = - (*chp->wdc->dma_finish)(chp->wdc->dma_arg, - chp->channel, xfer->drive); - if (chp->wdc->dma_status != 0) { if (drv_err != WDC_ATA_ERR) { ata_bio->error = ERR_DMA; @@ -466,9 +469,6 @@ wdc_ata_bio_intr(chp, xfer, irq) ata_dmaerr(drvp); } - if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) - chp->wdc->irqack(chp); - /* if we had an error, end */ if (drv_err == WDC_ATA_ERR) { wdc_ata_bio_done(chp, xfer); @@ -509,12 +509,8 @@ end: return 1; timeout: - if (xfer->c_flags & C_DMA) { - chp->wdc->dma_status = - (*chp->wdc->dma_finish)(chp->wdc->dma_arg, - chp->channel, xfer->drive); + if (xfer->c_flags & C_DMA) ata_dmaerr(drvp); - } ata_bio->error = TIMEOUT; wdc_ata_bio_done(chp, xfer); diff --git a/sys/dev/atapiscsi/atapiscsi.c b/sys/dev/atapiscsi/atapiscsi.c index ae8ab9f5beb..78e7f3417c4 100644 --- a/sys/dev/atapiscsi/atapiscsi.c +++ b/sys/dev/atapiscsi/atapiscsi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: atapiscsi.c,v 1.65 2003/10/16 11:30:00 grange Exp $ */ +/* $OpenBSD: atapiscsi.c,v 1.66 2003/10/17 08:14:09 grange Exp $ */ /* * This code is derived from code with the copyright below. @@ -1101,11 +1101,6 @@ wdc_atapi_intr_complete(chp, xfer, timeout, ret) int retry; if (timeout) { - chp->wdc->dma_status = - (*chp->wdc->dma_finish) - (chp->wdc->dma_arg, chp->channel, - xfer->drive); - sc_xfer->error = XS_TIMEOUT; ata_dmaerr(drvp); @@ -1127,7 +1122,7 @@ wdc_atapi_intr_complete(chp, xfer, timeout, ret) chp->wdc->dma_status = (*chp->wdc->dma_finish) (chp->wdc->dma_arg, chp->channel, - xfer->drive); + xfer->drive, 1); if (chp->wdc->dma_status & WDC_DMAST_UNDER) xfer->c_bcount = 1; diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c index 966bb79f250..80fea5b6198 100644 --- a/sys/dev/ic/wdc.c +++ b/sys/dev/ic/wdc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wdc.c,v 1.66 2003/10/16 11:58:16 grange Exp $ */ +/* $OpenBSD: wdc.c,v 1.67 2003/10/17 08:14:09 grange Exp $ */ /* $NetBSD: wdc.c,v 1.68 1999/06/23 19:00:17 bouyer Exp $ */ @@ -988,6 +988,16 @@ wdcintr(arg) WDCDEBUG_PRINT(("wdcintr\n"), DEBUG_INTR); xfer = TAILQ_FIRST(&chp->ch_queue->sc_xfer); + if (chp->ch_flags & WDCF_DMA_WAIT) { + chp->wdc->dma_status = + (*chp->wdc->dma_finish)(chp->wdc->dma_arg, chp->channel, + xfer->drive, 0); + if (chp->wdc->dma_status & WDC_DMAST_NOIRQ) { + /* IRQ not for us, not detected by DMA engine */ + return 0; + } + chp->ch_flags &= ~WDCF_DMA_WAIT; + } chp->ch_flags &= ~WDCF_IRQ_WAIT; ret = xfer->c_intr(chp, xfer, 1); if (ret == 0) /* irq was not for us, still waiting for irq */ @@ -1157,6 +1167,30 @@ wdc_wait_for_status(chp, mask, bits, timeout) return time; } +/* + * Busy-wait for DMA to complete + */ +int +wdc_dmawait(chp, xfer, timeout) + struct channel_softc *chp; + struct wdc_xfer *xfer; + int timeout; +{ + int time; + for (time = 0; time < timeout * 1000 / WDCDELAY; time++) { + chp->wdc->dma_status = + (*chp->wdc->dma_finish)(chp->wdc->dma_arg, + chp->channel, xfer->drive, 0); + if ((chp->wdc->dma_status & WDC_DMAST_NOIRQ) == 0) + return 0; + delay(WDCDELAY); + } + /* timeout, force a DMA halt */ + chp->wdc->dma_status = (*chp->wdc->dma_finish)(chp->wdc->dma_arg, + chp->channel, xfer->drive, 1); + return 1; +} + void wdctimeout(arg) void *arg; @@ -1182,6 +1216,12 @@ wdctimeout(arg) "atapi":"ata"); printf("\tc_bcount: %d\n", xfer->c_bcount); printf("\tc_skip: %d\n", xfer->c_skip); + if (chp->ch_flags & WDCF_DMA_WAIT) { + chp->wdc->dma_status = + (*chp->wdc->dma_finish)(chp->wdc->dma_arg, + chp->channel, xfer->drive, 1); + chp->ch_flags &= ~WDCF_DMA_WAIT; + } /* * Call the interrupt routine. If we just missed and interrupt, * it will do what's needed. Else, it will take the needed diff --git a/sys/dev/ic/wdcvar.h b/sys/dev/ic/wdcvar.h index e06341dc171..d093cd94193 100644 --- a/sys/dev/ic/wdcvar.h +++ b/sys/dev/ic/wdcvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wdcvar.h,v 1.30 2003/10/16 14:18:34 grange Exp $ */ +/* $OpenBSD: wdcvar.h,v 1.31 2003/10/17 08:14:09 grange Exp $ */ /* $NetBSD: wdcvar.h,v 1.17 1999/04/11 20:50:29 bouyer Exp $ */ /*- @@ -68,6 +68,7 @@ struct channel_softc { /* Per channel data */ #define WDCF_ACTIVE 0x01 /* channel is active */ #define WDCF_ONESLAVE 0x02 /* slave-only channel */ #define WDCF_IRQ_WAIT 0x10 /* controller is waiting for irq */ +#define WDCF_DMA_WAIT 0x20 /* controller is waiting for DMA */ #define WDCF_VERBOSE_PROBE 0x40 /* verbose probe */ u_int8_t ch_status; /* copy of status register */ u_int8_t ch_prev_log_status; /* previous logged value of status reg */ @@ -175,7 +176,7 @@ struct wdc_softc { /* Per controller state */ int (*dma_init)(void *, int, int, void *, size_t, int); void (*dma_start)(void *, int, int); - int (*dma_finish)(void *, int, int); + int (*dma_finish)(void *, int, int, int); /* flags passed to DMA functions */ #define WDC_DMA_READ 0x01 #define WDC_DMA_IRQW 0x02 diff --git a/sys/dev/isa/wdc_isa.c b/sys/dev/isa/wdc_isa.c index c9cc5dbf20f..6c0a62e9fc3 100644 --- a/sys/dev/isa/wdc_isa.c +++ b/sys/dev/isa/wdc_isa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wdc_isa.c,v 1.9 2002/03/14 03:16:05 millert Exp $ */ +/* $OpenBSD: wdc_isa.c,v 1.10 2003/10/17 08:14:09 grange Exp $ */ /* $NetBSD: wdc_isa.c,v 1.15 1999/05/19 14:41:25 bouyer Exp $ */ /*- @@ -92,7 +92,7 @@ struct cfattach wdc_isa_ca = { static void wdc_isa_dma_setup(struct wdc_isa_softc *); static int wdc_isa_dma_init(void *, int, int, void *, size_t, int); static void wdc_isa_dma_start(void *, int, int); -static int wdc_isa_dma_finish(void *, int, int); +static int wdc_isa_dma_finish(void *, int, int, int); #endif /* NISADMA > 0 */ int @@ -252,9 +252,10 @@ wdc_isa_dma_start(v, channel, drive) } static int -wdc_isa_dma_finish(v, channel, drive) +wdc_isa_dma_finish(v, channel, drive, force) void *v; int channel, drive; + int force; { struct wdc_isa_softc *sc = v; diff --git a/sys/dev/pci/pciide.c b/sys/dev/pci/pciide.c index 1df19517202..8f1d947c22e 100644 --- a/sys/dev/pci/pciide.c +++ b/sys/dev/pci/pciide.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pciide.c,v 1.143 2003/10/16 14:52:58 grange Exp $ */ +/* $OpenBSD: pciide.c,v 1.144 2003/10/17 08:14:09 grange Exp $ */ /* $NetBSD: pciide.c,v 1.127 2001/08/03 01:31:08 tsutsui Exp $ */ /* @@ -264,7 +264,7 @@ void pdc20268_setup_channel(struct channel_softc*); int pdc202xx_pci_intr(void *); int pdc20265_pci_intr(void *); void pdc20262_dma_start(void *, int, int); -int pdc20262_dma_finish(void *, int, int); +int pdc20262_dma_finish(void *, int, int, int); void opti_chip_map(struct pciide_softc*, struct pci_attach_args*); void opti_setup_channel(struct channel_softc*); @@ -291,7 +291,7 @@ void pciide_channel_dma_setup(struct pciide_channel *); int pciide_dma_table_setup(struct pciide_softc*, int, int); int pciide_dma_init(void *, int, int, void *, size_t, int); void pciide_dma_start(void *, int, int); -int pciide_dma_finish(void *, int, int); +int pciide_dma_finish(void *, int, int, int); void pciide_irqack(struct channel_softc *); void pciide_print_modes(struct pciide_channel *); void pciide_print_channels(int, pcireg_t); @@ -1306,9 +1306,10 @@ pciide_dma_start(v, channel, drive) } int -pciide_dma_finish(v, channel, drive) +pciide_dma_finish(v, channel, drive, force) void *v; int channel, drive; + int force; { struct pciide_softc *sc = v; u_int8_t status; @@ -1323,6 +1324,9 @@ pciide_dma_finish(v, channel, drive) WDCDEBUG_PRINT(("pciide_dma_finish: status 0x%x\n", status), DEBUG_XFERS); + if (force == 0 && (status & IDEDMA_CTL_INTR) == 0) + return WDC_DMAST_NOIRQ; + /* stop DMA channel */ bus_space_write_1(sc->sc_dma_iot, sc->sc_dma_ioh, IDEDMA_CMD(channel), @@ -5150,7 +5154,7 @@ pdc20262_dma_start(void *v, int channel, int drive) } int -pdc20262_dma_finish(void *v, int channel, int drive) +pdc20262_dma_finish(void *v, int channel, int drive, int force) { struct pciide_softc *sc = v; struct pciide_dma_maps *dma_maps = @@ -5166,7 +5170,7 @@ pdc20262_dma_finish(void *v, int channel, int drive) PDC262_ATAPI(channel), 0); } - return (pciide_dma_finish(v, channel, drive)); + return (pciide_dma_finish(v, channel, drive, force)); } /* |