1/*	$NetBSD: rl.c,v 1.41 2009/03/18 17:06:50 cegger 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.41 2009/03/18 17:06:50 cegger 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
73static	int rlcmatch(device_t, cfdata_t, void *);
74static	void rlcattach(device_t, device_t, void *);
75static	int rlcprint(void *, const char *);
76static	void rlcintr(void *);
77static	int rlmatch(device_t, cfdata_t, void *);
78static	void rlattach(device_t, device_t, void *);
79static	void rlcstart(struct rlc_softc *, struct buf *);
80static	void waitcrdy(struct rlc_softc *);
81static	void rlcreset(device_t);
82
83CFATTACH_DECL_NEW(rlc, sizeof(struct rlc_softc),
84    rlcmatch, rlcattach, NULL, NULL);
85
86CFATTACH_DECL_NEW(rl, sizeof(struct rl_softc),
87    rlmatch, rlattach, NULL, NULL);
88
89static dev_type_open(rlopen);
90static dev_type_close(rlclose);
91static dev_type_read(rlread);
92static dev_type_write(rlwrite);
93static dev_type_ioctl(rlioctl);
94static dev_type_strategy(rlstrategy);
95static dev_type_dump(rldump);
96static dev_type_size(rlpsize);
97
98const struct bdevsw rl_bdevsw = {
99	rlopen, rlclose, rlstrategy, rlioctl, rldump, rlpsize, D_DISK
100};
101
102const 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
114static 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
125static const struct dkdriver rldkdriver = {
126	rlstrategy, minphys
127};
128
129static const char *
130rlstate(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
145void
146waitcrdy(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
158int
159rlcprint(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 */
173int
174rlcmatch(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
194void
195rlcattach(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
239int
240rlmatch(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
250void
251rlattach(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
290int
291rlopen(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
368int
369rlclose(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
394void
395rlstrategy(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
421done:	biodone(bp);
422}
423
424int
425rlioctl(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
528int
529rlpsize(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
543int
544rldump(dev_t dev, daddr_t blkno, void *va, size_t size)
545{
546	/* Not likely... */
547	return 0;
548}
549
550int
551rlread(dev_t dev, struct uio *uio, int ioflag)
552{
553	return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
554}
555
556int
557rlwrite(dev_t dev, struct uio *uio, int ioflag)
558{
559	return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
560}
561
562static 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
581void
582rlcintr(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 */
614void
615rlcstart(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 */
695void
696rlcreset(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