Home | History | Annotate | Line # | Download | only in qbus
rl.c revision 1.10
      1 /*	$NetBSD: rl.c,v 1.10 2001/11/13 07:11:25 lukem 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.10 2001/11/13 07:11:25 lukem 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 
     67 #include "ioconf.h"
     68 #include "locators.h"
     69 
     70 struct rlc_softc {
     71 	struct device sc_dev;
     72 	struct evcnt sc_intrcnt;
     73 	bus_space_tag_t sc_iot;
     74 	bus_space_handle_t sc_ioh;
     75 	bus_dma_tag_t sc_dmat;
     76 	bus_dmamap_t sc_dmam;
     77 	struct buf_queue sc_q;		/* Queue of waiting bufs */
     78 	struct buf *sc_active;		/* Currently active buf */
     79 	caddr_t sc_bufaddr;		/* Current in-core address */
     80 	int sc_diskblk;			/* Current block on disk */
     81 	int sc_bytecnt;			/* How much left to transfer */
     82 };
     83 
     84 struct rl_softc {
     85 	struct device rc_dev;
     86 	struct disk rc_disk;
     87 	int rc_state;
     88 	int rc_head;
     89 	int rc_cyl;
     90 	int rc_hwid;
     91 };
     92 
     93 static	int rlcmatch(struct device *, struct cfdata *, void *);
     94 static	void rlcattach(struct device *, struct device *, void *);
     95 static	int rlcprint(void *, const char *);
     96 static	void rlcintr(void *);
     97 static	int rlmatch(struct device *, struct cfdata *, void *);
     98 static	void rlattach(struct device *, struct device *, void *);
     99 static	void rlcstart(struct rlc_softc *, struct buf *);
    100 static	void waitcrdy(struct rlc_softc *);
    101 static	void rlreset(struct device *);
    102 cdev_decl(rl);
    103 bdev_decl(rl);
    104 
    105 struct cfattach rlc_ca = {
    106 	sizeof(struct rlc_softc), rlcmatch, rlcattach
    107 };
    108 
    109 struct cfattach rl_ca = {
    110 	sizeof(struct rl_softc), rlmatch, rlattach
    111 };
    112 
    113 struct rlc_attach_args {
    114 	u_int16_t type;
    115 	int hwid;
    116 };
    117 
    118 #define	MAXRLXFER (RL_BPS * RL_SPT)
    119 #define	RLMAJOR	14
    120 
    121 #define	RL_WREG(reg, val) \
    122 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, (reg), (val))
    123 #define RL_RREG(reg) \
    124 	bus_space_read_2(sc->sc_iot, sc->sc_ioh, (reg))
    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 	printf("\n");
    190 
    191 	/*
    192 	 * The RL11 can only have one transfer going at a time,
    193 	 * and max transfer size is one track, so only one dmamap
    194 	 * is needed.
    195 	 */
    196 	error = bus_dmamap_create(sc->sc_dmat, MAXRLXFER, 1, MAXRLXFER, 0,
    197 	    BUS_DMA_ALLOCNOW, &sc->sc_dmam);
    198 	if (error) {
    199 		printf(": Failed to allocate DMA map, error %d\n", error);
    200 		return;
    201 	}
    202 	BUFQ_INIT(&sc->sc_q);
    203 	for (i = 0; i < RL_MAXDPC; i++) {
    204 		waitcrdy(sc);
    205 		RL_WREG(RL_DA, RLDA_GS|RLDA_RST);
    206 		RL_WREG(RL_CS, RLCS_GS|(i << RLCS_USHFT));
    207 		waitcrdy(sc);
    208 		ra.type = RL_RREG(RL_MP);
    209 		ra.hwid = i;
    210 		if ((RL_RREG(RL_CS) & RLCS_ERR) == 0)
    211 			config_found(&sc->sc_dev, &ra, rlcprint);
    212 	}
    213 }
    214 
    215 int
    216 rlmatch(struct device *parent, struct cfdata *cf, void *aux)
    217 {
    218 	struct rlc_attach_args *ra = aux;
    219 
    220 	if (cf->cf_loc[RLCCF_DRIVE] != RLCCF_DRIVE_DEFAULT &&
    221 	    cf->cf_loc[RLCCF_DRIVE] != ra->hwid)
    222 		return 0;
    223 	return 1;
    224 }
    225 
    226 void
    227 rlattach(struct device *parent, struct device *self, void *aux)
    228 {
    229 	struct rl_softc *rc = (struct rl_softc *)self;
    230 	struct rlc_attach_args *ra = aux;
    231 	struct disklabel *dl;
    232 
    233 	uba_reset_establish(rlreset, self);
    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\n", dl->d_typename);
    258 }
    259 
    260 int
    261 rlopen(dev_t dev, int flag, int fmt, struct proc *p)
    262 {
    263 	int part, unit, mask;
    264 	struct disklabel *dl;
    265 	struct rlc_softc *sc;
    266 	struct rl_softc *rc;
    267 	char *msg;
    268 	/*
    269 	 * Make sure this is a reasonable open request.
    270 	 */
    271 	unit = DISKUNIT(dev);
    272 	if (unit >= rl_cd.cd_ndevs)
    273 		return ENXIO;
    274 	rc = rl_cd.cd_devs[unit];
    275 	if (rc == 0)
    276 		return ENXIO;
    277 
    278 	sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
    279 	/* XXX - check that the disk actually is useable */
    280 	/*
    281 	 * If this is the first open; read in where on the disk we are.
    282 	 */
    283 	dl = rc->rc_disk.dk_label;
    284 	if (rc->rc_state == DK_CLOSED) {
    285 		u_int16_t mp;
    286 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    287 		waitcrdy(sc);
    288 		mp = RL_RREG(RL_MP);
    289 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    290 		rc->rc_cyl = (mp >> 7) & 0777;
    291 		rc->rc_state = DK_OPEN;
    292 		/* Get disk label */
    293 		printf("%s: ", rc->rc_dev.dv_xname);
    294 		if ((msg = readdisklabel(MAKEDISKDEV(RLMAJOR,
    295 		    rc->rc_dev.dv_unit, RAW_PART), rlstrategy, dl, NULL)))
    296 			printf("%s: ", msg);
    297 		printf("size %d sectors\n", dl->d_secperunit);
    298 	}
    299 	part = DISKPART(dev);
    300 	if (part >= dl->d_npartitions)
    301 		return ENXIO;
    302 
    303 	mask = 1 << part;
    304 	switch (fmt) {
    305 	case S_IFCHR:
    306 		rc->rc_disk.dk_copenmask |= mask;
    307 		break;
    308 	case S_IFBLK:
    309 		rc->rc_disk.dk_bopenmask |= mask;
    310 		break;
    311 	}
    312 	rc->rc_disk.dk_openmask |= mask;
    313 	return 0;
    314 }
    315 
    316 int
    317 rlclose(dev_t dev, int flag, int fmt, struct proc *p)
    318 {
    319 	int unit = DISKUNIT(dev);
    320 	struct rl_softc *rc = rl_cd.cd_devs[unit];
    321 	int mask = (1 << DISKPART(dev));
    322 
    323 	switch (fmt) {
    324 	case S_IFCHR:
    325 		rc->rc_disk.dk_copenmask &= ~mask;
    326 		break;
    327 	case S_IFBLK:
    328 		rc->rc_disk.dk_bopenmask &= ~mask;
    329 		break;
    330 	}
    331 	rc->rc_disk.dk_openmask =
    332 	    rc->rc_disk.dk_copenmask | rc->rc_disk.dk_bopenmask;
    333 
    334 	if (rc->rc_disk.dk_openmask == 0)
    335 		rc->rc_state = DK_CLOSED; /* May change pack */
    336 	return 0;
    337 }
    338 
    339 void
    340 rlstrategy(struct buf *bp)
    341 {
    342 	struct disklabel *lp;
    343 	struct rlc_softc *sc;
    344         struct rl_softc *rc;
    345         int unit, s, err;
    346         /*
    347          * Make sure this is a reasonable drive to use.
    348          */
    349         unit = DISKUNIT(bp->b_dev);
    350         if (unit > rl_cd.cd_ndevs || (rc = rl_cd.cd_devs[unit]) == NULL) {
    351                 bp->b_error = ENXIO;
    352                 bp->b_flags |= B_ERROR;
    353                 goto done;
    354         }
    355 	if (rc->rc_state != DK_OPEN) /* How did we end up here at all? */
    356 		panic("rlstrategy: state impossible");
    357 
    358 	lp = rc->rc_disk.dk_label;
    359 	if ((err = bounds_check_with_label(bp, lp, 1)) <= 0)
    360 		goto done;
    361 
    362 	if (bp->b_bcount == 0)
    363 		goto done;
    364 
    365 	bp->b_rawblkno =
    366 	    bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
    367 	bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
    368 	sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
    369 
    370 	s = splbio();
    371 	disksort_cylinder(&sc->sc_q, bp);
    372 	rlcstart(sc, 0);
    373 	splx(s);
    374 	return;
    375 
    376 done:	biodone(bp);
    377 }
    378 
    379 int
    380 rlioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
    381 {
    382 	struct rl_softc *rc = rl_cd.cd_devs[DISKUNIT(dev)];
    383 	struct disklabel *lp = rc->rc_disk.dk_label;
    384 	int err = 0;
    385 #ifdef __HAVE_OLD_DISKLABEL
    386 	struct disklabel newlabel;
    387 #endif
    388 
    389 	switch (cmd) {
    390 	case DIOCGDINFO:
    391 		bcopy(lp, addr, sizeof (struct disklabel));
    392 		break;
    393 
    394 #ifdef __HAVE_OLD_DISKLABEL
    395 	case ODIOCGDINFO:
    396 		newlabel = *lp;
    397 		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
    398 			return ENOTTY;
    399 		bcopy(&newlabel, addr, sizeof (struct olddisklabel));
    400 		break;
    401 #endif
    402 
    403 	case DIOCGPART:
    404 		((struct partinfo *)addr)->disklab = lp;
    405 		((struct partinfo *)addr)->part =
    406 		    &lp->d_partitions[DISKPART(dev)];
    407 		break;
    408 
    409 	case DIOCSDINFO:
    410 	case DIOCWDINFO:
    411 #ifdef __HAVE_OLD_DISKLABEL
    412 	case ODIOCWDINFO:
    413 	case ODIOCSDINFO:
    414 #endif
    415 	{
    416 		struct disklabel *tp;
    417 
    418 #ifdef __HAVE_OLD_DISKLABEL
    419 		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
    420 			memset(&newlabel, 0, sizeof newlabel);
    421 			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
    422 			tp = &newlabel;
    423 		} else
    424 #endif
    425 		tp = (struct disklabel *)addr;
    426 
    427 		if ((flag & FWRITE) == 0)
    428 			err = EBADF;
    429 		else
    430 			err = ((
    431 #ifdef __HAVE_OLD_DISKLABEL
    432 			       cmd == ODIOCSDINFO ||
    433 #endif
    434 			       cmd == DIOCSDINFO) ?
    435 			    setdisklabel(lp, tp, 0, 0) :
    436 			    writedisklabel(dev, rlstrategy, lp, 0));
    437 		break;
    438 	}
    439 
    440 	case DIOCWLABEL:
    441 		if ((flag & FWRITE) == 0)
    442 			err = EBADF;
    443 		break;
    444 
    445 	default:
    446 		err = ENOTTY;
    447 	}
    448 	return err;
    449 }
    450 
    451 int
    452 rlsize(dev_t dev)
    453 {
    454 	struct disklabel *dl;
    455 	struct rl_softc *rc;
    456 	int size, unit = DISKUNIT(dev);
    457 
    458 	if ((unit >= rl_cd.cd_ndevs) || ((rc = rl_cd.cd_devs[unit]) == 0))
    459 		return -1;
    460 	dl = rc->rc_disk.dk_label;
    461 	size = dl->d_partitions[DISKPART(dev)].p_size *
    462 	    (dl->d_secsize / DEV_BSIZE);
    463 	return size;
    464 }
    465 
    466 int
    467 rldump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
    468 {
    469 	/* Not likely... */
    470 	return 0;
    471 }
    472 
    473 int
    474 rlread(dev_t dev, struct uio *uio, int ioflag)
    475 {
    476 	return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
    477 }
    478 
    479 int
    480 rlwrite(dev_t dev, struct uio *uio, int ioflag)
    481 {
    482 	return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
    483 }
    484 
    485 static char *rlerr[] = {
    486 	"no",
    487 	"operation incomplete",
    488 	"read data CRC",
    489 	"header CRC",
    490 	"data late",
    491 	"header not found",
    492 	"",
    493 	"",
    494 	"non-existent memory",
    495 	"memory parity error",
    496 	"",
    497 	"",
    498 	"",
    499 	"",
    500 	"",
    501 	"",
    502 };
    503 
    504 void
    505 rlcintr(void *arg)
    506 {
    507 	struct rlc_softc *sc = arg;
    508 	struct buf *bp;
    509 	u_int16_t cs;
    510 
    511 	bp = sc->sc_active;
    512 	if (bp == 0) {
    513 		printf("%s: strange interrupt\n", sc->sc_dev.dv_xname);
    514 		return;
    515 	}
    516 	bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
    517 	sc->sc_active = 0;
    518 	cs = RL_RREG(RL_CS);
    519 	if (cs & RLCS_ERR) {
    520 		int error = (cs & RLCS_ERRMSK) >> 10;
    521 
    522 		printf("%s: %s\n", sc->sc_dev.dv_xname, rlerr[error]);
    523 		bp->b_flags |= B_ERROR;
    524 		bp->b_error = EIO;
    525 		bp->b_resid = bp->b_bcount;
    526 		sc->sc_bytecnt = 0;
    527 	}
    528 	if (sc->sc_bytecnt == 0) /* Finished transfer */
    529 		biodone(bp);
    530 	rlcstart(sc, sc->sc_bytecnt ? bp : 0);
    531 }
    532 
    533 /*
    534  * Start routine. First position the disk to the given position,
    535  * then start reading/writing. An optimization would be to be able
    536  * to handle overlapping seeks between disks.
    537  */
    538 void
    539 rlcstart(struct rlc_softc *sc, struct buf *ob)
    540 {
    541 	struct disklabel *lp;
    542 	struct rl_softc *rc;
    543 	struct buf *bp;
    544 	int bn, cn, sn, tn, blks, err;
    545 
    546 	if (sc->sc_active)
    547 		return;	/* Already doing something */
    548 
    549 	if (ob == 0) {
    550 		bp = BUFQ_FIRST(&sc->sc_q);
    551 		if (bp == NULL)
    552 			return;	/* Nothing to do */
    553 		BUFQ_REMOVE(&sc->sc_q, bp);
    554 		sc->sc_bufaddr = bp->b_data;
    555 		sc->sc_diskblk = bp->b_rawblkno;
    556 		sc->sc_bytecnt = bp->b_bcount;
    557 		bp->b_resid = 0;
    558 	} else
    559 		bp = ob;
    560 	sc->sc_active = bp;
    561 
    562 	rc = rl_cd.cd_devs[DISKUNIT(bp->b_dev)];
    563 	bn = sc->sc_diskblk;
    564 	lp = rc->rc_disk.dk_label;
    565 	if (bn) {
    566 		cn = bn / lp->d_secpercyl;
    567 		sn = bn % lp->d_secpercyl;
    568 		tn = sn / lp->d_nsectors;
    569 		sn = sn % lp->d_nsectors;
    570 	} else
    571 		cn = sn = tn = 0;
    572 
    573 	/*
    574 	 * Check if we have to position disk first.
    575 	 */
    576 	if (rc->rc_cyl != cn || rc->rc_head != tn) {
    577 		u_int16_t da = RLDA_SEEK;
    578 		if (cn > rc->rc_cyl)
    579 			da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
    580 		else
    581 			da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
    582 		if (tn)
    583 			da |= RLDA_HSSEEK;
    584 		waitcrdy(sc);
    585 		RL_WREG(RL_DA, da);
    586 		RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
    587 		waitcrdy(sc);
    588 		rc->rc_cyl = cn;
    589 		rc->rc_head = tn;
    590 	}
    591 	RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
    592 	blks = sc->sc_bytecnt/DEV_BSIZE;
    593 
    594 	if (sn + blks > RL_SPT/2)
    595 		blks = RL_SPT/2 - sn;
    596 	RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
    597 	err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
    598 	    (blks*DEV_BSIZE), bp->b_proc, BUS_DMA_NOWAIT);
    599 	if (err)
    600 		panic("%s: bus_dmamap_load failed: %d",
    601 		    sc->sc_dev.dv_xname, err);
    602 	RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
    603 
    604 	/* Count up vars */
    605 	sc->sc_bufaddr += (blks*DEV_BSIZE);
    606 	sc->sc_diskblk += blks;
    607 	sc->sc_bytecnt -= (blks*DEV_BSIZE);
    608 
    609 	if (bp->b_flags & B_READ)
    610 		RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
    611 	else
    612 		RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
    613 }
    614 
    615 void
    616 rlreset(struct device *dev)
    617 {
    618 	struct rl_softc *rc = (struct rl_softc *)dev;
    619 	struct rlc_softc *sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
    620 	u_int16_t mp;
    621 
    622 	if (rc->rc_state != DK_OPEN)
    623 		return;
    624 	RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    625 	waitcrdy(sc);
    626 	mp = RL_RREG(RL_MP);
    627 	rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    628 	rc->rc_cyl = (mp >> 7) & 0777;
    629 	if (sc->sc_active == 0)
    630 		return;
    631 
    632 	BUFQ_INSERT_HEAD(&sc->sc_q, sc->sc_active);
    633 	sc->sc_active = 0;
    634 	rlcstart(sc, 0);
    635 }
    636