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