Home | History | Annotate | Line # | Download | only in qbus
rl.c revision 1.1
      1 /*	$NetBSD: rl.c,v 1.1 2000/04/22 16:46:46 ragge 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/param.h>
     46 #include <sys/device.h>
     47 #include <sys/systm.h>
     48 #include <sys/conf.h>
     49 #include <sys/disk.h>
     50 #include <sys/disklabel.h>
     51 #include <sys/buf.h>
     52 #include <sys/stat.h>
     53 #include <sys/dkio.h>
     54 #include <sys/fcntl.h>
     55 
     56 #include <ufs/ufs/dinode.h>
     57 #include <ufs/ffs/fs.h>
     58 
     59 #include <machine/bus.h>
     60 
     61 #include <dev/qbus/ubavar.h>
     62 #include <dev/qbus/rlreg.h>
     63 
     64 #include "ioconf.h"
     65 #include "locators.h"
     66 
     67 struct rlc_softc {
     68 	struct device sc_dev;
     69 	bus_space_tag_t sc_iot;
     70 	bus_space_handle_t sc_ioh;
     71 	bus_dma_tag_t sc_dmat;
     72 	bus_dmamap_t sc_dmam;
     73 	struct buf_queue sc_q;		/* Queue of waiting bufs */
     74 	struct buf *sc_active;		/* Currently active buf */
     75 	caddr_t sc_bufaddr;		/* Current in-core address */
     76 	int sc_diskblk;			/* Current block on disk */
     77 	int sc_bytecnt;			/* How much left to transfer */
     78 };
     79 
     80 struct rl_softc {
     81 	struct device rc_dev;
     82 	struct disk rc_disk;
     83 	int rc_state;
     84 	int rc_head;
     85 	int rc_cyl;
     86 	int rc_hwid;
     87 };
     88 
     89 static	int rlcmatch(struct device *, struct cfdata *, void *);
     90 static	void rlcattach(struct device *, struct device *, void *);
     91 static	int rlcprint(void *, const char *);
     92 static	void rlcintr(void *);
     93 static	int rlmatch(struct device *, struct cfdata *, void *);
     94 static	void rlattach(struct device *, struct device *, void *);
     95 static	void rlcstart(struct rlc_softc *, struct buf *);
     96 static	void waitcrdy(struct rlc_softc *);
     97 cdev_decl(rl);
     98 bdev_decl(rl);
     99 
    100 struct cfattach rlc_ca = {
    101 	sizeof(struct rlc_softc), rlcmatch, rlcattach
    102 };
    103 
    104 struct cfattach rl_ca = {
    105 	sizeof(struct rl_softc), rlmatch, rlattach
    106 };
    107 
    108 struct rlc_attach_args {
    109 	u_int16_t type;
    110 	int hwid;
    111 };
    112 
    113 #define	MAXRLXFER (RL_BPS * RL_SPT)
    114 #define	RLMAJOR	14
    115 
    116 #define	RL_WREG(reg, val) \
    117 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, (reg), (val))
    118 #define RL_RREG(reg) \
    119 	bus_space_read_2(sc->sc_iot, sc->sc_ioh, (reg))
    120 
    121 void
    122 waitcrdy(struct rlc_softc *sc)
    123 {
    124 	int i;
    125 
    126 	for (i = 0; i < 1000; i++) {
    127 		DELAY(10000);
    128 		if (RL_RREG(RL_CS) & RLCS_CRDY)
    129 			return;
    130 	}
    131 	printf("%s: never got ready\n", sc->sc_dev.dv_xname); /* ?panic? */
    132 }
    133 
    134 int
    135 rlcprint(void *aux, const char *name)
    136 {
    137 	struct rlc_attach_args *ra = aux;
    138 
    139 	if (name)
    140 		printf("RL0%d at %s", ra->type & RLMP_DT ? '2' : '1', name);
    141 	printf(" drive %d", ra->hwid);
    142 	return UNCONF;
    143 }
    144 
    145 /*
    146  * Force the controller to interrupt.
    147  */
    148 int
    149 rlcmatch(struct device *parent, struct cfdata *cf, void *aux)
    150 {
    151 	struct uba_attach_args *ua = aux;
    152 	struct rlc_softc ssc, *sc = &ssc;
    153 	int i;
    154 
    155 	sc->sc_iot = ua->ua_iot;
    156 	sc->sc_ioh = ua->ua_ioh;
    157 	/* Force interrupt by issuing a "Get Status" command */
    158 	RL_WREG(RL_DA, RLDA_GS);
    159 	RL_WREG(RL_CS, RLCS_GS|RLCS_IE);
    160 
    161 	for (i = 0; i < 100; i++) {
    162 		DELAY(100000);
    163 		if (RL_RREG(RL_CS) & RLCS_CRDY)
    164 			return 1;
    165 	}
    166 	return 0;
    167 }
    168 
    169 void
    170 rlcattach(struct device *parent, struct device *self, void *aux)
    171 {
    172 	struct rlc_softc *sc = (struct rlc_softc *)self;
    173 	struct uba_attach_args *ua = aux;
    174 	struct rlc_attach_args ra;
    175 	int i, error;
    176 
    177 	sc->sc_iot = ua->ua_iot;
    178 	sc->sc_ioh = ua->ua_ioh;
    179 	sc->sc_dmat = ua->ua_dmat;
    180 	uba_intr_establish(ua->ua_icookie, ua->ua_cvec, rlcintr, sc);
    181 	printf("\n");
    182 
    183 	/*
    184 	 * The RL11 can only have one transfer going at a time,
    185 	 * and max transfer size is one track, so only one dmamap
    186 	 * is needed.
    187 	 */
    188 	error = bus_dmamap_create(sc->sc_dmat, MAXRLXFER, 1, MAXRLXFER, 0,
    189 	    BUS_DMA_ALLOCNOW, &sc->sc_dmam);
    190 	if (error) {
    191 		printf(": Failed to allocate DMA map, error %d\n", error);
    192 		return;
    193 	}
    194 	BUFQ_INIT(&sc->sc_q);
    195 	for (i = 0; i < RL_MAXDPC; i++) {
    196 		waitcrdy(sc);
    197 		RL_WREG(RL_DA, RLDA_GS|RLDA_RST);
    198 		RL_WREG(RL_CS, RLCS_GS|(i << RLCS_USHFT));
    199 		waitcrdy(sc);
    200 		ra.type = RL_RREG(RL_MP);
    201 		ra.hwid = i;
    202 		if ((RL_RREG(RL_CS) & RLCS_ERR) == 0)
    203 			config_found(&sc->sc_dev, &ra, rlcprint);
    204 	}
    205 }
    206 
    207 int
    208 rlmatch(struct device *parent, struct cfdata *cf, void *aux)
    209 {
    210 	struct rlc_attach_args *ra = aux;
    211 
    212 	if (cf->cf_loc[RLCCF_DRIVE] != RLCCF_DRIVE_DEFAULT &&
    213 	    cf->cf_loc[RLCCF_DRIVE] != ra->hwid)
    214 		return 0;
    215 	return 1;
    216 }
    217 
    218 void
    219 rlattach(struct device *parent, struct device *self, void *aux)
    220 {
    221 	struct rl_softc *rc = (struct rl_softc *)self;
    222 	struct rlc_attach_args *ra = aux;
    223 	struct disklabel *dl;
    224 
    225 	rc->rc_hwid = ra->hwid;
    226 	rc->rc_disk.dk_name = rc->rc_dev.dv_xname;
    227 	disk_attach(&rc->rc_disk);
    228 	dl = rc->rc_disk.dk_label;
    229 	dl->d_npartitions = 3;
    230 	strcpy(dl->d_typename, "RL01");
    231 	if (ra->type & RLMP_DT)
    232 		dl->d_typename[3] = '2';
    233 	dl->d_secsize = DEV_BSIZE; /* XXX - wrong, but OK for now */
    234 	dl->d_nsectors = RL_SPT/2;
    235 	dl->d_ntracks = RL_SPD;
    236 	dl->d_ncylinders = ra->type & RLMP_DT ? RL_TPS02 : RL_TPS01;
    237 	dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
    238 	dl->d_secperunit = dl->d_ncylinders * dl->d_secpercyl;
    239 	dl->d_partitions[0].p_size = dl->d_partitions[2].p_size =
    240 	    dl->d_secperunit;
    241 	dl->d_partitions[0].p_offset = dl->d_partitions[2].p_offset = 0;
    242 	dl->d_interleave = dl->d_headswitch = 1;
    243 	dl->d_bbsize = BBSIZE;
    244 	dl->d_sbsize = SBSIZE;
    245 	dl->d_rpm = 2400;
    246 	dl->d_type = DTYPE_DEC;
    247 	printf(": %s\n", dl->d_typename);
    248 }
    249 
    250 int
    251 rlopen(dev_t dev, int flag, int fmt, struct proc *p)
    252 {
    253 	int part, unit, mask;
    254 	struct disklabel *dl;
    255 	struct rlc_softc *sc;
    256 	struct rl_softc *rc;
    257 	char *msg;
    258 	/*
    259 	 * Make sure this is a reasonable open request.
    260 	 */
    261 	unit = DISKUNIT(dev);
    262 	if (unit >= rl_cd.cd_ndevs)
    263 		return ENXIO;
    264 	rc = rl_cd.cd_devs[unit];
    265 	if (rc == 0)
    266 		return ENXIO;
    267 
    268 	sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
    269 	/* XXX - check that the disk actually is useable */
    270 	/*
    271 	 * If this is the first open; read in where on the disk we are.
    272 	 */
    273 	dl = rc->rc_disk.dk_label;
    274 	if (rc->rc_state == DK_CLOSED) {
    275 		u_int16_t mp;
    276 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
    277 		waitcrdy(sc);
    278 		mp = RL_RREG(RL_MP);
    279 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
    280 		rc->rc_cyl = (mp >> 7) & 0777;
    281 		rc->rc_state = DK_OPEN;
    282 		/* Get disk label */
    283 		printf("%s: ", rc->rc_dev.dv_xname);
    284 		if ((msg = readdisklabel(MAKEDISKDEV(RLMAJOR,
    285 		    rc->rc_dev.dv_unit, RAW_PART), rlstrategy, dl, NULL)))
    286 			printf("%s: ", msg);
    287 		printf("size %d sectors\n", dl->d_secperunit);
    288 	}
    289 	part = DISKPART(dev);
    290 	if (part >= dl->d_npartitions)
    291 		return ENXIO;
    292 
    293 	mask = 1 << part;
    294 	switch (fmt) {
    295 	case S_IFCHR:
    296 		rc->rc_disk.dk_copenmask |= mask;
    297 		break;
    298 	case S_IFBLK:
    299 		rc->rc_disk.dk_bopenmask |= mask;
    300 		break;
    301 	}
    302 	rc->rc_disk.dk_openmask |= mask;
    303 	return 0;
    304 }
    305 
    306 int
    307 rlclose(dev_t dev, int flag, int fmt, struct proc *p)
    308 {
    309 	int unit = DISKUNIT(dev);
    310 	struct rl_softc *rc = rl_cd.cd_devs[unit];
    311 	int mask = (1 << DISKPART(dev));
    312 
    313 	switch (fmt) {
    314 	case S_IFCHR:
    315 		rc->rc_disk.dk_copenmask &= ~mask;
    316 		break;
    317 	case S_IFBLK:
    318 		rc->rc_disk.dk_bopenmask &= ~mask;
    319 		break;
    320 	}
    321 	rc->rc_disk.dk_openmask =
    322 	    rc->rc_disk.dk_copenmask | rc->rc_disk.dk_bopenmask;
    323 
    324 	if (rc->rc_disk.dk_openmask == 0)
    325 		rc->rc_state = DK_CLOSED; /* May change pack */
    326 	return 0;
    327 }
    328 
    329 void
    330 rlstrategy(struct buf *bp)
    331 {
    332 	struct disklabel *lp;
    333 	struct rlc_softc *sc;
    334         struct rl_softc *rc;
    335         int unit, s, err;
    336         /*
    337          * Make sure this is a reasonable drive to use.
    338          */
    339         unit = DISKUNIT(bp->b_dev);
    340         if (unit > rl_cd.cd_ndevs || (rc = rl_cd.cd_devs[unit]) == NULL) {
    341                 bp->b_error = ENXIO;
    342                 bp->b_flags |= B_ERROR;
    343                 goto done;
    344         }
    345 	if (rc->rc_state != DK_OPEN) /* How did we end up here at all? */
    346 		panic("rlstrategy: state impossible");
    347 
    348 	lp = rc->rc_disk.dk_label;
    349 	if ((err = bounds_check_with_label(bp, lp, 1)) <= 0)
    350 		goto done;
    351 
    352 	if (bp->b_bcount == 0)
    353 		goto done;
    354 
    355 	bp->b_rawblkno =
    356 	    bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
    357 	bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
    358 	sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
    359 
    360 	s = splimp();
    361 	disksort_cylinder(&sc->sc_q, bp);
    362 	rlcstart(sc, 0);
    363 	splx(s);
    364 	return;
    365 
    366 done:	biodone(bp);
    367 }
    368 
    369 int
    370 rlioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
    371 {
    372 	struct rl_softc *rc = rl_cd.cd_devs[DISKUNIT(dev)];
    373 	struct disklabel *lp = rc->rc_disk.dk_label;
    374 	int err = 0;
    375 
    376 	switch (cmd) {
    377 	case DIOCGDINFO:
    378 		bcopy(lp, addr, sizeof (struct disklabel));
    379 		break;
    380 
    381 	case DIOCGPART:
    382 		((struct partinfo *)addr)->disklab = lp;
    383 		((struct partinfo *)addr)->part =
    384 		    &lp->d_partitions[DISKPART(dev)];
    385 		break;
    386 
    387 	case DIOCSDINFO:
    388 	case DIOCWDINFO:
    389 		if ((flag & FWRITE) == 0)
    390 			err = EBADF;
    391 		else
    392 			err = (cmd == DIOCSDINFO ?
    393 			    setdisklabel(lp, (struct disklabel *)addr, 0, 0) :
    394 			    writedisklabel(dev, rlstrategy, lp, 0));
    395 		break;
    396 
    397 	case DIOCWLABEL:
    398 		if ((flag & FWRITE) == 0)
    399 			err = EBADF;
    400 		break;
    401 
    402 	default:
    403 		err = ENOTTY;
    404 	}
    405 	return err;
    406 }
    407 
    408 int
    409 rlsize(dev_t dev)
    410 {
    411 	struct disklabel *dl;
    412 	struct rl_softc *rc;
    413 	int size, unit = DISKUNIT(dev);
    414 
    415 	if ((unit >= rl_cd.cd_ndevs) || ((rc = rl_cd.cd_devs[unit]) == 0))
    416 		return -1;
    417 	dl = rc->rc_disk.dk_label;
    418 	size = dl->d_partitions[DISKPART(dev)].p_size *
    419 	    (dl->d_secsize / DEV_BSIZE);
    420 	return size;
    421 }
    422 
    423 int
    424 rldump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
    425 {
    426 	/* Not likely... */
    427 	return 0;
    428 }
    429 
    430 int
    431 rlread(dev_t dev, struct uio *uio, int ioflag)
    432 {
    433 	return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
    434 }
    435 
    436 int
    437 rlwrite(dev_t dev, struct uio *uio, int ioflag)
    438 {
    439 	return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
    440 }
    441 
    442 static char *rlerr[] = {
    443 	"no",
    444 	"operation incomplete",
    445 	"read data CRC",
    446 	"header CRC",
    447 	"data late",
    448 	"header not found",
    449 	"",
    450 	"",
    451 	"non-existant memory",
    452 	"memory parity error",
    453 	"",
    454 	"",
    455 	"",
    456 	"",
    457 	"",
    458 	"",
    459 };
    460 
    461 void
    462 rlcintr(void *arg)
    463 {
    464 	struct rlc_softc *sc = arg;
    465 	struct buf *bp;
    466 	u_int16_t cs;
    467 
    468 	bp = sc->sc_active;
    469 	if (bp == 0) {
    470 		printf("%s: strange interrupt\n", sc->sc_dev.dv_xname);
    471 		return;
    472 	}
    473 	bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
    474 	sc->sc_active = 0;
    475 	cs = RL_RREG(RL_CS);
    476 	if (cs & RLCS_ERR) {
    477 		int error = (cs & RLCS_ERRMSK) >> 10;
    478 
    479 		printf("%s: %s\n", sc->sc_dev.dv_xname, rlerr[error]);
    480 		bp->b_flags |= B_ERROR;
    481 		bp->b_error = EIO;
    482 		bp->b_resid = bp->b_bcount;
    483 		sc->sc_bytecnt = 0;
    484 	}
    485 	if (sc->sc_bytecnt == 0) /* Finished transfer */
    486 		biodone(bp);
    487 	rlcstart(sc, sc->sc_bytecnt ? bp : 0);
    488 }
    489 
    490 /*
    491  * Start routine. First position the disk to the given position,
    492  * then start reading/writing. An optimization would be to be able
    493  * to handle overlapping seeks between disks.
    494  */
    495 void
    496 rlcstart(struct rlc_softc *sc, struct buf *ob)
    497 {
    498 	struct disklabel *lp;
    499 	struct rl_softc *rc;
    500 	struct buf *bp;
    501 	int bn, cn, sn, tn, blks, err;
    502 
    503 	if (sc->sc_active)
    504 		return;	/* Already doing something */
    505 
    506 	if (ob == 0) {
    507 		bp = BUFQ_FIRST(&sc->sc_q);
    508 		if (bp == NULL)
    509 			return;	/* Nothing to do */
    510 		BUFQ_REMOVE(&sc->sc_q, bp);
    511 		sc->sc_bufaddr = bp->b_un.b_addr;
    512 		sc->sc_diskblk = bp->b_rawblkno;
    513 		sc->sc_bytecnt = bp->b_bcount;
    514 		bp->b_resid = 0;
    515 	} else
    516 		bp = ob;
    517 	sc->sc_active = bp;
    518 
    519 	rc = rl_cd.cd_devs[DISKUNIT(bp->b_dev)];
    520 	bn = sc->sc_diskblk;
    521 	lp = rc->rc_disk.dk_label;
    522 	if (bn) {
    523 		cn = bn / lp->d_secpercyl;
    524 		sn = bn % lp->d_secpercyl;
    525 		tn = sn / lp->d_nsectors;
    526 		sn = sn % lp->d_nsectors;
    527 	} else
    528 		cn = sn = tn = 0;
    529 
    530 	/*
    531 	 * Check if we have to position disk first.
    532 	 */
    533 	if (rc->rc_cyl != cn || rc->rc_head != tn) {
    534 		u_int16_t da = RLDA_SEEK;
    535 		if (cn > rc->rc_cyl)
    536 			da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
    537 		else
    538 			da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
    539 		if (tn)
    540 			da |= RLDA_HSSEEK;
    541 		waitcrdy(sc);
    542 		RL_WREG(RL_DA, da);
    543 		RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
    544 		waitcrdy(sc);
    545 		rc->rc_cyl = cn;
    546 		rc->rc_head = tn;
    547 	}
    548 	RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
    549 	blks = sc->sc_bytecnt/DEV_BSIZE;
    550 
    551 	if (sn + blks > RL_SPT/2)
    552 		blks = RL_SPT/2 - sn;
    553 	RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
    554 	err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
    555 	    (blks*DEV_BSIZE), bp->b_proc, BUS_DMA_NOWAIT);
    556 	if (err)
    557 		panic("%s: bus_dmamap_load failed: %d",
    558 		    sc->sc_dev.dv_xname, err);
    559 	RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
    560 
    561 	/* Count up vars */
    562 	sc->sc_bufaddr += (blks*DEV_BSIZE);
    563 	sc->sc_diskblk += blks;
    564 	sc->sc_bytecnt -= (blks*DEV_BSIZE);
    565 
    566 	if (bp->b_flags & B_READ)
    567 		RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
    568 	else
    569 		RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
    570 }
    571