xref: /netbsd-src/sys/dev/qbus/rl.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: rl.c,v 1.23 2004/10/28 07:07:41 yamt 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.23 2004/10/28 07:07:41 yamt 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 <machine/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(struct device *, struct cfdata *, void *);
74 static	void rlcattach(struct device *, struct device *, void *);
75 static	int rlcprint(void *, const char *);
76 static	void rlcintr(void *);
77 static	int rlmatch(struct device *, struct cfdata *, void *);
78 static	void rlattach(struct device *, struct device *, void *);
79 static	void rlcstart(struct rlc_softc *, struct buf *);
80 static	void waitcrdy(struct rlc_softc *);
81 static	void rlcreset(struct device *);
82 
83 CFATTACH_DECL(rlc, sizeof(struct rlc_softc),
84     rlcmatch, rlcattach, NULL, NULL);
85 
86 CFATTACH_DECL(rl, sizeof(struct rl_softc),
87     rlmatch, rlattach, NULL, NULL);
88 
89 dev_type_open(rlopen);
90 dev_type_close(rlclose);
91 dev_type_read(rlread);
92 dev_type_write(rlwrite);
93 dev_type_ioctl(rlioctl);
94 dev_type_strategy(rlstrategy);
95 dev_type_dump(rldump);
96 dev_type_size(rlsize);
97 
98 const struct bdevsw rl_bdevsw = {
99 	rlopen, rlclose, rlstrategy, rlioctl, rldump, rlsize, 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 char *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 struct dkdriver rldkdriver = {
126 	rlstrategy, minphys
127 };
128 
129 static 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 	printf("%s: never got ready\n", sc->sc_dev.dv_xname); /* ?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(struct device *parent, struct cfdata *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(struct device *parent, struct device *self, void *aux)
196 {
197 	struct rlc_softc *sc = (struct rlc_softc *)self;
198 	struct uba_attach_args *ua = aux;
199 	struct rlc_attach_args ra;
200 	int i, error;
201 
202 	sc->sc_iot = ua->ua_iot;
203 	sc->sc_ioh = ua->ua_ioh;
204 	sc->sc_dmat = ua->ua_dmat;
205 	uba_intr_establish(ua->ua_icookie, ua->ua_cvec,
206 		rlcintr, sc, &sc->sc_intrcnt);
207 	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
208 		sc->sc_dev.dv_xname, "intr");
209 	uba_reset_establish(rlcreset, self);
210 
211 	printf("\n");
212 
213 	/*
214 	 * The RL11 can only have one transfer going at a time,
215 	 * and max transfer size is one track, so only one dmamap
216 	 * is needed.
217 	 */
218 	error = bus_dmamap_create(sc->sc_dmat, MAXRLXFER, 1, MAXRLXFER, 0,
219 	    BUS_DMA_ALLOCNOW, &sc->sc_dmam);
220 	if (error) {
221 		printf(": Failed to allocate DMA map, error %d\n", error);
222 		return;
223 	}
224 	bufq_alloc(&sc->sc_q, BUFQ_DISKSORT|BUFQ_SORT_CYLINDER);
225 	for (i = 0; i < RL_MAXDPC; i++) {
226 		waitcrdy(sc);
227 		RL_WREG(RL_DA, RLDA_GS|RLDA_RST);
228 		RL_WREG(RL_CS, RLCS_GS|(i << RLCS_USHFT));
229 		waitcrdy(sc);
230 		ra.type = RL_RREG(RL_MP);
231 		ra.hwid = i;
232 		if ((RL_RREG(RL_CS) & RLCS_ERR) == 0)
233 			config_found(&sc->sc_dev, &ra, rlcprint);
234 	}
235 }
236 
237 int
238 rlmatch(struct device *parent, struct cfdata *cf, void *aux)
239 {
240 	struct rlc_attach_args *ra = aux;
241 
242 	if (cf->cf_loc[RLCCF_DRIVE] != RLCCF_DRIVE_DEFAULT &&
243 	    cf->cf_loc[RLCCF_DRIVE] != ra->hwid)
244 		return 0;
245 	return 1;
246 }
247 
248 void
249 rlattach(struct device *parent, struct device *self, void *aux)
250 {
251 	struct rl_softc *rc = (struct rl_softc *)self;
252 	struct rlc_attach_args *ra = aux;
253 	struct disklabel *dl;
254 
255 	rc->rc_hwid = ra->hwid;
256 	rc->rc_disk.dk_name = rc->rc_dev.dv_xname;
257 	rc->rc_disk.dk_driver = &rldkdriver;
258 	disk_attach(&rc->rc_disk);
259 	dl = rc->rc_disk.dk_label;
260 	dl->d_npartitions = 3;
261 	strcpy(dl->d_typename, "RL01");
262 	if (ra->type & RLMP_DT)
263 		dl->d_typename[3] = '2';
264 	dl->d_secsize = DEV_BSIZE; /* XXX - wrong, but OK for now */
265 	dl->d_nsectors = RL_SPT/2;
266 	dl->d_ntracks = RL_SPD;
267 	dl->d_ncylinders = ra->type & RLMP_DT ? RL_TPS02 : RL_TPS01;
268 	dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
269 	dl->d_secperunit = dl->d_ncylinders * dl->d_secpercyl;
270 	dl->d_partitions[0].p_size = dl->d_partitions[2].p_size =
271 	    dl->d_secperunit;
272 	dl->d_partitions[0].p_offset = dl->d_partitions[2].p_offset = 0;
273 	dl->d_interleave = dl->d_headswitch = 1;
274 	dl->d_bbsize = BBSIZE;
275 	dl->d_sbsize = SBLOCKSIZE;
276 	dl->d_rpm = 2400;
277 	dl->d_type = DTYPE_DEC;
278 	printf(": %s, %s\n", dl->d_typename,
279 	    rlstate((struct rlc_softc *)parent, ra->hwid));
280 
281 	/*
282 	 * XXX We should try to discovery wedges here, but
283 	 * XXX that would mean loading up the pack and being
284 	 * XXX able to do I/O.  Should use config_defer() here.
285 	 */
286 }
287 
288 int
289 rlopen(dev_t dev, int flag, int fmt, struct proc *p)
290 {
291 	int error, part, unit, mask;
292 	struct disklabel *dl;
293 	struct rlc_softc *sc;
294 	struct rl_softc *rc;
295 	const char *msg;
296 
297 	/*
298 	 * Make sure this is a reasonable open request.
299 	 */
300 	unit = DISKUNIT(dev);
301 	if (unit >= rl_cd.cd_ndevs)
302 		return ENXIO;
303 	rc = rl_cd.cd_devs[unit];
304 	if (rc == 0)
305 		return ENXIO;
306 
307 	part = DISKPART(dev);
308 
309 	if ((error = lockmgr(&rc->rc_disk.dk_openlock, LK_EXCLUSIVE,
310 			     NULL)) != 0)
311 		return (error);
312 
313 	/*
314 	 * If there are wedges, and this is not RAW_PART, then we
315 	 * need to fail.
316 	 */
317 	if (rc->rc_disk.dk_nwedges != 0 && part != RAW_PART) {
318 		error = EBUSY;
319 		goto bad1;
320 	}
321 
322 	sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
323 	/* Check that the disk actually is useable */
324 	msg = rlstate(sc, rc->rc_hwid);
325 	if (msg == NULL || msg == rlstates[RLMP_UNLOAD] ||
326 	    msg == rlstates[RLMP_SPUNDOWN]) {
327 		error = ENXIO;
328 		goto bad1;
329 	}
330 	/*
331 	 * If this is the first open; read in where on the disk we are.
332 	 */
333 	dl = rc->rc_disk.dk_label;
334 	if (rc->rc_state == DK_CLOSED) {
335 		u_int16_t mp;
336 		int maj;
337 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
338 		waitcrdy(sc);
339 		mp = RL_RREG(RL_MP);
340 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
341 		rc->rc_cyl = (mp >> 7) & 0777;
342 		rc->rc_state = DK_OPEN;
343 		/* Get disk label */
344 		printf("%s: ", rc->rc_dev.dv_xname);
345 		maj = cdevsw_lookup_major(&rl_cdevsw);
346 		if ((msg = readdisklabel(MAKEDISKDEV(maj,
347 		    rc->rc_dev.dv_unit, RAW_PART), rlstrategy, dl, NULL)))
348 			printf("%s: ", msg);
349 		printf("size %d sectors\n", dl->d_secperunit);
350 	}
351 	if (part >= dl->d_npartitions) {
352 		error = ENXIO;
353 		goto bad1;
354 	}
355 
356 	mask = 1 << part;
357 	switch (fmt) {
358 	case S_IFCHR:
359 		rc->rc_disk.dk_copenmask |= mask;
360 		break;
361 	case S_IFBLK:
362 		rc->rc_disk.dk_bopenmask |= mask;
363 		break;
364 	}
365 	rc->rc_disk.dk_openmask |= mask;
366 	(void) lockmgr(&rc->rc_disk.dk_openlock, LK_RELEASE, NULL);
367 	return 0;
368 
369  bad1:
370 	(void) lockmgr(&rc->rc_disk.dk_openlock, LK_RELEASE, NULL);
371 	return (error);
372 }
373 
374 int
375 rlclose(dev_t dev, int flag, int fmt, struct proc *p)
376 {
377 	int error, unit = DISKUNIT(dev);
378 	struct rl_softc *rc = rl_cd.cd_devs[unit];
379 	int mask = (1 << DISKPART(dev));
380 
381 	if ((error = lockmgr(&rc->rc_disk.dk_openlock, LK_EXCLUSIVE,
382 			     NULL)) != 0)
383 		return (error);
384 
385 	switch (fmt) {
386 	case S_IFCHR:
387 		rc->rc_disk.dk_copenmask &= ~mask;
388 		break;
389 	case S_IFBLK:
390 		rc->rc_disk.dk_bopenmask &= ~mask;
391 		break;
392 	}
393 	rc->rc_disk.dk_openmask =
394 	    rc->rc_disk.dk_copenmask | rc->rc_disk.dk_bopenmask;
395 
396 	if (rc->rc_disk.dk_openmask == 0)
397 		rc->rc_state = DK_CLOSED; /* May change pack */
398 	(void) lockmgr(&rc->rc_disk.dk_openlock, LK_RELEASE, NULL);
399 	return 0;
400 }
401 
402 void
403 rlstrategy(struct buf *bp)
404 {
405 	struct disklabel *lp;
406 	struct rlc_softc *sc;
407         struct rl_softc *rc;
408         int unit, s, err;
409         /*
410          * Make sure this is a reasonable drive to use.
411          */
412         unit = DISKUNIT(bp->b_dev);
413         if (unit > rl_cd.cd_ndevs || (rc = rl_cd.cd_devs[unit]) == NULL) {
414                 bp->b_error = ENXIO;
415                 bp->b_flags |= B_ERROR;
416                 goto done;
417         }
418 	if (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 ((err = 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 	sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
432 
433 	s = splbio();
434 	BUFQ_PUT(&sc->sc_q, bp);
435 	rlcstart(sc, 0);
436 	splx(s);
437 	return;
438 
439 done:	biodone(bp);
440 }
441 
442 int
443 rlioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
444 {
445 	struct rl_softc *rc = rl_cd.cd_devs[DISKUNIT(dev)];
446 	struct disklabel *lp = rc->rc_disk.dk_label;
447 	int err = 0;
448 #ifdef __HAVE_OLD_DISKLABEL
449 	struct disklabel newlabel;
450 #endif
451 
452 	switch (cmd) {
453 	case DIOCGDINFO:
454 		bcopy(lp, addr, sizeof (struct disklabel));
455 		break;
456 
457 #ifdef __HAVE_OLD_DISKLABEL
458 	case ODIOCGDINFO:
459 		newlabel = *lp;
460 		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
461 			return ENOTTY;
462 		bcopy(&newlabel, addr, sizeof (struct olddisklabel));
463 		break;
464 #endif
465 
466 	case DIOCGPART:
467 		((struct partinfo *)addr)->disklab = lp;
468 		((struct partinfo *)addr)->part =
469 		    &lp->d_partitions[DISKPART(dev)];
470 		break;
471 
472 	case DIOCSDINFO:
473 	case DIOCWDINFO:
474 #ifdef __HAVE_OLD_DISKLABEL
475 	case ODIOCWDINFO:
476 	case ODIOCSDINFO:
477 #endif
478 	{
479 		struct disklabel *tp;
480 
481 #ifdef __HAVE_OLD_DISKLABEL
482 		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
483 			memset(&newlabel, 0, sizeof newlabel);
484 			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
485 			tp = &newlabel;
486 		} else
487 #endif
488 		tp = (struct disklabel *)addr;
489 
490 		if ((flag & FWRITE) == 0)
491 			err = EBADF;
492 		else {
493 			if ((err = lockmgr(&rc->rc_disk.dk_openlock,
494 					   LK_EXCLUSIVE, NULL)) != 0)
495 				break;
496 			err = ((
497 #ifdef __HAVE_OLD_DISKLABEL
498 			       cmd == ODIOCSDINFO ||
499 #endif
500 			       cmd == DIOCSDINFO) ?
501 			    setdisklabel(lp, tp, 0, 0) :
502 			    writedisklabel(dev, rlstrategy, lp, 0));
503 			(void) lockmgr(&rc->rc_disk.dk_openlock,
504 				       LK_RELEASE, NULL);
505 		}
506 		break;
507 	}
508 
509 	case DIOCWLABEL:
510 		if ((flag & FWRITE) == 0)
511 			err = EBADF;
512 		break;
513 
514 	case DIOCAWEDGE:
515 	    {
516 	    	struct dkwedge_info *dkw = (void *) addr;
517 
518 		if ((flag & FWRITE) == 0)
519 			return (EBADF);
520 
521 		/* If the ioctl happens here, the parent is us. */
522 		strcpy(dkw->dkw_parent, rc->rc_dev.dv_xname);
523 		return (dkwedge_add(dkw));
524 	    }
525 
526 	case DIOCDWEDGE:
527 	    {
528 	    	struct dkwedge_info *dkw = (void *) addr;
529 
530 		if ((flag & FWRITE) == 0)
531 			return (EBADF);
532 
533 		/* If the ioctl happens here, the parent is us. */
534 		strcpy(dkw->dkw_parent, rc->rc_dev.dv_xname);
535 		return (dkwedge_del(dkw));
536 	    }
537 
538 	case DIOCLWEDGES:
539 	    {
540 	    	struct dkwedge_list *dkwl = (void *) addr;
541 
542 		return (dkwedge_list(&rc->rc_disk, dkwl, p));
543 	    }
544 
545 	default:
546 		err = ENOTTY;
547 	}
548 	return err;
549 }
550 
551 int
552 rlsize(dev_t dev)
553 {
554 	struct disklabel *dl;
555 	struct rl_softc *rc;
556 	int size, unit = DISKUNIT(dev);
557 
558 	if ((unit >= rl_cd.cd_ndevs) || ((rc = rl_cd.cd_devs[unit]) == 0))
559 		return -1;
560 	dl = rc->rc_disk.dk_label;
561 	size = dl->d_partitions[DISKPART(dev)].p_size *
562 	    (dl->d_secsize / DEV_BSIZE);
563 	return size;
564 }
565 
566 int
567 rldump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
568 {
569 	/* Not likely... */
570 	return 0;
571 }
572 
573 int
574 rlread(dev_t dev, struct uio *uio, int ioflag)
575 {
576 	return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
577 }
578 
579 int
580 rlwrite(dev_t dev, struct uio *uio, int ioflag)
581 {
582 	return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
583 }
584 
585 static char *rlerr[] = {
586 	"no",
587 	"operation incomplete",
588 	"read data CRC",
589 	"header CRC",
590 	"data late",
591 	"header not found",
592 	"",
593 	"",
594 	"non-existent memory",
595 	"memory parity error",
596 	"",
597 	"",
598 	"",
599 	"",
600 	"",
601 	"",
602 };
603 
604 void
605 rlcintr(void *arg)
606 {
607 	struct rlc_softc *sc = arg;
608 	struct buf *bp;
609 	u_int16_t cs;
610 
611 	bp = sc->sc_active;
612 	if (bp == 0) {
613 		printf("%s: strange interrupt\n", sc->sc_dev.dv_xname);
614 		return;
615 	}
616 	bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
617 	sc->sc_active = 0;
618 	cs = RL_RREG(RL_CS);
619 	if (cs & RLCS_ERR) {
620 		int error = (cs & RLCS_ERRMSK) >> 10;
621 
622 		printf("%s: %s\n", sc->sc_dev.dv_xname, rlerr[error]);
623 		bp->b_flags |= B_ERROR;
624 		bp->b_error = EIO;
625 		bp->b_resid = bp->b_bcount;
626 		sc->sc_bytecnt = 0;
627 	}
628 	if (sc->sc_bytecnt == 0) /* Finished transfer */
629 		biodone(bp);
630 	rlcstart(sc, sc->sc_bytecnt ? bp : 0);
631 }
632 
633 /*
634  * Start routine. First position the disk to the given position,
635  * then start reading/writing. An optimization would be to be able
636  * to handle overlapping seeks between disks.
637  */
638 void
639 rlcstart(struct rlc_softc *sc, struct buf *ob)
640 {
641 	struct disklabel *lp;
642 	struct rl_softc *rc;
643 	struct buf *bp;
644 	int bn, cn, sn, tn, blks, err;
645 
646 	if (sc->sc_active)
647 		return;	/* Already doing something */
648 
649 	if (ob == 0) {
650 		bp = BUFQ_GET(&sc->sc_q);
651 		if (bp == NULL)
652 			return;	/* Nothing to do */
653 		sc->sc_bufaddr = bp->b_data;
654 		sc->sc_diskblk = bp->b_rawblkno;
655 		sc->sc_bytecnt = bp->b_bcount;
656 		bp->b_resid = 0;
657 	} else
658 		bp = ob;
659 	sc->sc_active = bp;
660 
661 	rc = rl_cd.cd_devs[DISKUNIT(bp->b_dev)];
662 	bn = sc->sc_diskblk;
663 	lp = rc->rc_disk.dk_label;
664 	if (bn) {
665 		cn = bn / lp->d_secpercyl;
666 		sn = bn % lp->d_secpercyl;
667 		tn = sn / lp->d_nsectors;
668 		sn = sn % lp->d_nsectors;
669 	} else
670 		cn = sn = tn = 0;
671 
672 	/*
673 	 * Check if we have to position disk first.
674 	 */
675 	if (rc->rc_cyl != cn || rc->rc_head != tn) {
676 		u_int16_t da = RLDA_SEEK;
677 		if (cn > rc->rc_cyl)
678 			da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
679 		else
680 			da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
681 		if (tn)
682 			da |= RLDA_HSSEEK;
683 		waitcrdy(sc);
684 		RL_WREG(RL_DA, da);
685 		RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
686 		waitcrdy(sc);
687 		rc->rc_cyl = cn;
688 		rc->rc_head = tn;
689 	}
690 	RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
691 	blks = sc->sc_bytecnt/DEV_BSIZE;
692 
693 	if (sn + blks > RL_SPT/2)
694 		blks = RL_SPT/2 - sn;
695 	RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
696 	err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
697 	    (blks*DEV_BSIZE), (bp->b_flags & B_PHYS ? bp->b_proc : 0),
698 	    BUS_DMA_NOWAIT);
699 	if (err)
700 		panic("%s: bus_dmamap_load failed: %d",
701 		    sc->sc_dev.dv_xname, err);
702 	RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
703 
704 	/* Count up vars */
705 	sc->sc_bufaddr += (blks*DEV_BSIZE);
706 	sc->sc_diskblk += blks;
707 	sc->sc_bytecnt -= (blks*DEV_BSIZE);
708 
709 	if (bp->b_flags & B_READ)
710 		RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
711 	else
712 		RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
713 }
714 
715 /*
716  * Called once per controller when an ubareset occurs.
717  * Retracts all disks and restarts active transfers.
718  */
719 void
720 rlcreset(struct device *dev)
721 {
722 	struct rlc_softc *sc = (struct rlc_softc *)dev;
723 	struct rl_softc *rc;
724 	int i;
725 	u_int16_t mp;
726 
727 	for (i = 0; i < rl_cd.cd_ndevs; i++) {
728 		if ((rc = rl_cd.cd_devs[i]) == NULL)
729 			continue;
730 		if (rc->rc_state != DK_OPEN)
731 			continue;
732 
733 		printf(" %s", rc->rc_dev.dv_xname);
734 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
735 		waitcrdy(sc);
736 		mp = RL_RREG(RL_MP);
737 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
738 		rc->rc_cyl = (mp >> 7) & 0777;
739 	}
740 	if (sc->sc_active == 0)
741 		return;
742 
743 	BUFQ_PUT(&sc->sc_q, sc->sc_active);
744 	sc->sc_active = 0;
745 	rlcstart(sc, 0);
746 }
747