summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ata/ata_wdc.c49
-rw-r--r--sys/dev/ic/wdc.c35
-rw-r--r--sys/dev/scsipi/atapi_wdc.c22
3 files changed, 85 insertions, 21 deletions
diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c
index 14e74ad4eca..79a39234d1c 100644
--- a/sys/dev/ata/ata_wdc.c
+++ b/sys/dev/ata/ata_wdc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: ata_wdc.c,v 1.19 1999/04/01 21:46:28 bouyer Exp $ */
+/* $NetBSD: ata_wdc.c,v 1.20 1999/08/06 12:00:23 bouyer Exp $ */
/*
* Copyright (c) 1998 Manuel Bouyer.
@@ -117,6 +117,7 @@ int wdcdebug_wd_mask = 0;
#define ATA_DELAY 10000 /* 10s for a drive I/O */
void wdc_ata_bio_start __P((struct channel_softc *,struct wdc_xfer *));
+void _wdc_ata_bio_start __P((struct channel_softc *,struct wdc_xfer *));
int wdc_ata_bio_intr __P((struct channel_softc *, struct wdc_xfer *, int));
void wdc_ata_bio_done __P((struct channel_softc *, struct wdc_xfer *));
int wdc_ata_ctrl_intr __P((struct channel_softc *, struct wdc_xfer *, int));
@@ -161,6 +162,22 @@ wdc_ata_bio_start(chp, xfer)
struct wdc_xfer *xfer;
{
struct ata_bio *ata_bio = xfer->cmd;
+ WDCDEBUG_PRINT(("wdc_ata_bio_start %s:%d:%d\n",
+ chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive),
+ DEBUG_XFERS);
+
+ /* start timeout machinery */
+ if ((ata_bio->flags & ATA_POLL) == 0)
+ timeout(wdctimeout, chp, ATA_DELAY / 1000 * hz);
+ _wdc_ata_bio_start(chp, xfer);
+}
+
+void
+_wdc_ata_bio_start(chp, xfer)
+ struct channel_softc *chp;
+ struct wdc_xfer *xfer;
+{
+ struct ata_bio *ata_bio = xfer->cmd;
struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
u_int16_t cyl;
u_int8_t head, sect, cmd = 0;
@@ -168,10 +185,9 @@ wdc_ata_bio_start(chp, xfer)
int ata_delay;
int dma_flags = 0;
- WDCDEBUG_PRINT(("wdc_ata_bio_start %s:%d:%d\n",
+ WDCDEBUG_PRINT(("_wdc_ata_bio_start %s:%d:%d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive),
- DEBUG_XFERS);
-
+ DEBUG_INTR | DEBUG_XFERS);
/* Do control operations specially. */
if (drvp->state < READY) {
/*
@@ -181,10 +197,10 @@ wdc_ata_bio_start(chp, xfer)
*/
/* at this point, we should only be in RECAL state */
if (drvp->state != RECAL) {
- printf("%s:%d:%d: bad state %d in wdc_ata_bio_start\n",
+ printf("%s:%d:%d: bad state %d in _wdc_ata_bio_start\n",
chp->wdc->sc_dev.dv_xname, chp->channel,
xfer->drive, drvp->state);
- panic("wdc_ata_bio_start: bad state");
+ panic("_wdc_ata_bio_start: bad state");
}
xfer->c_intr = wdc_ata_ctrl_intr;
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
@@ -195,8 +211,6 @@ wdc_ata_bio_start(chp, xfer)
drvp->state = RECAL_WAIT;
if ((ata_bio->flags & ATA_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp,
- ATA_DELAY / 1000 * hz);
} else {
/* Wait for at last 400ns for status bit to be valid */
DELAY(1);
@@ -368,7 +382,6 @@ again:
intr: /* Wait for IRQ (either real or polled) */
if ((ata_bio->flags & ATA_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, ata_delay / 1000 * hz);
} else {
/* Wait for at last 400ns for status bit to be valid */
delay(1);
@@ -416,6 +429,16 @@ wdc_ata_bio_intr(chp, xfer, irq)
dma_flags |= (ata_bio->flags & ATA_POLL) ? WDC_DMA_POLL : 0;
}
+ /*
+ * if we missed an interrupt in a PIO transfer, reset and restart.
+ * Don't try to continue transfer, we may have missed cycles.
+ */
+ if ((xfer->c_flags & (C_TIMEOU | C_DMA)) == C_TIMEOU) {
+ ata_bio->error = TIMEOUT;
+ wdc_ata_bio_done(chp, xfer);
+ return 1;
+ }
+
/* Ack interrupt done by wait_for_unbusy */
if (wait_for_unbusy(chp,
(irq == 0) ? ATA_DELAY : 0) < 0) {
@@ -532,9 +555,9 @@ end:
if (xfer->c_bcount > 0) {
if ((ata_bio->flags & ATA_POLL) == 0) {
/* Start the next operation */
- wdc_ata_bio_start(chp, xfer);
+ _wdc_ata_bio_start(chp, xfer);
} else {
- /* Let wdc_ata_bio_start do the loop */
+ /* Let _wdc_ata_bio_start do the loop */
return 1;
}
} else { /* Done with this transfer */
@@ -559,6 +582,7 @@ wdc_ata_bio_done(chp, xfer)
(u_int)xfer->c_flags),
DEBUG_XFERS);
+ untimeout(wdctimeout, chp);
if (ata_bio->error == NOERROR)
drvp->n_dmaerrs = 0;
else if (drvp->n_dmaerrs >= NERRS_MAX) {
@@ -696,13 +720,12 @@ again:
* The drive is usable now
*/
xfer->c_intr = wdc_ata_bio_intr;
- wdc_ata_bio_start(chp, xfer);
+ _wdc_ata_bio_start(chp, xfer);
return 1;
}
if ((ata_bio->flags & ATA_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, ATA_DELAY / 1000 * hz);
} else {
goto again;
}
diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c
index 2dc11adc4f3..b858ee32164 100644
--- a/sys/dev/ic/wdc.c
+++ b/sys/dev/ic/wdc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: wdc.c,v 1.69 1999/07/30 14:59:10 bouyer Exp $ */
+/* $NetBSD: wdc.c,v 1.70 1999/08/06 12:00:25 bouyer Exp $ */
/*
@@ -533,7 +533,6 @@ wdcintr(arg)
}
WDCDEBUG_PRINT(("wdcintr\n"), DEBUG_INTR);
- untimeout(wdctimeout, chp);
chp->ch_flags &= ~WDCF_IRQ_WAIT;
xfer = chp->ch_queue->sc_xfer.tqh_first;
return xfer->c_intr(chp, xfer, 1);
@@ -595,16 +594,32 @@ __wdcwait_reset(chp, drv_mask)
{
int timeout;
u_int8_t st0, st1;
+#ifdef WDCDEBUG
+ u_int8_t sc0, sn0, cl0, ch0;
+ u_int8_t sc1, sn1, cl1, ch1;
+#endif
/* wait for BSY to deassert */
for (timeout = 0; timeout < WDCNDELAY_RST;timeout++) {
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM); /* master */
delay(10);
st0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status);
+#ifdef WDCDEBUG
+ sc0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt);
+ sn0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_sector);
+ cl0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo);
+ ch0 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi);
+#endif
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM | 0x10); /* slave */
delay(10);
st1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_status);
+#ifdef WDCDEBUG
+ sc1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_seccnt);
+ sn1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_sector);
+ cl1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_lo);
+ ch1 = bus_space_read_1(chp->cmd_iot, chp->cmd_ioh, wd_cyl_hi);
+#endif
if ((drv_mask & 0x01) == 0) {
/* no master */
@@ -632,6 +647,15 @@ __wdcwait_reset(chp, drv_mask)
if (st1 & WDCS_BSY)
drv_mask &= ~0x02;
end:
+ WDCDEBUG_PRINT(("%s:%d:0: after reset, sc=0x%x sn=0x%x "
+ "cl=0x%x ch=0x%x\n",
+ chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe",
+ chp->channel, sc0, sn0, cl0, ch0), DEBUG_PROBE);
+ WDCDEBUG_PRINT(("%s:%d:1: after reset, sc=0x%x sn=0x%x "
+ "cl=0x%x ch=0x%x\n",
+ chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe",
+ chp->channel, sc1, sn1, cl1, ch1), DEBUG_PROBE);
+
WDCDEBUG_PRINT(("%s:%d: wdcwait_reset() end, st0=0x%x, st1=0x%x\n",
chp->wdc ? chp->wdc->sc_dev.dv_xname : "wdcprobe", chp->channel,
st0, st1), DEBUG_PROBE);
@@ -717,7 +741,11 @@ wdctimeout(arg)
* Call the interrupt routine. If we just missed and interrupt,
* it will do what's needed. Else, it will take the needed
* action (reset the device).
+ * Before that we need to reinstall the timeout callback,
+ * in case it will miss another irq while in this transfer
+ * We arbitray chose it to be 1s
*/
+ timeout(wdctimeout, chp, hz);
xfer->c_flags |= C_TIMEOU;
chp->ch_flags &= ~WDCF_IRQ_WAIT;
xfer->c_intr(chp, xfer, 1);
@@ -1121,6 +1149,9 @@ __wdccommand_done(chp, xfer)
WDCDEBUG_PRINT(("__wdccommand_done %s:%d:%d\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_FUNCS);
+
+ untimeout(wdctimeout, chp);
+
if (chp->ch_status & WDCS_DWF)
wdc_c->flags |= AT_DF;
if (chp->ch_status & WDCS_ERR) {
diff --git a/sys/dev/scsipi/atapi_wdc.c b/sys/dev/scsipi/atapi_wdc.c
index 0db4d23ea55..e118708cd05 100644
--- a/sys/dev/scsipi/atapi_wdc.c
+++ b/sys/dev/scsipi/atapi_wdc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: atapi_wdc.c,v 1.23 1999/05/05 21:35:15 perry Exp $ */
+/* $NetBSD: atapi_wdc.c,v 1.24 1999/08/06 12:00:26 bouyer Exp $ */
/*
* Copyright (c) 1998 Manuel Bouyer.
@@ -238,6 +238,9 @@ wdc_atapi_start(chp, xfer)
xfer->c_flags |= C_DMA;
else
xfer->c_flags &= ~C_DMA;
+ /* start timeout machinery */
+ if ((sc_xfer->flags & SCSI_POLL) == 0)
+ timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000);
/* Do control operations specially. */
if (drvp->state < READY) {
if (drvp->state != PIOMODE) {
@@ -285,7 +288,6 @@ wdc_atapi_start(chp, xfer)
wdc_atapi_intr(chp, xfer, 0);
} else {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, hz);
}
if (sc_xfer->flags & SCSI_POLL) {
while ((sc_xfer->flags & ITSDONE) == 0) {
@@ -322,6 +324,15 @@ wdc_atapi_intr(chp, xfer, irq)
drvp->state);
panic("wdc_atapi_intr: bad state\n");
}
+ /*
+ * If we missed an interrupt in a PIO transfer, reset and restart.
+ * Don't try to continue transfer, we may have missed cycles.
+ */
+ if ((xfer->c_flags & (C_TIMEOU | C_DMA)) == C_TIMEOU) {
+ sc_xfer->error = XS_TIMEOUT;
+ wdc_atapi_reset(chp, xfer);
+ }
+
/* Ack interrupt done in wait_for_unbusy */
bus_space_write_1(chp->cmd_iot, chp->cmd_ioh, wd_sdh,
WDSD_IBM | (xfer->drive << 4));
@@ -425,7 +436,6 @@ again:
if ((sc_xfer->flags & SCSI_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000);
}
return 1;
@@ -503,7 +513,6 @@ again:
}
if ((sc_xfer->flags & SCSI_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000);
}
return 1;
@@ -580,7 +589,6 @@ again:
}
if ((sc_xfer->flags & SCSI_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
- timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000);
}
return 1;
@@ -636,6 +644,7 @@ again:
sizeof(sc_xfer->sense.scsi_sense);
xfer->c_skip = 0;
xfer->c_flags |= C_SENSE;
+ untimeout(wdctimeout, chp);
wdc_atapi_start(chp, xfer);
return 1;
}
@@ -753,13 +762,13 @@ piomode:
ready:
drvp->state = READY;
xfer->c_intr = wdc_atapi_intr;
+ untimeout(wdctimeout, chp);
wdc_atapi_start(chp, xfer);
return 1;
}
if ((sc_xfer->flags & SCSI_POLL) == 0) {
chp->ch_flags |= WDCF_IRQ_WAIT;
xfer->c_intr = wdc_atapi_ctrl;
- timeout(wdctimeout, chp, sc_xfer->timeout * hz / 1000);
} else {
goto again;
}
@@ -797,6 +806,7 @@ wdc_atapi_done(chp, xfer)
WDCDEBUG_PRINT(("wdc_atapi_done %s:%d:%d: flags 0x%x\n",
chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
(u_int)xfer->c_flags), DEBUG_XFERS);
+ untimeout(wdctimeout, chp);
/* remove this command from xfer queue */
wdc_free_xfer(chp, xfer);
sc_xfer->flags |= ITSDONE;