Home | History | Annotate | Line # | Download | only in qbus
rl.c revision 1.43
      1 /*	$NetBSD: rl.c,v 1.43 2014/03/16 05:20:29 dholland Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2000 Ludd, University of Lule}, Sweden. All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. All advertising materials mentioning features or use of this software
     15  *    must display the following acknowledgement:
     16  *      This product includes software developed at Ludd, University of
     17  *      Lule}, Sweden and its contributors.
     18  * 4. The name of the author may not be used to endorse or promote products
     19  *    derived from this software without specific prior written permission
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * RL11/RLV11/RLV12 disk controller driver and
     35  * RL01/RL02 disk device driver.
     36  *
     37  * TODO:
     38  *	Handle disk errors more gracefully
     39  *	Do overlapping seeks on multiple drives
     40  *
     41  * Implementation comments:
     42  *
     43  */
     44 
     45 #include <sys/cdefs.h>
     46 __KERNEL_RCSID(0, "$NetBSD: rl.c,v 1.43 2014/03/16 05:20:29 dholland Exp $");
     47 
     48 #include <sys/param.h>
     49 #include <sys/device.h>
     50 #include <sys/systm.h>
     51 #include <sys/conf.h>
     52 #include <sys/disk.h>
     53 #include <sys/disklabel.h>
     54 #include <sys/buf.h>
     55 #include <sys/bufq.h>
     56 #include <sys/stat.h>
     57 #include <sys/dkio.h>
     58 #include <sys/fcntl.h>
     59 #include <sys/event.h>
     60 
     61 #include <ufs/ufs/dinode.h>
     62 #include <ufs/ffs/fs.h>
     63 
     64 #include <sys/bus.h>
     65 
     66 #include <dev/qbus/ubavar.h>
     67 #include <dev/qbus/rlreg.h>
     68 #include <dev/qbus/rlvar.h>
     69 
     70 #include "ioconf.h"
     71 #include "locators.h"
     72 
     73 static	int rlcmatch(device_t, cfdata_t, void *);
     74 static	void rlcattach(device_t, device_t, void *);
     75 static	int rlcprint(void *, const char *);
     76 static	void rlcintr(void *);
     77 static	int rlmatch(device_t, cfdata_t, void *);
     78 static	void rlattach(device_t, device_t, void *);
     79 static	void rlcstart(struct rlc_softc *, struct buf *);
     80 static	void waitcrdy(struct rlc_softc *);
     81 static	void rlcreset(device_t);
     82 
     83 CFATTACH_DECL_NEW(rlc, sizeof(struct rlc_softc),
     84     rlcmatch, rlcattach, NULL, NULL);
     85 
     86 CFATTACH_DECL_NEW(rl, sizeof(struct rl_softc),
     87     rlmatch, rlattach, NULL, NULL);
     88 
     89 static dev_type_open(rlopen);
     90 static dev_type_close(rlclose);
     91 static dev_type_read(rlread);
     92 static dev_type_write(rlwrite);
     93 static dev_type_ioctl(rlioctl);
     94 static dev_type_strategy(rlstrategy);
     95 static dev_type_dump(rldump);
     96 static dev_type_size(rlpsize);
     97 
     98 const struct bdevsw rl_bdevsw = {
     99 	.d_open = rlopen,
    100 	.d_close = rlclose,
    101 	.d_strategy = rlstrategy,
    102 	.d_ioctl = rlioctl,
    103 	.d_dump = rldump,
    104 	.d_psize = rlpsize,
    105 	.d_flag = D_DISK
    106 };
    107 
    108 const struct cdevsw rl_cdevsw = {
    109 	.d_open = rlopen,
    110 	.d_close = rlclose,
    111 	.d_read = rlread,
    112 	.d_write = rlwrite,
    113 	.d_ioctl = rlioctl,
    114 	.d_stop = nostop,
    115 	.d_tty = notty,
    116 	.d_poll = nopoll,
    117 	.d_mmap = nommap,
    118 	.d_kqfilter = nokqfilter,
    119 	.d_flag = D_DISK
    120 };
    121 
    122 #define	MAXRLXFER (RL_BPS * RL_SPT)
    123 
    124 #define	RL_WREG(reg, val) \
    125 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, (reg), (val))
    126 #define RL_RREG(reg) \
    127 	bus_space_read_2(sc->sc_iot, sc->sc_ioh, (reg))
    128 
    129 static const char * const rlstates[] = {
    130 	"drive not loaded",
    131 	"drive spinning up",
    132 	"drive brushes out",
    133 	"drive loading heads",
    134 	"drive seeking",
    135 	"drive ready",
    136 	"drive unloading heads",
    137 	"drive spun down",
    138 };
    139 
    140 static const struct dkdriver rldkdriver = {
    141 	rlstrategy, minphys
    142 };
    143 
    144 static const char *
    145 rlstate(struct rlc_softc *sc, int unit)
    146 {
    147 	int i = 0;
    148 
    149 	do {
    150 		RL_WREG(RL_DA, RLDA_GS);
    151 		RL_WREG(RL_CS, RLCS_GS|(unit << RLCS_USHFT));
    152 		waitcrdy(sc);
    153 	} while (((RL_RREG(RL_CS) & RLCS_ERR) != 0) && i++ < 10);
    154 	if (i == 10)
    155 		return NULL;
    156 	i = RL_RREG(RL_MP) & RLMP_STATUS;
    157 	return rlstates[i];
    158 }
    159 
    160 void
    161 waitcrdy(struct rlc_softc *sc)
    162 {
    163 	int i;
    164 
    165 	for (i = 0; i < 1000; i++) {
    166 		DELAY(10000);
    167 		if (RL_RREG(RL_CS) & RLCS_CRDY)
    168 			return;
    169 	}
    170 	aprint_error_dev(sc->sc_dev, "never got ready\n"); /* ?panic? */
    171 }
    172 
    173 int
    174 rlcprint(void *aux, const char *name)
    175 {
    176 	struct rlc_attach_args *ra = aux;
    177 
    178 	if (name)
    179 		aprint_normal("RL0%d at %s",
    180 		    ra->type & RLMP_DT ? '2' : '1', name);
    181 	aprint_normal(" drive %d", ra->hwid);
    182 	return UNCONF;
    183 }
    184 
    185 /*
    186  * Force the controller to interrupt.
    187  */
    188 int
    189 rlcmatch(device_t parent, cfdata_t cf, void *aux)
    190 {
    191 	struct uba_attach_args *ua = aux;
    192 	struct rlc_softc ssc, *sc = &ssc;
    193 	int i;
    194 
    195 	sc->sc_iot = ua->ua_iot;
    196 	sc->sc_ioh = ua->ua_ioh;
    197 	/* Force interrupt by issuing a "Get Status" command */
    198 	RL_WREG(RL_DA, RLDA_GS);
    199 	RL_WREG(RL_CS, RLCS_GS|RLCS_IE);
    200 
    201 	for (i = 0; i < 100; i++) {
    202 		DELAY(100000);
    203 		if (RL_RREG(RL_CS) & RLCS_CRDY)
    204 			return 1;
    205 	}
    206 	return 0;
    207 }
    208 
    209 void
    210 rlcattach(device_t parent, device_t self, void *aux)
    211 {
    212 	struct rlc_softc *sc = device_private(self);
    213 	struct uba_attach_args *ua = aux;
    214 	struct rlc_attach_args ra;
    215 	int i, error;
    216 
    217 	sc->sc_dev = self;
    218 	sc->sc_uh = device_private(parent);
    219 	sc->sc_iot = ua->ua_iot;
    220 	sc->sc_ioh = ua->ua_ioh;
    221 	sc->sc_dmat = ua->ua_dmat;
    222 	uba_intr_establish(ua->ua_icookie, ua->ua_cvec,
    223 		rlcintr, sc, &sc->sc_intrcnt);
    224 	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
    225 		device_xname(sc->sc_dev), "intr");
    226 	uba_reset_establish(rlcreset, self);
    227 
    228 	printf("\n");
    229 
    230 	/*
    231 	 * The RL11 can only have one transfer going at a time,
    232 	 * and max transfer size is one track, so only one dmamap
    233 	 * is needed.
    234 	 */
    235 	error = bus_dmamap_create(sc->sc_dmat, MAXRLXFER, 1, MAXRLXFER, 0,
    236 	    BUS_DMA_ALLOCNOW, &sc->sc_dmam);
    237 	if (error) {
    238 		aprint_error(": Failed to allocate DMA map, error %d\n", error);
    239 		return;
    240 	}
    241 	bufq_alloc(&sc->sc_q, "disksort", BUFQ_SORT_CYLINDER);
    242 	for (i = 0; i < RL_MAXDPC; i++) {
    243 		waitcrdy(sc);
    244 		RL_WREG(RL_DA, RLDA_GS|RLDA_RST);
    245 		RL_WREG(RL_CS, RLCS_GS|(i << RLCS_USHFT));
    246 		waitcrdy(sc);
    247 		ra.type = RL_RREG(RL_MP);
    248 		ra.hwid = i;
    249 		if ((RL_RREG(RL_CS) & RLCS_ERR) == 0)
    250 			config_found(sc->sc_dev, &ra, rlcprint);
    251 	}
    252 }
    253 
    254 int
    255 rlmatch(device_t parent, cfdata_t cf, void *aux)
    256 {
    257 	struct rlc_attach_args *ra = aux;
    258 
    259 	if (cf->cf_loc[RLCCF_DRIVE] != RLCCF_DRIVE_DEFAULT &&
    260 	    cf->cf_loc[RLCCF_DRIVE] != ra->hwid)
    261 		return 0;
    262 	return 1;
    263 }
    264 
    265 void
    266 rlattach(device_t parent, device_t self, void *aux)
    267 {
    268 	struct rl_softc *rc = device_private(self);
    269 	struct rlc_attach_args *ra = aux;
    270 	struct disklabel *dl;
    271 
    272 	rc->rc_dev = self;
    273 	rc->rc_rlc = device_private(parent);
    274 	rc->rc_hwid = ra->hwid;
    275 	disk_init(&rc->rc_disk, device_xname(rc->rc_dev), &rldkdriver);
    276 	disk_attach(&rc->rc_disk);
    277 	dl = rc->rc_disk.dk_label;
    278 	dl->d_npartitions = 3;
    279 	strcpy(dl->d_typename, "RL01");
    280 	if (ra->type & RLMP_DT)
    281 		dl->d_typename[3] = '2';
    282 	dl->d_secsize = DEV_BSIZE; /* XXX - wrong, but OK for now */
    283 	dl->d_nsectors = RL_SPT/2;
    284 	dl->d_ntracks = RL_SPD;
    285 	dl->d_ncylinders = ra->type & RLMP_DT ? RL_TPS02 : RL_TPS01;
    286 	dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
    287 	dl->d_secperunit = dl->d_ncylinders * dl->d_secpercyl;
    288 	dl->d_partitions[0].p_size = dl->d_partitions[2].p_size =
    289 	    dl->d_secperunit;
    290 	dl->d_partitions[0].p_offset = dl->d_partitions[2].p_offset = 0;
    291 	dl->d_interleave = dl->d_headswitch = 1;
    292 	dl->d_bbsize = BBSIZE;
    293 	dl->d_sbsize = SBLOCKSIZE;
    294 	dl->d_rpm = 2400;
    295 	dl->d_type = DTYPE_DEC;
    296 	printf(": %s, %s\n", dl->d_typename, rlstate(rc->rc_rlc, ra->hwid));
    297 
    298 	/*
    299 	 * XXX We should try to discovery wedges here, but
    300 	 * XXX that would mean loading up the pack and being
    301 	 * XXX able to do I/O.  Should use config_defer() here.
    302 	 */
    303 }
    304 
    305 int
    306 rlopen(dev_t dev, int flag, int fmt, struct lwp *l)
    307 {
    308 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
    309 	struct rlc_softc *sc;
    310 	int error, part, mask;
    311 	struct disklabel *dl;
    312 	const char *msg;
    313 
    314 	/*
    315 	 * Make sure this is a reasonable open request.
    316 	 */
    317 	if (rc == NULL)
    318 		return ENXIO;
    319 
    320 	sc = rc->rc_rlc;
    321 	part = DISKPART(dev);
    322 
    323 	mutex_enter(&rc->rc_disk.dk_openlock);
    324 
    325 	/*
    326 	 * If there are wedges, and this is not RAW_PART, then we
    327 	 * need to fail.
    328 	 */
    329 	if (rc->rc_disk.dk_nwedges != 0 && part != RAW_PART) {
    330 		error = EBUSY;
    331 		goto bad1;
    332 	}
    333 
    334 	/* Check that the disk actually is useable */
    335 	msg = rlstate(sc, rc->rc_hwid);
    336 	if (msg == NULL || msg == rlstates[RLMP_UNLOAD] ||
    337 	    msg == rlstates[RLMP_SPUNDOWN]) {
    338 		error = ENXIO;
    339 		goto bad1;
    340 	}
    341 	/*
    342 	 * If this is the first open; read in where on the disk we are.
    343 	 */
    344 	dl = rc->rc_disk.dk_label;
    345 	if (rc->rc_state == DK_CLOSED) {
    346 		u_int16_t mp;
    347 		int maj;
    348 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    349 		waitcrdy(sc);
    350 		mp = RL_RREG(RL_MP);
    351 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    352 		rc->rc_cyl = (mp >> 7) & 0777;
    353 		rc->rc_state = DK_OPEN;
    354 		/* Get disk label */
    355 		maj = cdevsw_lookup_major(&rl_cdevsw);
    356 		if ((msg = readdisklabel(MAKEDISKDEV(maj,
    357 		    device_unit(rc->rc_dev), RAW_PART), rlstrategy, dl, NULL)))
    358 			aprint_normal_dev(rc->rc_dev, "%s", msg);
    359 		aprint_normal_dev(rc->rc_dev, "size %d sectors\n",
    360 		    dl->d_secperunit);
    361 	}
    362 	if (part >= dl->d_npartitions) {
    363 		error = ENXIO;
    364 		goto bad1;
    365 	}
    366 
    367 	mask = 1 << part;
    368 	switch (fmt) {
    369 	case S_IFCHR:
    370 		rc->rc_disk.dk_copenmask |= mask;
    371 		break;
    372 	case S_IFBLK:
    373 		rc->rc_disk.dk_bopenmask |= mask;
    374 		break;
    375 	}
    376 	rc->rc_disk.dk_openmask |= mask;
    377 	error = 0;
    378  bad1:
    379 	mutex_exit(&rc->rc_disk.dk_openlock);
    380 	return (error);
    381 }
    382 
    383 int
    384 rlclose(dev_t dev, int flag, int fmt, struct lwp *l)
    385 {
    386 	int unit = DISKUNIT(dev);
    387 	struct rl_softc *rc = device_lookup_private(&rl_cd, unit);
    388 	int mask = (1 << DISKPART(dev));
    389 
    390 	mutex_enter(&rc->rc_disk.dk_openlock);
    391 
    392 	switch (fmt) {
    393 	case S_IFCHR:
    394 		rc->rc_disk.dk_copenmask &= ~mask;
    395 		break;
    396 	case S_IFBLK:
    397 		rc->rc_disk.dk_bopenmask &= ~mask;
    398 		break;
    399 	}
    400 	rc->rc_disk.dk_openmask =
    401 	    rc->rc_disk.dk_copenmask | rc->rc_disk.dk_bopenmask;
    402 
    403 	if (rc->rc_disk.dk_openmask == 0)
    404 		rc->rc_state = DK_CLOSED; /* May change pack */
    405 	mutex_exit(&rc->rc_disk.dk_openlock);
    406 	return 0;
    407 }
    408 
    409 void
    410 rlstrategy(struct buf *bp)
    411 {
    412 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(bp->b_dev));
    413 	struct disklabel *lp;
    414 	int s;
    415 
    416 	if (rc == NULL || rc->rc_state != DK_OPEN) /* How did we end up here at all? */
    417 		panic("rlstrategy: state impossible");
    418 
    419 	lp = rc->rc_disk.dk_label;
    420 	if (bounds_check_with_label(&rc->rc_disk, bp, 1) <= 0)
    421 		goto done;
    422 
    423 	if (bp->b_bcount == 0)
    424 		goto done;
    425 
    426 	bp->b_rawblkno =
    427 	    bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
    428 	bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
    429 
    430 	s = splbio();
    431 	bufq_put(rc->rc_rlc->sc_q, bp);
    432 	rlcstart(rc->rc_rlc, 0);
    433 	splx(s);
    434 	return;
    435 
    436 done:	biodone(bp);
    437 }
    438 
    439 int
    440 rlioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
    441 {
    442 	struct rl_softc *rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
    443 	struct disklabel *lp = rc->rc_disk.dk_label;
    444 	int err = 0;
    445 #ifdef __HAVE_OLD_DISKLABEL
    446 	struct disklabel newlabel;
    447 #endif
    448 
    449 	switch (cmd) {
    450 	case DIOCGDINFO:
    451 		memcpy(addr, lp, sizeof (struct disklabel));
    452 		break;
    453 
    454 #ifdef __HAVE_OLD_DISKLABEL
    455 	case ODIOCGDINFO:
    456 		newlabel = *lp;
    457 		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
    458 			return ENOTTY;
    459 		memcpy(addr, &newlabel, sizeof (struct olddisklabel));
    460 		break;
    461 #endif
    462 
    463 	case DIOCGPART:
    464 		((struct partinfo *)addr)->disklab = lp;
    465 		((struct partinfo *)addr)->part =
    466 		    &lp->d_partitions[DISKPART(dev)];
    467 		break;
    468 
    469 	case DIOCSDINFO:
    470 	case DIOCWDINFO:
    471 #ifdef __HAVE_OLD_DISKLABEL
    472 	case ODIOCWDINFO:
    473 	case ODIOCSDINFO:
    474 #endif
    475 	{
    476 		struct disklabel *tp;
    477 
    478 #ifdef __HAVE_OLD_DISKLABEL
    479 		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
    480 			memset(&newlabel, 0, sizeof newlabel);
    481 			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
    482 			tp = &newlabel;
    483 		} else
    484 #endif
    485 		tp = (struct disklabel *)addr;
    486 
    487 		if ((flag & FWRITE) == 0)
    488 			err = EBADF;
    489 		else {
    490 			mutex_enter(&rc->rc_disk.dk_openlock);
    491 			err = ((
    492 #ifdef __HAVE_OLD_DISKLABEL
    493 			       cmd == ODIOCSDINFO ||
    494 #endif
    495 			       cmd == DIOCSDINFO) ?
    496 			    setdisklabel(lp, tp, 0, 0) :
    497 			    writedisklabel(dev, rlstrategy, lp, 0));
    498 			mutex_exit(&rc->rc_disk.dk_openlock);
    499 		}
    500 		break;
    501 	}
    502 
    503 	case DIOCWLABEL:
    504 		if ((flag & FWRITE) == 0)
    505 			err = EBADF;
    506 		break;
    507 
    508 	case DIOCAWEDGE: {
    509 		struct dkwedge_info *dkw = (void *) addr;
    510 
    511 		if ((flag & FWRITE) == 0)
    512 			return (EBADF);
    513 
    514 		/* If the ioctl happens here, the parent is us. */
    515 		strcpy(dkw->dkw_parent, device_xname(rc->rc_dev));
    516 		return dkwedge_add(dkw);
    517 	}
    518 
    519 	case DIOCDWEDGE: {
    520 		struct dkwedge_info *dkw = (void *) addr;
    521 
    522 		if ((flag & FWRITE) == 0)
    523 			return (EBADF);
    524 
    525 		/* If the ioctl happens here, the parent is us. */
    526 		strcpy(dkw->dkw_parent, device_xname(rc->rc_dev));
    527 		return dkwedge_del(dkw);
    528 	}
    529 
    530 	case DIOCLWEDGES: {
    531 		struct dkwedge_list *dkwl = (void *) addr;
    532 
    533 		return dkwedge_list(&rc->rc_disk, dkwl, l);
    534 	}
    535 
    536 	default:
    537 		err = ENOTTY;
    538 		break;
    539 	}
    540 	return err;
    541 }
    542 
    543 int
    544 rlpsize(dev_t dev)
    545 {
    546 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
    547 	struct disklabel *dl;
    548 	int size;
    549 
    550 	if (rc == NULL)
    551 		return -1;
    552 	dl = rc->rc_disk.dk_label;
    553 	size = dl->d_partitions[DISKPART(dev)].p_size *
    554 	    (dl->d_secsize / DEV_BSIZE);
    555 	return size;
    556 }
    557 
    558 int
    559 rldump(dev_t dev, daddr_t blkno, void *va, size_t size)
    560 {
    561 	/* Not likely... */
    562 	return 0;
    563 }
    564 
    565 int
    566 rlread(dev_t dev, struct uio *uio, int ioflag)
    567 {
    568 	return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
    569 }
    570 
    571 int
    572 rlwrite(dev_t dev, struct uio *uio, int ioflag)
    573 {
    574 	return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
    575 }
    576 
    577 static const char * const rlerr[] = {
    578 	"no",
    579 	"operation incomplete",
    580 	"read data CRC",
    581 	"header CRC",
    582 	"data late",
    583 	"header not found",
    584 	"",
    585 	"",
    586 	"non-existent memory",
    587 	"memory parity error",
    588 	"",
    589 	"",
    590 	"",
    591 	"",
    592 	"",
    593 	"",
    594 };
    595 
    596 void
    597 rlcintr(void *arg)
    598 {
    599 	struct rlc_softc *sc = arg;
    600 	struct buf *bp;
    601 	u_int16_t cs;
    602 
    603 	bp = sc->sc_active;
    604 	if (bp == 0) {
    605 		aprint_error_dev(sc->sc_dev, "strange interrupt\n");
    606 		return;
    607 	}
    608 	bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
    609 	sc->sc_active = 0;
    610 	cs = RL_RREG(RL_CS);
    611 	if (cs & RLCS_ERR) {
    612 		int error = (cs & RLCS_ERRMSK) >> 10;
    613 
    614 		aprint_error_dev(sc->sc_dev, "%s\n", rlerr[error]);
    615 		bp->b_error = EIO;
    616 		bp->b_resid = bp->b_bcount;
    617 		sc->sc_bytecnt = 0;
    618 	}
    619 	if (sc->sc_bytecnt == 0) /* Finished transfer */
    620 		biodone(bp);
    621 	rlcstart(sc, sc->sc_bytecnt ? bp : 0);
    622 }
    623 
    624 /*
    625  * Start routine. First position the disk to the given position,
    626  * then start reading/writing. An optimization would be to be able
    627  * to handle overlapping seeks between disks.
    628  */
    629 void
    630 rlcstart(struct rlc_softc *sc, struct buf *ob)
    631 {
    632 	struct disklabel *lp;
    633 	struct rl_softc *rc;
    634 	struct buf *bp;
    635 	int bn, cn, sn, tn, blks, err;
    636 
    637 	if (sc->sc_active)
    638 		return;	/* Already doing something */
    639 
    640 	if (ob == 0) {
    641 		bp = bufq_get(sc->sc_q);
    642 		if (bp == NULL)
    643 			return;	/* Nothing to do */
    644 		sc->sc_bufaddr = bp->b_data;
    645 		sc->sc_diskblk = bp->b_rawblkno;
    646 		sc->sc_bytecnt = bp->b_bcount;
    647 		bp->b_resid = 0;
    648 	} else
    649 		bp = ob;
    650 	sc->sc_active = bp;
    651 
    652 	rc = device_lookup_private(&rl_cd, DISKUNIT(bp->b_dev));
    653 	bn = sc->sc_diskblk;
    654 	lp = rc->rc_disk.dk_label;
    655 	if (bn) {
    656 		cn = bn / lp->d_secpercyl;
    657 		sn = bn % lp->d_secpercyl;
    658 		tn = sn / lp->d_nsectors;
    659 		sn = sn % lp->d_nsectors;
    660 	} else
    661 		cn = sn = tn = 0;
    662 
    663 	/*
    664 	 * Check if we have to position disk first.
    665 	 */
    666 	if (rc->rc_cyl != cn || rc->rc_head != tn) {
    667 		u_int16_t da = RLDA_SEEK;
    668 		if (cn > rc->rc_cyl)
    669 			da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
    670 		else
    671 			da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
    672 		if (tn)
    673 			da |= RLDA_HSSEEK;
    674 		waitcrdy(sc);
    675 		RL_WREG(RL_DA, da);
    676 		RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
    677 		waitcrdy(sc);
    678 		rc->rc_cyl = cn;
    679 		rc->rc_head = tn;
    680 	}
    681 	RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
    682 	blks = sc->sc_bytecnt/DEV_BSIZE;
    683 
    684 	if (sn + blks > RL_SPT/2)
    685 		blks = RL_SPT/2 - sn;
    686 	RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
    687 	err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
    688 	    (blks*DEV_BSIZE), (bp->b_flags & B_PHYS ? bp->b_proc : 0),
    689 	    BUS_DMA_NOWAIT);
    690 	if (err)
    691 		panic("%s: bus_dmamap_load failed: %d",
    692 		    device_xname(sc->sc_dev), err);
    693 	RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
    694 
    695 	/* Count up vars */
    696 	sc->sc_bufaddr = (char *)sc->sc_bufaddr + (blks*DEV_BSIZE);
    697 	sc->sc_diskblk += blks;
    698 	sc->sc_bytecnt -= (blks*DEV_BSIZE);
    699 
    700 	if (bp->b_flags & B_READ)
    701 		RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
    702 	else
    703 		RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
    704 }
    705 
    706 /*
    707  * Called once per controller when an ubareset occurs.
    708  * Retracts all disks and restarts active transfers.
    709  */
    710 void
    711 rlcreset(device_t dev)
    712 {
    713 	struct rlc_softc *sc = device_private(dev);
    714 	struct rl_softc *rc;
    715 	int i;
    716 	u_int16_t mp;
    717 
    718 	for (i = 0; i < rl_cd.cd_ndevs; i++) {
    719 		if ((rc = device_lookup_private(&rl_cd, i)) == NULL)
    720 			continue;
    721 		if (rc->rc_state != DK_OPEN)
    722 			continue;
    723 		if (rc->rc_rlc != sc)
    724 			continue;
    725 
    726 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    727 		waitcrdy(sc);
    728 		mp = RL_RREG(RL_MP);
    729 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    730 		rc->rc_cyl = (mp >> 7) & 0777;
    731 	}
    732 	if (sc->sc_active == 0)
    733 		return;
    734 
    735 	bufq_put(sc->sc_q, sc->sc_active);
    736 	sc->sc_active = 0;
    737 	rlcstart(sc, 0);
    738 }
    739