diff options
| author | jdolecek <jdolecek@NetBSD.org> | 2001-11-23 22:53:09 +0000 |
|---|---|---|
| committer | jdolecek <jdolecek@NetBSD.org> | 2001-11-23 22:53:09 +0000 |
| commit | 07312f0fbbc7fe070866fd36999bfb352234ef4d (patch) | |
| tree | 63109b887f70d1bfe7138e9137e74c2005b11aec /sys/dev/mca | |
| parent | 90fac765dadfa371fbf728224e46a7911f4970a0 (diff) | |
Convert over to use the new MCA DMA facilities, which encapsulate
the MCA DMA controller access. This also means we gain >16MB RAM support.
While here, overhaul the driver to saner state, especially:
* simplify and clean the attach code a lot, and support the 'drive'
locator
* pass proper dev_t to readdisklabel() - formerly, the passed value was
completely broken (used incorrect major and wrong minor), but worked
by accident
* worker thread is now spawned per controller, rather than per-drive;
i/o cannot be done concurrently by different drivers, only one
i/o can be pending at any time
* simplify the command queue code, just sleep appropriately when
!poll case, g/c 'async' code formerly needed, move the bio code
from ed_mca.c to edc_mca.c:edc_bio()
* put all string arrays used by edc_dump_status_block() within #ifdef EDC_DEBUG,
and use numbers instead if it's not defined; this cuts object size by 5KB
* other misc cleanups
Diffstat (limited to 'sys/dev/mca')
| -rw-r--r-- | sys/dev/mca/ed_mca.c | 562 | ||||
| -rw-r--r-- | sys/dev/mca/edc_mca.c | 501 |
2 files changed, 395 insertions, 668 deletions
diff --git a/sys/dev/mca/ed_mca.c b/sys/dev/mca/ed_mca.c index e40ad166589..befe720b66e 100644 --- a/sys/dev/mca/ed_mca.c +++ b/sys/dev/mca/ed_mca.c @@ -1,4 +1,4 @@ -/* $NetBSD: ed_mca.c,v 1.9 2001/11/13 07:46:25 lukem Exp $ */ +/* $NetBSD: ed_mca.c,v 1.10 2001/11/23 22:53:10 jdolecek Exp $ */ /* * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -34,13 +34,14 @@ */ /* - * Disk goo for MCA ESDI controller driver. + * Disk drive goo for MCA ESDI controller driver. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ed_mca.c,v 1.9 2001/11/13 07:46:25 lukem Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ed_mca.c,v 1.10 2001/11/23 22:53:10 jdolecek Exp $"); #include "rnd.h" +#include "locators.h" #include <sys/param.h> #include <sys/systm.h> @@ -58,7 +59,6 @@ __KERNEL_RCSID(0, "$NetBSD: ed_mca.c,v 1.9 2001/11/13 07:46:25 lukem Exp $"); #include <sys/syslog.h> #include <sys/proc.h> #include <sys/vnode.h> -#include <sys/kthread.h> #if NRND > 0 #include <sys/rnd.h> #endif @@ -95,19 +95,11 @@ struct cfattach ed_mca_ca = { extern struct cfdriver ed_cd; -static int ed_get_params __P((struct ed_softc *)); +static int ed_get_params __P((struct ed_softc *, int *)); static int ed_lock __P((struct ed_softc *)); static void ed_unlock __P((struct ed_softc *)); -static void edgetdisklabel __P((struct ed_softc *)); +static void edgetdisklabel __P((dev_t, struct ed_softc *)); static void edgetdefaultlabel __P((struct ed_softc *, struct disklabel *)); -static void ed_shutdown __P((void*)); -static void __edstart __P((struct ed_softc*, struct buf *)); -static void bad144intern __P((struct ed_softc *)); -static void edworker __P((void *)); -static void ed_spawn_worker __P((void *)); -static void edmcadone __P((struct ed_softc *, struct buf *)); -static void ed_bio __P((struct ed_softc *, int, int)); -static void ed_bio_done __P((struct ed_softc *)); static struct dkdriver eddkdriver = { edmcastrategy }; @@ -115,22 +107,29 @@ static struct dkdriver eddkdriver = { edmcastrategy }; * Just check if it's possible to identify the disk. */ static int -ed_mca_probe(parent, match, aux) +ed_mca_probe(parent, cf, aux) struct device *parent; - struct cfdata *match; + struct cfdata *cf; void *aux; { u_int16_t cmd_args[2]; struct edc_mca_softc *sc = (void *) parent; - struct ed_attach_args *eda = (void *) aux; + struct ed_attach_args *eda = (struct ed_attach_args *) aux; int found = 1; /* + * Check we match hardwired config. + */ + if (cf->edccf_unit != EDCCF_DRIVE_DEFAULT && + cf->edccf_unit != eda->edc_drive) + return (0); + + /* * Get Device Configuration (09). */ cmd_args[0] = 14; /* Options: 00s110, s: 0=Physical 1=Pseudo */ cmd_args[1] = 0; - if (edc_run_cmd(sc, CMD_GET_DEV_CONF, eda->sc_devno, cmd_args, 2, 0, 1)) + if (edc_run_cmd(sc, CMD_GET_DEV_CONF, eda->edc_drive, cmd_args, 2, 1)) found = 0; return (found); @@ -143,20 +142,20 @@ ed_mca_attach(parent, self, aux) { struct ed_softc *ed = (void *) self; struct edc_mca_softc *sc = (void *) parent; - struct ed_attach_args *eda = (void *) aux; - char pbuf[8]; - int error, nsegs; + struct ed_attach_args *eda = (struct ed_attach_args *) aux; + char pbuf[8], lckname[10]; + int drv_flags; ed->edc_softc = sc; - ed->sc_dmat = eda->sc_dmat; - ed->sc_devno = eda->sc_devno; - edc_add_disk(sc, ed, eda->sc_devno); + ed->sc_devno = eda->edc_drive; + edc_add_disk(sc, ed); BUFQ_INIT(&ed->sc_q); simple_lock_init(&ed->sc_q_lock); - lockinit(&ed->sc_lock, PRIBIO | PCATCH, "edlck", 0, 0); + snprintf(lckname, sizeof(lckname), "%slck", ed->sc_dev.dv_xname); + lockinit(&ed->sc_lock, PRIBIO | PCATCH, lckname, 0, 0); - if (ed_get_params(ed)) { + if (ed_get_params(ed, &drv_flags)) { printf(": IDENTIFY failed, no disk found\n"); return; } @@ -170,88 +169,27 @@ ed_mca_attach(parent, self, aux) printf("%s: %u spares/cyl, %s, %s, %s, %s, %s\n", ed->sc_dev.dv_xname, ed->spares, - (ed->drv_flags & (1 << 0)) ? "NoRetries" : "Retries", - (ed->drv_flags & (1 << 1)) ? "Removable" : "Fixed", - (ed->drv_flags & (1 << 2)) ? "SkewedFormat" : "NoSkew", - (ed->drv_flags & (1 << 3)) ? "ZeroDefect" : "Defects", - (ed->drv_flags & (1 << 4)) ? "InvalidSecondary" : "SecondaryOK" + (drv_flags & (1 << 0)) ? "NoRetries" : "Retries", + (drv_flags & (1 << 1)) ? "Removable" : "Fixed", + (drv_flags & (1 << 2)) ? "SkewedFormat" : "NoSkew", + (drv_flags & (1 << 3)) ? "ZeroDefect" : "Defects", + (drv_flags & (1 << 4)) ? "InvalidSecondary" : "SecondaryOK" ); - /* Create a DMA map for mapping individual transfer bufs */ - if ((error = bus_dmamap_create(ed->sc_dmat, 65536, 1, - 65536, 65536, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, - &ed->dmamap_xfer)) != 0) { - printf("%s: unable to create xfer DMA map, error=%d\n", - ed->sc_dev.dv_xname, error); - return; - } - - /* - * Allocate DMA memory used in case where passed buf isn't - * physically contiguous. - */ - ed->sc_dmam_sz = MAXPHYS; - if ((error = bus_dmamem_alloc(ed->sc_dmat, ed->sc_dmam_sz, - ed->sc_dmam_sz, 65536, ed->sc_dmam, 1, &nsegs, - BUS_DMA_WAITOK|BUS_DMA_STREAMING)) != 0) { - printf("%s: unable to allocate DMA memory for xfer, errno=%d\n", - ed->sc_dev.dv_xname, error); - bus_dmamap_destroy(ed->sc_dmat, ed->dmamap_xfer); - return; - } - /* - * Map the memory. - */ - if ((error = bus_dmamem_map(ed->sc_dmat, ed->sc_dmam, 1, - ed->sc_dmam_sz, &ed->sc_dmamkva, BUS_DMA_WAITOK)) != 0) { - printf("%s: unable to map DMA memory, error=%d\n", - ed->sc_dev.dv_xname, error); - bus_dmamem_free(ed->sc_dmat, ed->sc_dmam, 1); - bus_dmamap_destroy(ed->sc_dmat, ed->dmamap_xfer); - return; - } - - /* * Initialize and attach the disk structure. */ ed->sc_dk.dk_driver = &eddkdriver; ed->sc_dk.dk_name = ed->sc_dev.dv_xname; disk_attach(&ed->sc_dk); -#if 0 - wd->sc_wdc_bio.lp = wd->sc_dk.dk_label; -#endif - ed->sc_sdhook = shutdownhook_establish(ed_shutdown, ed); - if (ed->sc_sdhook == NULL) - printf("%s: WARNING: unable to establish shutdown hook\n", - ed->sc_dev.dv_xname); #if NRND > 0 rnd_attach_source(&ed->rnd_source, ed->sc_dev.dv_xname, RND_TYPE_DISK, 0); #endif - config_pending_incr(); - kthread_create(ed_spawn_worker, (void *) ed); - ed->sc_flags |= EDF_INIT; } -void -ed_spawn_worker(arg) - void *arg; -{ - struct ed_softc *ed = (struct ed_softc *) arg; - int error; - - /* Now, everything is ready, start a kthread */ - if ((error = kthread_create1(edworker, ed, &ed->sc_worker, - "%s", ed->sc_dev.dv_xname))) { - printf("%s: cannot spawn worker thread: errno=%d\n", - ed->sc_dev.dv_xname, error); - panic("ed_spawn_worker"); - } -} - /* * Read/write routine for a buffer. Validates the arguments and schedules the * transfer. Does not wait for the transfer to complete. @@ -260,12 +198,12 @@ void edmcastrategy(bp) struct buf *bp; { - struct ed_softc *wd = device_lookup(&ed_cd, DISKUNIT(bp->b_dev)); - struct disklabel *lp = wd->sc_dk.dk_label; + struct ed_softc *ed = device_lookup(&ed_cd, DISKUNIT(bp->b_dev)); + struct disklabel *lp = ed->sc_dk.dk_label; daddr_t blkno; int s; - WDCDEBUG_PRINT(("edmcastrategy (%s)\n", wd->sc_dev.dv_xname), + WDCDEBUG_PRINT(("edmcastrategy (%s)\n", ed->sc_dev.dv_xname), DEBUG_XFERS); /* Valid request? */ @@ -277,7 +215,7 @@ edmcastrategy(bp) } /* If device invalidated (e.g. media change, door open), error. */ - if ((wd->sc_flags & WDF_LOADED) == 0) { + if ((ed->sc_flags & WDF_LOADED) == 0) { bp->b_error = EIO; goto bad; } @@ -291,8 +229,8 @@ edmcastrategy(bp) * If end of partition, just return. */ if (DISKPART(bp->b_dev) != RAW_PART && - bounds_check_with_label(bp, wd->sc_dk.dk_label, - (wd->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) + bounds_check_with_label(bp, ed->sc_dk.dk_label, + (ed->sc_flags & (WDF_WLABEL|WDF_LABELLING)) != 0) <= 0) goto done; /* @@ -311,13 +249,12 @@ edmcastrategy(bp) /* Queue transfer on drive, activate drive and controller if idle. */ s = splbio(); - simple_lock(&wd->sc_q_lock); - disksort_blkno(&wd->sc_q, bp); - simple_unlock(&wd->sc_q_lock); + simple_lock(&ed->sc_q_lock); + disksort_blkno(&ed->sc_q, bp); + simple_unlock(&ed->sc_q_lock); /* Ring the worker thread */ - wd->sc_flags |= EDF_PROCESS_QUEUE; - wakeup_one(&wd->sc_q); + wakeup_one(ed->edc_softc); splx(s); return; @@ -329,141 +266,6 @@ done: biodone(bp); } -static void -ed_bio(struct ed_softc *ed, int async, int poll) -{ - u_int16_t cmd_args[4]; - int error=0; - u_int16_t track; - u_int16_t cyl; - u_int8_t head; - u_int8_t sector; - - /* Get physical bus mapping for buf. */ - if (bus_dmamap_load(ed->sc_dmat, ed->dmamap_xfer, - ed->sc_data, ed->sc_bcount, NULL, - BUS_DMA_WAITOK|BUS_DMA_STREAMING) != 0) { - - /* - * Use our DMA safe memory to get data to/from device. - */ - if ((error = bus_dmamap_load(ed->sc_dmat, ed->dmamap_xfer, - ed->sc_dmamkva, ed->sc_bcount, NULL, - BUS_DMA_WAITOK|BUS_DMA_STREAMING)) != 0) { - printf("%s: unable to load raw data for xfer, errno=%d\n", - ed->sc_dev.dv_xname, error); - goto out; - } - ed->sc_flags |= EDF_BOUNCEBUF; - - /* If data write, copy the data to our bounce buffer. */ - if (!ed->sc_read) - memcpy(ed->sc_dmamkva, ed->sc_data, ed->sc_bcount); - } - - ed->sc_flags |= EDF_DMAMAP_LOADED; - - track = ed->sc_rawblkno / ed->sectors; - head = track % ed->heads; - cyl = track / ed->heads; - sector = ed->sc_rawblkno % ed->sectors; - - WDCDEBUG_PRINT(("__edstart %s: map: %u %u %u\n", ed->sc_dev.dv_xname, - cyl, sector, head), - DEBUG_XFERS); - - mca_disk_busy(); - - /* Read or Write Data command */ - cmd_args[0] = 2; /* Options 0000010 */ - cmd_args[1] = ed->sc_bcount / DEV_BSIZE; - cmd_args[2] = ((cyl & 0x1f) << 11) | (head << 5) | sector; - cmd_args[3] = ((cyl & 0x3E0) >> 5); - if (edc_run_cmd(ed->edc_softc, - (ed->sc_read) ? CMD_READ_DATA : CMD_WRITE_DATA, - ed->sc_devno, cmd_args, 4, async, poll)) { - printf("%s: data i/o command failed\n", ed->sc_dev.dv_xname); - mca_disk_unbusy(); - error = EIO; - } - - out: - if (error) - ed->sc_error = error; -} - -static void -__edstart(ed, bp) - struct ed_softc *ed; - struct buf *bp; -{ - WDCDEBUG_PRINT(("__edstart %s (%s): %lu %lu %u\n", ed->sc_dev.dv_xname, - (bp->b_flags & B_READ) ? "read" : "write", - bp->b_bcount, bp->b_resid, bp->b_rawblkno), - DEBUG_XFERS); - - /* Instrumentation. */ - disk_busy(&ed->sc_dk); - ed->sc_flags |= EDF_DK_BUSY; - - ed->sc_data = bp->b_data; - ed->sc_rawblkno = bp->b_rawblkno; - ed->sc_bcount = bp->b_bcount; - ed->sc_read = bp->b_flags & B_READ; - ed_bio(ed, 1, 0); -} - -static void -ed_bio_done(ed) - struct ed_softc *ed; -{ - /* - * If read transfer finished without error and using a bounce - * buffer, copy the data to buf. - */ - if (ed->sc_error == 0 && (ed->sc_flags & EDF_BOUNCEBUF) && ed->sc_read) - memcpy(ed->sc_data, ed->sc_dmamkva, ed->sc_bcount); - ed->sc_flags &= ~EDF_BOUNCEBUF; - - /* Unload buf from DMA map */ - if (ed->sc_flags & EDF_DMAMAP_LOADED) { - bus_dmamap_unload(ed->sc_dmat, ed->dmamap_xfer); - ed->sc_flags &= ~EDF_DMAMAP_LOADED; - } - - mca_disk_unbusy(); -} - -static void -edmcadone(ed, bp) - struct ed_softc *ed; - struct buf *bp; -{ - WDCDEBUG_PRINT(("eddone %s\n", ed->sc_dev.dv_xname), - DEBUG_XFERS); - - if (ed->sc_error) { - bp->b_error = ed->sc_error; - bp->b_flags |= B_ERROR; - } else { - /* Set resid, most commonly to zero. */ - bp->b_resid = ed->sc_status_block[SB_RESBLKCNT_IDX] * DEV_BSIZE; - } - - ed_bio_done(ed); - - /* If disk was busied, unbusy it now */ - if (ed->sc_flags & EDF_DK_BUSY) { - disk_unbusy(&ed->sc_dk, (bp->b_bcount - bp->b_resid)); - ed->sc_flags &= ~EDF_DK_BUSY; - } - -#if NRND > 0 - rnd_add_uint32(&ed->rnd_source, bp->b_blkno); -#endif - biodone(bp); -} - int edmcaread(dev, uio, flags) dev_t dev; @@ -543,13 +345,17 @@ edmcaopen(dev, flag, fmt, p) } } else { if ((wd->sc_flags & WDF_LOADED) == 0) { + int s; + wd->sc_flags |= WDF_LOADED; /* Load the physical device parameters. */ - ed_get_params(wd); + s = splbio(); + ed_get_params(wd, NULL); + splx(s); /* Load the partition info if not already loaded. */ - edgetdisklabel(wd); + edgetdisklabel(dev, wd); } } @@ -629,24 +435,24 @@ edmcaclose(dev, flag, fmt, p) } static void -edgetdefaultlabel(wd, lp) - struct ed_softc *wd; +edgetdefaultlabel(ed, lp) + struct ed_softc *ed; struct disklabel *lp; { WDCDEBUG_PRINT(("edgetdefaultlabel\n"), DEBUG_FUNCS); memset(lp, 0, sizeof(struct disklabel)); lp->d_secsize = DEV_BSIZE; - lp->d_ntracks = wd->heads; - lp->d_nsectors = wd->sectors; - lp->d_ncylinders = wd->cyl; + lp->d_ntracks = ed->heads; + lp->d_nsectors = ed->sectors; + lp->d_ncylinders = ed->cyl; lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; lp->d_type = DTYPE_ESDI; strncpy(lp->d_typename, "ESDI", 16); strncpy(lp->d_packname, "fictitious", 16); - lp->d_secperunit = wd->sc_capacity; + lp->d_secperunit = ed->sc_capacity; lp->d_rpm = 3600; lp->d_interleave = 1; lp->d_flags = 0; @@ -666,26 +472,21 @@ edgetdefaultlabel(wd, lp) * Fabricate a default disk label, and try to read the correct one. */ static void -edgetdisklabel(wd) - struct ed_softc *wd; +edgetdisklabel(dev, ed) + dev_t dev; + struct ed_softc *ed; { - struct disklabel *lp = wd->sc_dk.dk_label; + struct disklabel *lp = ed->sc_dk.dk_label; char *errstring; WDCDEBUG_PRINT(("edgetdisklabel\n"), DEBUG_FUNCS); - memset(wd->sc_dk.dk_cpulabel, 0, sizeof(struct cpu_disklabel)); - - edgetdefaultlabel(wd, lp); + memset(ed->sc_dk.dk_cpulabel, 0, sizeof(struct cpu_disklabel)); -#if 0 - wd->sc_badsect[0] = -1; + edgetdefaultlabel(ed, lp); - if (wd->drvp->state > RECAL) - wd->drvp->drive_flags |= DRIVE_RESET; -#endif - errstring = readdisklabel(MAKEDISKDEV(0, wd->sc_dev.dv_unit, RAW_PART), - edmcastrategy, lp, wd->sc_dk.dk_cpulabel); + errstring = readdisklabel( + EDLABELDEV(dev), edmcastrategy, lp, ed->sc_dk.dk_cpulabel); if (errstring) { /* * This probably happened because the drive's default @@ -697,22 +498,13 @@ edgetdisklabel(wd) if (wd->drvp->state > RECAL) wd->drvp->drive_flags |= DRIVE_RESET; #endif - errstring = readdisklabel(MAKEDISKDEV(0, wd->sc_dev.dv_unit, - RAW_PART), edmcastrategy, lp, wd->sc_dk.dk_cpulabel); + errstring = readdisklabel(EDLABELDEV(dev), + edmcastrategy, lp, ed->sc_dk.dk_cpulabel); } if (errstring) { - printf("%s: %s\n", wd->sc_dev.dv_xname, errstring); + printf("%s: %s\n", ed->sc_dev.dv_xname, errstring); return; } - -#if 0 - if (wd->drvp->state > RECAL) - wd->drvp->drive_flags |= DRIVE_RESET; -#endif -#ifdef HAS_BAD144_HANDLING - if ((lp->d_flags & D_BADSECT) != 0) - bad144intern(wd); -#endif } int @@ -723,123 +515,79 @@ edmcaioctl(dev, xfer, addr, flag, p) int flag; struct proc *p; { - struct ed_softc *wd = device_lookup(&ed_cd, DISKUNIT(dev)); + struct ed_softc *ed = device_lookup(&ed_cd, DISKUNIT(dev)); int error; -#ifdef __HAVE_OLD_DISKLABEL - struct disklabel newlabel; -#endif WDCDEBUG_PRINT(("edioctl\n"), DEBUG_FUNCS); - if ((wd->sc_flags & WDF_LOADED) == 0) + if ((ed->sc_flags & WDF_LOADED) == 0) return EIO; switch (xfer) { -#ifdef HAS_BAD144_HANDLING - case DIOCSBAD: - if ((flag & FWRITE) == 0) - return EBADF; - wd->sc_dk.dk_cpulabel->bad = *(struct dkbad *)addr; - wd->sc_dk.dk_label->d_flags |= D_BADSECT; - bad144intern(wd); - return 0; -#endif - case DIOCGDINFO: - *(struct disklabel *)addr = *(wd->sc_dk.dk_label); + *(struct disklabel *)addr = *(ed->sc_dk.dk_label); return 0; -#ifdef __HAVE_OLD_DISKLABEL - case ODIOCGDINFO: - newlabel = *(wd->sc_dk.dk_label); - if (newlabel.d_npartitions > OLDMAXPARTITIONS) - return ENOTTY; - memcpy(addr, &newlabel, sizeof (struct olddisklabel)); - return 0; -#endif case DIOCGPART: - ((struct partinfo *)addr)->disklab = wd->sc_dk.dk_label; + ((struct partinfo *)addr)->disklab = ed->sc_dk.dk_label; ((struct partinfo *)addr)->part = - &wd->sc_dk.dk_label->d_partitions[DISKPART(dev)]; + &ed->sc_dk.dk_label->d_partitions[DISKPART(dev)]; return 0; case DIOCWDINFO: case DIOCSDINFO: -#ifdef __HAVE_OLD_DISKLABEL - case ODIOCWDINFO: - case ODIOCSDINFO: -#endif { struct disklabel *lp; -#ifdef __HAVE_OLD_DISKLABEL - if (xfer == ODIOCSDINFO || xfer == ODIOCWDINFO) { - memset(&newlabel, 0, sizeof newlabel); - memcpy(&newlabel, addr, sizeof (struct olddisklabel)); - lp = &newlabel; - } else -#endif lp = (struct disklabel *)addr; if ((flag & FWRITE) == 0) return EBADF; - if ((error = ed_lock(wd)) != 0) + if ((error = ed_lock(ed)) != 0) return error; - wd->sc_flags |= WDF_LABELLING; + ed->sc_flags |= WDF_LABELLING; - error = setdisklabel(wd->sc_dk.dk_label, + error = setdisklabel(ed->sc_dk.dk_label, lp, /*wd->sc_dk.dk_openmask : */0, - wd->sc_dk.dk_cpulabel); + ed->sc_dk.dk_cpulabel); if (error == 0) { #if 0 if (wd->drvp->state > RECAL) wd->drvp->drive_flags |= DRIVE_RESET; #endif - if (xfer == DIOCWDINFO -#ifdef __HAVE_OLD_DISKLABEL - || xfer == ODIOCWDINFO -#endif - ) + if (xfer == DIOCWDINFO) error = writedisklabel(EDLABELDEV(dev), - edmcastrategy, wd->sc_dk.dk_label, - wd->sc_dk.dk_cpulabel); + edmcastrategy, ed->sc_dk.dk_label, + ed->sc_dk.dk_cpulabel); } - wd->sc_flags &= ~WDF_LABELLING; - ed_unlock(wd); - return error; + ed->sc_flags &= ~WDF_LABELLING; + ed_unlock(ed); + return (error); } case DIOCKLABEL: if (*(int *)addr) - wd->sc_flags |= WDF_KLABEL; + ed->sc_flags |= WDF_KLABEL; else - wd->sc_flags &= ~WDF_KLABEL; + ed->sc_flags &= ~WDF_KLABEL; return 0; case DIOCWLABEL: if ((flag & FWRITE) == 0) return EBADF; if (*(int *)addr) - wd->sc_flags |= WDF_WLABEL; + ed->sc_flags |= WDF_WLABEL; else - wd->sc_flags &= ~WDF_WLABEL; + ed->sc_flags &= ~WDF_WLABEL; return 0; case DIOCGDEFLABEL: - edgetdefaultlabel(wd, (struct disklabel *)addr); - return 0; -#ifdef __HAVE_OLD_DISKLABEL - case ODIOCGDEFLABEL: - edgetdefaultlabel(wd, &newlabel); - if (newlabel.d_npartitions > OLDMAXPARTITIONS) - return ENOTTY; - memcpy(addr, &newlabel, sizeof (struct olddisklabel)); + edgetdefaultlabel(ed, (struct disklabel *)addr); return 0; -#endif -#ifdef notyet +#if 0 case DIOCWFORMAT: if ((flag & FWRITE) == 0) return EBADF; @@ -876,18 +624,6 @@ edmcaioctl(dev, xfer, addr, flag, p) #endif } -#if 0 -#ifdef B_FORMAT -int -edmcaformat(struct buf *bp) -{ - - bp->b_flags |= B_FORMAT; - return edmcastrategy(bp); -} -#endif -#endif - int edmcasize(dev) dev_t dev; @@ -936,6 +672,7 @@ edmcadump(dev, blkno, va, size) struct disklabel *lp; /* disk's disklabel */ int part; int nblks; /* total number of sectors left to write */ + int error; /* Check if recursive dump; if so, punt. */ if (eddoingadump) @@ -976,16 +713,10 @@ edmcadump(dev, blkno, va, size) } while (nblks > 0) { - ed->sc_data = va; - ed->sc_rawblkno = blkno; - ed->sc_bcount = min(nblks, eddumpmulti) * lp->d_secsize; - ed->sc_read = 0; - - ed_bio(ed, 0, 1); - if (ed->sc_error) - return (ed->sc_error); - - ed_bio_done(ed); + error = edc_bio(ed->edc_softc, ed, va, blkno, + min(nblks, eddumpmulti) * lp->d_secsize, 0, 1); + if (error) + return (error); /* update block count */ nblks -= min(nblks, eddumpmulti); @@ -997,36 +728,10 @@ edmcadump(dev, blkno, va, size) return (0); } -#ifdef HAS_BAD144_HANDLING -/* - * Internalize the bad sector table. - */ -static void -bad144intern(wd) - struct ed_softc *wd; -{ - struct dkbad *bt = &wd->sc_dk.dk_cpulabel->bad; - struct disklabel *lp = wd->sc_dk.dk_label; - int i = 0; - - WDCDEBUG_PRINT(("bad144intern\n"), DEBUG_XFERS); - - for (; i < NBT_BAD; i++) { - if (bt->bt_bad[i].bt_cyl == 0xffff) - break; - wd->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 < NBT_BAD+1; i++) - wd->sc_badsect[i] = -1; -} -#endif - static int -ed_get_params(ed) +ed_get_params(ed, drv_flags) struct ed_softc *ed; + int *drv_flags; { u_int16_t cmd_args[2]; @@ -1036,17 +741,17 @@ ed_get_params(ed) cmd_args[0] = 14; /* Options: 00s110, s: 0=Physical 1=Pseudo */ cmd_args[1] = 0; if (edc_run_cmd(ed->edc_softc, CMD_GET_DEV_CONF, ed->sc_devno, - cmd_args, 2, 0, 1)) + cmd_args, 2, 1)) return (1); - ed->spares = ed->sc_status_block[1] >> 8; - ed->drv_flags = ed->sc_status_block[1] & 0x1f; - ed->rba = ed->sc_status_block[2] | - (ed->sc_status_block[3] << 16); + ed->spares = ed->sense_data[1] >> 8; + if (drv_flags) + *drv_flags = ed->sense_data[1] & 0x1f; + ed->rba = ed->sense_data[2] | (ed->sense_data[3] << 16); /* Instead of using: - ed->cyl = ed->sc_status_block[4]; - ed->heads = ed->sc_status_block[5] & 0xff; - ed->sectors = ed->sc_status_block[5] >> 8; + ed->cyl = ed->sense_data[4]; + ed->heads = ed->sense_data[5] & 0xff; + ed->sectors = ed->sense_data[5] >> 8; * we fabricate the numbers from RBA count, so that * number of sectors is 32 and heads 64. This seems * to be necessary for integrated ESDI controller. @@ -1058,72 +763,3 @@ ed_get_params(ed) return (0); } - -/* - * Our shutdown hook. We attempt to park disk's head only. - */ -void -ed_shutdown(arg) - void *arg; -{ -#if 0 - struct ed_softc *ed = arg; - u_int16_t cmd_args[2]; - - /* Issue Park Head command */ - cmd_args[0] = 6; /* Options: 000110 */ - cmd_args[1] = 0; - (void) edc_run_cmd(ed->edc_softc, CMD_PARK_HEAD, ed->sc_devno, - cmd_args, 2, 0); -#endif -} - -/* - * Main worker thread function. - */ -void -edworker(arg) - void *arg; -{ - struct ed_softc *ed = (struct ed_softc *) arg; - struct buf *bp; - int s; - - config_pending_decr(); - - for(;;) { - /* Wait until awakened */ - (void) tsleep(&ed->sc_q, PRIBIO, "edidle", 0); - - if ((ed->sc_flags & EDF_PROCESS_QUEUE) == 0) - panic("edworker: expecting process queue"); - ed->sc_flags &= ~EDF_PROCESS_QUEUE; - - for(;;) { - /* Is there a buf for us ? */ - simple_lock(&ed->sc_q_lock); - if ((bp = BUFQ_FIRST(&ed->sc_q)) == NULL) { - simple_unlock(&ed->sc_q_lock); - break; - } - BUFQ_REMOVE(&ed->sc_q, bp); - simple_unlock(&ed->sc_q_lock); - - /* Schedule i/o operation */ - ed->sc_error = 0; - s = splbio(); - __edstart(ed, bp); - - /* - * Wait until the command executes; edc_intr() wakes - * us up. - */ - if (ed->sc_error == 0) - (void)tsleep(&ed->edc_softc, PRIBIO, "edwrk",0); - - /* Handle i/o results */ - edmcadone(ed, bp); - splx(s); - } - } -} diff --git a/sys/dev/mca/edc_mca.c b/sys/dev/mca/edc_mca.c index c8f15f430b7..647600de49d 100644 --- a/sys/dev/mca/edc_mca.c +++ b/sys/dev/mca/edc_mca.c @@ -1,4 +1,4 @@ -/* $NetBSD: edc_mca.c,v 1.10 2001/11/13 07:46:25 lukem Exp $ */ +/* $NetBSD: edc_mca.c,v 1.11 2001/11/23 22:53:09 jdolecek Exp $ */ /* * Copyright (c) 2001 The NetBSD Foundation, Inc. @@ -41,19 +41,16 @@ * for MCA rev. 2.2 in hands, thanks to Scott Telford <st@epcc.ed.ac.uk>. * * TODO: - * - move the MCA DMA controller (edc_setup_dma()) goo to device driver - * independant location * - improve error recovery - * add any soft resets when anything gets stuck? - * - test with > 1 disk (this is supported by some controllers), eliminate - * any remaining devno=0 assumptions if there are any still + * Issue soft reset on error or timeout? + * - test with > 1 disk (this is supported by some controllers) * - test with > 1 ESDI controller in machine; shared interrupts * necessary for this to work should be supported - edc_intr() specifically * checks if the interrupt is for this controller */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: edc_mca.c,v 1.10 2001/11/13 07:46:25 lukem Exp $"); +__KERNEL_RCSID(0, "$NetBSD: edc_mca.c,v 1.11 2001/11/23 22:53:09 jdolecek Exp $"); #include "rnd.h" @@ -69,6 +66,7 @@ __KERNEL_RCSID(0, "$NetBSD: edc_mca.c,v 1.10 2001/11/13 07:46:25 lukem Exp $"); #include <sys/proc.h> #include <sys/vnode.h> #include <sys/kernel.h> +#include <sys/kthread.h> #if NRND > 0 #include <sys/rnd.h> #endif @@ -85,6 +83,7 @@ __KERNEL_RCSID(0, "$NetBSD: edc_mca.c,v 1.10 2001/11/13 07:46:25 lukem Exp $"); #include <dev/mca/edcvar.h> #define EDC_ATTN_MAXTRIES 10000 /* How many times check for unbusy */ +#define EDC_MAX_CMD_RES_LEN 8 struct edc_mca_softc { struct device sc_dev; @@ -92,19 +91,23 @@ struct edc_mca_softc { bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; + /* DMA related stuff */ bus_dma_tag_t sc_dmat; /* DMA tag as passed by parent */ - bus_space_handle_t sc_dmaextcmdh; - bus_space_handle_t sc_dmaexech; + bus_dmamap_t sc_dmamap_xfer; /* transfer dma map */ void *sc_ih; /* interrupt handle */ - int sc_drq; /* DRQ number */ - int sc_cmd_async; /* asynchronous cmd pending */ int sc_flags; #define DASD_QUIET 0x01 /* don't dump cmd error info */ + #define DASD_MAXDEVS 8 struct ed_softc *sc_ed[DASD_MAXDEVS]; - struct ed_softc sc_controller; + int sc_maxdevs; /* max number of disks attached to this + * controller */ + + /* I/O results variables */ + volatile int sc_error; + volatile int sc_resblk; /* residual block count */ }; int edc_mca_probe __P((struct device *, struct cfdata *, void *)); @@ -114,15 +117,13 @@ struct cfattach edc_mca_ca = { sizeof(struct edc_mca_softc), edc_mca_probe, edc_mca_attach }; -#define DMA_EXTCMD 0x18 -#define DMA_EXEC 0x1A - static int edc_intr __P((void *)); -static void edc_dump_status_block __P((struct edc_mca_softc *, int, int)); -static int edc_setup_dma __P((struct edc_mca_softc *, int, - bus_addr_t, bus_size_t)); +static void edc_dump_status_block __P((struct edc_mca_softc *, + u_int16_t *, int)); static int edc_do_attn __P((struct edc_mca_softc *, int, int, int)); -static int edc_cmd_wait __P((struct edc_mca_softc *, int, int, int)); +static int edc_cmd_wait __P((struct edc_mca_softc *, int, int)); +static void edcworker __P((void *)); +static void edc_spawn_worker __P((void *)); int edc_mca_probe(parent, match, aux) @@ -148,12 +149,11 @@ edc_mca_attach(parent, self, aux) { struct edc_mca_softc *sc = (void *) self; struct mca_attach_args *ma = aux; + struct ed_attach_args eda; int pos2, pos3, pos4; int irq, drq, iobase; const char *typestr; - struct ed_softc *ed; - struct ed_attach_args eda; - int devno, maxdevs; + int devno, error; pos2 = mca_conf_read(ma->ma_mc, ma->ma_slot, 2); pos3 = mca_conf_read(ma->ma_mc, ma->ma_slot, 3); @@ -231,7 +231,6 @@ edc_mca_attach(parent, self, aux) printf("DMA pacing control disabled\n"); sc->sc_iot = ma->ma_iot; - sc->sc_drq = drq; if (bus_space_map(sc->sc_iot, iobase, ESDIC_REG_NPORTS, 0, &sc->sc_ioh)) { @@ -240,19 +239,6 @@ edc_mca_attach(parent, self, aux) return; } - if (bus_space_map(sc->sc_iot, DMA_EXTCMD, 1, 0, &sc->sc_dmaextcmdh)) { - printf("%s: couldn't map registers\n", - sc->sc_dev.dv_xname); - return; - } - if (bus_space_map(sc->sc_iot, DMA_EXEC, 1, 0, &sc->sc_dmaexech)) { - printf("%s: couldn't map registers\n", - sc->sc_dev.dv_xname); - return; - } - - sc->sc_dmat = ma->ma_dmat; - sc->sc_ih = mca_intr_establish(ma->ma_mc, irq, IPL_BIO, edc_intr, sc); if (sc->sc_ih == NULL) { printf("%s: couldn't establish interrupt handler\n", @@ -260,22 +246,23 @@ edc_mca_attach(parent, self, aux) return; } + /* Create a MCA DMA map, used for data transfer */ + sc->sc_dmat = ma->ma_dmat; + if ((error = mca_dmamap_create(sc->sc_dmat, MAXPHYS, + BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &sc->sc_dmamap_xfer, drq)) != 0){ + printf("%s: couldn't create DMA map - error %d\n", + sc->sc_dev.dv_xname, error); + return; + } + /* * Integrated ESDI controller supports only one disk, other * controllers support two disks. */ if (ma->ma_id == MCA_PRODUCT_IBM_ESDIC_IG) - maxdevs = 1; + sc->sc_maxdevs = 1; else - maxdevs = 2; - - /* - * Initialize the controller ed softc. We could do without this, - * but absence of checks for controller devno simplifies code logic - * somewhat. - */ - sc->sc_ed[DASD_DEVNO_CONTROLLER] = &sc->sc_controller; - strcpy(sc->sc_controller.sc_dev.dv_xname, sc->sc_dev.dv_xname);/*safe*/ + sc->sc_maxdevs = 2; /* * Reset controller and attach individual disks. ed attach routine @@ -306,53 +293,53 @@ edc_mca_attach(parent, self, aux) delay(100); } - /* - * Get dummy ed_softc to be used during probe. Once a disk is - * found, ed_mca_attach() calls edc_add_disk() to insert the - * right pointer into sc->sc_ed[] array. - */ - MALLOC(ed, struct ed_softc *, sizeof(struct ed_softc), - M_TEMP, M_WAITOK); - - /* be quiet duting probes */ + /* be quiet during probes */ sc->sc_flags |= DASD_QUIET; /* check for attached disks */ - for(devno=0; devno < maxdevs; devno++) { - eda.sc_devno = devno; - eda.sc_dmat = sc->sc_dmat; - sc->sc_ed[devno] = ed; - (void *) config_found_sm(self, &eda, NULL, NULL); + for(devno=0; devno < sc->sc_maxdevs; devno++) { + eda.edc_drive = devno; + sc->sc_ed[devno] = + (void *) config_found_sm(self, &eda, NULL, NULL); + + /* If initialization did not succeed, NULL the pointer. */ + if (sc->sc_ed[devno] + && (sc->sc_ed[devno]->sc_flags & EDF_INIT) == 0) + sc->sc_ed[devno] = NULL; } /* enable full error dumps again */ sc->sc_flags &= ~DASD_QUIET; - /* cleanup */ - FREE(ed, M_TEMP); - /* * Check if there are any disks attached. If not, disestablish * the interrupt. */ - for(devno=0; devno < maxdevs; devno++) { - if (sc->sc_ed[devno] && (sc->sc_ed[devno]->sc_flags & EDF_INIT)) + for(devno=0; devno < sc->sc_maxdevs; devno++) { + if (sc->sc_ed[devno]) break; } - if (devno == maxdevs) { + + if (devno == sc->sc_maxdevs) { printf("%s: disabling controller (no drives attached)\n", sc->sc_dev.dv_xname); mca_intr_disestablish(ma->ma_mc, sc->sc_ih); + return; } + + /* + * Run the worker thread. + */ + config_pending_incr(); + kthread_create(edc_spawn_worker, (void *) sc); } void -edc_add_disk(sc, ed, devno) +edc_add_disk(sc, ed) struct edc_mca_softc *sc; struct ed_softc *ed; - int devno; { - sc->sc_ed[devno] = ed; + sc->sc_ed[ed->sc_devno] = ed; } static int @@ -362,8 +349,8 @@ edc_intr(arg) struct edc_mca_softc *sc = arg; u_int8_t isr, intr_id; u_int16_t sifr; - int cmd=-1, devno, bioerror=0; - struct ed_softc *ed=NULL; + int cmd=-1, devno, error=0; + u_int16_t status_block[EDC_MAX_CMD_RES_LEN]; /* CMD status block */ /* * Check if the interrupt was for us. @@ -413,14 +400,14 @@ edc_intr(arg) cmd = sifr & SIFR_CMD_MASK; /* Read whole status block */ - ed = sc->sc_ed[devno]; - ed->sc_status_block[0] = sifr; + memset(status_block, 0, sizeof(status_block)); /* zero first */ + status_block[0] = sifr; for(i=1; i < len; i++) { while((bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_SIFR_FULL) == 0) delay(1); - ed->sc_status_block[i] = le16toh( + status_block[i] = le16toh( bus_space_read_2(sc->sc_iot, sc->sc_ioh, SIFR)); } } @@ -428,29 +415,27 @@ edc_intr(arg) switch (intr_id) { case ISR_DATA_TRANSFER_RDY: /* - * Ready to do DMA, setup DMA controller and kick DASD - * controller to do the transfer. + * Ready to do DMA. The DMA controller has already been + * setup, now just kick disk controller to do the transfer. */ - ed = sc->sc_ed[devno]; - if (!edc_setup_dma(sc, ed->sc_read, - ed->dmamap_xfer->dm_segs[0].ds_addr, - ed->dmamap_xfer->dm_segs[0].ds_len)) { - /* XXX bail out? */ - printf("%s: edc_setup_dma() failed\n", - ed->sc_dev.dv_xname); - bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, - BCR_INT_ENABLE); - } else { - /* OK, proceed with DMA */ - bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, - BCR_INT_ENABLE|BCR_DMA_ENABLE); - } + bus_space_write_1(sc->sc_iot, sc->sc_ioh, BCR, + BCR_INT_ENABLE|BCR_DMA_ENABLE); break; case ISR_COMPLETED: case ISR_COMPLETED_WITH_ECC: case ISR_COMPLETED_RETRIES: case ISR_COMPLETED_WARNING: - bioerror = 0; + error = 0; + + /* + * Copy device config data if appropriate. sc->sc_ed[] + * entry might be NULL during probe. + */ + if (cmd == CMD_GET_DEV_CONF && sc->sc_ed[devno]) { + memcpy(sc->sc_ed[devno]->sense_data, status_block, + sizeof(sc->sc_ed[devno]->sense_data)); + } + break; case ISR_RESET_COMPLETED: case ISR_ABORT_COMPLETED: @@ -458,9 +443,9 @@ edc_intr(arg) break; default: if ((sc->sc_flags & DASD_QUIET) == 0) - edc_dump_status_block(sc, devno, intr_id); + edc_dump_status_block(sc, status_block, intr_id); - bioerror = EIO; + error = EIO; break; } @@ -476,8 +461,9 @@ edc_intr(arg) /* If Read or Write Data, wakeup worker thread to finish it */ if (intr_id != ISR_DATA_TRANSFER_RDY && (cmd == CMD_READ_DATA || cmd == CMD_WRITE_DATA)) { - sc->sc_ed[devno]->sc_error = bioerror; - wakeup_one(&sc->sc_ed[devno]->edc_softc); + if ((sc->sc_error = error) == 0) + sc->sc_resblk = status_block[SB_RESBLKCNT_IDX]; + wakeup_one(sc); } return (1); @@ -541,22 +527,32 @@ edc_do_attn(sc, attn_type, devno, intr_id) * interval. */ static int -edc_cmd_wait(sc, devno, secs, poll) +edc_cmd_wait(sc, secs, poll) struct edc_mca_softc *sc; - int devno, secs, poll; + int secs, poll; { int val, delayed; + if (!poll) { + int error; + + /* Not polling, can sleep. Sleep until we are awakened, + * but maximum secs seconds. + */ + error = tsleep(sc, PRIBIO, "edcwcmd", secs * hz); + if (error) + goto err; + return (0); + } + + /* Poll the controller until command finishes */ delayed = 0; do { val = bus_space_read_1(sc->sc_iot,sc->sc_ioh, BSR); if ((val & BSR_CMD_INPROGRESS) == 0) break; - if (poll && (val & BSR_INTR)) - goto out; - - if (secs == 0) + if (val & BSR_INTR) break; delay(1); @@ -570,44 +566,33 @@ edc_cmd_wait(sc, devno, secs, poll) delayed = 0; secs--; } -#if 0 - if (delayed % 1000) - printf("looping ..."); -#endif - } while(1); + } while(secs > 0); if (secs == 0 && bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_CMD_INPROGRESS){ - printf("%s: timed out waiting for previous cmd to finish\n", - sc->sc_ed[devno]->sc_dev.dv_xname); + err: + printf("%s: timed out waiting for cmd to finish\n", + sc->sc_dev.dv_xname); return (EAGAIN); } - out: return (0); } int -edc_run_cmd(sc, cmd, devno, cmd_args, cmd_len, async, poll) +edc_run_cmd(sc, cmd, devno, cmd_args, cmd_len, poll) struct edc_mca_softc *sc; int cmd; int devno; u_int16_t cmd_args[]; - int cmd_len, async, poll; + int cmd_len, poll; { int i, error, tries; u_int16_t cmd0; - /* - * If there has been an asynchronous command executed, first wait for it - * to finish. - */ - if (sc->sc_cmd_async) { - /* Wait maximum 15s */ - if (edc_cmd_wait(sc, devno, 15, 0)) - return (EAGAIN); /* Busy */ - - sc->sc_cmd_async = 0; + if (bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_BUSY) { + printf("%s: device busy?\n", sc->sc_dev.dv_xname); + return (EAGAIN); } /* Do Attention Request for Command Request. */ @@ -635,7 +620,8 @@ edc_run_cmd(sc, cmd, devno, cmd_args, cmd_len, async, poll) /* * Write word of CMD to the CIFR. This sets "Command * Interface Register Full (CMD IN)" in BSR. Once the attachment - * detects it, it reads the word and clears CMD IN. + * detects it, it reads the word and clears CMD IN. This all should + * be quite fast, so don't bother with sleeps for !poll case. */ for(i=0; i < cmd_len; i++) { bus_space_write_2(sc->sc_iot, sc->sc_ioh, CIFR, @@ -647,22 +633,20 @@ edc_run_cmd(sc, cmd, devno, cmd_args, cmd_len, async, poll) * soon. Quirky hw ? */ tries = 0; - while(bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) & BSR_CIFR_FULL) + for(; (bus_space_read_1(sc->sc_iot, sc->sc_ioh, BSR) + & BSR_CIFR_FULL) && tries < 1000 ; tries++) delay(poll ? 1000 : 1); - } - /* - * Attachment is now executing the command. Unless we are executing - * command asynchronously, wait until it finishes. - */ - if (async) { - sc->sc_cmd_async = 1; - return (0); + if (tries == 10000) { + printf("%s: device too slow to accept command %d\n", + sc->sc_dev.dv_xname, cmd); + return (EAGAIN); + } } /* Wait for command to complete, but maximum 15 seconds. */ - if (edc_cmd_wait(sc, devno, 15, poll)) - return (EAGAIN); + if ((error = edc_cmd_wait(sc, 15, poll))) + return (error); /* If polling, call edc_intr() explicitly */ if (poll) { @@ -672,70 +656,21 @@ edc_run_cmd(sc, cmd, devno, cmd_args, cmd_len, async, poll) * If got attention id DATA TRANSFER READY, wait for * the transfer to finish. */ - if (sc->sc_ed[devno]->sc_error == 0 - && (cmd == CMD_READ_DATA || cmd == CMD_WRITE_DATA)) { - if (edc_cmd_wait(sc, devno, 15, 1)) - return (EAGAIN); + if ((cmd == CMD_READ_DATA || cmd == CMD_WRITE_DATA) + && sc->sc_error == 0) { + if ((error = edc_cmd_wait(sc, 15, poll))) + return (error); edc_intr(sc); } - if (edc_cmd_wait(sc, devno, 15, 0)) - return (EAGAIN); - } - - /* Check if the command completed successfully; if not, return error */ - switch(SB_GET_CMD_STATUS(sc->sc_ed[devno]->sc_status_block)) { - case ISR_COMPLETED: - case ISR_COMPLETED_WITH_ECC: - case ISR_COMPLETED_RETRIES: - case ISR_COMPLETED_WARNING: - return (0); - default: - return (EIO); + if ((error = edc_cmd_wait(sc, 15, poll))) + return (error); } -} - -static int -edc_setup_dma(sc, isread, phys, cnt) - struct edc_mca_softc *sc; - int isread; - bus_addr_t phys; - bus_size_t cnt; -{ - /* XXX magic constants, should be moved to device-independant location*/ - /* The exact sequence to setup MCA DMA controller is taken from Minix */ - - bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, - 0x90 + sc->sc_drq); - /* Disable access to dma channel */ - bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, - 0x20 + sc->sc_drq); - /* Clear the address byte pointer */ - bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, - (phys >> 0) & 0xff); /* address bits 0..7 */ - bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, - (phys >> 8) & 0xff); /* address bits 8..15 */ - bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, - (phys >> 16) & 0xff); /* address bits 16..23 */ - bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, - 0x40 + sc->sc_drq); - /* Clear the count byte pointer */ - bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, - ((cnt - 1) >> 0) & 0xff); /* count bits 0..7 */ - bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, - ((cnt - 1) >> 8) & 0xff); /* count bits 8..15 */ - bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, - 0x70 + sc->sc_drq); - /* Set the transfer mode */ - bus_space_write_1(sc->sc_iot, sc->sc_dmaexech, 0, - (isread) ? 0x4C : 0x44); - bus_space_write_1(sc->sc_iot, sc->sc_dmaextcmdh, 0, - 0xA0 + sc->sc_drq); - /* Enable access to dma channel */ - return (1); + return (sc->sc_error); } +#ifdef EDC_DEBUG static const char * const edc_commands[] = { "Invalid Command", "Read Data", @@ -832,36 +767,51 @@ static const char * const edc_dev_errors[] = { "No ID found on track (ID search)", /* 0x19 - 0xff reserved */ }; +#endif /* EDC_DEBUG */ static void -edc_dump_status_block(sc, devno, intr_id) +edc_dump_status_block(sc, status_block, intr_id) struct edc_mca_softc *sc; - int devno, intr_id; + u_int16_t *status_block; + int intr_id; { - struct ed_softc *ed = sc->sc_ed[devno]; +#ifdef EDC_DEBUG printf("%s: Command: %s, Status: %s\n", - ed->sc_dev.dv_xname, - edc_commands[ed->sc_status_block[0] & 0x1f], - edc_cmd_status[SB_GET_CMD_STATUS(ed->sc_status_block)] + sc->sc_dev.dv_xname, + edc_commands[status_block[0] & 0x1f], + edc_cmd_status[SB_GET_CMD_STATUS(status_block)] ); +#else + printf("%s: Command: %d, Status: %d\n", + sc->sc_dev.dv_xname, + status_block[0] & 0x1f, + SB_GET_CMD_STATUS(status_block)); +#endif printf("%s: # left blocks: %u, last processed RBA: %u\n", - ed->sc_dev.dv_xname, - ed->sc_status_block[SB_RESBLKCNT_IDX], - (ed->sc_status_block[5] << 16) | ed->sc_status_block[4]); + sc->sc_dev.dv_xname, + status_block[SB_RESBLKCNT_IDX], + (status_block[5] << 16) | status_block[4]); if (intr_id == ISR_COMPLETED_WARNING) { +#ifdef EDC_DEBUG printf("%s: Command Error Code: %s\n", - ed->sc_dev.dv_xname, - edc_cmd_error[ed->sc_status_block[1] & 0xff]); + sc->sc_dev.dv_xname, + edc_cmd_error[status_block[1] & 0xff]); +#else + printf("%s: Command Error Code: %d\n", + sc->sc_dev.dv_xname, + status_block[1] & 0xff); +#endif } if (intr_id == ISR_CMD_FAILED) { +#ifdef EDC_DEBUG char buf[100]; printf("%s: Device Error Code: %s\n", - ed->sc_dev.dv_xname, - edc_dev_errors[ed->sc_status_block[2] & 0xff]); - bitmask_snprintf((ed->sc_status_block[2] & 0xff00) >> 8, + sc->sc_dev.dv_xname, + edc_dev_errors[status_block[2] & 0xff]); + bitmask_snprintf((status_block[2] & 0xff00) >> 8, "\20" "\01SeekOrCmdComplete" "\02Track0Flag" @@ -873,6 +823,147 @@ edc_dump_status_block(sc, devno, intr_id) "\010Reserved0", buf, sizeof(buf)); printf("%s: Device Status: %s\n", - ed->sc_dev.dv_xname, buf); + sc->sc_dev.dv_xname, buf); +#else + printf("%s: Device Error Code: %d, Device Status: %d\n", + sc->sc_dev.dv_xname, + status_block[2] & 0xff, + (status_block[2] & 0xff00) >> 8); +#endif + } +} + +static void +edc_spawn_worker(arg) + void *arg; +{ + struct edc_mca_softc *sc = (struct edc_mca_softc *) arg; + int error; + struct proc *wrk; + + /* Now, everything is ready, start a kthread */ + if ((error = kthread_create1(edcworker, sc, &wrk, + "%s", sc->sc_dev.dv_xname))) { + printf("%s: cannot spawn worker thread: errno=%d\n", + sc->sc_dev.dv_xname, error); + panic("edc_spawn_worker"); } } + +/* + * Main worker thread function. + */ +void +edcworker(arg) + void *arg; +{ + struct edc_mca_softc *sc = (struct edc_mca_softc *) arg; + struct ed_softc *ed; + struct buf *bp; + int s, i, error; + + config_pending_decr(); + + s = splbio(); + + for(;;) { + /* Wait until awakened */ + (void) tsleep(sc, PRIBIO, "edcidle", 0); + + for(i=0; i<sc->sc_maxdevs; ) { + if ((ed = sc->sc_ed[i]) == NULL) { + i++; + continue; + } + + /* Is there a buf for us ? */ + simple_lock(&ed->sc_q_lock); + if ((bp = BUFQ_FIRST(&ed->sc_q)) == NULL) { + simple_unlock(&ed->sc_q_lock); + i++; + continue; + } + BUFQ_REMOVE(&ed->sc_q, bp); + simple_unlock(&ed->sc_q_lock); + + /* Instrumentation. */ + disk_busy(&ed->sc_dk); + + error = edc_bio(sc, ed, bp->b_data, bp->b_bcount, + bp->b_rawblkno, (bp->b_flags & B_READ), 0); + + if (error) { + bp->b_error = error; + bp->b_flags |= B_ERROR; + } else { + /* Set resid, most commonly to zero. */ + bp->b_resid = sc->sc_resblk * DEV_BSIZE; + } + + disk_unbusy(&ed->sc_dk, (bp->b_bcount - bp->b_resid)); +#if NRND > 0 + rnd_add_uint32(&ed->rnd_source, bp->b_blkno); +#endif + biodone(bp); + } + } + + splx(s); +} + +int +edc_bio(struct edc_mca_softc *sc, struct ed_softc *ed, void *data, + size_t bcount, daddr_t rawblkno, int isread, int poll) +{ + u_int16_t cmd_args[4]; + int error=0, fl; + u_int16_t track; + u_int16_t cyl; + u_int8_t head; + u_int8_t sector; + + mca_disk_busy(); + + /* set WAIT and R/W flag appropriately for the DMA transfer */ + fl = ((poll) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK) + | ((isread) ? BUS_DMA_READ : BUS_DMA_WRITE); + + /* Load the buffer for DMA transfer. */ + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap_xfer, data, + bcount, NULL, BUS_DMA_STREAMING|fl))) { + printf("%s: ed_bio: unable to load DMA buffer - error %d\n", + ed->sc_dev.dv_xname, error); + goto out; + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_xfer, 0, + bcount, (isread) ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + + track = rawblkno / ed->sectors; + head = track % ed->heads; + cyl = track / ed->heads; + sector = rawblkno % ed->sectors; + + /* Read or Write Data command */ + cmd_args[0] = 2; /* Options 0000010 */ + cmd_args[1] = bcount / DEV_BSIZE; + cmd_args[2] = ((cyl & 0x1f) << 11) | (head << 5) | sector; + cmd_args[3] = ((cyl & 0x3E0) >> 5); + error = edc_run_cmd(sc, + (isread) ? CMD_READ_DATA : CMD_WRITE_DATA, + ed->sc_devno, cmd_args, 4, poll); + + /* Sync the DMA memory */ + if (!error) { + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap_xfer, 0, bcount, + (isread)? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + } + + /* We are done, unload buffer from DMA map */ + bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap_xfer); + + out: + mca_disk_unbusy(); + + return (error); +} |
