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