From e9b9aedd515ee49ea7b8249d47df26d75ee12f30 Mon Sep 17 00:00:00 2001 From: mark Date: Fri, 17 Oct 1997 06:41:35 +0000 Subject: [PATCH] Synchronise with recent changes to the ISA wd driver. Remove the remaining ISA dependencies, busify and support 32 bit PIO. --- sys/arch/arm32/dev/wd.c | 1343 +++++------------------- sys/arch/arm32/dev/wdc.c | 1935 +++++++++++++++++++++++++++++++++++ sys/arch/arm32/dev/wdcvar.h | 82 -- sys/arch/arm32/dev/wdlink.h | 174 ++++ sys/arch/arm32/dev/wdreg.h | 36 +- 5 files changed, 2387 insertions(+), 1183 deletions(-) create mode 100644 sys/arch/arm32/dev/wdc.c delete mode 100644 sys/arch/arm32/dev/wdcvar.h create mode 100644 sys/arch/arm32/dev/wdlink.h diff --git a/sys/arch/arm32/dev/wd.c b/sys/arch/arm32/dev/wd.c index 0e0eb6273aa3..88485ef15921 100644 --- a/sys/arch/arm32/dev/wd.c +++ b/sys/arch/arm32/dev/wd.c @@ -1,4 +1,4 @@ -/* $NetBSD: wd.c,v 1.19 1997/10/14 19:13:54 mark Exp $ */ +/* $NetBSD: wd.c,v 1.20 1997/10/17 06:41:35 mark Exp $ */ /* * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. @@ -31,9 +31,11 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * from: wd.c,v 1.156 1997/01/17 20:45:29 perry Exp + * $NetBSD: wd.c,v 1.166 1997/10/13 00:47:33 explorer Exp */ +#include "rnd.h" + #include #include #include @@ -49,25 +51,21 @@ #include #include #include +#if NRND > 0 +#include +#endif #include #include +#include #include -#include -#include +#include +#include #include "locators.h" #define WAITTIME (4 * hz) /* time to wait for a completion */ -#define RECOVERYTIME (hz / 2) /* time to recover from an error */ - -#define WDCDELAY 100 -#define WDCNDELAY 100000 /* delay = 100us; so 10s for a controller state change */ -#if 0 -/* If you enable this, it will report any delays more than 100us * N long. */ -#define WDCNDELAY_DEBUG 10 -#endif #define WDIORETRIES 5 /* number of retries before giving up */ @@ -77,56 +75,25 @@ #define WDLABELDEV(dev) (MAKEWDDEV(major(dev), WDUNIT(dev), RAW_PART)) +#ifdef WDDEBUG +#define WDDEBUG_PRINT(args) printf args +#else +#define WDDEBUG_PRINT(args) +#endif + struct wd_softc { struct device sc_dev; struct disk sc_dk; - - /* Information about the current transfer: */ - daddr_t sc_blkno; /* starting block number */ - int sc_bcount; /* byte count left */ - int sc_skip; /* bytes already transferred */ - int sc_nblks; /* number of blocks currently transferring */ - int sc_nbytes; /* number of bytes currently transferring */ - - /* Long-term state: */ - int sc_drive; /* physical unit number */ - int sc_state; /* control state */ -#define RECAL 0 /* recalibrate */ -#define RECAL_WAIT 1 /* done recalibrating */ -#define GEOMETRY 2 /* upload geometry */ -#define GEOMETRY_WAIT 3 /* done uploading geometry */ -#define MULTIMODE 4 /* set multiple mode */ -#define MULTIMODE_WAIT 5 /* done setting multiple mode */ -#define READY 6 /* ready for use */ - int sc_mode; /* transfer mode */ -#define WDM_PIOSINGLE 0 /* single-sector PIO */ -#define WDM_PIOMULTI 1 /* multi-sector PIO */ -#define WDM_DMA 2 /* DMA */ - int sc_multiple; /* multiple for WDM_PIOMULTI */ - int sc_flags; /* drive characteistics found */ -#define WDF_LOCKED 0x01 -#define WDF_WANTED 0x02 -#define WDF_WLABEL 0x04 /* label is writable */ -#define WDF_LABELLING 0x08 /* writing label */ -/* XXX Nothing resets this yet, but disk change sensing will when ATAPI is - implemented. */ -#define WDF_LOADED 0x10 /* parameters loaded */ -#define WDF_32BIT 0x20 /* can do 32-bit transfer */ - - struct wdparams sc_params; /* ESDI/ATA drive parameters */ - daddr_t sc_badsect[127]; /* 126 plus trailing -1 marker */ - - TAILQ_ENTRY(wd_softc) sc_drivechain; + struct wd_link *d_link; struct buf sc_q; +#if NRND > 0 + rndsource_element_t rnd_source; +#endif }; -struct cfdriver wdc_cd = { - NULL, "wdc", DV_DULL -}; - -int wdprobe __P((struct device *, struct cfdata *, void *)); -void wdattach __P((struct device *, struct device *, void *)); -int wdprint __P((void *, const char *)); +int wdprobe __P((struct device *, struct cfdata *, void *)); +void wdattach __P((struct device *, struct device *, void *)); +int wdprint __P((void *, char *)); struct cfattach wd_ca = { sizeof(struct wd_softc), wdprobe, wdattach @@ -140,7 +107,7 @@ void wdgetdefaultlabel __P((struct wd_softc *, struct disklabel *)); void wdgetdisklabel __P((struct wd_softc *)); int wd_get_parms __P((struct wd_softc *)); void wdstrategy __P((struct buf *)); -void wdstart __P((struct wd_softc *)); +void wdstart __P((void *)); struct dkdriver wddkdriver = { wdstrategy }; @@ -149,141 +116,29 @@ cdev_decl(wd); bdev_decl(wd); void wdfinish __P((struct wd_softc *, struct buf *)); -int dcintr __P((void *)); -void wdcstart __P((struct wdc_softc *)); -int wdcommand __P((struct wd_softc *, int, int, int, int, int)); -int wdcommandshort __P((struct wdc_softc *, int, int)); -int wdcontrol __P((struct wd_softc *)); -int wdsetctlr __P((struct wd_softc *)); +int wdsetctlr __P((struct wd_link *)); static void bad144intern __P((struct wd_softc *)); -int wdcreset __P((struct wdc_softc *)); -void wdcrestart __P((void *arg)); -void wdcunwedge __P((struct wdc_softc *)); -void wdctimeout __P((void *arg)); -void wderror __P((void *, struct buf *, char *)); -int wdcwait __P((struct wdc_softc *, int)); -int wdlock __P((struct wd_softc *)); -void wdunlock __P((struct wd_softc *)); - -/* ST506 spec says that if READY or SEEKCMPLT go off, then the read or write - command is aborted. */ -#define wait_for_drq(d) wdcwait(d, WDCS_DRDY | WDCS_DSC | WDCS_DRQ) -#define wait_for_ready(d) wdcwait(d, WDCS_DRDY | WDCS_DSC) -#define wait_for_unbusy(d) wdcwait(d, 0) +int wdlock __P((struct wd_link *)); +void wdunlock __P((struct wd_link *)); int -wdcprobe_internal(iot, ioh, aux_ioh, data_ioh, data32_ioh, name) - bus_space_tag_t iot; - bus_space_handle_t ioh; - bus_space_handle_t aux_ioh; - bus_space_handle_t data_ioh; - bus_space_handle_t data32_ioh; - char *name; -{ - struct wdc_softc wdc; - - /* fake a wdc structure for the benefit of the routines called */ - - wdc.sc_iot = iot; - wdc.sc_ioh = ioh; - wdc.sc_auxioh = aux_ioh; - wdc.sc_dataioh = data_ioh; - wdc.sc_data32ioh = data32_ioh; - wdc.sc_inten = NULL; - wdc.sc_flags = WDCF_QUIET; /* Supress warning during probe */ - if (data32_ioh != -1) - wdc.sc_flags |= WDCF_32BIT; /* Use 32 bit xfers if possible */ - strcpy(wdc.sc_dev.dv_xname, name); - - /* Check if we have registers that work. */ - bus_space_write_1(iot, ioh, wd_error, 0x5a); /* Error register not writable, */ - bus_space_write_1(iot, ioh, wd_cyl_lo, 0xa5); /* but all of cyllo are. */ - if (bus_space_read_1(iot, ioh, wd_error) == 0x5a - || bus_space_read_1(iot, ioh, wd_cyl_lo) != 0xa5) - return(0); - - if (wdcreset(&wdc) != 0) { - delay(500000); - if (wdcreset(&wdc) != 0) - return(0); - } - - /* Select drive 0. */ - bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM | 0); - - /* Wait for controller to become ready. */ - if (wait_for_unbusy(&wdc) < 0) - return(0); - - /* Start drive diagnostics. */ - bus_space_write_1(iot, ioh, wd_command, WDCC_DIAGNOSE); - - /* Wait for command to complete. */ - if (wait_for_unbusy(&wdc) < 0) - return(0); - - return(1); -} - -struct wdc_attach_args { - int wa_drive; -}; - -int -wdprint(aux, wdc) - void *aux; - const char *wdc; -{ - struct wdc_attach_args *wa = aux; - - if (!wdc) - printf(" drive %d", wa->wa_drive); - return QUIET; -} - -void -wdcattach_internal(wdc, iot, ioh, aux_ioh, data_ioh, data32_ioh, drq) - struct wdc_softc *wdc; - bus_space_tag_t iot; - bus_space_handle_t ioh; - bus_space_handle_t aux_ioh; - bus_space_handle_t data_ioh; - bus_space_handle_t data32_ioh; - int drq; -{ - struct wdc_attach_args wa; - - TAILQ_INIT(&wdc->sc_drives); - wdc->sc_drq = drq; - wdc->sc_iot = iot; - wdc->sc_ioh = ioh; - wdc->sc_auxioh = aux_ioh; - wdc->sc_dataioh = data_ioh; - wdc->sc_data32ioh = data32_ioh; - wdc->sc_inten = NULL; - - printf("\n"); - - for (wa.wa_drive = 0; wa.wa_drive < 2; wa.wa_drive++) - (void)config_found((struct device *)wdc, (void *)&wa, wdprint); -} - -int -wdprobe(parent, cf, aux) +wdprobe(parent, match, aux) struct device *parent; - struct cfdata *cf; + struct cfdata *match; void *aux; { - struct wdc_softc *wdc = (void *)parent; - struct wdc_attach_args *wa = aux; - int drive = wa->wa_drive; - - if (cf->cf_loc[WDCCF_DRIVE] != WDCCF_DRIVE_DEFAULT && - cf->cf_loc[WDCCF_DRIVE] != drive) - return 0; + struct cfdata *cf = match; + struct wd_link *d_link = aux; + int drive; - if (wdcommandshort(wdc, drive, WDCC_RECAL) != 0 || - wait_for_ready(wdc) != 0) + if (d_link == NULL) + return 0; + if (d_link->type != ATA) + return 0; + + drive = d_link->drive; + if (cf->cf_loc[ATACF_DRIVE] != ATACF_DRIVE_DEFAULT && + cf->cf_loc[ATACF_DRIVE] != drive) return 0; return 1; @@ -295,24 +150,26 @@ wdattach(parent, self, aux) void *aux; { struct wd_softc *wd = (void *)self; - struct wdc_softc *wdc = (void *)parent; - struct wdc_attach_args *wa = aux; + struct wd_link *d_link= aux; int i, blank; char buf[41], c, *p, *q; - wd->sc_drive = wa->wa_drive; + wd->d_link = d_link; + d_link->openings = 1; + d_link->wd_softc = (caddr_t)wd; /* * Initialize and attach the disk structure. */ - wd->sc_flags = ((wdc->sc_flags & WDCF_32BIT) ? WDF_32BIT : 0); wd->sc_dk.dk_driver = &wddkdriver; wd->sc_dk.dk_name = wd->sc_dev.dv_xname; disk_attach(&wd->sc_dk); - wd_get_parms(wd); - for (blank = 0, p = wd->sc_params.wdp_model, q = buf, i = 0; - i < sizeof(wd->sc_params.wdp_model); i++) { + d_link->sc_lp = wd->sc_dk.dk_label; + + wdc_get_parms((struct wdc_softc *)d_link->wdc_softc, d_link); + for (blank = 0, p = d_link->sc_params.wdp_model, q = buf, i = 0; + i < sizeof(d_link->sc_params.wdp_model); i++) { c = *p++; if (c == '\0') break; @@ -328,36 +185,41 @@ wdattach(parent, self, aux) *q++ = '\0'; printf(": <%s>\n%s: %dMB, %d cyl, %d head, %d sec, %d bytes/sec\n", - buf, wd->sc_dev.dv_xname, - wd->sc_params.wdp_cylinders * - (wd->sc_params.wdp_heads * wd->sc_params.wdp_sectors) / - (1048576 / DEV_BSIZE), - wd->sc_params.wdp_cylinders, - wd->sc_params.wdp_heads, - wd->sc_params.wdp_sectors, + buf, self->dv_xname, + d_link->sc_params.wdp_cylinders * + (d_link->sc_params.wdp_heads * d_link->sc_params.wdp_sectors) / + (1048576 / DEV_BSIZE), + d_link->sc_params.wdp_cylinders, + d_link->sc_params.wdp_heads, + d_link->sc_params.wdp_sectors, DEV_BSIZE); - if ((wd->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 && - wdc->sc_drq != -1) { - wd->sc_mode = WDM_DMA; - } else if (wd->sc_params.wdp_maxmulti > 1) { - wd->sc_mode = WDM_PIOMULTI; - wd->sc_multiple = min(wd->sc_params.wdp_maxmulti, 16); + if ((d_link->sc_params.wdp_capabilities & WD_CAP_DMA) != 0 && + d_link->sc_mode == WDM_DMA) { + d_link->sc_mode = WDM_DMA; + } else if (d_link->sc_params.wdp_maxmulti > 1) { + d_link->sc_mode = WDM_PIOMULTI; + d_link->sc_multiple = min(d_link->sc_params.wdp_maxmulti, 16); } else { - wd->sc_mode = WDM_PIOSINGLE; - wd->sc_multiple = 1; + d_link->sc_mode = WDM_PIOSINGLE; + d_link->sc_multiple = 1; } printf("%s: using", wd->sc_dev.dv_xname); - if (wd->sc_mode == WDM_DMA) + if (d_link->sc_mode == WDM_DMA) printf(" dma transfers,"); else printf(" %d-sector %d-bit pio transfers,", - wd->sc_multiple, (wd->sc_flags & WDF_32BIT) == 0 ? 16 : 32); - if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) + d_link->sc_multiple, + (d_link->sc_flags & WDF_32BIT) == 0 ? 16 : 32); + if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) printf(" lba addressing\n"); else printf(" chs addressing\n"); + +#if NRND > 0 + rnd_attach_source(&wd->rnd_source, wd->sc_dev.dv_xname, RND_TYPE_DISK); +#endif } /* @@ -369,6 +231,7 @@ wdstrategy(bp) struct buf *bp; { struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(bp->b_dev)]; + struct wd_link *d_link= wd->d_link; int s; /* Valid request? */ @@ -380,7 +243,7 @@ wdstrategy(bp) } /* If device invalidated (e.g. media change, door open), error. */ - if ((wd->sc_flags & WDF_LOADED) == 0) { + if ((d_link->sc_flags & WDF_LOADED) == 0) { bp->b_error = EIO; goto bad; } @@ -395,24 +258,14 @@ wdstrategy(bp) */ if (WDPART(bp->b_dev) != RAW_PART && bounds_check_with_label(bp, wd->sc_dk.dk_label, - (wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) + (d_link->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) goto done; /* Queue transfer on drive, activate drive and controller if idle. */ s = splbio(); disksort(&wd->sc_q, bp); - if (!wd->sc_q.b_active) - wdstart(wd); -#if 0 - else { - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - if ((wdc->sc_flags & (WDCF_ACTIVE|WDCF_ERROR)) == 0) { - printf("wdstrategy: controller inactive\n"); - wdcstart(wdc); - } - } -#endif - (void)splx(s); + wdstart(wd); + splx(s); return; bad: @@ -427,60 +280,50 @@ done: * Queue a drive for I/O. */ void -wdstart(wd) - struct wd_softc *wd; +wdstart(arg) + void *arg; { - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - int active = wdc->sc_drives.tqh_first != 0; + struct wd_softc *wd = arg; + struct buf *dp, *bp=0; + struct wd_link *d_link = wd->d_link; + struct wdc_xfer *xfer; + u_long p_offset; - /* Link onto controller queue. */ - wd->sc_q.b_active = 1; - TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain); + while (d_link->openings > 0) { - disk_busy(&wd->sc_dk); - - /* If controller not already active, start it. */ - if (!active) - wdcstart(wdc); -} + /* Is there a buf for us ? */ + dp = &wd->sc_q; + if ((bp = dp->b_actf) == NULL) /* yes, an assign */ + return; + dp->b_actf = bp->b_actf; + + /* + * Make the command. First lock the device + */ + d_link->openings--; + if (WDPART(bp->b_dev) != RAW_PART) + p_offset = + wd->sc_dk.dk_label->d_partitions[WDPART(bp->b_dev)].p_offset; + else + p_offset = 0; -/* - * Finish an I/O operation. Clean up the drive and controller state, set the - * residual count, and inform the upper layers that the operation is complete. - */ -void -wdfinish(wd, bp) - struct wd_softc *wd; - struct buf *bp; -{ - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; + xfer = wdc_get_xfer(0); + if (xfer == NULL) + panic("wdc_xfer"); - wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR); - wdc->sc_errors = 0; - /* - * Move this drive to the end of the queue to give others a `fair' - * chance. - */ - if (wd->sc_drivechain.tqe_next) { - TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain); - if (bp->b_actf) { - TAILQ_INSERT_TAIL(&wdc->sc_drives, wd, sc_drivechain); - } else - wd->sc_q.b_active = 0; - } - bp->b_resid = wd->sc_bcount; - wd->sc_skip = 0; - wd->sc_q.b_actf = bp->b_actf; + xfer->d_link = d_link; + xfer->c_bp = bp; + xfer->c_p_offset = p_offset; + xfer->databuf = bp->b_data; + xfer->c_bcount = bp->b_bcount; + xfer->c_flags |= bp->b_flags & (B_READ|B_WRITE); + xfer->c_blkno = bp->b_blkno; - disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid)); - - if (!wd->sc_q.b_actf) { - TAILQ_REMOVE(&wdc->sc_drives, wd, sc_drivechain); - wd->sc_q.b_active = 0; - } else + /* Instrumentation. */ disk_busy(&wd->sc_dk); - - biodone(bp); + wdc_exec_xfer((struct wdc_softc *)wd->d_link->wdc_softc, + wd->d_link, xfer); + } } int @@ -490,6 +333,7 @@ wdread(dev, uio, flags) int flags; { + WDDEBUG_PRINT(("wdread\n")); return (physio(wdstrategy, NULL, dev, B_READ, minphys, uio)); } @@ -500,392 +344,10 @@ wdwrite(dev, uio, flags) int flags; { + WDDEBUG_PRINT(("wdwrite\n")); return (physio(wdstrategy, NULL, dev, B_WRITE, minphys, uio)); } -/* - * Start I/O on a controller. This does the calculation, and starts a read or - * write operation. Called to from wdstart() to start a transfer, from - * wdcintr() to continue a multi-sector transfer or start the next transfer, or - * wdcrestart() after recovering from an error. - */ -void -wdcstart(wdc) - struct wdc_softc *wdc; -{ - struct wd_softc *wd; - struct buf *bp; - struct disklabel *lp; - int nblks; - -#ifdef DIAGNOSTIC - if ((wdc->sc_flags & WDCF_ACTIVE) != 0) - panic("wdcstart: controller still active"); -#endif - - /* - * XXX - * This is a kluge. See comments in wd_get_parms(). - */ - if ((wdc->sc_flags & WDCF_WANTED) != 0) { - wdc->sc_flags &= ~WDCF_WANTED; - wakeup(wdc); - return; - } - -loop: - /* Is there a drive for the controller to do a transfer with? */ - wd = wdc->sc_drives.tqh_first; - if (wd == NULL) - return; - - /* Is there a transfer to this drive? If not, deactivate drive. */ - bp = wd->sc_q.b_actf; - - if (wdc->sc_errors >= WDIORETRIES) { - wderror(wd, bp, "wdcstart hard error"); - bp->b_error = EIO; - bp->b_flags |= B_ERROR; - wdfinish(wd, bp); - goto loop; - } - - /* Do control operations specially. */ - if (wd->sc_state < READY) { - /* - * Actually, we want to be careful not to mess with the control - * state if the device is currently busy, but we can assume - * that we never get to this point if that's the case. - */ - if (wdcontrol(wd) == 0) { - /* The drive is busy. Wait. */ - return; - } - } - - /* - * WDCF_ERROR is set by wdcunwedge() and wdcintr() when an error is - * encountered. If we are in multi-sector mode, then we switch to - * single-sector mode and retry the operation from the start. - */ - if (wdc->sc_flags & WDCF_ERROR) { - wdc->sc_flags &= ~WDCF_ERROR; - if ((wdc->sc_flags & WDCF_SINGLE) == 0) { - wdc->sc_flags |= WDCF_SINGLE; - wd->sc_skip = 0; - } - } - - lp = wd->sc_dk.dk_label; - - /* When starting a transfer... */ - if (wd->sc_skip == 0) { - int part = WDPART(bp->b_dev); - daddr_t blkno; - -#ifdef WDDEBUG - printf("\n%s: wdcstart %s %ld@%d; map ", wd->sc_dev.dv_xname, - (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, - bp->b_blkno); -#endif - wd->sc_bcount = bp->b_bcount; - blkno = bp->b_blkno; - if (part != RAW_PART) - blkno += lp->d_partitions[part].p_offset; - wd->sc_blkno = blkno / (lp->d_secsize / DEV_BSIZE); - } else { -#ifdef WDDEBUG - printf(" %d)%x", wd->sc_skip, bus_space_read_1(wdc->sc_iot, - wdc->sc_auxioh, wd_altsts)); -#endif - } - - /* When starting a multi-sector transfer, or doing single-sector - transfers... */ - if (wd->sc_skip == 0 || (wdc->sc_flags & WDCF_SINGLE) != 0 || - wd->sc_mode == WDM_DMA) { - daddr_t blkno = wd->sc_blkno; - long cylin, head, sector; - int command; - - if ((wdc->sc_flags & WDCF_SINGLE) != 0) - nblks = 1; - else if (wd->sc_mode != WDM_DMA) - nblks = wd->sc_bcount / lp->d_secsize; - else - nblks = min(wd->sc_bcount / lp->d_secsize, 8); - - /* Check for bad sectors and adjust transfer, if necessary. */ - if ((lp->d_flags & D_BADSECT) != 0 -#ifdef B_FORMAT - && (bp->b_flags & B_FORMAT) == 0 -#endif - ) { - long blkdiff; - int i; - - for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) { - blkdiff -= blkno; - if (blkdiff < 0) - continue; - if (blkdiff == 0) { - /* Replace current block of transfer. */ - blkno = - lp->d_secperunit - lp->d_nsectors - i - 1; - } - if (blkdiff < nblks) { - /* Bad block inside transfer. */ - wdc->sc_flags |= WDCF_SINGLE; - nblks = 1; - } - break; - } - /* Tranfer is okay now. */ - } - - if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { - sector = (blkno >> 0) & 0xff; - cylin = (blkno >> 8) & 0xffff; - head = (blkno >> 24) & 0xf; - head |= WDSD_LBA; - } else { - sector = blkno % lp->d_nsectors; - sector++; /* Sectors begin with 1, not 0. */ - blkno /= lp->d_nsectors; - head = blkno % lp->d_ntracks; - blkno /= lp->d_ntracks; - cylin = blkno; - head |= WDSD_CHS; - } - - if (wd->sc_mode == WDM_PIOSINGLE || - (wdc->sc_flags & WDCF_SINGLE) != 0) - wd->sc_nblks = 1; - else if (wd->sc_mode == WDM_PIOMULTI) - wd->sc_nblks = min(nblks, wd->sc_multiple); - else - wd->sc_nblks = nblks; - wd->sc_nbytes = wd->sc_nblks * lp->d_secsize; - -#ifdef B_FORMAT - if (bp->b_flags & B_FORMAT) { - sector = lp->d_gap3; - nblks = lp->d_nsectors; - command = WDCC_FORMAT; - } else -#endif - switch (wd->sc_mode) { - case WDM_DMA: -#if 0 - command = (bp->b_flags & B_READ) ? - WDCC_READDMA : WDCC_WRITEDMA; - /* Start the DMA channel and bounce the buffer if - necessary. */ - isa_dmastart( - bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE, - bp->b_data + wd->sc_skip, - wd->sc_nbytes, wdc->sc_drq); -#endif - panic("wd cannot do DMA yet\n"); - break; - case WDM_PIOMULTI: - command = (bp->b_flags & B_READ) ? - WDCC_READMULTI : WDCC_WRITEMULTI; - break; - case WDM_PIOSINGLE: - command = (bp->b_flags & B_READ) ? - WDCC_READ : WDCC_WRITE; - break; - default: -#ifdef DIAGNOSTIC - panic("bad wd mode"); -#endif - return; - } - - /* Initiate command! */ - if (wdcommand(wd, command, cylin, head, sector, nblks) != 0) { - wderror(wd, NULL, - "wdcstart: timeout waiting for unbusy"); - wdcunwedge(wdc); - return; - } - -#ifdef WDDEBUG - printf("sector %ld cylin %ld head %ld addr %p sts %x\n", - sector, cylin, head, bp->b_data, - bus_space_read_1(wdc->sc_iot, wdc->sc_auxioh, wd_altsts)); -#endif - } else if (wd->sc_nblks > 1) { - /* The number of blocks in the last stretch may be smaller. */ - nblks = wd->sc_bcount / lp->d_secsize; - if (wd->sc_nblks > nblks) { - wd->sc_nblks = nblks; - wd->sc_nbytes = wd->sc_bcount; - } - } - - /* If this was a write and not using DMA, push the data. */ - if (wd->sc_mode != WDM_DMA && - (bp->b_flags & (B_READ|B_WRITE)) == B_WRITE) { - if (wait_for_drq(wdc) < 0) { - wderror(wd, NULL, "wdcstart: timeout waiting for drq"); - wdcunwedge(wdc); - return; - } - - /* Push out data. */ - if ((wd->sc_flags & WDF_32BIT) == 0) - bus_space_write_multi_2(wdc->sc_iot, wdc->sc_dataioh, - wd_data, (u_int16_t *)(bp->b_data + wd->sc_skip), - wd->sc_nbytes >> 1); - else - bus_space_write_multi_4(wdc->sc_iot, wdc->sc_data32ioh, - wd_data, (u_int32_t *)(bp->b_data + wd->sc_skip), - wd->sc_nbytes >> 2); - } - - wdc->sc_flags |= WDCF_ACTIVE; - if (wdc->sc_inten) wdc->sc_inten(wdc, 1); - timeout(wdctimeout, wdc, WAITTIME); -} - -/* - * 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 wd_softc *wd; - struct buf *bp; - - if ((wdc->sc_flags & WDCF_ACTIVE) == 0) { - /* Clear the pending interrupt and abort. */ - (void) bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_status); - return 0; - } - - wdc->sc_flags &= ~WDCF_ACTIVE; - if (wdc->sc_inten) wdc->sc_inten(wdc, 0); - untimeout(wdctimeout, wdc); - - wd = wdc->sc_drives.tqh_first; - bp = wd->sc_q.b_actf; - -#ifdef WDDEBUG - printf("I%d ", wdc->sc_dev.dv_unit); -#endif - - if (wait_for_unbusy(wdc) < 0) { - wderror(wd, NULL, "wdcintr: timeout waiting for unbusy"); - wdc->sc_status |= WDCS_ERR; /* XXX */ - } - - /* Is it not a transfer, but a control operation? */ - if (wd->sc_state < READY) { - if (wdcontrol(wd) == 0) { - /* The drive is busy. Wait. */ - return 1; - } - wdcstart(wdc); - return 1; - } - - /* Turn off the DMA channel and unbounce the buffer. */ - if (wd->sc_mode == WDM_DMA) -#if 0 - isa_dmadone(bp->b_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE, - bp->b_data + wd->sc_skip, wd->sc_nbytes, wdc->sc_drq); -#else - panic("wd cannot do DMA yet\n"); -#endif - /* Have we an error? */ - if (wdc->sc_status & WDCS_ERR) { -#ifdef WDDEBUG - wderror(wd, NULL, "wdcintr"); -#endif - if ((wdc->sc_flags & WDCF_SINGLE) == 0) { - wdc->sc_flags |= WDCF_ERROR; - goto restart; - } - -#ifdef B_FORMAT - if (bp->b_flags & B_FORMAT) - goto bad; -#endif - - if (++wdc->sc_errors < WDIORETRIES) { - if (wdc->sc_errors == (WDIORETRIES + 1) / 2) { -#if 0 - wderror(wd, NULL, "wedgie"); -#endif - wdcunwedge(wdc); - return 1; - } - goto restart; - } - wderror(wd, bp, "wdcintr hard error"); -#ifdef B_FORMAT - bad: -#endif - bp->b_error = EIO; - bp->b_flags |= B_ERROR; - goto done; - } - - /* If this was a read and not using DMA, fetch the data. */ - if (wd->sc_mode != WDM_DMA && - (bp->b_flags & (B_READ|B_WRITE)) == B_READ) { - if ((wdc->sc_status & (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) - != (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) { - wderror(wd, NULL, "wdcintr: read intr before drq"); - wdcunwedge(wdc); - return 1; - } - - /* Pull in data. */ - if ((wd->sc_flags & WDF_32BIT) == 0) - bus_space_read_multi_2(wdc->sc_iot, wdc->sc_dataioh, - wd_data, (u_int16_t *)(bp->b_data + wd->sc_skip), - wd->sc_nbytes >> 1); - else - bus_space_read_multi_4(wdc->sc_iot, wdc->sc_data32ioh, - wd_data, (u_int32_t *)(bp->b_data + wd->sc_skip), - wd->sc_nbytes >> 2); - } - - /* If we encountered any abnormalities, flag it as a soft error. */ - if (wdc->sc_errors > 0 || - (wdc->sc_status & WDCS_CORR) != 0) { - wderror(wd, bp, "soft error (corrected)"); - wdc->sc_errors = 0; - } - - /* Adjust pointers for the next block, if any. */ - wd->sc_blkno += wd->sc_nblks; - wd->sc_skip += wd->sc_nbytes; - wd->sc_bcount -= wd->sc_nbytes; - - /* See if this transfer is complete. */ - if (wd->sc_bcount > 0) - goto restart; - -done: - /* Done with this transfer, with or without error. */ - wdfinish(wd, bp); - -restart: - /* Start the next operation, if any. */ - wdcstart(wdc); - - return 1; -} - /* * Wait interruptibly for an exclusive lock. * @@ -893,17 +355,26 @@ restart: * Several drivers do this; it should be abstracted and made MP-safe. */ int -wdlock(wd) - struct wd_softc *wd; +wdlock(d_link) + struct wd_link *d_link; { int error; + int s; - while ((wd->sc_flags & WDF_LOCKED) != 0) { - wd->sc_flags |= WDF_WANTED; - if ((error = tsleep(wd, PRIBIO | PCATCH, "wdlck", 0)) != 0) + WDDEBUG_PRINT(("wdlock\n")); + + s = splbio(); + + while ((d_link->sc_flags & WDF_LOCKED) != 0) { + d_link->sc_flags |= WDF_WANTED; + if ((error = tsleep(d_link, PRIBIO | PCATCH, + "wdlck", 0)) != 0) { + splx(s); return error; + } } - wd->sc_flags |= WDF_LOCKED; + d_link->sc_flags |= WDF_LOCKED; + splx(s); return 0; } @@ -911,14 +382,16 @@ wdlock(wd) * Unlock and wake up any waiters. */ void -wdunlock(wd) - struct wd_softc *wd; +wdunlock(d_link) + struct wd_link *d_link; { - wd->sc_flags &= ~WDF_LOCKED; - if ((wd->sc_flags & WDF_WANTED) != 0) { - wd->sc_flags &= ~WDF_WANTED; - wakeup(wd); + WDDEBUG_PRINT(("wdunlock")); + + d_link->sc_flags &= ~WDF_LOCKED; + if ((d_link->sc_flags & WDF_WANTED) != 0) { + d_link->sc_flags &= ~WDF_WANTED; + wakeup(d_link); } } @@ -929,9 +402,12 @@ wdopen(dev, flag, fmt, p) struct proc *p; { struct wd_softc *wd; + struct wd_link *d_link; int unit, part; int error; - + + WDDEBUG_PRINT(("wdopen\n")); + unit = WDUNIT(dev); if (unit >= wd_cd.cd_ndevs) return ENXIO; @@ -939,7 +415,8 @@ wdopen(dev, flag, fmt, p) if (wd == NULL) return ENXIO; - if ((error = wdlock(wd)) != 0) + d_link = wd->d_link; + if ((error = wdlock(d_link)) != 0) return error; if (wd->sc_dk.dk_openmask != 0) { @@ -947,16 +424,17 @@ wdopen(dev, flag, fmt, p) * If any partition is open, but the disk has been invalidated, * disallow further opens. */ - if ((wd->sc_flags & WDF_LOADED) == 0) { + if ((d_link->sc_flags & WDF_LOADED) == 0) { error = EIO; goto bad3; } } else { - if ((wd->sc_flags & WDF_LOADED) == 0) { - wd->sc_flags |= WDF_LOADED; + if ((d_link->sc_flags & WDF_LOADED) == 0) { + d_link->sc_flags |= WDF_LOADED; /* Load the physical device parameters. */ - if (wd_get_parms(wd) != 0) { + if (wdc_get_parms((struct wdc_softc *)d_link->wdc_softc, + d_link) != 0) { error = ENXIO; goto bad2; } @@ -987,18 +465,18 @@ wdopen(dev, flag, fmt, p) } wd->sc_dk.dk_openmask = wd->sc_dk.dk_copenmask | wd->sc_dk.dk_bopenmask; - wdunlock(wd); + wdunlock(d_link); return 0; bad2: - wd->sc_flags &= ~WDF_LOADED; + d_link->sc_flags &= ~WDF_LOADED; bad: if (wd->sc_dk.dk_openmask == 0) { } bad3: - wdunlock(wd); + wdunlock(d_link); return error; } @@ -1012,7 +490,7 @@ wdclose(dev, flag, fmt, p) int part = WDPART(dev); int error; - if ((error = wdlock(wd)) != 0) + if ((error = wdlock(wd->d_link)) != 0) return error; switch (fmt) { @@ -1029,7 +507,7 @@ wdclose(dev, flag, fmt, p) /* XXXX Must wait for I/O to complete! */ } - wdunlock(wd); + wdunlock(wd->d_link); return 0; } @@ -1038,20 +516,21 @@ wdgetdefaultlabel(wd, lp) struct wd_softc *wd; struct disklabel *lp; { + struct wd_link *d_link = wd->d_link; bzero(lp, sizeof(struct disklabel)); lp->d_secsize = DEV_BSIZE; - lp->d_ntracks = wd->sc_params.wdp_heads; - lp->d_nsectors = wd->sc_params.wdp_sectors; - lp->d_ncylinders = wd->sc_params.wdp_cylinders; + lp->d_ntracks = d_link->sc_params.wdp_heads; + lp->d_nsectors = d_link->sc_params.wdp_sectors; + lp->d_ncylinders = d_link->sc_params.wdp_cylinders; lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; #if 0 strncpy(lp->d_typename, "ST506 disk", 16); lp->d_type = DTYPE_ST506; #endif - strncpy(lp->d_packname, wd->sc_params.wdp_model, 16); + strncpy(lp->d_packname, d_link->sc_params.wdp_model, 16); lp->d_secperunit = lp->d_secpercyl * lp->d_ncylinders; lp->d_rpm = 3600; lp->d_interleave = 1; @@ -1076,16 +555,19 @@ wdgetdisklabel(wd) struct wd_softc *wd; { struct disklabel *lp = wd->sc_dk.dk_label; + struct wd_link *d_link = wd->d_link; char *errstring; + WDDEBUG_PRINT(("wdgetdisklabel\n")); + bzero(wd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel)); wdgetdefaultlabel(wd, lp); - wd->sc_badsect[0] = -1; + d_link->sc_badsect[0] = -1; - if (wd->sc_state > RECAL) - wd->sc_state = RECAL; + if (d_link->sc_state > RECAL) + d_link->sc_state = RECAL; errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART), wdstrategy, lp, wd->sc_dk.dk_cpulabel); if (errstring) { @@ -1095,8 +577,8 @@ wdgetdisklabel(wd) * assume the DOS geometry is now in the label and try * again. XXX This is a kluge. */ - if (wd->sc_state > GEOMETRY) - wd->sc_state = GEOMETRY; + if (d_link->sc_state > GEOMETRY) + d_link->sc_state = GEOMETRY; errstring = readdisklabel(MAKEWDDEV(0, wd->sc_dev.dv_unit, RAW_PART), wdstrategy, lp, wd->sc_dk.dk_cpulabel); } @@ -1105,275 +587,56 @@ wdgetdisklabel(wd) return; } - if (wd->sc_state > GEOMETRY) - wd->sc_state = GEOMETRY; + if (d_link->sc_state > GEOMETRY) + d_link->sc_state = GEOMETRY; if ((lp->d_flags & D_BADSECT) != 0) bad144intern(wd); } -/* - * Implement operations needed before read/write. - * Returns 0 if operation still in progress, 1 if completed. - */ -int -wdcontrol(wd) - struct wd_softc *wd; -{ - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - - switch (wd->sc_state) { - case RECAL: /* Set SDH, step rate, do recal. */ - if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0) { - wderror(wd, NULL, "wdcontrol: recal failed (1)"); - goto bad; - } - wd->sc_state = RECAL_WAIT; - break; - - case RECAL_WAIT: - if (wdc->sc_status & WDCS_ERR) { - wderror(wd, NULL, "wdcontrol: recal failed (2)"); - goto bad; - } - /* fall through */ - case GEOMETRY: - if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) - goto multimode; - if (wdsetctlr(wd) != 0) { - /* Already printed a message. */ - goto bad; - } - wd->sc_state = GEOMETRY_WAIT; - break; - - case GEOMETRY_WAIT: - if (wdc->sc_status & WDCS_ERR) { - wderror(wd, NULL, "wdcontrol: geometry failed"); - goto bad; - } - /* fall through */ - case MULTIMODE: - multimode: - if (wd->sc_mode != WDM_PIOMULTI) - goto ready; - bus_space_write_1(wdc->sc_iot, wdc->sc_ioh, wd_seccnt, wd->sc_multiple); - if (wdcommandshort(wdc, wd->sc_drive, WDCC_SETMULTI) != 0) { - wderror(wd, NULL, "wdcontrol: setmulti failed (1)"); - goto bad; - } - wd->sc_state = MULTIMODE_WAIT; - break; - - case MULTIMODE_WAIT: - if (wdc->sc_status & WDCS_ERR) { - wderror(wd, NULL, "wdcontrol: setmulti failed (2)"); - goto bad; - } - /* fall through */ - case READY: - ready: - wdc->sc_errors = 0; - wd->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_ACTIVE; - if (wdc->sc_inten) wdc->sc_inten(wdc, 1); - timeout(wdctimeout, wdc, WAITTIME); - return 0; -} - -/* - * 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 -wdcommand(wd, command, cylin, head, sector, count) - struct wd_softc *wd; - int command; - int cylin, head, sector, count; -{ - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - int stat; - - /* Select drive, head, and addressing mode. */ - bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM | (wd->sc_drive << 4) | head); - - /* Wait for it to become ready to accept a command. */ - if (command == WDCC_IDP) - stat = wait_for_unbusy(wdc); - else - stat = wdcwait(wdc, WDCS_DRDY); - if (stat < 0) - return -1; - - /* Load parameters. */ - if (wd->sc_dk.dk_label->d_type == DTYPE_ST506) - bus_space_write_1(iot, ioh, wd_precomp, wd->sc_dk.dk_label->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 wdcommand(). - */ -int -wdcommandshort(wdc, drive, command) - struct wdc_softc *wdc; - int drive; - int command; -{ - /* Select drive. */ - bus_space_write_1(wdc->sc_iot, wdc->sc_ioh, wd_sdh, WDSD_IBM | (drive << 4)); - - if (wdcwait(wdc, WDCS_DRDY) < 0) - return -1; - - bus_space_write_1(wdc->sc_iot, wdc->sc_ioh, wd_command, command); - - return 0; -} /* * Tell the drive what geometry to use. */ int -wdsetctlr(wd) - struct wd_softc *wd; +wdsetctlr(d_link) + struct wd_link *d_link; { + struct wd_softc *wd=(struct wd_softc *)d_link->wd_softc; -#ifdef WDDEBUG - printf("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit, wd->sc_drive, - wd->sc_dk.dk_label->d_ncylinders, wd->sc_dk.dk_label->d_ntracks, - wd->sc_dk.dk_label->d_nsectors); -#endif - - if (wdcommand(wd, WDCC_IDP, wd->sc_dk.dk_label->d_ncylinders, + WDDEBUG_PRINT(("wd(%d,%d) C%dH%dS%d\n", wd->sc_dev.dv_unit, + d_link->drive, wd->sc_dk.dk_label->d_ncylinders, + wd->sc_dk.dk_label->d_ntracks, wd->sc_dk.dk_label->d_nsectors)); + + if (wdccommand((struct wdc_softc *)d_link->wdc_softc, + d_link, WDCC_IDP, d_link->drive, + wd->sc_dk.dk_label->d_ncylinders, wd->sc_dk.dk_label->d_ntracks - 1, 0, wd->sc_dk.dk_label->d_nsectors) != 0) { - wderror(wd, NULL, "wdsetctlr: geometry upload failed"); + wderror(d_link, NULL, "wdsetctlr: geometry upload failed"); return -1; } return 0; } -/* - * Get the drive parameters, if ESDI or ATA, or create fake ones for ST506. - */ int -wd_get_parms(wd) - struct wd_softc *wd; -{ - struct wdc_softc *wdc = (void *)wd->sc_dev.dv_parent; - int i; - char tb[DEV_BSIZE]; - int s, error; - - /* - * XXX - * The locking done here, and the length of time this may keep the rest - * of the system suspended, is a kluge. This should be rewritten to - * set up a transfer and queue it through wdstart(), but it's called - * infrequently enough that this isn't a pressing matter. - */ - - s = splbio(); - - while ((wdc->sc_flags & WDCF_ACTIVE) != 0) { - wdc->sc_flags |= WDCF_WANTED; - if ((error = tsleep(wdc, PRIBIO | PCATCH, "wdprm", 0)) != 0) { - (void)splx(s); - return error; - } - } - - if (wdcommandshort(wdc, wd->sc_drive, WDCC_IDENTIFY) != 0 || - wait_for_drq(wdc) != 0) { - /* - * We `know' there's a drive here; just assume it's old. - * This geometry is only used to read the MBR and print a - * (false) attach message. - */ - strncpy(wd->sc_dk.dk_label->d_typename, "ST506", - sizeof wd->sc_dk.dk_label->d_typename); - wd->sc_dk.dk_label->d_type = DTYPE_ST506; - - strncpy(wd->sc_params.wdp_model, "unknown", - sizeof wd->sc_params.wdp_model); - wd->sc_params.wdp_config = WD_CFG_FIXED; - wd->sc_params.wdp_cylinders = 1024; - wd->sc_params.wdp_heads = 8; - wd->sc_params.wdp_sectors = 17; - wd->sc_params.wdp_maxmulti = 0; - wd->sc_params.wdp_usedmovsd = 0; - wd->sc_params.wdp_capabilities = 0; - } else { - strncpy(wd->sc_dk.dk_label->d_typename, "ESDI/IDE", - sizeof wd->sc_dk.dk_label->d_typename); - wd->sc_dk.dk_label->d_type = DTYPE_ESDI; - - /* Read in parameter block. */ - if ((wd->sc_flags & WDF_32BIT) == 0) - bus_space_read_multi_2(wdc->sc_iot, wdc->sc_dataioh, wd_data, - (u_int16_t *)tb, sizeof(tb) >> 1); - else - bus_space_read_multi_4(wdc->sc_iot, wdc->sc_data32ioh, wd_data, - (u_int32_t *)tb, sizeof(tb) >> 2); - bcopy(tb, &wd->sc_params, sizeof(struct wdparams)); - - /* Shuffle string byte order. */ - for (i = 0; i < sizeof(wd->sc_params.wdp_model); i += 2) { - u_short *p; - p = (u_short *)(wd->sc_params.wdp_model + i); - *p = ntohs(*p); - } - } - - /* Clear any leftover interrupt. */ - (void) bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_status); - - /* Restart the queue. */ - wdcstart(wdc); - - (void)splx(s); - return 0; -} - -int -wdioctl(dev, cmd, addr, flag, p) +wdioctl(dev, xfer, addr, flag, p) dev_t dev; - u_long cmd; + u_long xfer; caddr_t addr; int flag; struct proc *p; { struct wd_softc *wd = wd_cd.cd_devs[WDUNIT(dev)]; + struct wd_link *d_link = wd->d_link; int error; - - if ((wd->sc_flags & WDF_LOADED) == 0) + + WDDEBUG_PRINT(("wdioctl\n")); + + if ((d_link->sc_flags & WDF_LOADED) == 0) return EIO; - switch (cmd) { + switch (xfer) { case DIOCSBAD: if ((flag & FWRITE) == 0) return EBADF; @@ -1397,33 +660,33 @@ wdioctl(dev, cmd, addr, flag, p) if ((flag & FWRITE) == 0) return EBADF; - if ((error = wdlock(wd)) != 0) + if ((error = wdlock(wd->d_link)) != 0) return error; - wd->sc_flags |= WDF_LABELLING; + d_link->sc_flags |= WDF_LABELLING; error = setdisklabel(wd->sc_dk.dk_label, (struct disklabel *)addr, /*wd->sc_dk.dk_openmask : */0, wd->sc_dk.dk_cpulabel); if (error == 0) { - if (wd->sc_state > GEOMETRY) - wd->sc_state = GEOMETRY; - if (cmd == DIOCWDINFO) + if (d_link->sc_state > GEOMETRY) + d_link->sc_state = GEOMETRY; + if (xfer == DIOCWDINFO) error = writedisklabel(WDLABELDEV(dev), wdstrategy, wd->sc_dk.dk_label, wd->sc_dk.dk_cpulabel); } - wd->sc_flags &= ~WDF_LABELLING; - wdunlock(wd); + d_link->sc_flags &= ~WDF_LABELLING; + wdunlock(d_link); return error; case DIOCWLABEL: if ((flag & FWRITE) == 0) return EBADF; if (*(int *)addr) - wd->sc_flags |= WDF_WLABEL; + d_link->sc_flags |= WDF_WLABEL; else - wd->sc_flags &= ~WDF_WLABEL; + d_link->sc_flags &= ~WDF_WLABEL; return 0; case DIOCGDEFLABEL: @@ -1457,7 +720,7 @@ wdioctl(dev, cmd, addr, flag, p) return error; } #endif - + default: return ENOTTY; } @@ -1484,7 +747,9 @@ wdsize(dev) struct wd_softc *wd; int part, unit, omask; int size; - + + WDDEBUG_PRINT(("wdsize\n")); + unit = WDUNIT(dev); if (unit >= wd_cd.cd_ndevs) return (-1); @@ -1497,12 +762,11 @@ wdsize(dev) if (omask == 0 && wdopen(dev, 0, S_IFBLK, NULL) != 0) return (-1); - wd = wd_cd.cd_devs[WDUNIT(dev)]; if (wd->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) size = -1; else size = wd->sc_dk.dk_label->d_partitions[part].p_size * - (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); + (wd->sc_dk.dk_label->d_secsize / DEV_BSIZE); if (omask == 0 && wdclose(dev, 0, S_IFBLK, NULL) != 0) return (-1); return (size); @@ -1516,6 +780,9 @@ static int wddumprecalibrated; /* * Dump core after a system crash. + * + * XXX: This needs work! Currently, it's a major hack: the + * use of wdc_softc is very bad and should go away. */ int wddump(dev, blkno, va, size) @@ -1527,6 +794,7 @@ wddump(dev, blkno, va, size) struct wd_softc *wd; /* disk unit to do the I/O */ struct wdc_softc *wdc; /* disk controller to do the I/O */ struct disklabel *lp; /* disk's disklabel */ + struct wd_link *d_link; int unit, part; int nblks; /* total number of sectors left to write */ @@ -1539,13 +807,14 @@ wddump(dev, blkno, va, size) if (unit >= wd_cd.cd_ndevs) return ENXIO; wd = wd_cd.cd_devs[unit]; - if (wd == 0) + if (wd == (struct wd_softc *)0) return ENXIO; + d_link = wd->d_link; part = WDPART(dev); /* Make sure it was initialized. */ - if (wd->sc_state < READY) + if (d_link->sc_state < READY) return ENXIO; wdc = (void *)wd->sc_dev.dv_parent; @@ -1567,10 +836,10 @@ wddump(dev, blkno, va, size) /* Recalibrate, if first dump transfer. */ if (wddumprecalibrated == 0) { wddumprecalibrated = 1; - if (wdcommandshort(wdc, wd->sc_drive, WDCC_RECAL) != 0 || - wait_for_ready(wdc) != 0 || wdsetctlr(wd) != 0 || + if (wdccommandshort(wdc, d_link->drive, WDCC_RECAL) != 0 || + wait_for_ready(wdc) != 0 || wdsetctlr(d_link) != 0 || wait_for_ready(wdc) != 0) { - wderror(wd, NULL, "wddump: recal failed"); + wderror(d_link, NULL, "wddump: recal failed"); return EIO; } } @@ -1583,7 +852,7 @@ wddump(dev, blkno, va, size) long blkdiff; int i; - for (i = 0; (blkdiff = wd->sc_badsect[i]) != -1; i++) { + for (i = 0; (blkdiff = d_link->sc_badsect[i]) != -1; i++) { blkdiff -= xlt_blkno; if (blkdiff < 0) continue; @@ -1597,7 +866,7 @@ wddump(dev, blkno, va, size) /* Tranfer is okay now. */ } - if ((wd->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { + if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { sector = (xlt_blkno >> 0) & 0xff; cylin = (xlt_blkno >> 8) & 0xffff; head = (xlt_blkno >> 24) & 0xf; @@ -1613,22 +882,21 @@ wddump(dev, blkno, va, size) } #ifndef WD_DUMP_NOT_TRUSTED - if (wdcommand(wd, WDCC_WRITE, cylin, head, sector, 1) != 0 || + if (wdccommand((struct wdc_softc *)d_link->wdc_softc, d_link, + WDCC_WRITE, d_link->drive, cylin, head, sector, 1) != 0 || wait_for_drq(wdc) != 0) { - wderror(wd, NULL, "wddump: write failed"); + wderror(d_link, NULL, "wddump: write failed"); return EIO; } - if ((wd->sc_flags & WDF_32BIT) == 0) - bus_space_write_multi_2(wdc->sc_iot, wdc->sc_dataioh, wd_data, - (u_int16_t *)va, lp->d_secsize >> 1); - else - bus_space_write_multi_4(wdc->sc_iot, wdc->sc_data32ioh, wd_data, - (u_int32_t *)va, lp->d_secsize >> 2); + /* XXX XXX XXX */ + bus_space_write_multi_2(wdc->sc_iot, wdc->sc_dataioh, wd_data, + (u_int16_t *)va, lp->d_secsize >> 1); /* Check data request (should be done). */ if (wait_for_ready(wdc) != 0) { - wderror(wd, NULL, "wddump: timeout waiting for ready"); + wderror(d_link, NULL, + "wddump: timeout waiting for ready"); return EIO; } #else /* WD_DUMP_NOT_TRUSTED */ @@ -1648,6 +916,8 @@ wddump(dev, blkno, va, size) return 0; } #else /* __BDEVSW_DUMP_NEW_TYPE */ + + int wddump(dev, blkno, va, size) dev_t dev; @@ -1670,165 +940,48 @@ bad144intern(wd) { struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad; struct disklabel *lp = wd->sc_dk.dk_label; + struct wd_link *d_link = wd->d_link; int i = 0; + WDDEBUG_PRINT(("bad144intern\n")); + for (; i < 126; i++) { if (bt->bt_bad[i].bt_cyl == 0xffff) break; - wd->sc_badsect[i] = + d_link->sc_badsect[i] = bt->bt_bad[i].bt_cyl * lp->d_secpercyl + (bt->bt_bad[i].bt_trksec >> 8) * lp->d_nsectors + (bt->bt_bad[i].bt_trksec & 0xff); } for (; i < 127; i++) - wd->sc_badsect[i] = -1; -} - -int -wdcreset(wdc) - struct wdc_softc *wdc; -{ - bus_space_tag_t iot = wdc->sc_iot; - bus_space_handle_t ioh = wdc->sc_ioh; - bus_space_handle_t aux_ioh = wdc->sc_auxioh; - - /* Reset the device. */ - if (!(wdc->sc_flags & WDCF_NORESET)) { - bus_space_write_1(iot, aux_ioh, wd_ctlr, WDCTL_RST | WDCTL_IDS); - delay(1000); - bus_space_write_1(iot, aux_ioh, wd_ctlr, WDCTL_IDS); - delay(1000); - (void) bus_space_read_1(iot, ioh, wd_error); - bus_space_write_1(iot, aux_ioh, wd_ctlr, WDCTL_4BIT); - } - - if (wait_for_unbusy(wdc) < 0) { - if (!(wdc->sc_flags & WDCF_QUIET)) - printf("%s: reset failed\n", wdc->sc_dev.dv_xname); - return 1; - } - -/*printf("wdcreset: %d\n", __LINE__);*/ - return 0; + d_link->sc_badsect[i] = -1; } void -wdcrestart(arg) - void *arg; -{ - struct wdc_softc *wdc = arg; - int s; - - s = splbio(); - wdcstart(wdc); - (void)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; - - untimeout(wdctimeout, wdc); - (void) wdcreset(wdc); - - /* Schedule recalibrate for all drives on this controller. */ - for (unit = 0; unit < wd_cd.cd_ndevs; unit++) { - struct wd_softc *wd = wd_cd.cd_devs[unit]; - if (!wd || (void *)wd->sc_dev.dv_parent != wdc) - continue; - if (wd->sc_state > RECAL) - wd->sc_state = RECAL; - } - - wdc->sc_flags |= WDCF_ERROR; - ++wdc->sc_errors; - - /* Wake up in a little bit and restart the operation. */ - timeout(wdcrestart, wdc, RECOVERYTIME); -} - -int -wdcwait(wdc, mask) - struct wdc_softc *wdc; - int mask; -{ - int timeout = 0; - u_char status; -#ifdef WDCNDELAY_DEBUG - extern int cold; -#endif - - for (;;) { - wdc->sc_status = status = bus_space_read_1(wdc->sc_iot, - wdc->sc_ioh, wd_status); - if ((status & WDCS_BSY) == 0 && (status & mask) == mask) - break; - if (++timeout > WDCNDELAY) - return -1; - delay(WDCDELAY); - } - if (status & WDCS_ERR) { - wdc->sc_error = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, - wd_error); - return WDCS_ERR; - } -#ifdef WDCNDELAY_DEBUG - /* After autoconfig, there should be no long delays. */ - if (!cold && timeout > WDCNDELAY_DEBUG) - printf("%s: warning: busy-wait took %dus\n", - wdc->sc_dev.dv_xname, WDCDELAY * timeout); -#endif - return 0; -} - -void -wdctimeout(arg) - void *arg; -{ - struct wdc_softc *wdc = (struct wdc_softc *)arg; - int s; - - s = splbio(); - if ((wdc->sc_flags & WDCF_ACTIVE) != 0) { - struct wd_softc *wd = wdc->sc_drives.tqh_first; - struct buf *bp = wd->sc_q.b_actf; - - wdc->sc_flags &= ~WDCF_ACTIVE; - if (wdc->sc_inten) wdc->sc_inten(wdc, 0); - wderror(wdc, NULL, "lost interrupt"); - printf("%s: lost interrupt: %sing %d@%s:%d\n", - wdc->sc_dev.dv_xname, - (bp->b_flags & B_READ) ? "read" : "writ", - wd->sc_nblks, wd->sc_dev.dv_xname, wd->sc_blkno); - wdcunwedge(wdc); - } else - wderror(wdc, NULL, "missing untimeout"); - (void)splx(s); -} - -void -wderror(dev, bp, msg) - void *dev; +wderror(d_link, bp, msg) + struct wd_link *d_link; struct buf *bp; char *msg; { - struct wd_softc *wd = dev; - struct wdc_softc *wdc = dev; + struct wd_softc *wd = (struct wd_softc *)d_link->wd_softc; if (bp) { - diskerr(bp, "wd", msg, LOG_PRINTF, wd->sc_skip / DEV_BSIZE, + diskerr(bp, "wd", msg, LOG_PRINTF, bp->b_bcount, wd->sc_dk.dk_label); printf("\n"); } else - printf("%s: %s: status %b error %b\n", wdc->sc_dev.dv_xname, - msg, wdc->sc_status, WDCS_BITS, wdc->sc_error, WDERR_BITS); + printf("%s: %s\n", wd->sc_dev.dv_xname, msg); +} + +void +wddone(d_link, bp) + struct wd_link *d_link; + struct buf *bp; +{ + struct wd_softc *wd = (void *)d_link->wd_softc; + + disk_unbusy(&wd->sc_dk, (bp->b_bcount - bp->b_resid)); +#if NRND > 0 + rnd_add_uint32(&wd->rnd_source, bp->b_blkno); +#endif } diff --git a/sys/arch/arm32/dev/wdc.c b/sys/arch/arm32/dev/wdc.c new file mode 100644 index 000000000000..243309c40683 --- /dev/null +++ b/sys/arch/arm32/dev/wdc.c @@ -0,0 +1,1935 @@ +/* $NetBSD: wdc.c,v 1.1 1997/10/17 06:41:38 mark Exp $ */ + +/* + * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. + * + * DMA and multi-sector PIO handling are derived from code contributed by + * Onno van der Linden. + * + * Atapi support added by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $NetBSD: wdc.c,v 1.6 1997/10/16 15:16:34 matt Exp + */ + +#undef ATAPI_DEBUG_WDC +#define WDDEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include "atapibus.h" +#include "wd.h" + +#if NATAPIBUS > 0 +#include +#include +#endif + +#define WAITTIME (10 * hz) /* time to wait for a completion */ + /* this is a lot for hard drives, but not for cdroms */ +#define RECOVERYTIME hz/2 +#define WDCDELAY 100 +#define WDCNDELAY 100000 /* delay = 100us; so 10s for a controller state change */ +#if 0 +/* If you enable this, it will report any delays more than 100us * N long. */ +#define WDCNDELAY_DEBUG 50 +#endif + +#define WDIORETRIES 5 /* number of retries before giving up */ + +#define WDPART(dev) DISKPART(dev) + +LIST_HEAD(xfer_free_list, wdc_xfer) xfer_free_list; + +int wdcintr __P((void *)); + +struct cfattach wdc_ca = { + sizeof(struct wdc_softc), NULL, NULL +}; + +struct cfdriver wdc_cd = { + NULL, "wdc", DV_DULL +}; + +void wdcstart __P((struct wdc_softc *)); +int wdcreset __P((struct wdc_softc *, int)); +#define VERBOSE 1 +#define SILENT 0 +void wdcrestart __P((void *arg)); +void wdcunwedge __P((struct wdc_softc *)); +void wdctimeout __P((void *arg)); +int wdccontrol __P((struct wdc_softc*, struct wd_link *)); +void wdc_free_xfer __P((struct wdc_xfer *)); +void wdcerror __P((struct wdc_softc*, char *)); +void wdcbit_bucket __P(( struct wdc_softc *, int)); +#if NWD > 0 +int wdprint __P((void *, const char *)); +int wdsetctlr __P((struct wd_link *)); +int wdc_ata_intr __P((struct wdc_softc *,struct wdc_xfer *)); +void wdc_ata_start __P((struct wdc_softc *,struct wdc_xfer *)); +void wdc_ata_done __P((struct wdc_softc *, struct wdc_xfer *)); +#endif /* NWD > 0 */ +#if NATAPIBUS > 0 +void wdc_atapi_minphys __P((struct buf *bp)); +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 *)); +int wdc_atapi_send_command_packet __P((struct scsipi_xfer *sc_xfer)); +#define MAX_SIZE MAXPHYS /* XXX */ +#endif + +#ifdef ATAPI_DEBUG +static int wdc_nxfer; +#endif + +#ifdef WDDEBUG +#define WDDEBUG_PRINT(args) printf args +#else +#define WDDEBUG_PRINT(args) +#endif + +#if NATAPIBUS > 0 +static struct scsipi_adapter wdc_switch = { + wdc_atapi_send_command_packet, + wdc_atapi_minphys, + 0, + 0 +}; +#endif + +int +wdcprobe_internal(iot, ioh, aux_ioh, data_ioh, data32_ioh, name) + bus_space_tag_t iot; + bus_space_handle_t ioh, aux_ioh, data_ioh, data32_ioh; + char *name; +{ + struct wdc_softc wdc; + + /* fake a wdc structure for the benefit of the routines called */ + wdc.sc_iot = iot; + wdc.sc_ioh = ioh; + wdc.sc_auxioh = aux_ioh; + wdc.sc_dataioh = data_ioh; + wdc.sc_data32ioh = data32_ioh; + wdc.sc_inten = NULL; + wdc.sc_flags = WDCF_QUIET; /* Supress warning during probe */ + if (data32_ioh != -1) + wdc.sc_flags |= WDCF_32BIT; /* Use 32 bit xfers if possible */ + strcpy(wdc.sc_dev.dv_xname, name); + + /* Check if we have registers that work. */ + bus_space_write_1(iot, ioh, wd_error, 0x5a); /* Error register not writable, */ + bus_space_write_1(iot, ioh, wd_cyl_lo, 0xa5); /* but all of cyllo are. */ + if (bus_space_read_1(iot, ioh, wd_error) == 0x5a || + bus_space_read_1(iot, ioh, wd_cyl_lo) != 0xa5) { + /* + * Test for a controller with no IDE master, just one + * ATAPI device. Select drive 1, and try again. + */ + bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM | 0x10); + bus_space_write_1(iot, ioh, wd_error, 0x5a); + bus_space_write_1(iot, ioh, wd_cyl_lo, 0xa5); + if (bus_space_read_1(iot, ioh, wd_error) == 0x5a || + bus_space_read_1(iot, ioh, wd_cyl_lo) != 0xa5) { + return 0; + } + wdc.sc_flags |= WDCF_ONESLAVE; + } + if (wdcreset(&wdc, SILENT) != 0) { + /* + * if the reset failed,, there is no master. test for ATAPI signature + * on the slave device. If no ATAPI slave, wait 5s and retry a reset. + */ +#if 0 + bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM | 0x10); /* slave */ + if (bus_space_read_1(iot, ioh, wd_cyl_lo) != 0x14 || + bus_space_read_1(iot, ioh, wd_cyl_hi) != 0xeb) { + delay(500000); + if (wdcreset(&wdc, SILENT) != 0) + return 0; + } + wdc.sc_flags |= WDCF_ONESLAVE; +#else + delay(500000); + if (wdcreset(&wdc, SILENT) != 0) { + return 0; + } +#endif + } + /* Select drive 0 or ATAPI slave device */ + if (wdc.sc_flags & WDCF_ONESLAVE) + bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM | 0x10); + else + bus_space_write_1(iot, ioh, wd_sdh, WDSD_IBM); + + /* Wait for controller to become ready. */ + if (wait_for_unbusy(&wdc) < 0) { + return 0; + } + + /* Start drive diagnostics. */ + bus_space_write_1(iot, ioh, wd_command, WDCC_DIAGNOSE); + + /* Wait for command to complete. */ + if (wait_for_unbusy(&wdc) < 0) { + return 0; + } + + return 1; +} + +void +wdcattach_internal(wdc, iot, ioh, aux_ioh, data_ioh, data32_ioh, drq) + struct wdc_softc *wdc; + bus_space_tag_t iot; + bus_space_handle_t ioh, aux_ioh, data_ioh, data32_ioh; + int drq; +{ +#if NWD > 0 + int drive; +#endif + + TAILQ_INIT(&wdc->sc_xfer); + wdc->sc_drq = drq; + wdc->sc_iot = iot; + wdc->sc_ioh = ioh; + wdc->sc_auxioh = aux_ioh; + wdc->sc_dataioh = data_ioh; + wdc->sc_data32ioh = data32_ioh; + wdc->sc_inten = NULL; + + printf("\n"); +#if 0 + if (wdc->sc_drq != -1) { + if (isa_dmamap_create(parent, wdc->sc_drq, MAXPHYS, + BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW)) { + printf("%s: can't create map for drq %d\n", + wdc->sc_dev.dv_xname, wdc->sc_drq); + wdc->sc_drq = -1; + } + } + + wdc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, + IPL_BIO, wdcintr, wdc); +#endif + +#ifdef ATAPI_DEBUG + wdc_nxfer = 0; +#endif + +#if NATAPIBUS > 0 + /* + * Attach an ATAPI bus, if configured. + */ + wdc->ab_link = malloc(sizeof(struct scsipi_link), M_DEVBUF, M_NOWAIT); + if (wdc->ab_link == NULL) { + printf("%s: can't allocate ATAPI link\n", wdc->sc_dev.dv_xname); + return; + } + bzero(wdc->ab_link,sizeof(struct scsipi_link)); + wdc->ab_link->type = BUS_ATAPI; + wdc->ab_link->openings = 1; + wdc->ab_link->scsipi_atapi.type = ATAPI; + wdc->ab_link->scsipi_atapi.channel = 0; + wdc->ab_link->adapter_softc = (caddr_t)wdc; + wdc->ab_link->adapter = &wdc_switch; + (void)config_found((struct device *)wdc, (void *)wdc->ab_link, NULL); +#endif /* NATAPIBUS > 0 */ +#if NWD > 0 + /* + * Attach standard IDE/ESDI/etc. disks to the controller. + */ + for (drive = 0; drive < 2; drive++) { + /* if a disk is already present, skip */ + if ((wdc->sc_drives_mask & (1 << drive)) != 0) { + continue; + } + /* test for ATAPI signature on this drive */ + bus_space_write_1(wdc->sc_iot, wdc->sc_ioh, wd_sdh, WDSD_IBM | (drive << 4)); + if (bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_cyl_lo) == 0x14 && + bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_cyl_hi) == 0xeb) { + continue; + } + /* controller active while autoconf */ + wdc->sc_flags |= WDCF_ACTIVE; + + if (wdccommandshort(wdc, drive, WDCC_RECAL) != 0 || + wait_for_ready(wdc) != 0) { + wdc->d_link[drive] = NULL; + wdc->sc_flags &= ~WDCF_ACTIVE; + } else { + wdc->sc_flags &= ~WDCF_ACTIVE; + wdc->d_link[drive] = malloc(sizeof(struct wd_link), + M_DEVBUF, M_NOWAIT); + if (wdc->d_link[drive] == NULL) { + printf("%s: can't allocate link for drive %d\n", + wdc->sc_dev.dv_xname, drive); + continue; + } + bzero(wdc->d_link[drive],sizeof(struct wd_link)); + wdc->d_link[drive]->type = ATA; + wdc->d_link[drive]->wdc_softc =(caddr_t) wdc; + wdc->d_link[drive]->drive = drive; +#if 0 + if (wdc->sc_drq != DRQUNK) + wdc->d_link[drive]->sc_mode = WDM_DMA; + else +#endif + wdc->d_link[drive]->sc_mode = 0; + + wdc->sc_drives_mask |= (1 << drive); + (void)config_found((struct device *)wdc, + (void *)wdc->d_link[drive], wdprint); + } + } +#endif /* NWD > 0 */ +} + +/* + * Start I/O on a controller. This does the calculation, and starts a read or + * write operation. Called to from wdstart() to start a transfer, from + * wdcintr() to continue a multi-sector transfer or start the next transfer, or + * wdcrestart() after recovering from an error. + */ +void +wdcstart(wdc) + struct wdc_softc *wdc; +{ + struct wdc_xfer *xfer; + + if ((wdc->sc_flags & WDCF_ACTIVE) != 0 ) { + WDDEBUG_PRINT(("wdcstart: already active\n")); + return; /* controller aleady active */ + } +#ifdef DIAGNOSTIC + if ((wdc->sc_flags & WDCF_IRQ_WAIT) != 0) + panic("wdcstart: controller waiting for irq\n"); +#endif + /* is there a xfer ? */ + xfer = wdc->sc_xfer.tqh_first; + if (xfer == NULL) { +#ifdef ATAPI_DEBUG2 + printf("wdcstart: null xfer\n"); +#endif + /* + * XXX + * This is a kluge. See comments in wd_get_parms(). + */ + if ((wdc->sc_flags & WDCF_WANTED) != 0) { +#ifdef ATAPI_DEBUG_WDC + printf("WDCF_WANTED\n"); +#endif + wdc->sc_flags &= ~WDCF_WANTED; + wakeup(wdc); + } + return; + } + wdc->sc_flags |= WDCF_ACTIVE; +#ifdef ATAPI_DEBUG2 + printf("wdcstart: drive %d\n", (int)xfer->d_link->drive); +#endif + bus_space_write_1(wdc->sc_iot, wdc->sc_ioh, wd_sdh, WDSD_IBM | xfer->d_link->drive << 4); +#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); +#else /* NATAPIBUS > 0 && NWD > 0 */ +#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 > 0 */ +#endif /* NATAPIBUS > 0 && NWD > 0 */ +} + +#if NWD > 0 +int +wdprint(aux, wdc) + void *aux; + const char *wdc; +{ + struct wd_link *d_link = aux; + + if (!wdc) + printf(" drive %d", d_link->drive); + return QUIET; +} + +void +wdc_ata_start(wdc, xfer) + struct wdc_softc *wdc; + struct wdc_xfer *xfer; +{ + struct wd_link *d_link; + struct buf *bp = xfer->c_bp; + int nblks; + + d_link=xfer->d_link; + + if (wdc->sc_errors >= WDIORETRIES) { + wderror(d_link, bp, "wdc_ata_start hard error"); + xfer->c_flags |= C_ERROR; + wdc_ata_done(wdc, xfer); + return; + } + + /* Do control operations specially. */ + if (d_link->sc_state < READY) { + /* + * Actually, we want to be careful not to mess with the control + * state if the device is currently busy, but we can assume + * that we never get to this point if that's the case. + */ + if (wdccontrol(wdc, d_link) == 0) { + /* The drive is busy. Wait. */ + return; + } + } + + /* + * WDCF_ERROR is set by wdcunwedge() and wdcintr() when an error is + * encountered. If we are in multi-sector mode, then we switch to + * single-sector mode and retry the operation from the start. + */ + if (wdc->sc_flags & WDCF_ERROR) { + wdc->sc_flags &= ~WDCF_ERROR; + if ((wdc->sc_flags & WDCF_SINGLE) == 0) { + wdc->sc_flags |= WDCF_SINGLE; + xfer->c_skip = 0; + } + } + + + /* When starting a transfer... */ + if (xfer->c_skip == 0) { + daddr_t blkno; + + WDDEBUG_PRINT(("\n%s: wdc_ata_start %s %d@%d; map ", + wdc->sc_dev.dv_xname, + (xfer->c_flags & B_READ) ? "read" : "write", + xfer->c_bcount, xfer->c_blkno)); + + blkno = xfer->c_blkno+xfer->c_p_offset; + xfer->c_blkno = blkno / (d_link->sc_lp->d_secsize / DEV_BSIZE); + } else { + WDDEBUG_PRINT((" %d)%x", xfer->c_skip, + bus_space_read_1(wdc->sc_iot, wdc->sc_auxioh, wd_altsts))); + } + + /* + * When starting a multi-sector transfer, or doing single-sector + * transfers... + */ + if (xfer->c_skip == 0 || (wdc->sc_flags & WDCF_SINGLE) != 0 || + d_link->sc_mode == WDM_DMA) { + daddr_t blkno = xfer->c_blkno; + long cylin, head, sector; + int command; + + if ((wdc->sc_flags & WDCF_SINGLE) != 0) + nblks = 1; + else if (d_link->sc_mode != WDM_DMA) + nblks = xfer->c_bcount / d_link->sc_lp->d_secsize; + else + nblks = + min(xfer->c_bcount / d_link->sc_lp->d_secsize, 8); + + /* Check for bad sectors and adjust transfer, if necessary. */ + if ((d_link->sc_lp->d_flags & D_BADSECT) != 0 +#ifdef B_FORMAT + && (bp->b_flags & B_FORMAT) == 0 +#endif + ) { + long blkdiff; + int i; + + for (i = 0; + (blkdiff = d_link->sc_badsect[i]) != -1; i++) { + blkdiff -= blkno; + if (blkdiff < 0) + continue; + if (blkdiff == 0) { + /* Replace current block of transfer. */ + blkno = + d_link->sc_lp->d_secperunit - + d_link->sc_lp->d_nsectors - i - 1; + } + if (blkdiff < nblks) { + /* Bad block inside transfer. */ + wdc->sc_flags |= WDCF_SINGLE; + nblks = 1; + } + break; + } + /* Tranfer is okay now. */ + } + + if ((d_link->sc_params.wdp_capabilities & WD_CAP_LBA) != 0) { + sector = (blkno >> 0) & 0xff; + cylin = (blkno >> 8) & 0xffff; + head = (blkno >> 24) & 0xf; + head |= WDSD_LBA; + } else { + sector = blkno % d_link->sc_lp->d_nsectors; + sector++; /* Sectors begin with 1, not 0. */ + blkno /= d_link->sc_lp->d_nsectors; + head = blkno % d_link->sc_lp->d_ntracks; + blkno /= d_link->sc_lp->d_ntracks; + cylin = blkno; + head |= WDSD_CHS; + } + + if (d_link->sc_mode == WDM_PIOSINGLE || + (wdc->sc_flags & WDCF_SINGLE) != 0) + xfer->c_nblks = 1; + else if (d_link->sc_mode == WDM_PIOMULTI) + xfer->c_nblks = min(nblks, d_link->sc_multiple); + else + xfer->c_nblks = nblks; + xfer->c_nbytes = xfer->c_nblks * d_link->sc_lp->d_secsize; + +#ifdef B_FORMAT + if (bp->b_flags & B_FORMAT) { + sector = d_link->sc_lp->d_gap3; + nblks = d_link->sc_lp->d_nsectors; + command = WDCC_FORMAT; + } else +#endif + switch (d_link->sc_mode) { +#if 0 + case WDM_DMA: + command = (xfer->c_flags & B_READ) ? + WDCC_READDMA : WDCC_WRITEDMA; + /* Start the DMA channel. */ + isa_dmastart(wdc->sc_dev.dv_parent, wdc->sc_drq, + xfer->databuf + xfer->c_skip, xfer->c_nbytes, + NULL, + xfer->c_flags & B_READ ? DMAMODE_READ : DMAMODE_WRITE, + BUS_DMA_NOWAIT); + break; +#endif + + case WDM_PIOMULTI: + command = (xfer->c_flags & B_READ) ? + WDCC_READMULTI : WDCC_WRITEMULTI; + break; + + case WDM_PIOSINGLE: + command = (xfer->c_flags & B_READ) ? + WDCC_READ : WDCC_WRITE; + break; + + default: +#ifdef DIAGNOSTIC + panic("bad wd mode"); +#endif + return; + } + + /* Initiate command! */ + if (wdccommand(wdc, d_link, command, d_link->drive, + cylin, head, sector, nblks) != 0) { + wderror(d_link, NULL, + "wdc_ata_start: timeout waiting for unbusy"); + wdcunwedge(wdc); + return; + } + + WDDEBUG_PRINT(("sector %d cylin %d head %d addr %x sts %x\n", + sector, cylin, head, xfer->databuf, + bus_space_read_1(wdc->sc_iot, wdc->sc_auxioh, wd_altsts))); + + } else if (xfer->c_nblks > 1) { + /* The number of blocks in the last stretch may be smaller. */ + nblks = xfer->c_bcount / d_link->sc_lp->d_secsize; + if (xfer->c_nblks > nblks) { + xfer->c_nblks = nblks; + xfer->c_nbytes = xfer->c_bcount; + } + } + + /* If this was a write and not using DMA, push the data. */ + if (d_link->sc_mode != WDM_DMA && + (xfer->c_flags & (B_READ|B_WRITE)) == B_WRITE) { + if (wait_for_drq(wdc) < 0) { + wderror(d_link, NULL, + "wdc_ata_start: timeout waiting for drq"); + wdcunwedge(wdc); + return; + } + + /* Push out data. */ + if ((wdc->sc_flags & WDCF_32BIT) == 0) + bus_space_write_multi_2(wdc->sc_iot, wdc->sc_dataioh, + wd_data, xfer->databuf + xfer->c_skip, + xfer->c_nbytes >> 1); + else + bus_space_write_multi_4(wdc->sc_iot, wdc->sc_data32ioh, + wd_data, xfer->databuf + xfer->c_skip, + xfer->c_nbytes >> 2); + } + + wdc->sc_flags |= WDCF_IRQ_WAIT; + WDDEBUG_PRINT(("wdc_ata_start: timeout ")); + timeout(wdctimeout, wdc, WAITTIME); + WDDEBUG_PRINT(("done\n")); +} + +int +wdc_ata_intr(wdc,xfer) + struct wdc_softc *wdc; + struct wdc_xfer *xfer; +{ + struct wd_link *d_link; + + d_link = xfer->d_link; + + if (wait_for_unbusy(wdc) < 0) { + wdcerror(wdc, "wdcintr: timeout waiting for unbusy"); + return 0; + } + + untimeout(wdctimeout, wdc); + + /* Is it not a transfer, but a control operation? */ + if (d_link->sc_state < READY) { + if (wdccontrol(wdc, d_link) == 0) { + /* The drive is busy. Wait. */ + return 1; + } + WDDEBUG_PRINT(("wdc_ata_start from wdc_ata_intr(open) flags 0x%x\n", + dc->sc_flags)); + wdc_ata_start(wdc,xfer); + return 1; + } + +#if 0 + /* Turn off the DMA channel. */ + if (d_link->sc_mode == WDM_DMA) + isa_dmadone(wdc->sc_dev.dv_parent, wdc->sc_drq); +#endif + + /* Have we an error? */ + if (wdc->sc_status & WDCS_ERR) { +#ifdef WDDEBUG + wderror(d_link, NULL, "wdc_ata_start"); +#endif + if ((wdc->sc_flags & WDCF_SINGLE) == 0) { + wdc->sc_flags |= WDCF_ERROR; + goto restart; + } + +#ifdef B_FORMAT + if (bp->b_flags & B_FORMAT) + goto bad; +#endif + + if (++wdc->sc_errors < WDIORETRIES) { + if (wdc->sc_errors == (WDIORETRIES + 1) / 2) { +#if 0 + wderror(wd, NULL, "wedgie"); +#endif + wdcunwedge(wdc); + return 1; + } + goto restart; + } + wderror(d_link, xfer->c_bp, "wdc_ata_intr hard error"); + +#ifdef B_FORMAT + bad: +#endif + xfer->c_flags |= C_ERROR; + goto done; + } + + /* If this was a read and not using DMA, fetch the data. */ + if (d_link->sc_mode != WDM_DMA && + (xfer->c_flags & (B_READ|B_WRITE)) == B_READ) { + if ((wdc->sc_status & (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) + != (WDCS_DRDY | WDCS_DSC | WDCS_DRQ)) { + wderror(d_link, NULL, "wdcintr: read intr before drq"); + wdcunwedge(wdc); + return 1; + } + + /* Pull in data. */ + if ((wdc->sc_flags & WDCF_32BIT) == 0) + bus_space_read_multi_2(wdc->sc_iot, wdc->sc_dataioh, + wd_data, xfer->databuf + xfer->c_skip, + xfer->c_nbytes >> 1); + else + bus_space_read_multi_4(wdc->sc_iot, wdc->sc_data32ioh, + wd_data, xfer->databuf + xfer->c_skip, + xfer->c_nbytes >> 2); + } + + /* If we encountered any abnormalities, flag it as a soft error. */ + if (wdc->sc_errors > 0 || + (wdc->sc_status & WDCS_CORR) != 0) { + wderror(d_link, xfer->c_bp, "soft error (corrected)"); + wdc->sc_errors = 0; + } + + /* Adjust pointers for the next block, if any. */ + xfer->c_blkno += xfer->c_nblks; + xfer->c_skip += xfer->c_nbytes; + xfer->c_bcount -= xfer->c_nbytes; + + /* See if this transfer is complete. */ + if (xfer->c_bcount > 0) + goto restart; + +done: + /* Done with this transfer, with or without error. */ + wdc_ata_done(wdc, xfer); + return 1; + +restart: + /* Start the next operation */ + WDDEBUG_PRINT(("wdc_ata_start from wdcintr flags 0x%x\n", + wdc->sc_flags)); + wdc_ata_start(wdc, xfer); + + return 1; +} + +void +wdc_ata_done(wdc, xfer) + struct wdc_softc *wdc; + struct wdc_xfer *xfer; +{ + struct buf *bp = xfer->c_bp; + struct wd_link *d_link = xfer->d_link; + int s; + + WDDEBUG_PRINT(("wdc_ata_done\n")); + + /* remove this command from xfer queue */ + s = splbio(); + TAILQ_REMOVE(&wdc->sc_xfer, xfer, c_xferchain); + wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR | WDCF_ACTIVE); + wdc->sc_errors = 0; + if (bp) { + if (xfer->c_flags & C_ERROR) { + bp->b_flags |= B_ERROR; + bp->b_error = EIO; + } + bp->b_resid = xfer->c_bcount; + wddone(d_link, bp); + biodone(bp); + } else { + wakeup(xfer->databuf); + } + xfer->c_skip = 0; + wdc_free_xfer(xfer); + d_link->openings++; + wdstart((void*)d_link->wd_softc); + WDDEBUG_PRINT(("wdcstart from wdc_ata_done, flags 0x%x\n", + wdc->sc_flags)); + wdcstart(wdc); + splx(s); +} + +/* + * Get the drive parameters, if ESDI or ATA, or create fake ones for ST506. + */ +int +wdc_get_parms(wdc, d_link) + struct wdc_softc * wdc; + struct wd_link *d_link; +{ + int i; + char tb[DEV_BSIZE]; + int s, error; + + /* + * XXX + * The locking done here, and the length of time this may keep the rest + * of the system suspended, is a kluge. This should be rewritten to + * set up a transfer and queue it through wdstart(), but it's called + * infrequently enough that this isn't a pressing matter. + */ + + s = splbio(); + + while ((wdc->sc_flags & WDCF_ACTIVE) != 0) { + wdc->sc_flags |= WDCF_WANTED; + if ((error = tsleep(wdc, PRIBIO | PCATCH, "wdprm", 0)) != 0) { + splx(s); + return error; + } + } + + wdc->sc_flags |= WDCF_ACTIVE; + + if (wdccommandshort(wdc, d_link->drive, WDCC_IDENTIFY) != 0 || + wait_for_drq(wdc) != 0) { + /* + * We `know' there's a drive here; just assume it's old. + * This geometry is only used to read the MBR and print a + * (false) attach message. + */ + strncpy(d_link->sc_lp->d_typename, "ST506", + sizeof d_link->sc_lp->d_typename); + d_link->sc_lp->d_type = DTYPE_ST506; + + strncpy(d_link->sc_params.wdp_model, "unknown", + sizeof d_link->sc_params.wdp_model); + d_link->sc_params.wdp_config = WD_CFG_FIXED; + d_link->sc_params.wdp_cylinders = 1024; + d_link->sc_params.wdp_heads = 8; + d_link->sc_params.wdp_sectors = 17; + d_link->sc_params.wdp_maxmulti = 0; + d_link->sc_params.wdp_usedmovsd = 0; + d_link->sc_params.wdp_capabilities = 0; + } else { + strncpy(d_link->sc_lp->d_typename, "ESDI/IDE", + sizeof d_link->sc_lp->d_typename); + d_link->sc_lp->d_type = DTYPE_ESDI; + + /* Read in parameter block. */ + if ((wdc->sc_flags & WDCF_32BIT) == 0) + bus_space_read_multi_2(wdc->sc_iot, wdc->sc_dataioh, wd_data, + (u_int16_t *)tb, sizeof(tb) / sizeof(short)); + else + bus_space_read_multi_4(wdc->sc_iot, wdc->sc_data32ioh, wd_data, + (u_int32_t *)tb, sizeof(tb) / sizeof(int)); + bcopy(tb, &d_link->sc_params, sizeof(struct wdparams)); + + /* Shuffle string byte order. */ + for (i = 0; i < sizeof(d_link->sc_params.wdp_model); i += 2) { + u_short *p; + p = (u_short *)(d_link->sc_params.wdp_model + i); + *p = ntohs(*p); + } + } + + /* Clear any leftover interrupt. */ + (void) bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_status); + + /* Restart the queue. */ + WDDEBUG_PRINT(("wdcstart from wdc_get_parms flags 0x%x\n", + wdc->sc_flags)); + wdc->sc_flags &= ~WDCF_ACTIVE; + wdcstart(wdc); + + splx(s); + return 0; +} + +/* + * Implement operations needed before read/write. + * Returns 0 if operation still in progress, 1 if completed. + */ +int +wdccontrol(wdc, d_link) + struct wdc_softc *wdc; + struct wd_link *d_link; +{ + WDDEBUG_PRINT(("wdccontrol\n")); + + switch (d_link->sc_state) { + case RECAL: /* Set SDH, step rate, do recal. */ + if (wdccommandshort(wdc, d_link->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(wdc->sc_iot, wdc->sc_ioh, wd_seccnt, d_link->sc_multiple); + if (wdccommandshort(wdc, d_link->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 > 0 */ + + +/* + * 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) { + /* Clear the pending interrupt and abort. */ + u_char s = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_status); + +#ifdef ATAPI_DEBUG_WDC + u_char e = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_error); + u_char i = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_seccnt); + printf("wdcintr: inactive controller, " + "punting st=%02x er=%02x irr=%02x\n", s, e, i); +#else + bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_error); + bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_seccnt); +#endif + + if (s & WDCS_DRQ) { + int len = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_cyl_lo) + 256 * bus_space_read_1(wdc->sc_iot, + wdc->sc_ioh, wd_cyl_hi); +#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) { + return wdc_atapi_intr(wdc,xfer); + } else + return wdc_ata_intr(wdc,xfer); +#else /* NATAPIBUS > 0 && NWD > 0 */ +#if NATAPIBUS > 0 + return wdc_atapi_intr(wdc,xfer); +#endif /* NATAPIBUS > 0 */ +#if NWD > 0 + return wdc_ata_intr(wdc,xfer); +#endif /* NWD > 0 */ +#endif /* NATAPIBUS > 0 && NWD > 0 */ +} + +int +wdcreset(wdc, verb) + struct wdc_softc *wdc; + int verb; +{ + bus_space_tag_t iot = wdc->sc_iot; + bus_space_handle_t ioh = wdc->sc_ioh; + bus_space_handle_t aux_ioh = wdc->sc_auxioh; + + /* Reset the device. */ + bus_space_write_1(iot, aux_ioh, wd_ctlr, WDCTL_RST | WDCTL_IDS); + delay(1000); + bus_space_write_1(iot, aux_ioh, wd_ctlr, WDCTL_IDS); + delay(1000); + (void) bus_space_read_1(iot, ioh, wd_error); + bus_space_write_1(iot, aux_ioh, wd_ctlr, WDCTL_4BIT); + + if (wait_for_unbusy(wdc) < 0) { + if (verb) + 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, VERBOSE); + + /* 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; +{ + int timeout = 0; + u_char status; +#ifdef WDCNDELAY_DEBUG + extern int cold; +#endif + + WDDEBUG_PRINT(("wdcwait iobase %x\n", iobase)); + + for (;;) { + wdc->sc_status = status = bus_space_read_1(wdc->sc_iot, + wdc->sc_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(wdc->sc_iot, wdc->sc_ioh, wd_sdh, WDSD_IBM | 0x10); + wdc->sc_status = status = bus_space_read_1(wdc->sc_iot, + wdc->sc_ioh, wd_status); + } + if ((status & WDCS_BSY) == 0 && (status & mask) == mask) + break; + if (++timeout > WDCNDELAY) { +#ifdef ATAPI_DEBUG + printf("wdcwait: timeout, status %x error %x\n", + status, bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_error)); +#endif + return -1; + } + delay(WDCDELAY); + } + if (status & WDCS_ERR) { + wdc->sc_error = bus_space_read_1(wdc->sc_iot, wdc->sc_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(wdc, d_link, command, drive, cylin, head, sector, count) + struct wdc_softc *wdc; + struct wd_link *d_link; + int command; + int drive, cylin, head, sector, count; +{ + 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 == ATAPI) + 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 == ATA && 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; +{ + WDDEBUG_PRINT(("wdccommandshort\n")); + +#if defined(DIAGNOSTIC) && defined(WDCDEBUG) + if ((wdc->sc_flags & WDCF_ACTIVE) == 0) + printf("wdccommandshort: controler not active (drive %d)\n", + drive); +#endif + + /* Select drive. */ + bus_space_write_1(wdc->sc_iot, wdc->sc_ioh, wd_sdh, + WDSD_IBM | (drive << 4)); + + if (wdcwait(wdc, WDCS_DRDY) < 0) + return -1; + + bus_space_write_1(wdc->sc_iot, wdc->sc_ioh, wd_command, command); + + return 0; +} + +void +wdc_exec_xfer(wdc, d_link, xfer) + struct wdc_softc *wdc; + struct wd_link *d_link; + struct wdc_xfer *xfer; +{ + 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 0x%x\n", + wdc->sc_flags)); + wdcstart(wdc); + xfer->c_flags |= C_NEEDDONE; /* we can now call upper level done() */ + splx(s); +} + +struct wdc_xfer * +wdc_get_xfer(flags) + 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; + 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(%d): %s\n", wdc->sc_dev.dv_xname, + xfer->d_link->drive, msg); +} + +/* + * the bit bucket + */ +void +wdcbit_bucket(wdc, size) + struct wdc_softc *wdc; + int size; +{ + if ((wdc->sc_flags & WDCF_32BIT) == 0) { + short null; + while (size >= 2) { + (void)bus_space_read_multi_2(wdc->sc_iot, wdc->sc_dataioh, + wd_data, &null, 1); + size -= 2; + } + } else { + int null; + while (size >= 4) { + (void)bus_space_read_multi_4(wdc->sc_iot, wdc->sc_data32ioh, + wd_data, &null, 1); + size -= 4; + } + } + while (size > 0) { + (void)bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_data); + --size; + } +} + + +#if NATAPIBUS > 0 + +void +wdc_atapi_minphys (struct buf *bp) +{ + if(bp->b_bcount > MAX_SIZE) + bp->b_bcount = MAX_SIZE; + minphys(bp); +} + + +void +wdc_atapi_start(wdc, xfer) + struct wdc_softc *wdc; + struct wdc_xfer *xfer; +{ + struct scsipi_xfer *sc_xfer = xfer->atapi_cmd; + +#ifdef ATAPI_DEBUG_WDC + printf("wdc_atapi_start, acp flags %x \n",sc_xfer->flags); +#endif + if (wdc->sc_errors >= WDIORETRIES) { + if ((wdc->sc_status & WDCS_ERR) == 0) { + sc_xfer->error = XS_DRIVER_STUFFUP; /* XXX do we know more ? */ + } else { + sc_xfer->error = XS_SENSE; + sc_xfer->sense.atapi_sense = + bus_space_read_1(wdc->sc_iot, wdc->sc_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); + sc_xfer->error = XS_SELTIMEOUT; + } +#if 0 /* don't get the sense yet, as this may be just UNIT ATTENTION */ + else { +#ifdef ATAPI_DEBUG_WDC + printf("wdc_atapi_start: sense %02x\n", wdc->sc_error); +#endif + sc_xfer->error = XS_SENSE; + sc_xfer->sense.atapi_sense = wdc->sc_error; + } + wdc_atapi_done(wdc, xfer); + return; +#endif + } + + if (wdccommand(wdc, (struct wd_link*)xfer->d_link, ATAPI_PACKET_COMMAND, + sc_xfer->sc_link->scsipi_atapi.drive, sc_xfer->datalen, + 0, 0, 0) != 0) { + printf("wdc_atapi_start: can't send atapi paket command\n"); + sc_xfer->error = XS_DRIVER_STUFFUP; + wdc->sc_flags |= WDCF_IRQ_WAIT; + wdc_atapi_done(wdc, xfer); + return; + } + if ((sc_xfer->sc_link->scsipi_atapi.cap & 0x0300) != ACAP_DRQ_INTR) { + int i, phase; + for (i=20000; i>0; --i) { + phase = (bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_ireason) & (WDCI_CMD | WDCI_IN)) | + (bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_status) & WDCS_DRQ); + if (phase == PHASE_CMDOUT) + break; + delay(10); + } + if (phase != PHASE_CMDOUT ) { + printf("wdc_atapi_start: timout waiting PHASE_CMDOUT"); + sc_xfer->error = XS_SELTIMEOUT; + wdc_atapi_done(wdc, xfer); + return; + } + if ((wdc->sc_flags & WDCF_32BIT) == 0) + bus_space_write_multi_2(wdc->sc_iot, wdc->sc_dataioh, wd_data, + (u_int16_t *)sc_xfer->cmd, sc_xfer->cmdlen / sizeof(short)); + else + bus_space_write_multi_4(wdc->sc_iot, wdc->sc_data32ioh, wd_data, + (u_int32_t *)sc_xfer->cmd, sc_xfer->cmdlen / sizeof(int)); + } + wdc->sc_flags |= WDCF_IRQ_WAIT; + +#ifdef ATAPI_DEBUG2 + printf("wdc_atapi_start: timeout\n"); +#endif + timeout(wdctimeout, wdc, WAITTIME); + return; +} + + +int +wdc_atapi_get_params(ab_link, drive, id) + struct scsipi_link *ab_link; + u_int8_t drive; + struct atapi_identify *id; +{ + struct wdc_softc *wdc = (void*)ab_link->adapter_softc; + int status, len, excess = 0; + int s, error; + + /* if a disk is already present, skip */ + if ((wdc->sc_drives_mask & (1 << drive)) != 0) { +#ifdef ATAPI_DEBUG_PROBE + printf("wdc_atapi_get_params: drive %d present\n", drive); +#endif + return 0; + } + + /* + * If there is only one ATAPI slave on the bus,don't probe + * drive 0 (master) + */ + + if (wdc->sc_flags & WDCF_ONESLAVE && drive != 1) + return 0; + +#ifdef ATAPI_DEBUG_PROBE + printf("wdc_atapi_get_params: probing drive %d\n", drive); +#endif + + /* + * XXX + * The locking done here, and the length of time this may keep the rest + * of the system suspended, is a kluge. This should be rewritten to + * set up a transfer and queue it through wdstart(), but it's called + * infrequently enough that this isn't a pressing matter. + */ + + s = splbio(); + + while ((wdc->sc_flags & WDCF_ACTIVE) != 0) { + wdc->sc_flags |= WDCF_WANTED; + if ((error = tsleep(wdc, PRIBIO | PCATCH, "atprm", 0)) != 0) { + splx(s); + return error; + } + } + + wdc->sc_flags |= WDCF_ACTIVE; + error = 1; + (void)wdcreset(wdc, VERBOSE); + if ((status = wdccommand(wdc, (struct wd_link*)(&(ab_link->scsipi_atapi)), + ATAPI_SOFT_RESET, drive, 0, 0, 0, 0)) != 0) { +#ifdef ATAPI_DEBUG + printf("wdc_atapi_get_params: ATAPI_SOFT_RESET" + "failed for drive %d: status %d error %d\n", + drive, status, wdc->sc_error); +#endif + error = 0; + goto end; + } + if ((status = wait_for_unbusy(wdc)) != 0) { +#ifdef ATAPI_DEBUG + printf("wdc_atapi_get_params: wait_for_unbusy failed " + "for drive %d: status %d error %d\n", + drive, status, wdc->sc_error); +#endif + error = 0; + goto end; + } + + if (wdccommand(wdc, (struct wd_link*)(&(ab_link->scsipi_atapi)), + ATAPI_IDENTIFY_DEVICE, drive, sizeof(struct atapi_identify), + 0, 0, 0) != 0 || + atapi_ready(wdc) != 0) { +#ifdef ATAPI_DEBUG_PROBE + printf("ATAPI_IDENTIFY_DEVICE failed for drive %d\n", drive); +#endif + error = 0; + goto end; + } + len = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_cyl_lo) + 256 * + bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_cyl_hi); + if (len != sizeof(struct atapi_identify)) { + printf("Warning drive %d returned %d/%d of " + "indentify device data\n", drive, len, + sizeof(struct atapi_identify)); + excess = len - sizeof(struct atapi_identify); + if (excess < 0) + excess = 0; + } + if ((wdc->sc_flags & WDCF_32BIT) == 0) + bus_space_read_multi_2(wdc->sc_iot, wdc->sc_dataioh, wd_data, + (u_int16_t *)id, sizeof(struct atapi_identify)/sizeof(short)); + else + bus_space_read_multi_4(wdc->sc_iot, wdc->sc_data32ioh, wd_data, + (u_int32_t *)id, sizeof(struct atapi_identify)/sizeof(int)); + wdcbit_bucket(wdc, excess); + wdc->sc_drives_mask |= (1 << drive); + + end: /* Restart the queue. */ + WDDEBUG_PRINT(("wdcstart from wdc_atapi_get_parms flags 0x%x\n", + wdc->sc_flags)); + wdc->sc_flags &= ~WDCF_ACTIVE; + wdcstart(wdc); + splx(s); + return error; +} + +int +wdc_atapi_send_command_packet(sc_xfer) + struct scsipi_xfer *sc_xfer; +{ + struct scsipi_link *sc_link = sc_xfer->sc_link; + struct wdc_softc *wdc = (void*)sc_link->adapter_softc; + struct wdc_xfer *xfer; + int flags = sc_xfer->flags; + + if (flags & SCSI_POLL) { /* should use the queue and wdc_atapi_start */ + struct wdc_xfer xfer_s; + int i, s; + + s = splbio(); +#ifdef ATAPI_DEBUG_WDC + printf("wdc_atapi_send_cmd: " + "flags 0x%x drive %d cmdlen %d datalen %d", + sc_xfer->flags, sc_link->scsipi_atapi.drive, sc_xfer->cmdlen, + sc_xfer->datalen); +#endif + xfer = &xfer_s; + bzero(xfer, sizeof(xfer_s)); + xfer->c_flags = C_INUSE|C_ATAPI|flags; + xfer->d_link = (struct wd_link *)(&sc_link->scsipi_atapi); + xfer->c_bp = sc_xfer->bp; + xfer->atapi_cmd = sc_xfer; + xfer->c_blkno = 0; + xfer->databuf = sc_xfer->data; + xfer->c_bcount = sc_xfer->datalen; + if (wait_for_unbusy (wdc) != 0) { + if ((wdc->sc_status & WDCS_ERR) == 0) { + printf("wdc_atapi_send_command: not ready, " + "st = %02x\n", wdc->sc_status); + sc_xfer->error = XS_SELTIMEOUT; + } else { + sc_xfer->error = XS_SENSE; + sc_xfer->sense.atapi_sense = wdc->sc_error; + } + splx(s); + return COMPLETE; + } + + if (wdccommand(wdc, (struct wd_link*)(&sc_link->scsipi_atapi), + ATAPI_PACKET_COMMAND, sc_link->scsipi_atapi.drive, sc_xfer->datalen, + 0, 0, 0) != 0) { + printf("can't send atapi paket command\n"); + sc_xfer->error = XS_DRIVER_STUFFUP; + splx(s); + return COMPLETE; + } + + /* Wait for cmd i/o phase. */ + for (i = 20000; i > 0; --i) { + int phase; + phase = (bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_ireason) & (WDCI_CMD | WDCI_IN)) | + (bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_status) & WDCS_DRQ); + if (phase == PHASE_CMDOUT) + break; + delay(10); + } +#ifdef ATAPI_DEBUG_WDC + printf("Wait for cmd i/o phase: i = %d\n", i); +#endif + + if ((wdc->sc_flags & WDCF_32BIT) == 0) + bus_space_read_multi_2(wdc->sc_iot, wdc->sc_dataioh, wd_data, + (u_int16_t *)sc_xfer->cmd, sc_xfer->cmdlen/ sizeof (short)); + else + bus_space_read_multi_4(wdc->sc_iot, wdc->sc_data32ioh, wd_data, + (u_int32_t *)sc_xfer->cmd, sc_xfer->cmdlen/ sizeof (int)); + + /* Wait for data i/o phase. */ + for ( i= 20000; i > 0; --i) { + int phase; + phase = (bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_ireason) & (WDCI_CMD | WDCI_IN)) | + (bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_status) & WDCS_DRQ); + if (phase != PHASE_CMDOUT) + break; + delay(10); + } + +#ifdef ATAPI_DEBUG_WDC + printf("Wait for data i/o phase: i = %d\n", i); +#endif + while ((sc_xfer->flags & ITSDONE) == 0) { + wdc_atapi_intr(wdc, xfer); + for (i = 2000; i > 0; --i) + if ((bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_status) & WDCS_DRQ) == 0) + break; +#ifdef ATAPI_DEBUG_WDC + printf("wdc_atapi_intr: i = %d\n", i); +#endif + } + wdc->sc_flags &= ~(WDCF_IRQ_WAIT | WDCF_SINGLE | WDCF_ERROR); + wdc->sc_errors = 0; + xfer->c_skip = 0; + splx(s); + return COMPLETE; + } else { /* POLLED */ + xfer = wdc_get_xfer(flags & SCSI_NOSLEEP ? IDE_NOSLEEP : 0); + if (xfer == NULL) { + return TRY_AGAIN_LATER; + } + xfer->c_flags |= C_ATAPI|sc_xfer->flags; + xfer->d_link = (struct wd_link*)(&sc_link->scsipi_atapi); + xfer->c_bp = sc_xfer->bp; + xfer->atapi_cmd = sc_xfer; + xfer->c_blkno = 0; + xfer->databuf = sc_xfer->data; + xfer->c_bcount = sc_xfer->datalen; + wdc_exec_xfer(wdc, xfer->d_link, xfer); +#ifdef ATAPI_DEBUG_WDC + printf("wdc_atapi_send_command_packet: wdc_exec_xfer, flags 0x%x\n", + sc_xfer->flags); +#endif + return (sc_xfer->flags & ITSDONE) ? COMPLETE : SUCCESSFULLY_QUEUED; + } +} + +int +wdc_atapi_intr(wdc, xfer) + struct wdc_softc *wdc; + struct wdc_xfer *xfer; +{ + struct scsipi_xfer *sc_xfer = xfer->atapi_cmd; + int len, phase, i, retries=0; + int err, st, ire; + +#ifdef ATAPI_DEBUG2 + printf("wdc_atapi_intr: %s\n", wdc->sc_dev.dv_xname); +#endif + + if (wait_for_unbusy(wdc) < 0) { + if ((wdc->sc_status & WDCS_ERR) == 0) { + printf("wdc_atapi_intr: controller busy\n"); + return 0; + } else { + sc_xfer->error = XS_SENSE; + sc_xfer->sense.atapi_sense = wdc->sc_error; + } +#ifdef ATAPI_DEBUG_WDC + printf("wdc_atapi_intr: wdc_atapi_done(), error %d\n", + sc_xfer->error); +#endif + wdc_atapi_done(wdc, xfer); + return 0; + } + + +again: + len = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_cyl_lo) + + 256 * bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_cyl_hi); + + st = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_status); + err = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_error); + ire = bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, wd_ireason); + + phase = (ire & (WDCI_CMD | WDCI_IN)) | (st & WDCS_DRQ); +#ifdef ATAPI_DEBUG_WDC + printf("wdc_atapi_intr: len %d st %d err %d ire %d :", + len, st, err, ire); +#endif + switch (phase) { + case PHASE_CMDOUT: + /* send packet command */ +#ifdef ATAPI_DEBUG_WDC + printf("PHASE_CMDOUT\n"); +#endif + +#ifdef ATAPI_DEBUG_WDC + { + int i; + char *c = (char *)sc_xfer->cmd; + printf("wdc_atapi_intr: cmd "); + for (i = 0; i < sc_xfer->cmdlen; i++) + printf("%x ", c[i]); + printf("\n"); + } +#endif + + wdc->sc_flags |= WDCF_IRQ_WAIT; + if ((wdc->sc_flags & WDCF_32BIT) == 0) + bus_space_write_multi_2(wdc->sc_iot, wdc->sc_dataioh, wd_data, + (u_int16_t *)sc_xfer->cmd, sc_xfer->cmdlen/ sizeof (short)); + else + bus_space_write_multi_4(wdc->sc_iot, wdc->sc_data32ioh, wd_data, + (u_int32_t *)sc_xfer->cmd, sc_xfer->cmdlen/ sizeof (int)); + return 1; + + case PHASE_DATAOUT: + /* write data */ +#ifdef ATAPI_DEBUG_WDC + printf("PHASE_DATAOUT\n"); +#endif + if ((sc_xfer->flags & SCSI_DATA_OUT) == 0) { + printf("wdc_atapi_intr: bad data phase\n"); + sc_xfer->error = XS_DRIVER_STUFFUP; + return 0; + } + wdc->sc_flags |= WDCF_IRQ_WAIT; + if (xfer->c_bcount < len) { + /* XXX 32 bit xfers ? */ + printf("wdc_atapi_intr: warning: write only " + "%d of %d requested bytes\n", xfer->c_bcount, len); + bus_space_write_multi_2(wdc->sc_iot, wdc->sc_dataioh, + wd_data, xfer->databuf + xfer->c_skip, + xfer->c_bcount / sizeof(short)); + for (i = xfer->c_bcount; i < len; i += sizeof(short)) + bus_space_write_2(wdc->sc_iot, wdc->sc_dataioh, + wd_data, 0); + xfer->c_bcount = 0; + return 1; + } else { + if ((wdc->sc_flags & WDCF_32BIT) == 0) + bus_space_write_multi_2(wdc->sc_iot, wdc->sc_dataioh, + wd_data, xfer->databuf + xfer->c_skip, + len / sizeof(short)); + else + bus_space_write_multi_4(wdc->sc_iot, wdc->sc_data32ioh, + wd_data, xfer->databuf + xfer->c_skip, + len / sizeof(int)); + xfer->c_skip += len; + xfer->c_bcount -= len; + return 1; + } + + case PHASE_DATAIN: + /* Read data */ +#ifdef ATAPI_DEBUG_WDC + printf("PHASE_DATAIN\n"); +#endif + if ((sc_xfer->flags & SCSI_DATA_IN) == 0) { + printf("wdc_atapi_intr: bad data phase\n"); + sc_xfer->error = XS_DRIVER_STUFFUP; + return 0; + } + wdc->sc_flags |= WDCF_IRQ_WAIT; + if (xfer->c_bcount < len) { + /* XXX 32 bit xfers ? */ + printf("wdc_atapi_intr: warning: reading only " + "%d of %d bytes\n", xfer->c_bcount, len); + bus_space_read_multi_2(wdc->sc_iot, wdc->sc_dataioh, + wd_data, xfer->databuf + xfer->c_skip, + xfer->c_bcount / sizeof(short)); + wdcbit_bucket(wdc, len - xfer->c_bcount); + xfer->c_bcount = 0; + return 1; + } else { + if ((wdc->sc_flags & WDCF_32BIT) == 0) + bus_space_read_multi_2(wdc->sc_iot, wdc->sc_dataioh, + wd_data, xfer->databuf + xfer->c_skip, + len / sizeof(short)); + else + bus_space_read_multi_4(wdc->sc_iot, wdc->sc_data32ioh, + wd_data, xfer->databuf + xfer->c_skip, + len / sizeof(int)); + xfer->c_skip += len; + xfer->c_bcount -=len; + return 1; + } + + case PHASE_ABORTED: + case PHASE_COMPLETED: +#ifdef ATAPI_DEBUG_WDC + printf("PHASE_COMPLETED\n"); +#endif + if (st & WDCS_ERR) { + sc_xfer->error = XS_SENSE; + sc_xfer->sense.atapi_sense = + bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_error); + } +#ifdef ATAPI_DEBUG_WDC + if (xfer->c_bcount != 0) { + printf("wdc_atapi_intr warning: bcount value " + "is %d after io\n", xfer->c_bcount); + } +#endif + break; + + default: + if (++retries<500) { + DELAY(100); + goto again; + } + printf("wdc_atapi_intr: unknown phase %d\n", phase); + if (st & WDCS_ERR) { + sc_xfer->error = XS_SENSE; + sc_xfer->sense.atapi_sense = + bus_space_read_1(wdc->sc_iot, wdc->sc_ioh, + wd_error); + } else { + sc_xfer->error = XS_DRIVER_STUFFUP; + } + } + +#ifdef ATAPI_DEBUG_WDC + printf("wdc_atapi_intr: wdc_atapi_done() (end), error %d\n", + sc_xfer->error); +#endif + wdc_atapi_done(wdc, xfer); + return (1); +} + + +void +wdc_atapi_done(wdc, xfer) + struct wdc_softc *wdc; + struct wdc_xfer *xfer; +{ + struct scsipi_xfer *sc_xfer = xfer->atapi_cmd; + int s; + int need_done = xfer->c_flags & C_NEEDDONE; + +#ifdef ATAPI_DEBUG + printf("wdc_atapi_done: flags 0x%x\n", (u_int)xfer->c_flags); +#endif + sc_xfer->resid = xfer->c_bcount; + + /* remove this command from xfer queue */ + wdc->sc_errors = 0; + xfer->c_skip = 0; + if ((xfer->c_flags & SCSI_POLL) == 0) { + s = splbio(); + untimeout(wdctimeout, wdc); + TAILQ_REMOVE(&wdc->sc_xfer, xfer, c_xferchain); + wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR | WDCF_ACTIVE); + wdc_free_xfer(xfer); + sc_xfer->flags |= ITSDONE; + if (need_done) { +#ifdef ATAPI_DEBUG + printf("wdc_atapi_done: scsipi_done\n"); +#endif + scsipi_done(sc_xfer); + } +#ifdef WDDEBUG + printf("wdcstart from wdc_atapi_intr, flags 0x%x\n", + wdc->sc_flags); +#endif + wdcstart(wdc); + splx(s); + } else + wdc->sc_flags &= ~(WDCF_SINGLE | WDCF_ERROR | WDCF_ACTIVE); + +} + +#endif /* NATAPIBUS > 0 */ diff --git a/sys/arch/arm32/dev/wdcvar.h b/sys/arch/arm32/dev/wdcvar.h deleted file mode 100644 index ab9f98140f2a..000000000000 --- a/sys/arch/arm32/dev/wdcvar.h +++ /dev/null @@ -1,82 +0,0 @@ -/* $NetBSD: wdcvar.h,v 1.2 1997/10/14 19:13:58 mark Exp $ */ - -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * William Jolitz. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)wdreg.h 7.1 (Berkeley) 5/9/91 - */ - -/* - * softc structure for the wdc device - */ - -struct wdc_softc { - struct device sc_dev; /* device node */ - void *sc_ih; /* interrupt pointer */ - - bus_space_tag_t sc_iot; /* Bus space tag */ - bus_space_handle_t sc_ioh; /* handle for drive registers */ - bus_space_handle_t sc_auxioh; /* handle for aux register */ - bus_space_handle_t sc_dataioh; /* handle for 16 bit data */ - bus_space_handle_t sc_data32ioh;/* handle for 32 bit data */ - - void (*sc_inten)(struct wdc_softc *, int); - - int sc_drq; /* DMA channel */ - - TAILQ_HEAD(drivehead, wd_softc) sc_drives; - int sc_flags; -#define WDCF_ACTIVE 0x01 /* controller is active */ -#define WDCF_SINGLE 0x02 /* sector at a time mode */ -#define WDCF_ERROR 0x04 /* processing a disk error */ -#define WDCF_WANTED 0x08 /* XXX locking for wd_get_parms() */ -#define WDCF_QUIET 0x10 /* Be quiet about errors */ -#define WDCF_32BIT 0x20 /* Use 32bit xfers */ -#define WDCF_NORESET 0x10000 /* Don't reset bus */ - int sc_errors; /* errors during current transfer */ - u_char sc_status; /* copy of status register */ - u_char sc_error; /* copy of error register */ -}; - -int wdcprobe_internal __P((bus_space_tag_t iot, bus_space_handle_t ioh, - bus_space_handle_t aux_ioh, bus_space_handle_t data_ioh, - bus_space_handle_t data32_ioh, char *name)); -void wdcattach_internal __P((struct wdc_softc *wdc, bus_space_tag_t iot, bus_space_handle_t ioh, - bus_space_handle_t aux_ioh, bus_space_handle_t data_ioh, - bus_space_handle_t data32_ioh, int drq)); - -int wdcintr __P((void *)); - -/* End of wdcvar.h */ diff --git a/sys/arch/arm32/dev/wdlink.h b/sys/arch/arm32/dev/wdlink.h new file mode 100644 index 000000000000..33fe4649d8b1 --- /dev/null +++ b/sys/arch/arm32/dev/wdlink.h @@ -0,0 +1,174 @@ +/* $NetBSD: wdlink.h,v 1.1 1997/10/17 06:41:42 mark Exp $ */ + +/* + * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. + * + * DMA and multi-sector PIO handling are derived from code contributed by + * Onno van der Linden. + * + * Atapi support added by Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Charles M. Hannum. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * from: wdlink.h,v 1.3 1997/09/03 07:57:53 bouyer Exp + */ + + +#undef WDDEBUG +/* #undef DIAGNOSTIC */ + +struct wdc_softc { + struct device sc_dev; + void *sc_ih; + struct wd_link *d_link[2]; + struct scsipi_link *ab_link; + + bus_space_tag_t sc_iot; /* Bus space tag */ + bus_space_handle_t sc_ioh; /* handle for drive registers */ + bus_space_handle_t sc_auxioh; /* handle for aux register */ + bus_space_handle_t sc_dataioh; /* handle for 16 bit data */ + bus_space_handle_t sc_data32ioh;/* handle for 32 bit data */ + + void (*sc_inten)(struct wdc_softc *, int); + + int sc_drq; /* DMA channel */ + + TAILQ_HEAD(xferhead, wdc_xfer) sc_xfer; + int sc_flags; +#define WDCF_ACTIVE 0x01 /* controller is active */ +#define WDCF_SINGLE 0x02 /* sector at a time mode */ +#define WDCF_ERROR 0x04 /* processing a disk error */ +#define WDCF_WANTED 0x08 /* XXX locking for wd_get_parms() */ +#define WDCF_IRQ_WAIT 0x10 /* controller is waiting for irq */ +#define WDCF_ONESLAVE 0x20 /* ctrl. has one ATAPI slave attached */ +#define WDCF_QUIET 0x40 /* Be quiet about errors */ +#define WDCF_32BIT 0x80 /* Use 32bit xfers */ +#define WDCF_NORESET 0x10000 /* Don't reset bus */ + + int sc_errors; /* errors during current transfer */ + u_char sc_status; /* copy of status register */ + u_char sc_error; /* copy of error register */ + u_char sc_drives_mask; /* bitmask for drives present/absent */ +}; + +int wdcprobe_internal __P((bus_space_tag_t iot, bus_space_handle_t ioh, + bus_space_handle_t aux_ioh, bus_space_handle_t data_ioh, + bus_space_handle_t data32_ioh, char *name)); +void wdcattach_internal __P((struct wdc_softc *wdc, bus_space_tag_t iot, + bus_space_handle_t ioh, bus_space_handle_t aux_ioh, + bus_space_handle_t data_ioh, bus_space_handle_t data32_ioh, int drq)); + +struct wd_link { + u_int8_t type; +#define ATA 0 +#define ATAPI 1 + u_int8_t channel; + u_int8_t drive; + caddr_t wdc_softc; + caddr_t wd_softc; + struct wdparams sc_params; + + /* Long-term state: */ + u_int8_t openings; + int sc_state; /* control state */ +#define RECAL 0 /* recalibrate */ +#define RECAL_WAIT 1 /* done recalibrating */ +#define GEOMETRY 2 /* upload geometry */ +#define GEOMETRY_WAIT 3 /* done uploading geometry */ +#define MULTIMODE 4 /* set multiple mode */ +#define MULTIMODE_WAIT 5 /* done setting multiple mode */ +#define READY 6 /* done with open */ + int sc_mode; /* transfer mode */ +#define WDM_PIOSINGLE 0 /* single-sector PIO */ +#define WDM_PIOMULTI 1 /* multi-sector PIO */ +#define WDM_DMA 2 /* DMA */ + int sc_multiple; /* multiple for WDM_PIOMULTI */ + int sc_flags; /* drive characteistics found */ +#define WDF_LOCKED 0x01 +#define WDF_WANTED 0x02 +#define WDF_WLABEL 0x04 /* label is writable */ +#define WDF_LABELLING 0x08 /* writing label */ + +/* + * XXX Nothing resets this yet, but disk change sensing will when ATAPI is + * implemented. + */ +#define WDF_LOADED 0x10 /* parameters loaded */ +#define WDF_32BIT 0x20 /* can do 32-bit transfer */ +#define WDF_WAIT 0x40 /* waiting for resourses */ + + daddr_t sc_badsect[127]; /* 126 plus trailing -1 marker */ + struct disklabel *sc_lp; /* label info for this disk */ +}; + +struct wdc_xfer { + struct wd_link *d_link; /* drive/bus structure info */ + volatile long c_flags; /* handle: scsi flags (0x0000ffff) + b_read/b_write (0x00f00000) + controllers flags (0x000f0000) */ +#define C_INUSE 0x010000 +#define C_ATAPI 0x020000 +#define C_ERROR 0x040000 +#define C_NEEDDONE 0x080000 + + /* Information about the current transfer */ + struct buf *c_bp; + void *atapi_cmd; + void *databuf; + daddr_t c_blkno; /* starting block number */ + int c_bcount; /* byte count left */ + int c_skip; /* bytes already transferred */ + int c_nblks; /* number of blocks currently transferring */ + int c_nbytes; /* number of bytes currently transferring */ + u_long c_p_offset; /* offset of the partition */ + TAILQ_ENTRY(wdc_xfer) c_xferchain; + LIST_ENTRY(wdc_xfer) free_list; +}; + +void wdc_exec_xfer __P((struct wdc_softc *, struct wd_link *, + struct wdc_xfer *)); +struct wdc_xfer *wdc_get_xfer __P((int)); +int wdc_get_parms __P((struct wdc_softc *, struct wd_link *)); +void wdstart __P((void*)); +void wderror __P((struct wd_link* , struct buf *, char *)); +void wddone __P((struct wd_link*, struct buf*)); +int wdccommand __P((struct wdc_softc *, struct wd_link *, int, int, + int, int, int, int)); +int wdccommandshort __P((struct wdc_softc *, int, int)); +int wdcwait __P((struct wdc_softc *, int)); + +int wdcintr __P((void *)); + +/* + * ST506 spec says that if READY or SEEKCMPLT go off, then the read or write + * command is aborted. + */ +#define wait_for_drq(d) wdcwait(d, WDCS_DRDY | WDCS_DSC | WDCS_DRQ) +#define wait_for_unbusy(d) wdcwait(d, 0) +#define wait_for_ready(d) wdcwait(d, WDCS_DRDY | WDCS_DSC) +#define atapi_ready(d) wdcwait(d, WDCS_DRQ) + +#define IDE_NOSLEEP 0x01 diff --git a/sys/arch/arm32/dev/wdreg.h b/sys/arch/arm32/dev/wdreg.h index 90af3e75cacd..8011093cc646 100644 --- a/sys/arch/arm32/dev/wdreg.h +++ b/sys/arch/arm32/dev/wdreg.h @@ -1,4 +1,4 @@ -/* $NetBSD: wdreg.h,v 1.2 1997/02/04 02:04:55 mark Exp $ */ +/* $NetBSD: wdreg.h,v 1.3 1997/10/17 06:41:45 mark Exp $ */ /*- * Copyright (c) 1991 The Regents of the University of California. @@ -36,7 +36,7 @@ * SUCH DAMAGE. * * @(#)wdreg.h 7.1 (Berkeley) 5/9/91 - * from: wdreg.h,v 1.13 1995/03/29 21:56:46 briggs Exp + * from: wdreg.h,v 1.14 1997/08/27 11:25:18 bouyer Exp */ /* @@ -47,6 +47,7 @@ #define wd_precomp 0x001 /* write precompensation (W) */ #define wd_features 0x001 /* features (W) */ #define wd_seccnt 0x002 /* sector count (R/W) */ +#define wd_ireason 0x002 /* interrupt reason (R/W) (for atapi) */ #define wd_sector 0x003 /* first sector number (R/W) */ #define wd_cyl_lo 0x004 /* cylinder address, low byte (R/W) */ #define wd_cyl_hi 0x005 /* cylinder address, high byte (R/W) */ @@ -54,13 +55,13 @@ #define wd_command 0x007 /* command register (W) */ #define wd_status 0x007 /* immediate status (R) */ -#define WD_ALTSTATUS 0x206 /* base offset for alt status */ -#define wd_altsts 0x000 /* alternate fixed disk status (via 1015) (R) */ -#define wd_ctlr 0x000 /* fixed disk controller control (via 1015) (W) */ +#define wd_altsts 0x206 /* alternate fixed disk status (via 1015) (R) */ +#define WD_ALTSTATUS wd_altsts +#define wd_ctlr 0x206 /* fixed disk controller control (via 1015) (W) */ #define WDCTL_4BIT 0x08 /* use four head bits (wd1003) */ #define WDCTL_RST 0x04 /* reset the controller */ #define WDCTL_IDS 0x02 /* disable controller interrupts */ -#define wd_digin 0x001 /* disk controller input (via 1015) (R) */ +#define wd_digin 0x207 /* disk controller input (via 1015) (R) */ /* * Status bits. @@ -120,6 +121,29 @@ #define WDSD_CHS 0x00 /* cylinder/head/sector addressing */ #define WDSD_LBA 0x40 /* logical block addressing */ +/* Commands for ATAPI devices */ +#define ATAPI_CHECK_POWER_MODE 0xe5 +#define ATAPI_EXEC_DRIVE_DIAGS 0x90 +#define ATAPI_IDLE_IMMEDIATE 0xe1 +#define ATAPI_NOP 0x00 +#define ATAPI_PACKET_COMMAND 0xa0 +#define ATAPI_IDENTIFY_DEVICE 0xa1 +#define ATAPI_SOFT_RESET 0x08 +#define ATAPI_SET_FEATURES 0xef +#define ATAPI_SLEEP 0xe6 +#define ATAPI_STANDBY_IMMEDIATE 0xe0 + +/* ireason */ +#define WDCI_CMD 0x01 /* command(1) or data(0) */ +#define WDCI_IN 0x02 /* transfer to(1) or from(0) the host */ +#define WDCI_RELEASE 0x04 /* bus released until completion */ + +#define PHASE_CMDOUT (WDCS_DRQ | WDCI_CMD) +#define PHASE_DATAIN (WDCS_DRQ | WDCI_IN) +#define PHASE_DATAOUT WDCS_DRQ +#define PHASE_COMPLETED (WDCI_IN | WDCI_CMD) +#define PHASE_ABORTED 0 + #ifdef _KERNEL /*