summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorAlexander Yurchenko <grange@cvs.openbsd.org>2003-10-17 08:14:10 +0000
committerAlexander Yurchenko <grange@cvs.openbsd.org>2003-10-17 08:14:10 +0000
commit6dbc421deb21d25ce2935f847921dc56a8399cbb (patch)
treedc0980203c1dd3c84fe617bd090ac4259903d662 /sys/dev
parentdc1014eb963fbe3c6148ff2c44c5843259f66b55 (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/dev')
-rw-r--r--sys/dev/ata/ata_wdc.c22
-rw-r--r--sys/dev/atapiscsi/atapiscsi.c9
-rw-r--r--sys/dev/ic/wdc.c42
-rw-r--r--sys/dev/ic/wdcvar.h5
-rw-r--r--sys/dev/isa/wdc_isa.c7
-rw-r--r--sys/dev/pci/pciide.c16
6 files changed, 69 insertions, 32 deletions
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));
}
/*