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