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