Home | History | Annotate | Line # | Download | only in qbus
rl.c revision 1.33.2.1
      1 /*	$NetBSD: rl.c,v 1.33.2.1 2007/08/19 19:24:32 ad 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.33.2.1 2007/08/19 19:24:32 ad 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 const 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 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 	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 = device_private(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, "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 = device_private(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 lwp *l)
    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 *)device_parent(&rc->rc_dev);
    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 		    device_unit(&rc->rc_dev), 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 lwp *l)
    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 		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 *)device_parent(&rc->rc_dev);
    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, void *addr, int flag, struct lwp *l)
    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, l));
    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, void *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 const 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_error = EIO;
    623 		bp->b_resid = bp->b_bcount;
    624 		sc->sc_bytecnt = 0;
    625 	}
    626 	if (sc->sc_bytecnt == 0) /* Finished transfer */
    627 		biodone(bp);
    628 	rlcstart(sc, sc->sc_bytecnt ? bp : 0);
    629 }
    630 
    631 /*
    632  * Start routine. First position the disk to the given position,
    633  * then start reading/writing. An optimization would be to be able
    634  * to handle overlapping seeks between disks.
    635  */
    636 void
    637 rlcstart(struct rlc_softc *sc, struct buf *ob)
    638 {
    639 	struct disklabel *lp;
    640 	struct rl_softc *rc;
    641 	struct buf *bp;
    642 	int bn, cn, sn, tn, blks, err;
    643 
    644 	if (sc->sc_active)
    645 		return;	/* Already doing something */
    646 
    647 	if (ob == 0) {
    648 		bp = BUFQ_GET(sc->sc_q);
    649 		if (bp == NULL)
    650 			return;	/* Nothing to do */
    651 		sc->sc_bufaddr = bp->b_data;
    652 		sc->sc_diskblk = bp->b_rawblkno;
    653 		sc->sc_bytecnt = bp->b_bcount;
    654 		bp->b_resid = 0;
    655 	} else
    656 		bp = ob;
    657 	sc->sc_active = bp;
    658 
    659 	rc = rl_cd.cd_devs[DISKUNIT(bp->b_dev)];
    660 	bn = sc->sc_diskblk;
    661 	lp = rc->rc_disk.dk_label;
    662 	if (bn) {
    663 		cn = bn / lp->d_secpercyl;
    664 		sn = bn % lp->d_secpercyl;
    665 		tn = sn / lp->d_nsectors;
    666 		sn = sn % lp->d_nsectors;
    667 	} else
    668 		cn = sn = tn = 0;
    669 
    670 	/*
    671 	 * Check if we have to position disk first.
    672 	 */
    673 	if (rc->rc_cyl != cn || rc->rc_head != tn) {
    674 		u_int16_t da = RLDA_SEEK;
    675 		if (cn > rc->rc_cyl)
    676 			da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
    677 		else
    678 			da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
    679 		if (tn)
    680 			da |= RLDA_HSSEEK;
    681 		waitcrdy(sc);
    682 		RL_WREG(RL_DA, da);
    683 		RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
    684 		waitcrdy(sc);
    685 		rc->rc_cyl = cn;
    686 		rc->rc_head = tn;
    687 	}
    688 	RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
    689 	blks = sc->sc_bytecnt/DEV_BSIZE;
    690 
    691 	if (sn + blks > RL_SPT/2)
    692 		blks = RL_SPT/2 - sn;
    693 	RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
    694 	err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
    695 	    (blks*DEV_BSIZE), (bp->b_flags & B_PHYS ? bp->b_proc : 0),
    696 	    BUS_DMA_NOWAIT);
    697 	if (err)
    698 		panic("%s: bus_dmamap_load failed: %d",
    699 		    sc->sc_dev.dv_xname, err);
    700 	RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
    701 
    702 	/* Count up vars */
    703 	sc->sc_bufaddr = (char *)sc->sc_bufaddr + (blks*DEV_BSIZE);
    704 	sc->sc_diskblk += blks;
    705 	sc->sc_bytecnt -= (blks*DEV_BSIZE);
    706 
    707 	if (bp->b_flags & B_READ)
    708 		RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
    709 	else
    710 		RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
    711 }
    712 
    713 /*
    714  * Called once per controller when an ubareset occurs.
    715  * Retracts all disks and restarts active transfers.
    716  */
    717 void
    718 rlcreset(struct device *dev)
    719 {
    720 	struct rlc_softc *sc = (struct rlc_softc *)dev;
    721 	struct rl_softc *rc;
    722 	int i;
    723 	u_int16_t mp;
    724 
    725 	for (i = 0; i < rl_cd.cd_ndevs; i++) {
    726 		if ((rc = rl_cd.cd_devs[i]) == NULL)
    727 			continue;
    728 		if (rc->rc_state != DK_OPEN)
    729 			continue;
    730 
    731 		printf(" %s", rc->rc_dev.dv_xname);
    732 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    733 		waitcrdy(sc);
    734 		mp = RL_RREG(RL_MP);
    735 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    736 		rc->rc_cyl = (mp >> 7) & 0777;
    737 	}
    738 	if (sc->sc_active == 0)
    739 		return;
    740 
    741 	BUFQ_PUT(sc->sc_q, sc->sc_active);
    742 	sc->sc_active = 0;
    743 	rlcstart(sc, 0);
    744 }
    745