diff options
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/ata/ata_wdc.c | 23 | ||||
| -rw-r--r-- | sys/dev/ata/atavar.h | 18 | ||||
| -rw-r--r-- | sys/dev/ata/wd.c | 9 | ||||
| -rw-r--r-- | sys/dev/ic/wdc.c | 101 | ||||
| -rw-r--r-- | sys/dev/scsipi/atapi_wdc.c | 25 |
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 |
