summaryrefslogtreecommitdiff
path: root/sys/dev/ic/wdc.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ic/wdc.c')
-rw-r--r--sys/dev/ic/wdc.c101
1 files changed, 80 insertions, 21 deletions
diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c
index d4989bf4ce3..57acd6b010d 100644
--- a/sys/dev/ic/wdc.c
+++ b/sys/dev/ic/wdc.c
@@ -1,4 +1,4 @@
-/* $NetBSD: wdc.c,v 1.181 2004/06/23 21:10:52 bouyer Exp $ */
+/* $NetBSD: wdc.c,v 1.182 2004/07/31 21:26:42 bouyer Exp $ */
/*
* Copyright (c) 1998, 2001, 2003 Manuel Bouyer. All rights reserved.
@@ -70,7 +70,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.181 2004/06/23 21:10:52 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.182 2004/07/31 21:26:42 bouyer Exp $");
#ifndef WDCDEBUG
#define WDCDEBUG
@@ -146,10 +146,13 @@ const struct ata_bustype wdc_ata_bustype = {
static int wdcprobe1(struct wdc_channel*, int);
static void __wdcerror(struct wdc_channel*, char *);
static int __wdcwait_reset(struct wdc_channel *, int, int);
+static void __wdc_reset_channel(struct wdc_channel *, int);
static void __wdccommand_done(struct wdc_channel *, struct ata_xfer *);
+static void __wdccommand_done_end(struct wdc_channel *, struct ata_xfer *);
+static void __wdccommand_kill_xfer(struct wdc_channel *,
+ struct ata_xfer *, int);
static void __wdccommand_start(struct wdc_channel *, struct ata_xfer *);
-static int __wdccommand_intr(struct wdc_channel *, struct ata_xfer *,
- int);
+static int __wdccommand_intr(struct wdc_channel *, struct ata_xfer *, int);
static int __wdcwait(struct wdc_channel *, int, int, int);
#define DEBUG_INTR 0x01
@@ -966,18 +969,46 @@ wdc_reset_channel(struct ata_drive_datas *drvp, int flags)
{
struct wdc_channel *chp = drvp->chnl_softc;
struct wdc_softc *wdc = chp->ch_wdc;
- int drive;
-
WDCDEBUG_PRINT(("ata_reset_channel %s:%d for drive %d\n",
wdc->sc_dev.dv_xname, chp->ch_channel, drvp->drive),
DEBUG_FUNCS);
+
+
+ __wdc_reset_channel(chp, flags);
+}
+
+static void
+__wdc_reset_channel(struct wdc_channel *chp, int flags)
+{
+ struct ata_xfer *xfer;
+ int drive;
+
+ /*
+ * look for pending xfers. If we have a shared queue, we'll also reset
+ * the other channel if the current xfer is running on it.
+ * Then we'll freese the queue, and dequeue only the xfers for this
+ * channel. xfer->c_kill_xfer() will reset any ATAPI device when
+ * needed.
+ */
+ chp->ch_queue->queue_freeze++;
+ if ((flags & AT_RST_NOCMD) == 0) {
+ xfer = TAILQ_FIRST(&chp->ch_queue->queue_xfer);
+ if (xfer && xfer->c_chp != chp)
+ __wdc_reset_channel(xfer->c_chp, flags);
+ for (xfer = TAILQ_FIRST(&chp->ch_queue->queue_xfer);
+ xfer != 0; ) {
+ if (xfer->c_chp != chp)
+ continue;
+ if ((flags & AT_RST_EMERG) == 0)
+ xfer->c_kill_xfer(chp, xfer, KILL_RESET);
+ }
+ }
if ((flags & AT_POLL) == 0) {
if (chp->ch_flags & WDCF_TH_RESET) {
/* no need to schedule a reset more than one time */
return;
}
chp->ch_flags |= WDCF_TH_RESET;
- chp->ch_queue->queue_freeze++;
wakeup(&chp->ch_thread);
return;
}
@@ -985,6 +1016,14 @@ wdc_reset_channel(struct ata_drive_datas *drvp, int flags)
for (drive = 0; drive < 2; drive++) {
chp->ch_drive[drive].state = 0;
}
+ if ((flags & AT_RST_EMERG) == 0) {
+ chp->ch_queue->queue_freeze--;
+ wdcstart(chp);
+ } else {
+ /* make sure that we can use polled commands */
+ TAILQ_INIT(&chp->ch_queue->queue_xfer);
+ chp->ch_queue->queue_freeze = 0;
+ }
}
int
@@ -1565,7 +1604,7 @@ wdc_downgrade_mode(struct ata_drive_datas *drvp, int flags)
wdc->set_modes(chp);
wdc_print_modes(chp);
/* reset the channel, which will shedule all drives for setup */
- wdc_reset_channel(drvp, flags);
+ wdc_reset_channel(drvp, flags | AT_RST_NOCMD);
return 1;
}
@@ -1598,7 +1637,7 @@ wdc_exec_command(struct ata_drive_datas *drvp, struct wdc_command *wdc_c)
xfer->c_cmd = wdc_c;
xfer->c_start = __wdccommand_start;
xfer->c_intr = __wdccommand_intr;
- xfer->c_kill_xfer = __wdccommand_done;
+ xfer->c_kill_xfer = __wdccommand_kill_xfer;
s = splbio();
wdc_exec_xfer(chp, xfer);
@@ -1780,7 +1819,6 @@ __wdccommand_done(struct wdc_channel *chp, struct ata_xfer *xfer)
wdc->sc_dev.dv_xname, chp->ch_channel, xfer->c_drive),
DEBUG_FUNCS);
- callout_stop(&chp->ch_callout);
if (chp->ch_status & WDCS_DWF)
wdc_c->flags |= AT_DF;
@@ -1788,7 +1826,6 @@ __wdccommand_done(struct wdc_channel *chp, struct ata_xfer *xfer)
wdc_c->flags |= AT_ERROR;
wdc_c->r_error = chp->ch_error;
}
- wdc_c->flags |= AT_DONE;
if ((wdc_c->flags & AT_READREG) != 0 &&
(wdc->sc_dev.dv_flags & DVF_ACTIVE) != 0 &&
(wdc_c->flags & (AT_ERROR | AT_DF)) == 0) {
@@ -1807,7 +1844,16 @@ __wdccommand_done(struct wdc_channel *chp, struct ata_xfer *xfer)
wdc_c->r_features = bus_space_read_1(chp->cmd_iot,
chp->cmd_iohs[wd_features], 0);
}
+ __wdccommand_done_end(chp, xfer);
+}
+static void
+__wdccommand_done_end(struct wdc_channel *chp, struct ata_xfer *xfer)
+{
+ struct wdc_command *wdc_c = xfer->c_cmd;
+
+ callout_stop(&chp->ch_callout);
+ wdc_c->flags |= AT_DONE;
if (wdc_c->flags & AT_POLL) {
/* enable interrupts */
bus_space_write_1(chp->ctl_iot, chp->ctl_ioh, wd_aux_ctlr,
@@ -1823,6 +1869,28 @@ __wdccommand_done(struct wdc_channel *chp, struct ata_xfer *xfer)
return;
}
+static void
+__wdccommand_kill_xfer(struct wdc_channel *chp, struct ata_xfer *xfer,
+ int reason)
+{
+ struct wdc_command *wdc_c = xfer->c_cmd;
+
+ switch (reason) {
+ case KILL_GONE:
+ wdc_c->flags |= AT_GONE;
+ break;
+ case KILL_RESET:
+ wdc_c->flags |= AT_RESET;
+ break;
+ default:
+ printf("__wdccommand_kill_xfer: unknown reason %d\n",
+ reason);
+ panic("__wdccommand_kill_xfer");
+ }
+ __wdccommand_done_end(chp, xfer);
+
+}
+
/*
* Send a command. The drive should be ready.
* Assumes interrupts are blocked.
@@ -1940,15 +2008,6 @@ wdc_exec_xfer(struct wdc_channel *chp, struct ata_xfer *xfer)
/* complete xfer setup */
xfer->c_chp = chp;
- /*
- * If we are a polled command, and the list is not empty,
- * we are doing a dump. Drop the list to allow the polled command
- * to complete, we're going to reboot soon anyway.
- */
- if ((xfer->c_flags & C_POLL) != 0 &&
- TAILQ_FIRST(&chp->ch_queue->queue_xfer) != NULL) {
- TAILQ_INIT(&chp->ch_queue->queue_xfer);
- }
/* insert at the end of command list */
TAILQ_INSERT_TAIL(&chp->ch_queue->queue_xfer, xfer, c_xferchain);
WDCDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags 0x%x\n",
@@ -1999,7 +2058,7 @@ wdc_kill_pending(struct wdc_channel *chp)
while ((xfer = TAILQ_FIRST(&chp->ch_queue->queue_xfer)) != NULL) {
chp = xfer->c_chp;
- (*xfer->c_kill_xfer)(chp, xfer);
+ (*xfer->c_kill_xfer)(chp, xfer, KILL_GONE);
}
}