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