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