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