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