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