rl.c revision 1.6 1 /* $NetBSD: rl.c,v 1.6 2001/01/07 18:09:02 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
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 #ifdef __HAVE_OLD_DISKLABEL
383 struct disklabel newlabel;
384 #endif
385
386 switch (cmd) {
387 case DIOCGDINFO:
388 bcopy(lp, addr, sizeof (struct disklabel));
389 break;
390
391 #ifdef __HAVE_OLD_DISKLABEL
392 case ODIOCGDINFO:
393 newlabel = *lp;
394 if (newlabel.d_npartitions > OLDMAXPARTITIONS)
395 newlabel.d_npartitions = OLDMAXPARTITIONS;
396 bcopy(&newlabel, addr, sizeof (struct olddisklabel));
397 break;
398 #endif
399
400 case DIOCGPART:
401 ((struct partinfo *)addr)->disklab = lp;
402 ((struct partinfo *)addr)->part =
403 &lp->d_partitions[DISKPART(dev)];
404 break;
405
406 case DIOCSDINFO:
407 case DIOCWDINFO:
408 #ifdef __HAVE_OLD_DISKLABEL
409 case ODIOCWDINFO:
410 case ODIOCSDINFO:
411 #endif
412 {
413 struct disklabel *tp;
414
415 #ifdef __HAVE_OLD_DISKLABEL
416 if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
417 memset(&newlabel, 0, sizeof newlabel);
418 memcpy(&newlabel, addr, sizeof (struct olddisklabel));
419 tp = &newlabel;
420 } else
421 #endif
422 tp = (struct disklabel *)addr;
423
424 if ((flag & FWRITE) == 0)
425 err = EBADF;
426 else
427 err = ((
428 #ifdef __HAVE_OLD_DISKLABEL
429 cmd == ODIOCSDINFO ||
430 #endif
431 cmd == DIOCSDINFO) ?
432 setdisklabel(lp, tp, 0, 0) :
433 writedisklabel(dev, rlstrategy, lp, 0));
434 break;
435 }
436
437 case DIOCWLABEL:
438 if ((flag & FWRITE) == 0)
439 err = EBADF;
440 break;
441
442 default:
443 err = ENOTTY;
444 }
445 return err;
446 }
447
448 int
449 rlsize(dev_t dev)
450 {
451 struct disklabel *dl;
452 struct rl_softc *rc;
453 int size, unit = DISKUNIT(dev);
454
455 if ((unit >= rl_cd.cd_ndevs) || ((rc = rl_cd.cd_devs[unit]) == 0))
456 return -1;
457 dl = rc->rc_disk.dk_label;
458 size = dl->d_partitions[DISKPART(dev)].p_size *
459 (dl->d_secsize / DEV_BSIZE);
460 return size;
461 }
462
463 int
464 rldump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
465 {
466 /* Not likely... */
467 return 0;
468 }
469
470 int
471 rlread(dev_t dev, struct uio *uio, int ioflag)
472 {
473 return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
474 }
475
476 int
477 rlwrite(dev_t dev, struct uio *uio, int ioflag)
478 {
479 return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
480 }
481
482 static char *rlerr[] = {
483 "no",
484 "operation incomplete",
485 "read data CRC",
486 "header CRC",
487 "data late",
488 "header not found",
489 "",
490 "",
491 "non-existant memory",
492 "memory parity error",
493 "",
494 "",
495 "",
496 "",
497 "",
498 "",
499 };
500
501 void
502 rlcintr(void *arg)
503 {
504 struct rlc_softc *sc = arg;
505 struct buf *bp;
506 u_int16_t cs;
507
508 bp = sc->sc_active;
509 if (bp == 0) {
510 printf("%s: strange interrupt\n", sc->sc_dev.dv_xname);
511 return;
512 }
513 bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
514 sc->sc_active = 0;
515 cs = RL_RREG(RL_CS);
516 if (cs & RLCS_ERR) {
517 int error = (cs & RLCS_ERRMSK) >> 10;
518
519 printf("%s: %s\n", sc->sc_dev.dv_xname, rlerr[error]);
520 bp->b_flags |= B_ERROR;
521 bp->b_error = EIO;
522 bp->b_resid = bp->b_bcount;
523 sc->sc_bytecnt = 0;
524 }
525 if (sc->sc_bytecnt == 0) /* Finished transfer */
526 biodone(bp);
527 rlcstart(sc, sc->sc_bytecnt ? bp : 0);
528 }
529
530 /*
531 * Start routine. First position the disk to the given position,
532 * then start reading/writing. An optimization would be to be able
533 * to handle overlapping seeks between disks.
534 */
535 void
536 rlcstart(struct rlc_softc *sc, struct buf *ob)
537 {
538 struct disklabel *lp;
539 struct rl_softc *rc;
540 struct buf *bp;
541 int bn, cn, sn, tn, blks, err;
542
543 if (sc->sc_active)
544 return; /* Already doing something */
545
546 if (ob == 0) {
547 bp = BUFQ_FIRST(&sc->sc_q);
548 if (bp == NULL)
549 return; /* Nothing to do */
550 BUFQ_REMOVE(&sc->sc_q, bp);
551 sc->sc_bufaddr = bp->b_data;
552 sc->sc_diskblk = bp->b_rawblkno;
553 sc->sc_bytecnt = bp->b_bcount;
554 bp->b_resid = 0;
555 } else
556 bp = ob;
557 sc->sc_active = bp;
558
559 rc = rl_cd.cd_devs[DISKUNIT(bp->b_dev)];
560 bn = sc->sc_diskblk;
561 lp = rc->rc_disk.dk_label;
562 if (bn) {
563 cn = bn / lp->d_secpercyl;
564 sn = bn % lp->d_secpercyl;
565 tn = sn / lp->d_nsectors;
566 sn = sn % lp->d_nsectors;
567 } else
568 cn = sn = tn = 0;
569
570 /*
571 * Check if we have to position disk first.
572 */
573 if (rc->rc_cyl != cn || rc->rc_head != tn) {
574 u_int16_t da = RLDA_SEEK;
575 if (cn > rc->rc_cyl)
576 da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
577 else
578 da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
579 if (tn)
580 da |= RLDA_HSSEEK;
581 waitcrdy(sc);
582 RL_WREG(RL_DA, da);
583 RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
584 waitcrdy(sc);
585 rc->rc_cyl = cn;
586 rc->rc_head = tn;
587 }
588 RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
589 blks = sc->sc_bytecnt/DEV_BSIZE;
590
591 if (sn + blks > RL_SPT/2)
592 blks = RL_SPT/2 - sn;
593 RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
594 err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
595 (blks*DEV_BSIZE), bp->b_proc, BUS_DMA_NOWAIT);
596 if (err)
597 panic("%s: bus_dmamap_load failed: %d",
598 sc->sc_dev.dv_xname, err);
599 RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
600
601 /* Count up vars */
602 sc->sc_bufaddr += (blks*DEV_BSIZE);
603 sc->sc_diskblk += blks;
604 sc->sc_bytecnt -= (blks*DEV_BSIZE);
605
606 if (bp->b_flags & B_READ)
607 RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
608 else
609 RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
610 }
611
612 void
613 rlreset(struct device *dev)
614 {
615 struct rl_softc *rc = (struct rl_softc *)dev;
616 struct rlc_softc *sc = (struct rlc_softc *)rc->rc_dev.dv_parent;
617 u_int16_t mp;
618
619 if (rc->rc_state != DK_OPEN)
620 return;
621 RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
622 waitcrdy(sc);
623 mp = RL_RREG(RL_MP);
624 rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
625 rc->rc_cyl = (mp >> 7) & 0777;
626 if (sc->sc_active == 0)
627 return;
628
629 BUFQ_INSERT_HEAD(&sc->sc_q, sc->sc_active);
630 sc->sc_active = 0;
631 rlcstart(sc, 0);
632 }
633