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