1 /* $NetBSD: rl.c,v 1.9 2001/06/19 13:42:18 wiz 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 = splbio(); 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 return ENOTTY; 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-existent 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