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