Home | History | Annotate | Line # | Download | only in qbus
rl.c revision 1.44
      1 /*	$NetBSD: rl.c,v 1.44 2014/07/25 08:02:20 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.44 2014/07/25 08:02:20 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_discard = nodiscard,
    106 	.d_flag = D_DISK
    107 };
    108 
    109 const struct cdevsw rl_cdevsw = {
    110 	.d_open = rlopen,
    111 	.d_close = rlclose,
    112 	.d_read = rlread,
    113 	.d_write = rlwrite,
    114 	.d_ioctl = rlioctl,
    115 	.d_stop = nostop,
    116 	.d_tty = notty,
    117 	.d_poll = nopoll,
    118 	.d_mmap = nommap,
    119 	.d_kqfilter = nokqfilter,
    120 	.d_flag = D_DISK
    121 };
    122 
    123 #define	MAXRLXFER (RL_BPS * RL_SPT)
    124 
    125 #define	RL_WREG(reg, val) \
    126 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, (reg), (val))
    127 #define RL_RREG(reg) \
    128 	bus_space_read_2(sc->sc_iot, sc->sc_ioh, (reg))
    129 
    130 static const char * const rlstates[] = {
    131 	"drive not loaded",
    132 	"drive spinning up",
    133 	"drive brushes out",
    134 	"drive loading heads",
    135 	"drive seeking",
    136 	"drive ready",
    137 	"drive unloading heads",
    138 	"drive spun down",
    139 };
    140 
    141 static const struct dkdriver rldkdriver = {
    142 	rlstrategy, minphys
    143 };
    144 
    145 static const char *
    146 rlstate(struct rlc_softc *sc, int unit)
    147 {
    148 	int i = 0;
    149 
    150 	do {
    151 		RL_WREG(RL_DA, RLDA_GS);
    152 		RL_WREG(RL_CS, RLCS_GS|(unit << RLCS_USHFT));
    153 		waitcrdy(sc);
    154 	} while (((RL_RREG(RL_CS) & RLCS_ERR) != 0) && i++ < 10);
    155 	if (i == 10)
    156 		return NULL;
    157 	i = RL_RREG(RL_MP) & RLMP_STATUS;
    158 	return rlstates[i];
    159 }
    160 
    161 void
    162 waitcrdy(struct rlc_softc *sc)
    163 {
    164 	int i;
    165 
    166 	for (i = 0; i < 1000; i++) {
    167 		DELAY(10000);
    168 		if (RL_RREG(RL_CS) & RLCS_CRDY)
    169 			return;
    170 	}
    171 	aprint_error_dev(sc->sc_dev, "never got ready\n"); /* ?panic? */
    172 }
    173 
    174 int
    175 rlcprint(void *aux, const char *name)
    176 {
    177 	struct rlc_attach_args *ra = aux;
    178 
    179 	if (name)
    180 		aprint_normal("RL0%d at %s",
    181 		    ra->type & RLMP_DT ? '2' : '1', name);
    182 	aprint_normal(" drive %d", ra->hwid);
    183 	return UNCONF;
    184 }
    185 
    186 /*
    187  * Force the controller to interrupt.
    188  */
    189 int
    190 rlcmatch(device_t parent, cfdata_t cf, void *aux)
    191 {
    192 	struct uba_attach_args *ua = aux;
    193 	struct rlc_softc ssc, *sc = &ssc;
    194 	int i;
    195 
    196 	sc->sc_iot = ua->ua_iot;
    197 	sc->sc_ioh = ua->ua_ioh;
    198 	/* Force interrupt by issuing a "Get Status" command */
    199 	RL_WREG(RL_DA, RLDA_GS);
    200 	RL_WREG(RL_CS, RLCS_GS|RLCS_IE);
    201 
    202 	for (i = 0; i < 100; i++) {
    203 		DELAY(100000);
    204 		if (RL_RREG(RL_CS) & RLCS_CRDY)
    205 			return 1;
    206 	}
    207 	return 0;
    208 }
    209 
    210 void
    211 rlcattach(device_t parent, device_t self, void *aux)
    212 {
    213 	struct rlc_softc *sc = device_private(self);
    214 	struct uba_attach_args *ua = aux;
    215 	struct rlc_attach_args ra;
    216 	int i, error;
    217 
    218 	sc->sc_dev = self;
    219 	sc->sc_uh = device_private(parent);
    220 	sc->sc_iot = ua->ua_iot;
    221 	sc->sc_ioh = ua->ua_ioh;
    222 	sc->sc_dmat = ua->ua_dmat;
    223 	uba_intr_establish(ua->ua_icookie, ua->ua_cvec,
    224 		rlcintr, sc, &sc->sc_intrcnt);
    225 	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
    226 		device_xname(sc->sc_dev), "intr");
    227 	uba_reset_establish(rlcreset, self);
    228 
    229 	printf("\n");
    230 
    231 	/*
    232 	 * The RL11 can only have one transfer going at a time,
    233 	 * and max transfer size is one track, so only one dmamap
    234 	 * is needed.
    235 	 */
    236 	error = bus_dmamap_create(sc->sc_dmat, MAXRLXFER, 1, MAXRLXFER, 0,
    237 	    BUS_DMA_ALLOCNOW, &sc->sc_dmam);
    238 	if (error) {
    239 		aprint_error(": Failed to allocate DMA map, error %d\n", error);
    240 		return;
    241 	}
    242 	bufq_alloc(&sc->sc_q, "disksort", BUFQ_SORT_CYLINDER);
    243 	for (i = 0; i < RL_MAXDPC; i++) {
    244 		waitcrdy(sc);
    245 		RL_WREG(RL_DA, RLDA_GS|RLDA_RST);
    246 		RL_WREG(RL_CS, RLCS_GS|(i << RLCS_USHFT));
    247 		waitcrdy(sc);
    248 		ra.type = RL_RREG(RL_MP);
    249 		ra.hwid = i;
    250 		if ((RL_RREG(RL_CS) & RLCS_ERR) == 0)
    251 			config_found(sc->sc_dev, &ra, rlcprint);
    252 	}
    253 }
    254 
    255 int
    256 rlmatch(device_t parent, cfdata_t cf, void *aux)
    257 {
    258 	struct rlc_attach_args *ra = aux;
    259 
    260 	if (cf->cf_loc[RLCCF_DRIVE] != RLCCF_DRIVE_DEFAULT &&
    261 	    cf->cf_loc[RLCCF_DRIVE] != ra->hwid)
    262 		return 0;
    263 	return 1;
    264 }
    265 
    266 void
    267 rlattach(device_t parent, device_t self, void *aux)
    268 {
    269 	struct rl_softc *rc = device_private(self);
    270 	struct rlc_attach_args *ra = aux;
    271 	struct disklabel *dl;
    272 
    273 	rc->rc_dev = self;
    274 	rc->rc_rlc = device_private(parent);
    275 	rc->rc_hwid = ra->hwid;
    276 	disk_init(&rc->rc_disk, device_xname(rc->rc_dev), &rldkdriver);
    277 	disk_attach(&rc->rc_disk);
    278 	dl = rc->rc_disk.dk_label;
    279 	dl->d_npartitions = 3;
    280 	strcpy(dl->d_typename, "RL01");
    281 	if (ra->type & RLMP_DT)
    282 		dl->d_typename[3] = '2';
    283 	dl->d_secsize = DEV_BSIZE; /* XXX - wrong, but OK for now */
    284 	dl->d_nsectors = RL_SPT/2;
    285 	dl->d_ntracks = RL_SPD;
    286 	dl->d_ncylinders = ra->type & RLMP_DT ? RL_TPS02 : RL_TPS01;
    287 	dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
    288 	dl->d_secperunit = dl->d_ncylinders * dl->d_secpercyl;
    289 	dl->d_partitions[0].p_size = dl->d_partitions[2].p_size =
    290 	    dl->d_secperunit;
    291 	dl->d_partitions[0].p_offset = dl->d_partitions[2].p_offset = 0;
    292 	dl->d_interleave = dl->d_headswitch = 1;
    293 	dl->d_bbsize = BBSIZE;
    294 	dl->d_sbsize = SBLOCKSIZE;
    295 	dl->d_rpm = 2400;
    296 	dl->d_type = DTYPE_DEC;
    297 	printf(": %s, %s\n", dl->d_typename, rlstate(rc->rc_rlc, ra->hwid));
    298 
    299 	/*
    300 	 * XXX We should try to discovery wedges here, but
    301 	 * XXX that would mean loading up the pack and being
    302 	 * XXX able to do I/O.  Should use config_defer() here.
    303 	 */
    304 }
    305 
    306 int
    307 rlopen(dev_t dev, int flag, int fmt, struct lwp *l)
    308 {
    309 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
    310 	struct rlc_softc *sc;
    311 	int error, part, mask;
    312 	struct disklabel *dl;
    313 	const char *msg;
    314 
    315 	/*
    316 	 * Make sure this is a reasonable open request.
    317 	 */
    318 	if (rc == NULL)
    319 		return ENXIO;
    320 
    321 	sc = rc->rc_rlc;
    322 	part = DISKPART(dev);
    323 
    324 	mutex_enter(&rc->rc_disk.dk_openlock);
    325 
    326 	/*
    327 	 * If there are wedges, and this is not RAW_PART, then we
    328 	 * need to fail.
    329 	 */
    330 	if (rc->rc_disk.dk_nwedges != 0 && part != RAW_PART) {
    331 		error = EBUSY;
    332 		goto bad1;
    333 	}
    334 
    335 	/* Check that the disk actually is useable */
    336 	msg = rlstate(sc, rc->rc_hwid);
    337 	if (msg == NULL || msg == rlstates[RLMP_UNLOAD] ||
    338 	    msg == rlstates[RLMP_SPUNDOWN]) {
    339 		error = ENXIO;
    340 		goto bad1;
    341 	}
    342 	/*
    343 	 * If this is the first open; read in where on the disk we are.
    344 	 */
    345 	dl = rc->rc_disk.dk_label;
    346 	if (rc->rc_state == DK_CLOSED) {
    347 		u_int16_t mp;
    348 		int maj;
    349 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    350 		waitcrdy(sc);
    351 		mp = RL_RREG(RL_MP);
    352 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    353 		rc->rc_cyl = (mp >> 7) & 0777;
    354 		rc->rc_state = DK_OPEN;
    355 		/* Get disk label */
    356 		maj = cdevsw_lookup_major(&rl_cdevsw);
    357 		if ((msg = readdisklabel(MAKEDISKDEV(maj,
    358 		    device_unit(rc->rc_dev), RAW_PART), rlstrategy, dl, NULL)))
    359 			aprint_normal_dev(rc->rc_dev, "%s", msg);
    360 		aprint_normal_dev(rc->rc_dev, "size %d sectors\n",
    361 		    dl->d_secperunit);
    362 	}
    363 	if (part >= dl->d_npartitions) {
    364 		error = ENXIO;
    365 		goto bad1;
    366 	}
    367 
    368 	mask = 1 << part;
    369 	switch (fmt) {
    370 	case S_IFCHR:
    371 		rc->rc_disk.dk_copenmask |= mask;
    372 		break;
    373 	case S_IFBLK:
    374 		rc->rc_disk.dk_bopenmask |= mask;
    375 		break;
    376 	}
    377 	rc->rc_disk.dk_openmask |= mask;
    378 	error = 0;
    379  bad1:
    380 	mutex_exit(&rc->rc_disk.dk_openlock);
    381 	return (error);
    382 }
    383 
    384 int
    385 rlclose(dev_t dev, int flag, int fmt, struct lwp *l)
    386 {
    387 	int unit = DISKUNIT(dev);
    388 	struct rl_softc *rc = device_lookup_private(&rl_cd, unit);
    389 	int mask = (1 << DISKPART(dev));
    390 
    391 	mutex_enter(&rc->rc_disk.dk_openlock);
    392 
    393 	switch (fmt) {
    394 	case S_IFCHR:
    395 		rc->rc_disk.dk_copenmask &= ~mask;
    396 		break;
    397 	case S_IFBLK:
    398 		rc->rc_disk.dk_bopenmask &= ~mask;
    399 		break;
    400 	}
    401 	rc->rc_disk.dk_openmask =
    402 	    rc->rc_disk.dk_copenmask | rc->rc_disk.dk_bopenmask;
    403 
    404 	if (rc->rc_disk.dk_openmask == 0)
    405 		rc->rc_state = DK_CLOSED; /* May change pack */
    406 	mutex_exit(&rc->rc_disk.dk_openlock);
    407 	return 0;
    408 }
    409 
    410 void
    411 rlstrategy(struct buf *bp)
    412 {
    413 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(bp->b_dev));
    414 	struct disklabel *lp;
    415 	int s;
    416 
    417 	if (rc == NULL || rc->rc_state != DK_OPEN) /* How did we end up here at all? */
    418 		panic("rlstrategy: state impossible");
    419 
    420 	lp = rc->rc_disk.dk_label;
    421 	if (bounds_check_with_label(&rc->rc_disk, bp, 1) <= 0)
    422 		goto done;
    423 
    424 	if (bp->b_bcount == 0)
    425 		goto done;
    426 
    427 	bp->b_rawblkno =
    428 	    bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
    429 	bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
    430 
    431 	s = splbio();
    432 	bufq_put(rc->rc_rlc->sc_q, bp);
    433 	rlcstart(rc->rc_rlc, 0);
    434 	splx(s);
    435 	return;
    436 
    437 done:	biodone(bp);
    438 }
    439 
    440 int
    441 rlioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
    442 {
    443 	struct rl_softc *rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
    444 	struct disklabel *lp = rc->rc_disk.dk_label;
    445 	int err = 0;
    446 #ifdef __HAVE_OLD_DISKLABEL
    447 	struct disklabel newlabel;
    448 #endif
    449 
    450 	switch (cmd) {
    451 	case DIOCGDINFO:
    452 		memcpy(addr, lp, sizeof (struct disklabel));
    453 		break;
    454 
    455 #ifdef __HAVE_OLD_DISKLABEL
    456 	case ODIOCGDINFO:
    457 		newlabel = *lp;
    458 		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
    459 			return ENOTTY;
    460 		memcpy(addr, &newlabel, sizeof (struct olddisklabel));
    461 		break;
    462 #endif
    463 
    464 	case DIOCGPART:
    465 		((struct partinfo *)addr)->disklab = lp;
    466 		((struct partinfo *)addr)->part =
    467 		    &lp->d_partitions[DISKPART(dev)];
    468 		break;
    469 
    470 	case DIOCSDINFO:
    471 	case DIOCWDINFO:
    472 #ifdef __HAVE_OLD_DISKLABEL
    473 	case ODIOCWDINFO:
    474 	case ODIOCSDINFO:
    475 #endif
    476 	{
    477 		struct disklabel *tp;
    478 
    479 #ifdef __HAVE_OLD_DISKLABEL
    480 		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
    481 			memset(&newlabel, 0, sizeof newlabel);
    482 			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
    483 			tp = &newlabel;
    484 		} else
    485 #endif
    486 		tp = (struct disklabel *)addr;
    487 
    488 		if ((flag & FWRITE) == 0)
    489 			err = EBADF;
    490 		else {
    491 			mutex_enter(&rc->rc_disk.dk_openlock);
    492 			err = ((
    493 #ifdef __HAVE_OLD_DISKLABEL
    494 			       cmd == ODIOCSDINFO ||
    495 #endif
    496 			       cmd == DIOCSDINFO) ?
    497 			    setdisklabel(lp, tp, 0, 0) :
    498 			    writedisklabel(dev, rlstrategy, lp, 0));
    499 			mutex_exit(&rc->rc_disk.dk_openlock);
    500 		}
    501 		break;
    502 	}
    503 
    504 	case DIOCWLABEL:
    505 		if ((flag & FWRITE) == 0)
    506 			err = EBADF;
    507 		break;
    508 
    509 	case DIOCAWEDGE: {
    510 		struct dkwedge_info *dkw = (void *) addr;
    511 
    512 		if ((flag & FWRITE) == 0)
    513 			return (EBADF);
    514 
    515 		/* If the ioctl happens here, the parent is us. */
    516 		strcpy(dkw->dkw_parent, device_xname(rc->rc_dev));
    517 		return dkwedge_add(dkw);
    518 	}
    519 
    520 	case DIOCDWEDGE: {
    521 		struct dkwedge_info *dkw = (void *) addr;
    522 
    523 		if ((flag & FWRITE) == 0)
    524 			return (EBADF);
    525 
    526 		/* If the ioctl happens here, the parent is us. */
    527 		strcpy(dkw->dkw_parent, device_xname(rc->rc_dev));
    528 		return dkwedge_del(dkw);
    529 	}
    530 
    531 	case DIOCLWEDGES: {
    532 		struct dkwedge_list *dkwl = (void *) addr;
    533 
    534 		return dkwedge_list(&rc->rc_disk, dkwl, l);
    535 	}
    536 
    537 	default:
    538 		err = ENOTTY;
    539 		break;
    540 	}
    541 	return err;
    542 }
    543 
    544 int
    545 rlpsize(dev_t dev)
    546 {
    547 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
    548 	struct disklabel *dl;
    549 	int size;
    550 
    551 	if (rc == NULL)
    552 		return -1;
    553 	dl = rc->rc_disk.dk_label;
    554 	size = dl->d_partitions[DISKPART(dev)].p_size *
    555 	    (dl->d_secsize / DEV_BSIZE);
    556 	return size;
    557 }
    558 
    559 int
    560 rldump(dev_t dev, daddr_t blkno, void *va, size_t size)
    561 {
    562 	/* Not likely... */
    563 	return 0;
    564 }
    565 
    566 int
    567 rlread(dev_t dev, struct uio *uio, int ioflag)
    568 {
    569 	return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
    570 }
    571 
    572 int
    573 rlwrite(dev_t dev, struct uio *uio, int ioflag)
    574 {
    575 	return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
    576 }
    577 
    578 static const char * const rlerr[] = {
    579 	"no",
    580 	"operation incomplete",
    581 	"read data CRC",
    582 	"header CRC",
    583 	"data late",
    584 	"header not found",
    585 	"",
    586 	"",
    587 	"non-existent memory",
    588 	"memory parity error",
    589 	"",
    590 	"",
    591 	"",
    592 	"",
    593 	"",
    594 	"",
    595 };
    596 
    597 void
    598 rlcintr(void *arg)
    599 {
    600 	struct rlc_softc *sc = arg;
    601 	struct buf *bp;
    602 	u_int16_t cs;
    603 
    604 	bp = sc->sc_active;
    605 	if (bp == 0) {
    606 		aprint_error_dev(sc->sc_dev, "strange interrupt\n");
    607 		return;
    608 	}
    609 	bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
    610 	sc->sc_active = 0;
    611 	cs = RL_RREG(RL_CS);
    612 	if (cs & RLCS_ERR) {
    613 		int error = (cs & RLCS_ERRMSK) >> 10;
    614 
    615 		aprint_error_dev(sc->sc_dev, "%s\n", rlerr[error]);
    616 		bp->b_error = EIO;
    617 		bp->b_resid = bp->b_bcount;
    618 		sc->sc_bytecnt = 0;
    619 	}
    620 	if (sc->sc_bytecnt == 0) /* Finished transfer */
    621 		biodone(bp);
    622 	rlcstart(sc, sc->sc_bytecnt ? bp : 0);
    623 }
    624 
    625 /*
    626  * Start routine. First position the disk to the given position,
    627  * then start reading/writing. An optimization would be to be able
    628  * to handle overlapping seeks between disks.
    629  */
    630 void
    631 rlcstart(struct rlc_softc *sc, struct buf *ob)
    632 {
    633 	struct disklabel *lp;
    634 	struct rl_softc *rc;
    635 	struct buf *bp;
    636 	int bn, cn, sn, tn, blks, err;
    637 
    638 	if (sc->sc_active)
    639 		return;	/* Already doing something */
    640 
    641 	if (ob == 0) {
    642 		bp = bufq_get(sc->sc_q);
    643 		if (bp == NULL)
    644 			return;	/* Nothing to do */
    645 		sc->sc_bufaddr = bp->b_data;
    646 		sc->sc_diskblk = bp->b_rawblkno;
    647 		sc->sc_bytecnt = bp->b_bcount;
    648 		bp->b_resid = 0;
    649 	} else
    650 		bp = ob;
    651 	sc->sc_active = bp;
    652 
    653 	rc = device_lookup_private(&rl_cd, DISKUNIT(bp->b_dev));
    654 	bn = sc->sc_diskblk;
    655 	lp = rc->rc_disk.dk_label;
    656 	if (bn) {
    657 		cn = bn / lp->d_secpercyl;
    658 		sn = bn % lp->d_secpercyl;
    659 		tn = sn / lp->d_nsectors;
    660 		sn = sn % lp->d_nsectors;
    661 	} else
    662 		cn = sn = tn = 0;
    663 
    664 	/*
    665 	 * Check if we have to position disk first.
    666 	 */
    667 	if (rc->rc_cyl != cn || rc->rc_head != tn) {
    668 		u_int16_t da = RLDA_SEEK;
    669 		if (cn > rc->rc_cyl)
    670 			da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
    671 		else
    672 			da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
    673 		if (tn)
    674 			da |= RLDA_HSSEEK;
    675 		waitcrdy(sc);
    676 		RL_WREG(RL_DA, da);
    677 		RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
    678 		waitcrdy(sc);
    679 		rc->rc_cyl = cn;
    680 		rc->rc_head = tn;
    681 	}
    682 	RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
    683 	blks = sc->sc_bytecnt/DEV_BSIZE;
    684 
    685 	if (sn + blks > RL_SPT/2)
    686 		blks = RL_SPT/2 - sn;
    687 	RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
    688 	err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
    689 	    (blks*DEV_BSIZE), (bp->b_flags & B_PHYS ? bp->b_proc : 0),
    690 	    BUS_DMA_NOWAIT);
    691 	if (err)
    692 		panic("%s: bus_dmamap_load failed: %d",
    693 		    device_xname(sc->sc_dev), err);
    694 	RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
    695 
    696 	/* Count up vars */
    697 	sc->sc_bufaddr = (char *)sc->sc_bufaddr + (blks*DEV_BSIZE);
    698 	sc->sc_diskblk += blks;
    699 	sc->sc_bytecnt -= (blks*DEV_BSIZE);
    700 
    701 	if (bp->b_flags & B_READ)
    702 		RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
    703 	else
    704 		RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
    705 }
    706 
    707 /*
    708  * Called once per controller when an ubareset occurs.
    709  * Retracts all disks and restarts active transfers.
    710  */
    711 void
    712 rlcreset(device_t dev)
    713 {
    714 	struct rlc_softc *sc = device_private(dev);
    715 	struct rl_softc *rc;
    716 	int i;
    717 	u_int16_t mp;
    718 
    719 	for (i = 0; i < rl_cd.cd_ndevs; i++) {
    720 		if ((rc = device_lookup_private(&rl_cd, i)) == NULL)
    721 			continue;
    722 		if (rc->rc_state != DK_OPEN)
    723 			continue;
    724 		if (rc->rc_rlc != sc)
    725 			continue;
    726 
    727 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    728 		waitcrdy(sc);
    729 		mp = RL_RREG(RL_MP);
    730 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    731 		rc->rc_cyl = (mp >> 7) & 0777;
    732 	}
    733 	if (sc->sc_active == 0)
    734 		return;
    735 
    736 	bufq_put(sc->sc_q, sc->sc_active);
    737 	sc->sc_active = 0;
    738 	rlcstart(sc, 0);
    739 }
    740