summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ata/ata_wdc.c23
-rw-r--r--sys/dev/ata/atavar.h18
-rw-r--r--sys/dev/ata/wd.c9
-rw-r--r--sys/dev/ic/wdc.c101
-rw-r--r--sys/dev/scsipi/atapi_wdc.c25
5 files changed, 139 insertions, 37 deletions
diff --git a/sys/dev/ata/ata_wdc.c b/sys/dev/ata/ata_wdc.c
index 4a6bc577bff..02da34e06e5 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.57 2004/06/22 19:20:56 mycroft Exp $ */
+/* $NetBSD: ata_wdc.c,v 1.58 2004/07/31 21:26:43 bouyer 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.57 2004/06/22 19:20:56 mycroft Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ata_wdc.c,v 1.58 2004/07/31 21:26:43 bouyer Exp $");
#ifndef WDCDEBUG
#define WDCDEBUG
@@ -119,7 +119,8 @@ static void wdc_ata_bio_start(struct wdc_channel *,struct ata_xfer *);
static void _wdc_ata_bio_start(struct wdc_channel *,struct ata_xfer *);
static int wdc_ata_bio_intr(struct wdc_channel *, struct ata_xfer *,
int);
-static void wdc_ata_bio_kill_xfer(struct wdc_channel *,struct ata_xfer *);
+static void wdc_ata_bio_kill_xfer(struct wdc_channel *,
+ struct ata_xfer *, int);
static void wdc_ata_bio_done(struct wdc_channel *, struct ata_xfer *);
static int wdc_ata_err(struct ata_drive_datas *, struct ata_bio *);
#define WDC_ATA_NOERR 0x00 /* Drive doesn't report an error */
@@ -740,7 +741,8 @@ wdc_ata_kill_pending(struct ata_drive_datas *drvp)
}
static void
-wdc_ata_bio_kill_xfer(struct wdc_channel *chp, struct ata_xfer *xfer)
+wdc_ata_bio_kill_xfer(struct wdc_channel *chp, struct ata_xfer *xfer,
+ int reason)
{
struct ata_bio *ata_bio = xfer->c_cmd;
int drive = xfer->c_drive;
@@ -750,7 +752,18 @@ wdc_ata_bio_kill_xfer(struct wdc_channel *chp, struct ata_xfer *xfer)
wdc_free_xfer(chp, xfer);
ata_bio->flags |= ATA_ITSDONE;
- ata_bio->error = ERR_NODEV;
+ switch (reason) {
+ case KILL_GONE:
+ ata_bio->error = ERR_NODEV;
+ break;
+ case KILL_RESET:
+ ata_bio->error = ERR_RESET;
+ break;
+ default:
+ printf("wdc_ata_bio_kill_xfer: unknown reason %d\n",
+ reason);
+ panic("wdc_ata_bio_kill_xfer");
+ }
ata_bio->r_error = WDCE_ABRT;
WDCDEBUG_PRINT(("wdc_ata_done: drv_done\n"), DEBUG_XFERS);
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc);
diff --git a/sys/dev/ata/atavar.h b/sys/dev/ata/atavar.h
index ac922ca7865..0844268b652 100644
--- a/sys/dev/ata/atavar.h
+++ b/sys/dev/ata/atavar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: atavar.h,v 1.43 2004/06/01 19:32:30 mycroft Exp $ */
+/* $NetBSD: atavar.h,v 1.44 2004/07/31 21:26:43 bouyer Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -58,14 +58,19 @@ struct ata_xfer {
/* Low-level protocol handlers. */
void (*c_start)(struct wdc_channel *, struct ata_xfer *);
int (*c_intr)(struct wdc_channel *, struct ata_xfer *, int);
- void (*c_kill_xfer)(struct wdc_channel *, struct ata_xfer *);
+ void (*c_kill_xfer)(struct wdc_channel *, struct ata_xfer *, int);
};
+/* vlags in c_flags */
#define C_ATAPI 0x0001 /* xfer is ATAPI request */
#define C_TIMEOU 0x0002 /* xfer processing timed out */
#define C_POLL 0x0004 /* command is polled */
#define C_DMA 0x0008 /* command uses DMA */
+/* reasons for c_kill_xfer() */
+#define KILL_GONE 1 /* device is gone */
+#define KILL_RESET 2 /* xfer was reset */
+
/* Per-channel queue of ata_xfers. May be shared by multiple channels. */
struct ata_queue {
TAILQ_HEAD(, ata_xfer) queue_xfer;
@@ -192,6 +197,7 @@ struct ata_bio {
#define ERR_DMA 3 /* DMA error */
#define TIMEOUT 4 /* device timed out */
#define ERR_NODEV 5 /* device has been gone */
+#define ERR_RESET 6 /* command was terminated by channel reset */
u_int8_t r_error;/* copy of error register */
daddr_t badsect[127];/* 126 plus trailing -1 marker */
};
@@ -232,7 +238,9 @@ struct wdc_command {
#define AT_ERROR 0x0080 /* command is done with error */
#define AT_TIMEOU 0x0100 /* command timed out */
#define AT_DF 0x0200 /* Drive fault */
-#define AT_READREG 0x0400 /* Read registers on completion */
+#define AT_RESET 0x0400 /* command terminated by channel reset */
+#define AT_GONE 0x0800 /* command terminated because device is gone */
+#define AT_READREG 0x1000 /* Read registers on completion */
int timeout; /* timeout (in ms) */
void *data; /* Data buffer address */
@@ -249,6 +257,10 @@ struct ata_bustype {
int bustype_type; /* symbolic name of type */
int (*ata_bio)(struct ata_drive_datas *, struct ata_bio *);
void (*ata_reset_channel)(struct ata_drive_datas *, int);
+/* extra flags for ata_reset_channel(), in addition to AT_* */
+#define AT_RST_EMERG 0x10000 /* emergency - e.g. for a dump */
+#define AT_RST_NOCMD 0x20000 /* XXX has to go - temporary until we have tagged queuing */
+
int (*ata_exec_command)(struct ata_drive_datas *,
struct wdc_command *);
diff --git a/sys/dev/ata/wd.c b/sys/dev/ata/wd.c
index 7198f2ff256..a949e820e69 100644
--- a/sys/dev/ata/wd.c
+++ b/sys/dev/ata/wd.c
@@ -1,4 +1,4 @@
-/* $NetBSD: wd.c,v 1.280 2004/06/22 19:20:56 mycroft Exp $ */
+/* $NetBSD: wd.c,v 1.281 2004/07/31 21:26:43 bouyer Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
@@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.280 2004/06/22 19:20:56 mycroft Exp $");
+__KERNEL_RCSID(0, "$NetBSD: wd.c,v 1.281 2004/07/31 21:26:43 bouyer Exp $");
#ifndef WDCDEBUG
#define WDCDEBUG
@@ -742,6 +742,9 @@ wddone(void *v)
case TIMEOUT:
errmsg = "device timeout";
goto retry;
+ case ERR_RESET:
+ errmsg = "channel reset";
+ goto retry2;
case ERROR:
/* Don't care about media change bits */
if (wd->sc_wdc_bio.r_error != 0 &&
@@ -751,6 +754,7 @@ wddone(void *v)
do_perror = 1;
retry: /* Just reset and retry. Can we do more ? */
wd->atabus->ata_reset_channel(wd->drvp, 0);
+retry2:
diskerr(bp, "wd", errmsg, LOG_PRINTF,
wd->sc_wdc_bio.blkdone, wd->sc_dk.dk_label);
if (wd->retries < WDIORETRIES)
@@ -1449,6 +1453,7 @@ wddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
if (wddumprecalibrated == 0) {
wddumpmulti = wd->sc_multi;
wddumprecalibrated = 1;
+ wd->atabus->ata_reset_channel(wd->drvp, AT_POLL | AT_RST_EMERG);
wd->drvp->state = RESET;
}
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);
}
}
diff --git a/sys/dev/scsipi/atapi_wdc.c b/sys/dev/scsipi/atapi_wdc.c
index b2a70f5f707..c3a5b8512b8 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.70 2004/05/08 15:03:32 bouyer Exp $ */
+/* $NetBSD: atapi_wdc.c,v 1.71 2004/07/31 21:26:43 bouyer Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer.
@@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.70 2004/05/08 15:03:32 bouyer Exp $");
+__KERNEL_RCSID(0, "$NetBSD: atapi_wdc.c,v 1.71 2004/07/31 21:26:43 bouyer Exp $");
#ifndef WDCDEBUG
#define WDCDEBUG
@@ -88,7 +88,8 @@ static void wdc_atapi_probe_device(struct atapibus_softc *, int);
static void wdc_atapi_minphys (struct buf *bp);
static void wdc_atapi_start(struct wdc_channel *,struct ata_xfer *);
static int wdc_atapi_intr(struct wdc_channel *, struct ata_xfer *, int);
-static void wdc_atapi_kill_xfer(struct wdc_channel *, struct ata_xfer *);
+static void wdc_atapi_kill_xfer(struct wdc_channel *,
+ struct ata_xfer *, int);
static void wdc_atapi_phase_complete(struct ata_xfer *);
static void wdc_atapi_done(struct wdc_channel *, struct ata_xfer *);
static void wdc_atapi_reset(struct wdc_channel *, struct ata_xfer *);
@@ -168,15 +169,27 @@ wdc_atapi_kill_pending(struct scsipi_periph *periph)
}
static void
-wdc_atapi_kill_xfer(struct wdc_channel *chp, struct ata_xfer *xfer)
+wdc_atapi_kill_xfer(struct wdc_channel *chp, struct ata_xfer *xfer, int reason)
{
struct scsipi_xfer *sc_xfer = xfer->c_cmd;
callout_stop(&chp->ch_callout);
/* remove this command from xfer queue */
wdc_free_xfer(chp, xfer);
- sc_xfer->error = XS_DRIVER_STUFFUP;
- scsipi_done(sc_xfer);
+ switch (reason) {
+ case KILL_GONE:
+ sc_xfer->error = XS_DRIVER_STUFFUP;
+ scsipi_done(sc_xfer);
+ break;
+ case KILL_RESET:
+ sc_xfer->error = XS_RESET;
+ wdc_atapi_reset(chp, xfer);
+ break;
+ default:
+ printf("wdc_ata_bio_kill_xfer: unknown reason %d\n",
+ reason);
+ panic("wdc_ata_bio_kill_xfer");
+ }
}
static int