diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/isa/wdc.c | 1255 | ||||
-rw-r--r-- | sys/dev/isa/wdlink.h | 13 |
2 files changed, 652 insertions, 616 deletions
diff --git a/sys/dev/isa/wdc.c b/sys/dev/isa/wdc.c index 7349321522d..48094436200 100644 --- a/sys/dev/isa/wdc.c +++ b/sys/dev/isa/wdc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wdc.c,v 1.18 1997/02/24 05:50:46 niklas Exp $ */ +/* $OpenBSD: wdc.c,v 1.19 1997/07/04 17:02:04 downsj Exp $ */ /* $NetBSD: wd.c,v 1.150 1996/05/12 23:54:03 mycroft Exp $ */ /* @@ -66,7 +66,10 @@ #include <dev/isa/wdreg.h> #include <dev/isa/wdlink.h> +#include "atapibus.h" +#if NATAPIBUS > 0 #include <dev/atapi/atapilink.h> +#endif /* NATAPIBUS */ #define WAITTIME (10 * hz) /* time to wait for a completion */ /* this is a lot for hard drives, but not for cdroms */ @@ -97,23 +100,28 @@ struct cfdriver wdc_cd = { NULL, "wdc", DV_DULL }; +#if NWD > 0 int wdc_ata_intr __P((struct wdc_softc *,struct wdc_xfer *)); -void wdcstart __P((struct wdc_softc *)); void wdc_ata_start __P((struct wdc_softc *,struct wdc_xfer *)); +void wdc_ata_done __P((struct wdc_softc *, struct wdc_xfer *)); +__inline static void u_int16_to_string __P((u_int16_t *, char *, size_t)); +#endif /* NWD */ int wait_for_phase __P((struct wdc_softc *, int)); int wait_for_unphase __P((struct wdc_softc *, int)); -void wdc_atapi_start __P((struct wdc_softc *,struct wdc_xfer *)); +void wdcstart __P((struct wdc_softc *)); int wdcreset __P((struct wdc_softc *)); void wdcrestart __P((void *arg)); void wdcunwedge __P((struct wdc_softc *)); void wdctimeout __P((void *arg)); int wdccontrol __P((struct wd_link *)); -void wdc_ata_done __P((struct wdc_softc *, struct wdc_xfer *)); void wdc_free_xfer __P((struct wdc_xfer *)); void wdcerror __P((struct wdc_softc*, char *)); void wdcbit_bucket __P(( struct wdc_softc *, int)); +#if NATAPIBUS > 0 +void wdc_atapi_start __P((struct wdc_softc *,struct wdc_xfer *)); int wdc_atapi_intr __P((struct wdc_softc *, struct wdc_xfer *)); void wdc_atapi_done __P((struct wdc_softc *, struct wdc_xfer *)); +#endif /* NATAPIBUS */ #ifdef ATAPI_DEBUG static int wdc_nxfer; @@ -222,7 +230,9 @@ wdcattach(parent, self, aux) { struct wdc_softc *wdc = (void *)self; struct isa_attach_args *ia = aux; +#if NWD > 0 int drive; +#endif /* NWD */ TAILQ_INIT(&wdc->sc_xfer); wdc->sc_drq = ia->ia_drq; @@ -237,6 +247,7 @@ wdcattach(parent, self, aux) wdc_nxfer = 0; #endif +#if NATAPIBUS > 0 /* * Attach an ATAPI bus, if configured. */ @@ -251,7 +262,8 @@ wdcattach(parent, self, aux) wdc->ab_link->ctlr_link = &(wdc->ctlr_link); wdc->ab_link->ctrl = self->dv_unit; (void)config_found(self, (void *)wdc->ab_link, NULL); - +#endif /* NATAPIBUS */ +#if NWD > 0 /* * Attach standard IDE/ESDI/etc. disks to the controller. */ @@ -281,13 +293,14 @@ wdcattach(parent, self, aux) if (wdc->sc_drq != DRQUNK) wdc->d_link[drive]->sc_mode = WDM_DMA; else -#endif +#endif /* NISADMA */ wdc->d_link[drive]->sc_mode = 0; (void)config_found(self, (void *)wdc->d_link[drive], wdcprint); } } +#endif /* NWD */ } /* @@ -331,17 +344,28 @@ wdcstart(wdc) return; } wdc->sc_flags |= WDCF_ACTIVE; +#if NATAPIBUS > 0 && NWD > 0 if (xfer->c_flags & C_ATAPI) { #ifdef ATAPI_DEBUG_WDC printf("wdcstart: atapi\n"); #endif - wdc_atapi_start(wdc,xfer); - } else { - wdc_ata_start(wdc,xfer); - } + wdc_atapi_start(wdc, xfer); + } else + wdc_ata_start(wdc, xfer); +#else +#if NATAPIBUS > 0 +#ifdef ATAPI_DEBUG_WDC + printf("wdcstart: atapi\n"); +#endif + wdc_atapi_start(wdc, xfer); +#endif /* NATAPIBUS */ +#if NWD > 0 + wdc_ata_start(wdc, xfer); +#endif /* NWD */ +#endif /* NATAPIBUS && NWD */ } - +#if NWD > 0 void wdc_ata_start(wdc, xfer) struct wdc_softc *wdc; @@ -568,172 +592,7 @@ wdc_ata_start(wdc, xfer) } int -wait_for_phase(wdc, wphase) - struct wdc_softc *wdc; - int wphase; -{ - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - int i, phase; - - for (i = 20000; i; i--) { - phase = (bus_space_read_1(iot, ioh, wd_ireason) & - (WDCI_CMD | WDCI_IN)) | - (bus_space_read_1(iot, ioh, wd_status) - & WDCS_DRQ); - if (phase == wphase) - break; - delay(10); - } - return (phase); -} - -int -wait_for_unphase(wdc, wphase) - struct wdc_softc *wdc; - int wphase; -{ - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - int i, phase; - - for (i = 20000; i; i--) { - phase = (bus_space_read_1(iot, ioh, wd_ireason) & - (WDCI_CMD | WDCI_IN)) | - (bus_space_read_1(iot, ioh, wd_status) - & WDCS_DRQ); - if (phase != wphase) - break; - delay(10); - } - return (phase); -} - -void -wdc_atapi_start(wdc, xfer) - struct wdc_softc *wdc; - struct wdc_xfer *xfer; -{ - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - struct atapi_command_packet *acp = xfer->atapi_cmd; - -#ifdef ATAPI_DEBUG_WDC - printf("wdc_atapi_start, acp flags %lx\n",acp->flags); -#endif - if (wdc->sc_errors >= WDIORETRIES) { - acp->status |= ERROR; - acp->error = bus_space_read_1(iot, ioh, wd_error); - wdc_atapi_done(wdc, xfer); - return; - } - if (wait_for_unbusy(wdc) != 0) { - if ((wdc->sc_status & WDCS_ERR) == 0) { - printf("wdc_atapi_start: not ready, st = %02x\n", - wdc->sc_status); - acp->status = ERROR; - return; - } - } - - if (wdccommand((struct wd_link*)xfer->d_link, ATAPI_PACKET_COMMAND, - acp->drive, acp->data_size, 0, 0, 0) != 0) { - printf("wdc_atapi_start: can't send atapi paket command\n"); - acp->status = ERROR; - wdc->sc_flags |= WDCF_IRQ_WAIT; - return; - } - if ((acp->flags & (ACAP_DRQ_INTR|ACAP_DRQ_ACCEL)) != ACAP_DRQ_INTR) { - if (!(wdc->sc_flags & WDCF_BROKENPOLL)) { - int phase = wait_for_phase(wdc, PHASE_CMDOUT); - - if (phase != PHASE_CMDOUT) { - printf("wdc_atapi_start: timeout waiting " - "PHASE_CMDOUT, got 0x%x\n", phase); - - /* NEC SUCKS. */ - wdc->sc_flags |= WDCF_BROKENPOLL; - } - } else - DELAY(10); /* Simply pray for the data. */ - - bus_space_write_raw_multi_2(iot, ioh, wd_data, acp->command, - acp->command_size); - } - wdc->sc_flags |= WDCF_IRQ_WAIT; - -#ifdef ATAPI_DEBUG2 - printf("wdc_atapi_start: timeout\n"); -#endif - timeout(wdctimeout, wdc, WAITTIME); - return; -} - - -/* - * Interrupt routine for the controller. Acknowledge the interrupt, check for - * errors on the current operation, mark it done if necessary, and start the - * next request. Also check for a partially done transfer, and continue with - * the next chunk if so. - */ -int -wdcintr(arg) - void *arg; -{ - struct wdc_softc *wdc = arg; - struct wdc_xfer *xfer; - - if ((wdc->sc_flags & WDCF_IRQ_WAIT) == 0) { - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - u_char s; -#ifdef ATAPI_DEBUG_WDC - u_char e, i; -#endif - DELAY(100); - - /* Clear the pending interrupt and abort. */ - s = bus_space_read_1(iot, ioh, wd_status); - if (s != (WDCS_DRDY|WDCS_DSC)) { -#ifdef ATAPI_DEBUG_WDC - e = bus_space_read_1(iot, ioh, wd_error); - i = bus_space_read_1(iot, ioh, wd_seccnt); - - printf("wdcintr: inactive controller, " - "punting st=%02x er=%02x irr=%02x\n", s, e, i); -#else - bus_space_read_1(iot, ioh, wd_error); - bus_space_read_1(iot, ioh, wd_seccnt); -#endif - - if (s & WDCS_DRQ) { - int len = 256 * bus_space_read_1(iot, ioh, - wd_cyl_hi) + - bus_space_read_1(iot, ioh, wd_cyl_lo); -#ifdef ATAPI_DEBUG_WDC - printf("wdcintr: clearing up %d bytes\n", len); -#endif - wdcbit_bucket(wdc, len); - } - } - return 0; - } - - WDDEBUG_PRINT(("wdcintr\n")); - - wdc->sc_flags &= ~WDCF_IRQ_WAIT; - xfer = wdc->sc_xfer.tqh_first; - if (xfer->c_flags & C_ATAPI) { - (void)wdc_atapi_intr(wdc, xfer); - return 0; - } else { - return wdc_ata_intr(wdc, xfer); - } -} - - -int -wdc_ata_intr(wdc,xfer) +wdc_ata_intr(wdc, xfer) struct wdc_softc *wdc; struct wdc_xfer *xfer; { @@ -851,245 +710,6 @@ restart: return 1; } -int -wdcreset(wdc) - struct wdc_softc *wdc; -{ - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - - /* Reset the device. */ - bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_RST|WDCTL_IDS); - delay(1000); - bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_IDS); - delay(1000); - (void) bus_space_read_1(iot, ioh, wd_error); - bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_4BIT); - - if (wait_for_unbusy(wdc) < 0) { - printf("%s: reset failed\n", wdc->sc_dev.dv_xname); - return 1; - } - - return 0; -} - -void -wdcrestart(arg) - void *arg; -{ - struct wdc_softc *wdc = arg; - int s; - - s = splbio(); - wdcstart(wdc); - splx(s); -} - -/* - * Unwedge the controller after an unexpected error. We do this by resetting - * it, marking all drives for recalibration, and stalling the queue for a short - * period to give the reset time to finish. - * NOTE: We use a timeout here, so this routine must not be called during - * autoconfig or dump. - */ -void -wdcunwedge(wdc) - struct wdc_softc *wdc; -{ - int unit; - -#ifdef ATAPI_DEBUG - printf("wdcunwedge\n"); -#endif - - untimeout(wdctimeout, wdc); - wdc->sc_flags &= ~WDCF_IRQ_WAIT; - (void) wdcreset(wdc); - - /* Schedule recalibrate for all drives on this controller. */ - for (unit = 0; unit < 2; unit++) { - if (!wdc->d_link[unit]) continue; - if (wdc->d_link[unit]->sc_state > RECAL) - wdc->d_link[unit]->sc_state = RECAL; - } - - wdc->sc_flags |= WDCF_ERROR; - ++wdc->sc_errors; - - /* Wake up in a little bit and restart the operation. */ - WDDEBUG_PRINT(("wdcrestart from wdcunwedge\n")); - wdc->sc_flags &= ~WDCF_ACTIVE; - timeout(wdcrestart, wdc, RECOVERYTIME); -} - -int -wdcwait(wdc, mask) - struct wdc_softc *wdc; - int mask; -{ - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - int timeout = 0; - u_char status; -#ifdef WDCNDELAY_DEBUG - extern int cold; -#endif - - WDDEBUG_PRINT(("wdcwait\n")); - - for (;;) { - wdc->sc_status = status = bus_space_read_1(iot, ioh, - wd_status); - /* - * XXX - * If a single slave ATAPI device is attached, it may - * have released the bus. Select it and try again. - */ - if (status == 0xff && wdc->sc_flags & WDCF_ONESLAVE) { - bus_space_write_1(iot, ioh, wd_sdh, - WDSD_IBM | 0x10); - wdc->sc_status = status = bus_space_read_1(iot, ioh, - wd_status); - } - if ((status & WDCS_BSY) == 0 && (status & mask) == mask) - break; - if (++timeout > WDCNDELAY) { -#ifdef ATAPI_DEBUG2 - printf("wdcwait: timeout, status 0x%x\n", status); -#endif - return -1; - } - delay(WDCDELAY); - } - if (status & WDCS_ERR) { - wdc->sc_error = bus_space_read_1(iot, ioh, wd_error); - return WDCS_ERR; - } -#ifdef WDCNDELAY_DEBUG - /* After autoconfig, there should be no long delays. */ - if (!cold && timeout > WDCNDELAY_DEBUG) { - struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first; - if (xfer == NULL) - printf("%s: warning: busy-wait took %dus\n", - wdc->sc_dev.dv_xname, WDCDELAY * timeout); - else - printf("%s(%s): warning: busy-wait took %dus\n", - wdc->sc_dev.dv_xname, - ((struct device*)xfer->d_link->wd_softc)->dv_xname, - WDCDELAY * timeout); - } -#endif - return 0; -} - -void -wdctimeout(arg) - void *arg; -{ - struct wdc_softc *wdc = (struct wdc_softc *)arg; - int s; - - WDDEBUG_PRINT(("wdctimeout\n")); - - s = splbio(); - if ((wdc->sc_flags & WDCF_IRQ_WAIT) != 0) { - wdc->sc_flags &= ~WDCF_IRQ_WAIT; - wdcerror(wdc, "lost interrupt"); - wdcunwedge(wdc); - } else - wdcerror(wdc, "missing untimeout"); - splx(s); -} - -/* - * Wait for the drive to become ready and send a command. - * Return -1 if busy for too long or 0 otherwise. - * Assumes interrupts are blocked. - */ -int -wdccommand(d_link, command, drive, cylin, head, sector, count) - struct wd_link *d_link; - int command; - int drive, cylin, head, sector, count; -{ - struct wdc_softc *wdc = (void*)d_link->wdc_softc; - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - int stat; - - WDDEBUG_PRINT(("wdccommand drive %d\n", drive)); - -#if defined(DIAGNOSTIC) && defined(WDCDEBUG) - if ((wdc->sc_flags & WDCF_ACTIVE) == 0) - printf("wdccommand: controler not active (drive %d)\n", drive); -#endif - - /* Select drive, head, and addressing mode. */ - bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM | (drive << 4) | head); - - /* Wait for it to become ready to accept a command. */ - if (command == WDCC_IDP || d_link->type == BUS) - stat = wait_for_unbusy(wdc); - else - stat = wdcwait(wdc, WDCS_DRDY); - - if (stat < 0) { -#ifdef ATAPI_DEBUG - printf("wdcommand: xfer failed (wait_for_unbusy) status %d\n", - stat); -#endif - return -1; - } - - /* Load parameters. */ - if (d_link->type == DRIVE && d_link->sc_lp->d_type == DTYPE_ST506) - bus_space_write_1(iot, ioh, wd_precomp, - d_link->sc_lp->d_precompcyl / 4); - else - bus_space_write_1(iot, ioh, wd_features, 0); - bus_space_write_1(iot, ioh, wd_cyl_lo, cylin); - bus_space_write_1(iot, ioh, wd_cyl_hi, cylin >> 8); - bus_space_write_1(iot, ioh, wd_sector, sector); - bus_space_write_1(iot, ioh, wd_seccnt, count); - - /* Send command. */ - bus_space_write_1(iot, ioh, wd_command, command); - - return 0; -} - -/* - * Simplified version of wdccommand(). - */ -int -wdccommandshort(wdc, drive, command) - struct wdc_softc *wdc; - int drive; - int command; -{ - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - - WDDEBUG_PRINT(("wdccommandshort\n")); - -#if defined(DIAGNOSTIC) && defined(WDCDEBUG) - if ((wdc->sc_flags & WDCF_ACTIVE) == 0) - printf("wdccommandshort: controller not active (drive %d)\n", - drive); -#endif - - /* Select drive. */ - bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM|(drive << 4)); - - if (wdcwait(wdc, WDCS_DRDY) < 0) - return -1; - - bus_space_write_1(iot, ioh, wd_command, command); - - return 0; -} - void wdc_ata_done(wdc, xfer) struct wdc_softc *wdc; @@ -1127,171 +747,6 @@ wdc_ata_done(wdc, xfer) splx(s); } -void -wdc_exec_xfer(d_link, xfer) - struct wd_link *d_link; - struct wdc_xfer *xfer; -{ - struct wdc_softc *wdc = (struct wdc_softc *)d_link->wdc_softc; - int s; - - WDDEBUG_PRINT(("wdc_exec_xfer\n")); - - s = splbio(); - - /* insert at the end of command list */ - TAILQ_INSERT_TAIL(&wdc->sc_xfer,xfer , c_xferchain) - WDDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags %d\n", - wdc->sc_flags)); - wdcstart(wdc); - splx(s); -} - -struct wdc_xfer * -wdc_get_xfer(c_link,flags) - struct wdc_link *c_link; - int flags; -{ - struct wdc_xfer *xfer; - int s; - - s = splbio(); - if ((xfer = xfer_free_list.lh_first) != NULL) { - LIST_REMOVE(xfer, free_list); - splx(s); -#ifdef DIAGNOSTIC - if ((xfer->c_flags & C_INUSE) != 0) - panic("wdc_get_xfer: xfer already in use\n"); -#endif - } else { - splx(s); -#ifdef ATAPI_DEBUG - printf("wdc:making xfer %d\n",wdc_nxfer); -#endif - xfer = malloc(sizeof(*xfer), M_DEVBUF, - ((flags & IDE_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK)); - if (xfer == NULL) - return 0; - -#ifdef DIAGNOSTIC - xfer->c_flags &= ~C_INUSE; -#endif -#ifdef ATAPI_DEBUG - wdc_nxfer++; -#endif - } -#ifdef DIAGNOSTIC - if ((xfer->c_flags & C_INUSE) != 0) - panic("wdc_get_xfer: xfer already in use\n"); -#endif - bzero(xfer, sizeof(struct wdc_xfer)); - xfer->c_flags = C_INUSE; - xfer->c_link = c_link; - return xfer; -} - -void -wdc_free_xfer(xfer) - struct wdc_xfer *xfer; -{ - int s; - - s = splbio(); - xfer->c_flags &= ~C_INUSE; - LIST_INSERT_HEAD(&xfer_free_list, xfer, free_list); - splx(s); -} - -/* - * Implement operations needed before read/write. - * Returns 0 if operation still in progress, 1 if completed. - */ -int -wdccontrol(d_link) - struct wd_link *d_link; -{ - struct wdc_softc *wdc = (void *)d_link->wdc_softc; - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - - WDDEBUG_PRINT(("wdccontrol\n")); - - switch (d_link->sc_state) { - case RECAL: /* Set SDH, step rate, do recal. */ - if (wdccommandshort(wdc, d_link->sc_drive, WDCC_RECAL) != 0) { - wderror(d_link, NULL, "wdccontrol: recal failed (1)"); - goto bad; - } - d_link->sc_state = RECAL_WAIT; - break; - - case RECAL_WAIT: - if (wdc->sc_status & WDCS_ERR) { - wderror(d_link, NULL, "wdccontrol: recal failed (2)"); - goto bad; - } - /* fall through */ - - case GEOMETRY: - if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) - goto multimode; - if (wdsetctlr(d_link) != 0) { - /* Already printed a message. */ - goto bad; - } - d_link->sc_state = GEOMETRY_WAIT; - break; - - case GEOMETRY_WAIT: - if (wdc->sc_status & WDCS_ERR) { - wderror(d_link, NULL, "wdccontrol: geometry failed"); - goto bad; - } - /* fall through */ - - case MULTIMODE: - multimode: - if (d_link->sc_mode != WDM_PIOMULTI) - goto ready; - bus_space_write_1(iot, ioh, wd_seccnt, d_link->sc_multiple); - if (wdccommandshort(wdc, d_link->sc_drive, - WDCC_SETMULTI) != 0) { - wderror(d_link, NULL, - "wdccontrol: setmulti failed (1)"); - goto bad; - } - d_link->sc_state = MULTIMODE_WAIT; - break; - - case MULTIMODE_WAIT: - if (wdc->sc_status & WDCS_ERR) { - wderror(d_link, NULL, - "wdccontrol: setmulti failed (2)"); - goto bad; - } - /* fall through */ - - case READY: - ready: - wdc->sc_errors = 0; - d_link->sc_state = READY; - /* - * The rest of the initialization can be done by normal means. - */ - return 1; - - bad: - wdcunwedge(wdc); - return 0; - } - - wdc->sc_flags |= WDCF_IRQ_WAIT; - timeout(wdctimeout, wdc, WAITTIME); - return 0; -} - -__inline static void u_int16_to_string __P((u_int16_t *, char *, size_t)); - /* decode IDE strings, stored as if the words are big-endian. */ __inline static void u_int16_to_string(from, to, cnt) @@ -1427,17 +882,196 @@ wdc_get_parms(d_link) return 0; } +/* + * Implement operations needed before read/write. + * Returns 0 if operation still in progress, 1 if completed. + */ +int +wdccontrol(d_link) + struct wd_link *d_link; +{ + struct wdc_softc *wdc = (void *)d_link->wdc_softc; + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + + WDDEBUG_PRINT(("wdccontrol\n")); + + switch (d_link->sc_state) { + case RECAL: /* Set SDH, step rate, do recal. */ + if (wdccommandshort(wdc, d_link->sc_drive, WDCC_RECAL) != 0) { + wderror(d_link, NULL, "wdccontrol: recal failed (1)"); + goto bad; + } + d_link->sc_state = RECAL_WAIT; + break; + + case RECAL_WAIT: + if (wdc->sc_status & WDCS_ERR) { + wderror(d_link, NULL, "wdccontrol: recal failed (2)"); + goto bad; + } + /* fall through */ + + case GEOMETRY: + if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) + goto multimode; + if (wdsetctlr(d_link) != 0) { + /* Already printed a message. */ + goto bad; + } + d_link->sc_state = GEOMETRY_WAIT; + break; + + case GEOMETRY_WAIT: + if (wdc->sc_status & WDCS_ERR) { + wderror(d_link, NULL, "wdccontrol: geometry failed"); + goto bad; + } + /* fall through */ + + case MULTIMODE: + multimode: + if (d_link->sc_mode != WDM_PIOMULTI) + goto ready; + bus_space_write_1(iot, ioh, wd_seccnt, d_link->sc_multiple); + if (wdccommandshort(wdc, d_link->sc_drive, + WDCC_SETMULTI) != 0) { + wderror(d_link, NULL, + "wdccontrol: setmulti failed (1)"); + goto bad; + } + d_link->sc_state = MULTIMODE_WAIT; + break; + + case MULTIMODE_WAIT: + if (wdc->sc_status & WDCS_ERR) { + wderror(d_link, NULL, + "wdccontrol: setmulti failed (2)"); + goto bad; + } + /* fall through */ + + case READY: + ready: + wdc->sc_errors = 0; + d_link->sc_state = READY; + /* + * The rest of the initialization can be done by normal means. + */ + return 1; + + bad: + wdcunwedge(wdc); + return 0; + } + + wdc->sc_flags |= WDCF_IRQ_WAIT; + timeout(wdctimeout, wdc, WAITTIME); + return 0; +} +#endif /* NWD */ + +int +wait_for_phase(wdc, wphase) + struct wdc_softc *wdc; + int wphase; +{ + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + int i, phase; + + for (i = 20000; i; i--) { + phase = (bus_space_read_1(iot, ioh, wd_ireason) & + (WDCI_CMD | WDCI_IN)) | + (bus_space_read_1(iot, ioh, wd_status) + & WDCS_DRQ); + if (phase == wphase) + break; + delay(10); + } + return (phase); +} + +int +wait_for_unphase(wdc, wphase) + struct wdc_softc *wdc; + int wphase; +{ + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + int i, phase; + + for (i = 20000; i; i--) { + phase = (bus_space_read_1(iot, ioh, wd_ireason) & + (WDCI_CMD | WDCI_IN)) | + (bus_space_read_1(iot, ioh, wd_status) + & WDCS_DRQ); + if (phase != wphase) + break; + delay(10); + } + return (phase); +} + +#if NATAPIBUS > 0 void -wdcerror(wdc, msg) +wdc_atapi_start(wdc, xfer) struct wdc_softc *wdc; - char *msg; + struct wdc_xfer *xfer; { - struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first; - if (xfer == NULL) - printf("%s: %s\n", wdc->sc_dev.dv_xname, msg); - else - printf("%s(%s): %s\n", wdc->sc_dev.dv_xname, - ((struct device*)xfer->d_link->wd_softc)->dv_xname, msg); + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + struct atapi_command_packet *acp = xfer->atapi_cmd; + +#ifdef ATAPI_DEBUG_WDC + printf("wdc_atapi_start, acp flags %lx\n",acp->flags); +#endif + if (wdc->sc_errors >= WDIORETRIES) { + acp->status |= ERROR; + acp->error = bus_space_read_1(iot, ioh, wd_error); + wdc_atapi_done(wdc, xfer); + return; + } + if (wait_for_unbusy(wdc) != 0) { + if ((wdc->sc_status & WDCS_ERR) == 0) { + printf("wdc_atapi_start: not ready, st = %02x\n", + wdc->sc_status); + acp->status = ERROR; + return; + } + } + + if (wdccommand((struct wd_link*)xfer->d_link, ATAPI_PACKET_COMMAND, + acp->drive, acp->data_size, 0, 0, 0) != 0) { + printf("wdc_atapi_start: can't send atapi paket command\n"); + acp->status = ERROR; + wdc->sc_flags |= WDCF_IRQ_WAIT; + return; + } + if ((acp->flags & (ACAP_DRQ_INTR|ACAP_DRQ_ACCEL)) != ACAP_DRQ_INTR) { + if (!(wdc->sc_flags & WDCF_BROKENPOLL)) { + int phase = wait_for_phase(wdc, PHASE_CMDOUT); + + if (phase != PHASE_CMDOUT) { + printf("wdc_atapi_start: timeout waiting " + "PHASE_CMDOUT, got 0x%x\n", phase); + + /* NEC SUCKS. */ + wdc->sc_flags |= WDCF_BROKENPOLL; + } + } else + DELAY(10); /* Simply pray for the data. */ + + bus_space_write_raw_multi_2(iot, ioh, wd_data, acp->command, + acp->command_size); + } + wdc->sc_flags |= WDCF_IRQ_WAIT; + +#ifdef ATAPI_DEBUG2 + printf("wdc_atapi_start: timeout\n"); +#endif + timeout(wdctimeout, wdc, WAITTIME); + return; } int @@ -1657,29 +1291,6 @@ wdc_atapi_send_command_packet(ab_link, acp) } } - -/* - * the bit bucket - */ -void -wdcbit_bucket(wdc, size) - struct wdc_softc *wdc; - int size; -{ - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - int i; - - for (i = 0 ; i < size / 2 ; i++) { - u_int16_t null; - - bus_space_read_multi_2(iot, ioh, wd_data, &null, 1); - } - - if (size % 2) - bus_space_read_1(iot, ioh, wd_data); -} - int wdc_atapi_intr(wdc, xfer) struct wdc_softc *wdc; @@ -1859,3 +1470,423 @@ wdc_atapi_done(wdc, xfer) splx(s); } +#endif /* NATAPIBUS */ + +/* + * Interrupt routine for the controller. Acknowledge the interrupt, check for + * errors on the current operation, mark it done if necessary, and start the + * next request. Also check for a partially done transfer, and continue with + * the next chunk if so. + */ +int +wdcintr(arg) + void *arg; +{ + struct wdc_softc *wdc = arg; + struct wdc_xfer *xfer; + + if ((wdc->sc_flags & WDCF_IRQ_WAIT) == 0) { + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + u_char s; +#ifdef ATAPI_DEBUG_WDC + u_char e, i; +#endif + DELAY(100); + + /* Clear the pending interrupt and abort. */ + s = bus_space_read_1(iot, ioh, wd_status); + if (s != (WDCS_DRDY|WDCS_DSC)) { +#ifdef ATAPI_DEBUG_WDC + e = bus_space_read_1(iot, ioh, wd_error); + i = bus_space_read_1(iot, ioh, wd_seccnt); + + printf("wdcintr: inactive controller, " + "punting st=%02x er=%02x irr=%02x\n", s, e, i); +#else + bus_space_read_1(iot, ioh, wd_error); + bus_space_read_1(iot, ioh, wd_seccnt); +#endif + + if (s & WDCS_DRQ) { + int len = 256 * bus_space_read_1(iot, ioh, + wd_cyl_hi) + + bus_space_read_1(iot, ioh, wd_cyl_lo); +#ifdef ATAPI_DEBUG_WDC + printf("wdcintr: clearing up %d bytes\n", len); +#endif + wdcbit_bucket(wdc, len); + } + } + return 0; + } + + WDDEBUG_PRINT(("wdcintr\n")); + + wdc->sc_flags &= ~WDCF_IRQ_WAIT; + xfer = wdc->sc_xfer.tqh_first; +#if NATAPIBUS > 0 && NWD > 0 + if (xfer->c_flags & C_ATAPI) { + (void)wdc_atapi_intr(wdc, xfer); + return 0; + } else + return wdc_ata_intr(wdc, xfer); +#else +#if NATAPIBUS > 0 + (void)wdc_atapi_intr(wdc, xfer); + return 0; +#endif /* NATAPIBUS */ +#if NWD > 0 + return wdc_ata_intr(wdc, xfer); +#endif /* NWD */ +#endif /* NATAPIBUS && NWD */ +} + +int +wdcreset(wdc) + struct wdc_softc *wdc; +{ + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + + /* Reset the device. */ + bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_RST|WDCTL_IDS); + delay(1000); + bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_IDS); + delay(1000); + (void) bus_space_read_1(iot, ioh, wd_error); + bus_space_write_1(iot, ioh, wd_ctlr, WDCTL_4BIT); + + if (wait_for_unbusy(wdc) < 0) { + printf("%s: reset failed\n", wdc->sc_dev.dv_xname); + return 1; + } + + return 0; +} + +void +wdcrestart(arg) + void *arg; +{ + struct wdc_softc *wdc = arg; + int s; + + s = splbio(); + wdcstart(wdc); + splx(s); +} + +/* + * Unwedge the controller after an unexpected error. We do this by resetting + * it, marking all drives for recalibration, and stalling the queue for a short + * period to give the reset time to finish. + * NOTE: We use a timeout here, so this routine must not be called during + * autoconfig or dump. + */ +void +wdcunwedge(wdc) + struct wdc_softc *wdc; +{ + int unit; + +#ifdef ATAPI_DEBUG + printf("wdcunwedge\n"); +#endif + + untimeout(wdctimeout, wdc); + wdc->sc_flags &= ~WDCF_IRQ_WAIT; + (void) wdcreset(wdc); + + /* Schedule recalibrate for all drives on this controller. */ + for (unit = 0; unit < 2; unit++) { + if (!wdc->d_link[unit]) continue; + if (wdc->d_link[unit]->sc_state > RECAL) + wdc->d_link[unit]->sc_state = RECAL; + } + + wdc->sc_flags |= WDCF_ERROR; + ++wdc->sc_errors; + + /* Wake up in a little bit and restart the operation. */ + WDDEBUG_PRINT(("wdcrestart from wdcunwedge\n")); + wdc->sc_flags &= ~WDCF_ACTIVE; + timeout(wdcrestart, wdc, RECOVERYTIME); +} + +int +wdcwait(wdc, mask) + struct wdc_softc *wdc; + int mask; +{ + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + int timeout = 0; + u_char status; +#ifdef WDCNDELAY_DEBUG + extern int cold; +#endif + + WDDEBUG_PRINT(("wdcwait\n")); + + for (;;) { + wdc->sc_status = status = bus_space_read_1(iot, ioh, + wd_status); + /* + * XXX + * If a single slave ATAPI device is attached, it may + * have released the bus. Select it and try again. + */ + if (status == 0xff && wdc->sc_flags & WDCF_ONESLAVE) { + bus_space_write_1(iot, ioh, wd_sdh, + WDSD_IBM | 0x10); + wdc->sc_status = status = bus_space_read_1(iot, ioh, + wd_status); + } + if ((status & WDCS_BSY) == 0 && (status & mask) == mask) + break; + if (++timeout > WDCNDELAY) { +#ifdef ATAPI_DEBUG2 + printf("wdcwait: timeout, status 0x%x\n", status); +#endif + return -1; + } + delay(WDCDELAY); + } + if (status & WDCS_ERR) { + wdc->sc_error = bus_space_read_1(iot, ioh, wd_error); + return WDCS_ERR; + } +#ifdef WDCNDELAY_DEBUG + /* After autoconfig, there should be no long delays. */ + if (!cold && timeout > WDCNDELAY_DEBUG) { + struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first; + if (xfer == NULL) + printf("%s: warning: busy-wait took %dus\n", + wdc->sc_dev.dv_xname, WDCDELAY * timeout); + else + printf("%s(%s): warning: busy-wait took %dus\n", + wdc->sc_dev.dv_xname, + ((struct device*)xfer->d_link->wd_softc)->dv_xname, + WDCDELAY * timeout); + } +#endif + return 0; +} + +void +wdctimeout(arg) + void *arg; +{ + struct wdc_softc *wdc = (struct wdc_softc *)arg; + int s; + + WDDEBUG_PRINT(("wdctimeout\n")); + + s = splbio(); + if ((wdc->sc_flags & WDCF_IRQ_WAIT) != 0) { + wdc->sc_flags &= ~WDCF_IRQ_WAIT; + wdcerror(wdc, "lost interrupt"); + wdcunwedge(wdc); + } else + wdcerror(wdc, "missing untimeout"); + splx(s); +} + +/* + * Wait for the drive to become ready and send a command. + * Return -1 if busy for too long or 0 otherwise. + * Assumes interrupts are blocked. + */ +int +wdccommand(d_link, command, drive, cylin, head, sector, count) + struct wd_link *d_link; + int command; + int drive, cylin, head, sector, count; +{ + struct wdc_softc *wdc = (void*)d_link->wdc_softc; + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + int stat; + + WDDEBUG_PRINT(("wdccommand drive %d\n", drive)); + +#if defined(DIAGNOSTIC) && defined(WDCDEBUG) + if ((wdc->sc_flags & WDCF_ACTIVE) == 0) + printf("wdccommand: controler not active (drive %d)\n", drive); +#endif + + /* Select drive, head, and addressing mode. */ + bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM | (drive << 4) | head); + + /* Wait for it to become ready to accept a command. */ + if (command == WDCC_IDP || d_link->type == BUS) + stat = wait_for_unbusy(wdc); + else + stat = wdcwait(wdc, WDCS_DRDY); + + if (stat < 0) { +#ifdef ATAPI_DEBUG + printf("wdcommand: xfer failed (wait_for_unbusy) status %d\n", + stat); +#endif + return -1; + } + + /* Load parameters. */ + if (d_link->type == DRIVE && d_link->sc_lp->d_type == DTYPE_ST506) + bus_space_write_1(iot, ioh, wd_precomp, + d_link->sc_lp->d_precompcyl / 4); + else + bus_space_write_1(iot, ioh, wd_features, 0); + bus_space_write_1(iot, ioh, wd_cyl_lo, cylin); + bus_space_write_1(iot, ioh, wd_cyl_hi, cylin >> 8); + bus_space_write_1(iot, ioh, wd_sector, sector); + bus_space_write_1(iot, ioh, wd_seccnt, count); + + /* Send command. */ + bus_space_write_1(iot, ioh, wd_command, command); + + return 0; +} + +/* + * Simplified version of wdccommand(). + */ +int +wdccommandshort(wdc, drive, command) + struct wdc_softc *wdc; + int drive; + int command; +{ + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + + WDDEBUG_PRINT(("wdccommandshort\n")); + +#if defined(DIAGNOSTIC) && defined(WDCDEBUG) + if ((wdc->sc_flags & WDCF_ACTIVE) == 0) + printf("wdccommandshort: controller not active (drive %d)\n", + drive); +#endif + + /* Select drive. */ + bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM|(drive << 4)); + + if (wdcwait(wdc, WDCS_DRDY) < 0) + return -1; + + bus_space_write_1(iot, ioh, wd_command, command); + + return 0; +} + +void +wdc_exec_xfer(d_link, xfer) + struct wd_link *d_link; + struct wdc_xfer *xfer; +{ + struct wdc_softc *wdc = (struct wdc_softc *)d_link->wdc_softc; + int s; + + WDDEBUG_PRINT(("wdc_exec_xfer\n")); + + s = splbio(); + + /* insert at the end of command list */ + TAILQ_INSERT_TAIL(&wdc->sc_xfer,xfer , c_xferchain) + WDDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags %d\n", + wdc->sc_flags)); + wdcstart(wdc); + splx(s); +} + +struct wdc_xfer * +wdc_get_xfer(c_link,flags) + struct wdc_link *c_link; + int flags; +{ + struct wdc_xfer *xfer; + int s; + + s = splbio(); + if ((xfer = xfer_free_list.lh_first) != NULL) { + LIST_REMOVE(xfer, free_list); + splx(s); +#ifdef DIAGNOSTIC + if ((xfer->c_flags & C_INUSE) != 0) + panic("wdc_get_xfer: xfer already in use\n"); +#endif + } else { + splx(s); +#ifdef ATAPI_DEBUG + printf("wdc:making xfer %d\n",wdc_nxfer); +#endif + xfer = malloc(sizeof(*xfer), M_DEVBUF, + ((flags & IDE_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK)); + if (xfer == NULL) + return 0; + +#ifdef DIAGNOSTIC + xfer->c_flags &= ~C_INUSE; +#endif +#ifdef ATAPI_DEBUG + wdc_nxfer++; +#endif + } +#ifdef DIAGNOSTIC + if ((xfer->c_flags & C_INUSE) != 0) + panic("wdc_get_xfer: xfer already in use\n"); +#endif + bzero(xfer, sizeof(struct wdc_xfer)); + xfer->c_flags = C_INUSE; + xfer->c_link = c_link; + return xfer; +} + +void +wdc_free_xfer(xfer) + struct wdc_xfer *xfer; +{ + int s; + + s = splbio(); + xfer->c_flags &= ~C_INUSE; + LIST_INSERT_HEAD(&xfer_free_list, xfer, free_list); + splx(s); +} + +void +wdcerror(wdc, msg) + struct wdc_softc *wdc; + char *msg; +{ + struct wdc_xfer *xfer = wdc->sc_xfer.tqh_first; + if (xfer == NULL) + printf("%s: %s\n", wdc->sc_dev.dv_xname, msg); + else + printf("%s(%s): %s\n", wdc->sc_dev.dv_xname, + ((struct device*)xfer->d_link->wd_softc)->dv_xname, msg); +} + +/* + * the bit bucket + */ +void +wdcbit_bucket(wdc, size) + struct wdc_softc *wdc; + int size; +{ + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + int i; + + for (i = 0 ; i < size / 2 ; i++) { + u_int16_t null; + + bus_space_read_multi_2(iot, ioh, wd_data, &null, 1); + } + + if (size % 2) + bus_space_read_1(iot, ioh, wd_data); +} diff --git a/sys/dev/isa/wdlink.h b/sys/dev/isa/wdlink.h index 66ceb721b31..154902ca047 100644 --- a/sys/dev/isa/wdlink.h +++ b/sys/dev/isa/wdlink.h @@ -1,4 +1,4 @@ -/* $OpenBSD: wdlink.h,v 1.6 1996/11/29 22:55:10 niklas Exp $ */ +/* $OpenBSD: wdlink.h,v 1.7 1997/07/04 17:02:04 downsj Exp $ */ /* * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. @@ -37,6 +37,8 @@ /* #undef WDDEBUG */ /* #undef DIAGNOSTIC */ +#include "wd.h" + struct wdc_link { int flags; int openings; @@ -134,13 +136,16 @@ struct wdc_xfer { void wdc_exec_xfer __P((struct wd_link *, struct wdc_xfer *)); struct wdc_xfer *wdc_get_xfer __P((struct wdc_link *, int)); int wdc_get_parms __P((struct wd_link *)); +int wdccommandshort __P((struct wdc_softc *, int, int)); +int wdcwait __P((struct wdc_softc *, int)); +int wdccommand __P((struct wd_link *, int, int, int, int, int, int)); + +#if NWD > 0 void wderror __P((struct wd_link* , struct buf *, char *)); int wdsetctlr __P((struct wd_link *)); void wdstart __P((void *)); void wddone __P((struct wd_link*, struct buf*)); -int wdccommand __P((struct wd_link *, int, int, int, int, int, int)); -int wdccommandshort __P((struct wdc_softc *, int, int)); -int wdcwait __P((struct wdc_softc *, int)); +#endif /* NWD */ /* * ST506 spec says that if READY or SEEKCMPLT go off, then the read or write |