1: /* 2: * SCCS id @(#)hp.c 2.1 (Berkeley) 8/31/83 3: */ 4: 5: /* 6: * RJP04/RWP04/RJP06/RWP06 disk driver 7: */ 8: #include "hp.h" 9: #if NHP > 0 10: #include "param.h" 11: #include <sys/systm.h> 12: #include <sys/buf.h> 13: #include <sys/conf.h> 14: #include <sys/dir.h> 15: #include <sys/user.h> 16: #include <sys/seg.h> 17: #include <sys/hpreg.h> 18: #ifndef INTRLVE 19: #include <sys/inline.h> 20: #endif 21: #include <sys/uba.h> 22: 23: #define HP_NSECT 22 24: #define HP_NTRAC 19 25: #define HP_SDIST 2 26: #define HP_RDIST 6 27: 28: extern struct size hp_sizes[]; 29: extern struct hpdevice *HPADDR; 30: 31: int hp_offset[] = 32: { 33: HPOF_P400, HPOF_M400, HPOF_P400, HPOF_M400, 34: HPOF_P800, HPOF_M800, HPOF_P800, HPOF_M800, 35: HPOF_P1200, HPOF_M1200, HPOF_P1200, HPOF_M1200, 36: 0, 0, 0, 0 37: }; 38: 39: struct buf hptab; 40: #ifdef UCB_DBUFS 41: struct buf rhpbuf[NHP]; 42: #else 43: struct buf rhpbuf; 44: #endif 45: struct buf hputab[NHP]; 46: 47: #ifdef INTRLVE 48: extern daddr_t dkblock(); 49: #endif 50: 51: void 52: hproot() 53: { 54: hpattach(HPADDR, 0); 55: } 56: 57: hpattach(addr, unit) 58: register struct hpdevice *addr; 59: { 60: if (unit != 0) 61: return(0); 62: if ((addr != (struct hpdevice *) NULL) && (fioword(addr) != -1)) { 63: HPADDR = addr; 64: #if PDP11 == 70 || PDP11 == GENERIC 65: if (fioword(&(addr->hpbae)) != -1) 66: hptab.b_flags |= B_RH70; 67: #endif 68: return(1); 69: } 70: HPADDR = (struct hpdevice *) NULL; 71: return(0); 72: } 73: 74: hpstrategy(bp) 75: register struct buf *bp; 76: { 77: register struct buf *dp; 78: register unit; 79: long bn; 80: 81: unit = minor(bp->b_dev) & 077; 82: if (unit >= (NHP << 3) || (HPADDR == (struct hpdevice *) NULL)) { 83: bp->b_error = ENXIO; 84: goto errexit; 85: } 86: if (bp->b_blkno < 0 || 87: (bn = dkblock(bp)) + (long) ((bp->b_bcount + 511) >> 9) 88: > hp_sizes[unit & 07].nblocks) { 89: bp->b_error = EINVAL; 90: errexit: 91: bp->b_flags |= B_ERROR; 92: iodone(bp); 93: return; 94: } 95: #ifdef UNIBUS_MAP 96: if ((hptab.b_flags & B_RH70) == 0) 97: mapalloc(bp); 98: #endif 99: bp->b_cylin = bn / (HP_NSECT * HP_NTRAC) + hp_sizes[unit & 07].cyloff; 100: unit = dkunit(bp); 101: dp = &hputab[unit]; 102: (void) _spl5(); 103: disksort(dp, bp); 104: if (dp->b_active == 0) { 105: hpustart(unit); 106: if (hptab.b_active == 0) 107: hpstart(); 108: } 109: (void) _spl0(); 110: } 111: 112: /* 113: * Unit start routine. 114: * Seek the drive to where the data are 115: * and then generate another interrupt 116: * to actually start the transfer. 117: * If there is only one drive on the controller 118: * or we are very close to the data, don't 119: * bother with the search. If called after 120: * searching once, don't bother to look 121: * where we are, just queue for transfer (to avoid 122: * positioning forever without transferring). 123: */ 124: hpustart(unit) 125: register unit; 126: { 127: register struct hpdevice *hpaddr = HPADDR; 128: register struct buf *dp; 129: struct buf *bp; 130: daddr_t bn; 131: int sn, cn, csn; 132: 133: hpaddr->hpcs2.w = unit; 134: hpaddr->hpcs1.c[0] = HP_IE; 135: hpaddr->hpas = 1 << unit; 136: 137: if (unit >= NHP) 138: return; 139: #ifdef HP_DKN 140: dk_busy &= ~(1 << (unit + HP_DKN)); 141: #endif HP_DKN 142: dp = &hputab[unit]; 143: if ((bp = dp->b_actf) == NULL) 144: return; 145: /* 146: * If we have already positioned this drive, 147: * then just put it on the ready queue. 148: */ 149: if (dp->b_active) 150: goto done; 151: dp->b_active++; 152: 153: /* 154: * If drive has just come up, 155: * set up the pack. 156: */ 157: if ((hpaddr->hpds & HPDS_VV) == 0) { 158: /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ 159: hpaddr->hpcs1.c[0] = HP_IE | HP_PRESET | HP_GO; 160: hpaddr->hpof = HPOF_FMT22; 161: } 162: 163: #if NHP > 1 164: /* 165: * If drive is offline, forget about positioning. 166: */ 167: if ((hpaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) 168: goto done; 169: 170: /* 171: * Figure out where this transfer is going to 172: * and see if we are close enough to justify not searching. 173: */ 174: bn = dkblock(bp); 175: cn = bp->b_cylin; 176: sn = bn % (HP_NSECT * HP_NTRAC); 177: sn = (sn + HP_NSECT - HP_SDIST) % HP_NSECT; 178: 179: if (hpaddr->hpcc != cn) 180: goto search; 181: csn = (hpaddr->hpla >> 6) - sn + HP_SDIST - 1; 182: if (csn < 0) 183: csn += HP_NSECT; 184: if (csn > HP_NSECT - HP_RDIST) 185: goto done; 186: 187: search: 188: hpaddr->hpdc = cn; 189: hpaddr->hpda = sn; 190: hpaddr->hpcs1.c[0] = HP_IE | HP_SEARCH | HP_GO; 191: #ifdef HP_DKN 192: /* 193: * Mark unit busy for iostat. 194: */ 195: unit += HP_DKN; 196: dk_busy |= 1 << unit; 197: dk_numb[unit]++; 198: #endif HP_DKN 199: return; 200: #endif NHP > 1 201: 202: done: 203: /* 204: * Device is ready to go. 205: * Put it on the ready queue for the controller. 206: */ 207: dp->b_forw = NULL; 208: if (hptab.b_actf == NULL) 209: hptab.b_actf = dp; 210: else 211: hptab.b_actl->b_forw = dp; 212: hptab.b_actl = dp; 213: } 214: 215: /* 216: * Start up a transfer on a drive. 217: */ 218: hpstart() 219: { 220: register struct hpdevice *hpaddr = HPADDR; 221: register struct buf *bp; 222: register unit; 223: struct buf *dp; 224: daddr_t bn; 225: int dn, sn, tn, cn; 226: 227: loop: 228: /* 229: * Pull a request off the controller queue. 230: */ 231: if ((dp = hptab.b_actf) == NULL) 232: return; 233: if ((bp = dp->b_actf) == NULL) { 234: hptab.b_actf = dp->b_forw; 235: goto loop; 236: } 237: /* 238: * Mark controller busy and 239: * determine destination of this request. 240: */ 241: hptab.b_active++; 242: unit = minor(bp->b_dev) & 077; 243: dn = dkunit(bp); 244: bn = dkblock(bp); 245: cn = bn / (HP_NSECT * HP_NTRAC) + hp_sizes[unit & 07].cyloff; 246: sn = bn % (HP_NSECT * HP_NTRAC); 247: tn = sn / HP_NSECT; 248: sn = sn % HP_NSECT; 249: 250: /* 251: * Select drive. 252: */ 253: hpaddr->hpcs2.w = dn; 254: /* 255: * Check that it is ready and online. 256: */ 257: if ((hpaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) { 258: hptab.b_active = 0; 259: hptab.b_errcnt = 0; 260: dp->b_actf = bp->av_forw; 261: bp->b_flags |= B_ERROR; 262: iodone(bp); 263: goto loop; 264: } 265: if (hptab.b_errcnt >= 16 && (bp->b_flags & B_READ)) { 266: hpaddr->hpof = hp_offset[hptab.b_errcnt & 017] | HPOF_FMT22; 267: hpaddr->hpcs1.w = HP_OFFSET | HP_GO; 268: while ((hpaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY) 269: ; 270: } 271: hpaddr->hpdc = cn; 272: hpaddr->hpda = (tn << 8) + sn; 273: hpaddr->hpba = bp->b_un.b_addr; 274: #if PDP11 == 70 || PDP11 == GENERIC 275: if (hptab.b_flags & B_RH70) 276: hpaddr->hpbae = bp->b_xmem; 277: #endif 278: hpaddr->hpwc = -(bp->b_bcount >> 1); 279: /* 280: * Warning: unit is being used as a temporary. 281: */ 282: unit = ((bp->b_xmem & 3) << 8) | HP_IE | HP_GO; 283: if (bp->b_flags & B_READ) 284: unit |= HP_RCOM; 285: else 286: unit |= HP_WCOM; 287: hpaddr->hpcs1.w = unit; 288: 289: #ifdef HP_DKN 290: dk_busy |= 1 << (HP_DKN + NHP); 291: dk_numb[HP_DKN + NHP]++; 292: dk_wds[HP_DKN + NHP] += bp->b_bcount >> 6; 293: #endif HP_DKN 294: } 295: 296: /* 297: * Handle a disk interrupt. 298: */ 299: hpintr() 300: { 301: register struct hpdevice *hpaddr = HPADDR; 302: register struct buf *dp; 303: register unit; 304: struct buf *bp; 305: int as, i, j; 306: 307: as = hpaddr->hpas & 0377; 308: if (hptab.b_active) { 309: #ifdef HP_DKN 310: dk_busy &= ~(1 << (HP_DKN + NHP)); 311: #endif HP_DKN 312: /* 313: * Get device and block structures. Select the drive. 314: */ 315: dp = hptab.b_actf; 316: bp = dp->b_actf; 317: unit = dkunit(bp); 318: hpaddr->hpcs2.c[0] = unit; 319: /* 320: * Check for and process errors. 321: */ 322: if (hpaddr->hpcs1.w & HP_TRE) { 323: while ((hpaddr->hpds & HPDS_DRY) == 0) 324: ; 325: if (hpaddr->hper1 & HPER1_WLE) { 326: /* 327: * Give up on write locked devices 328: * immediately. 329: */ 330: printf("hp%d: write locked\n", unit); 331: bp->b_flags |= B_ERROR; 332: } else { 333: /* 334: * After 28 retries (16 without offset and 335: * 12 with offset positioning), give up. 336: */ 337: if (++hptab.b_errcnt > 28) { 338: bp->b_flags |= B_ERROR; 339: #ifdef UCB_DEVERR 340: harderr(bp, "hp"); 341: printf("cs2=%b er1=%b\n", hpaddr->hpcs2.w, 342: HPCS2_BITS, hpaddr->hper1, HPER1_BITS); 343: #else 344: deverror(bp, hpaddr->hpcs2.w, hpaddr->hper1); 345: #endif 346: } else 347: hptab.b_active = 0; 348: } 349: #ifdef UCB_ECC 350: /* 351: * If soft ecc, correct it (continuing 352: * by returning if necessary). 353: * Otherwise, fall through and retry the transfer. 354: */ 355: if((hpaddr->hper1 & (HPER1_DCK|HPER1_ECH)) == HPER1_DCK) 356: if (hpecc(bp)) 357: return; 358: #endif 359: hpaddr->hpcs1.w = HP_TRE | HP_IE | HP_DCLR | HP_GO; 360: if ((hptab.b_errcnt & 07) == 4) { 361: hpaddr->hpcs1.w = HP_RECAL | HP_IE | HP_GO; 362: while ((hpaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY) 363: ; 364: } 365: } 366: if (hptab.b_active) { 367: if (hptab.b_errcnt) { 368: hpaddr->hpcs1.w = HP_RTC | HP_GO; 369: while ((hpaddr->hpds & (HPDS_PIP | HPDS_DRY)) != HPDS_DRY) 370: ; 371: } 372: hptab.b_active = 0; 373: hptab.b_errcnt = 0; 374: hptab.b_actf = dp->b_forw; 375: dp->b_active = 0; 376: dp->b_actf = bp->av_forw; 377: bp->b_resid = - (hpaddr->hpwc << 1); 378: iodone(bp); 379: hpaddr->hpcs1.w = HP_IE; 380: if (dp->b_actf) 381: hpustart(unit); 382: } 383: as &= ~(1 << unit); 384: } else 385: { 386: if (as == 0) 387: hpaddr->hpcs1.w = HP_IE; 388: hpaddr->hpcs1.c[1] = HP_TRE >> 8; 389: } 390: for (unit = 0; unit < NHP; unit++) 391: if (as & (1 << unit)) 392: hpustart(unit); 393: hpstart(); 394: } 395: 396: hpread(dev) 397: dev_t dev; 398: { 399: #ifdef UCB_DBUFS 400: register int unit = (minor(dev) >> 3) & 07; 401: 402: if (unit >= NHP) 403: u.u_error = ENXIO; 404: else 405: physio(hpstrategy, &rhpbuf[unit], dev, B_READ); 406: #else 407: physio(hpstrategy, &rhpbuf, dev, B_READ); 408: #endif 409: } 410: 411: hpwrite(dev) 412: dev_t dev; 413: { 414: #ifdef UCB_DBUFS 415: register int unit = (minor(dev) >> 3) & 07; 416: 417: if (unit >= NHP) 418: u.u_error = ENXIO; 419: else 420: physio(hpstrategy, &rhpbuf[unit], dev, B_WRITE); 421: #else 422: physio(hpstrategy, &rhpbuf, dev, B_WRITE); 423: #endif 424: } 425: 426: #ifdef UCB_ECC 427: #define exadr(x,y) (((long)(x) << 16) | (unsigned)(y)) 428: 429: /* 430: * Correct an ECC error and restart the i/o to complete 431: * the transfer if necessary. This is quite complicated because 432: * the correction may be going to an odd memory address base 433: * and the transfer may cross a sector boundary. 434: */ 435: hpecc(bp) 436: register struct buf *bp; 437: { 438: register struct hpdevice *hpaddr = HPADDR; 439: register unsigned byte; 440: ubadr_t bb, addr; 441: long wrong; 442: int bit, wc; 443: unsigned ndone, npx; 444: int ocmd; 445: int cn, tn, sn; 446: daddr_t bn; 447: #ifdef UNIBUS_MAP 448: struct ubmap *ubp; 449: #endif 450: 451: /* 452: * ndone is #bytes including the error 453: * which is assumed to be in the last disk page transferred. 454: */ 455: wc = hpaddr->hpwc; 456: ndone = (wc * NBPW) + bp->b_bcount; 457: npx = ndone / PGSIZE; 458: printf("hp%d%c: soft ecc bn %D\n", 459: dkunit(bp), 'a' + (minor(bp->b_dev) & 07), 460: bp->b_blkno + (npx - 1)); 461: wrong = hpaddr->hpec2; 462: if (wrong == 0) { 463: hpaddr->hpof = HPOF_FMT22; 464: hpaddr->hpcs1.w |= HP_IE; 465: return (0); 466: } 467: 468: /* 469: * Compute the byte/bit position of the err 470: * within the last disk page transferred. 471: * Hpec1 is origin-1. 472: */ 473: byte = hpaddr->hpec1 - 1; 474: bit = byte & 07; 475: byte >>= 3; 476: byte += ndone - PGSIZE; 477: bb = exadr(bp->b_xmem, bp->b_un.b_addr); 478: wrong <<= bit; 479: 480: /* 481: * Correct until mask is zero or until end of transfer, 482: * whichever comes first. 483: */ 484: while (byte < bp->b_bcount && wrong != 0) { 485: addr = bb + byte; 486: #ifdef UNIBUS_MAP 487: if (bp->b_flags & (B_MAP|B_UBAREMAP)) { 488: /* 489: * Simulate UNIBUS map if UNIBUS transfer. 490: */ 491: ubp = UBMAP + ((addr >> 13) & 037); 492: addr = exadr(ubp->ub_hi, ubp->ub_lo) + (addr & 017777); 493: } 494: #endif 495: putmemc(addr, getmemc(addr) ^ (int) wrong); 496: byte++; 497: wrong >>= 8; 498: } 499: 500: hptab.b_active++; 501: if (wc == 0) 502: return (0); 503: 504: /* 505: * Have to continue the transfer. Clear the drive 506: * and compute the position where the transfer is to continue. 507: * We have completed npx sectors of the transfer already. 508: */ 509: ocmd = (hpaddr->hpcs1.w & ~HP_RDY) | HP_IE | HP_GO; 510: hpaddr->hpcs2.w = dkunit(bp); 511: hpaddr->hpcs1.w = HP_TRE | HP_DCLR | HP_GO; 512: 513: bn = dkblock(bp); 514: cn = bp->b_cylin - bn / (HP_NSECT * HP_NTRAC); 515: bn += npx; 516: addr = bb + ndone; 517: 518: cn += bn / (HP_NSECT * HP_NTRAC); 519: sn = bn % (HP_NSECT * HP_NTRAC); 520: tn = sn / HP_NSECT; 521: sn %= HP_NSECT; 522: 523: hpaddr->hpdc = cn; 524: hpaddr->hpda = (tn << 8) + sn; 525: hpaddr->hpwc = ((int)(ndone - bp->b_bcount)) / NBPW; 526: hpaddr->hpba = (int) addr; 527: #if PDP11 == 70 || PDP11 == GENERIC 528: if (hptab.b_flags & B_RH70) 529: hpaddr->hpbae = (int) (addr >> 16); 530: #endif 531: hpaddr->hpcs1.w = ocmd; 532: return (1); 533: } 534: #endif UCB_ECC 535: 536: #if defined(HP_DUMP) && defined(UCB_AUTOBOOT) 537: /* 538: * Dump routine for RP04/05/06. 539: * Dumps from dumplo to end of memory/end of disk section for minor(dev). 540: * It uses the UNIBUS map to dump all of memory if there is a UNIBUS map 541: * and this isn't an RH70. This depends on UNIBUS_MAP being defined. 542: */ 543: 544: #ifdef UNIBUS_MAP 545: #define DBSIZE (UBPAGE/PGSIZE) /* unit of transfer, one UBPAGE */ 546: #else 547: #define DBSIZE 16 /* unit of transfer, same number */ 548: #endif 549: 550: hpdump(dev) 551: dev_t dev; 552: { 553: register struct hpdevice *hpaddr = HPADDR; 554: daddr_t bn, dumpsize; 555: long paddr; 556: register sn; 557: register count; 558: #ifdef UNIBUS_MAP 559: extern bool_t ubmap; 560: register struct ubmap *ubp; 561: #endif 562: 563: if ((bdevsw[major(dev)].d_strategy != hpstrategy) /* paranoia */ 564: || ((dev=minor(dev)) > (NHP << 3))) 565: return(EINVAL); 566: dumpsize = hp_sizes[dev & 07].nblocks; 567: if ((dumplo < 0) || (dumplo >= dumpsize)) 568: return(EINVAL); 569: dumpsize -= dumplo; 570: 571: hpaddr->hpcs2.w = dev >> 3; 572: if ((hpaddr->hpds & HPDS_VV) == 0) { 573: hpaddr->hpcs1.w = HP_DCLR | HP_GO; 574: hpaddr->hpcs1.w = HP_PRESET | HP_GO; 575: hpaddr->hpof = HPOF_FMT22; 576: } 577: if ((hpaddr->hpds & (HPDS_DPR | HPDS_MOL)) != (HPDS_DPR | HPDS_MOL)) 578: return(EFAULT); 579: dev &= 07; 580: #ifdef UNIBUS_MAP 581: ubp = &UBMAP[0]; 582: #endif 583: for (paddr = 0L; dumpsize > 0; dumpsize -= count) { 584: count = dumpsize>DBSIZE? DBSIZE: dumpsize; 585: bn = dumplo + (paddr >> PGSHIFT); 586: hpaddr->hpdc = bn / (HP_NSECT*HP_NTRAC) + hp_sizes[dev].cyloff; 587: sn = bn % (HP_NSECT * HP_NTRAC); 588: hpaddr->hpda = ((sn / HP_NSECT) << 8) | (sn % HP_NSECT); 589: hpaddr->hpwc = -(count << (PGSHIFT - 1)); 590: #ifdef UNIBUS_MAP 591: /* 592: * If UNIBUS_MAP exists, use 593: * the map, unless on an 11/70 with RH70. 594: */ 595: if (ubmap && ((hptab.b_flags & B_RH70) == 0)) { 596: ubp->ub_lo = loint(paddr); 597: ubp->ub_hi = hiint(paddr); 598: hpaddr->hpba = 0; 599: hpaddr->hpcs1.w = HP_WCOM | HP_GO; 600: } 601: else 602: #endif 603: { 604: /* 605: * Non-UNIBUS map, or 11/70 RH70 (MASSBUS) 606: */ 607: hpaddr->hpba = loint(paddr); 608: #if PDP11 == 70 || PDP11 == GENERIC 609: if (hptab.b_flags & B_RH70) 610: hpaddr->hpbae = hiint(paddr); 611: #endif 612: hpaddr->hpcs1.w = HP_WCOM | HP_GO | ((paddr >> 8) & (03 << 8)); 613: } 614: while (hpaddr->hpcs1.w & HP_GO) 615: ; 616: if (hpaddr->hpcs1.w & HP_TRE) { 617: if (hpaddr->hpcs2.w & HPCS2_NEM) 618: return(0); /* made it to end of memory */ 619: return(EIO); 620: } 621: paddr += (DBSIZE << PGSHIFT); 622: } 623: return(0); /* filled disk minor dev */ 624: } 625: #endif HP_DUMP 626: #endif NHP