1: /* 2: * Copyright (c) 1986 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: * 6: * @(#)si.c 1.5 (2.11BSD GTE) 1995/04/13 7: */ 8: 9: /* 10: * SI 9500 driver for CDC 9766 disks 11: */ 12: 13: #include "si.h" 14: #if NSI > 0 15: #include "param.h" 16: #include "../machine/seg.h" 17: 18: #include "systm.h" 19: #include "buf.h" 20: #include "conf.h" 21: #include "sireg.h" 22: #include "map.h" 23: #include "uba.h" 24: #include "dk.h" 25: #include "disklabel.h" 26: #include "disk.h" 27: #include "xp.h" 28: #include "errno.h" 29: 30: #define SI_NSECT 32 31: #define SI_NTRAC 19 32: 33: #if NXPD > 0 34: extern struct size { 35: daddr_t nblocks; 36: int cyloff; 37: } rm5_sizes[8]; 38: #else !NXPD 39: /* 40: * We reserve room for bad block forwarding information even though this 41: * driver doesn't support bad block forwarding. This allows us to simply 42: * copy the table from xp.c. 43: */ 44: struct size { 45: daddr_t nblocks; 46: int cyloff; 47: } rm5_sizes[8] = { /* RM05, CDC 9766 */ 48: 9120, 0, /* a: cyl 0 - 14 */ 49: 9120, 15, /* b: cyl 15 - 29 */ 50: 234080, 30, /* c: cyl 30 - 414 */ 51: 247906, 415, /* d: cyl 415 - 822, reserve 1 track + 126 */ 52: 164160, 30, /* e: cyl 30 - 299 */ 53: 152000, 300, /* f: cyl 300 - 549 */ 54: 165826, 550, /* g: cyl 550 - 822, reserve 1 track + 126 */ 55: 500384, 0, /* h: cyl 0 - 822 */ 56: }; 57: #endif NXPD 58: 59: struct sidevice *SIADDR; 60: 61: int si_offset[] = { SI_OFP, SI_OFM }; 62: 63: struct buf sitab; 64: struct buf siutab[NSI]; 65: 66: int sicc[NSI]; /* Current cylinder */ 67: int dualsi = 0; /* dual port flag */ 68: 69: #ifdef UCB_METER 70: static int si_dkn = -1; /* number for iostat */ 71: #endif 72: 73: void 74: siroot() 75: { 76: siattach((struct sidevice *)0176700, 0); 77: } 78: 79: siattach(addr, unit) 80: register struct sidevice *addr; 81: { 82: #ifdef UCB_METER 83: if (si_dkn < 0) { 84: dk_alloc(&si_dkn, NSI+1, "si", 60L * 32L * 256L); 85: if (si_dkn >= 0) 86: dk_wps[si_dkn+NSI] = 0L; 87: } 88: #endif 89: 90: if (unit != 0) 91: return(0); 92: if ((addr != (struct sidevice *) NULL) && (fioword(addr) != -1)) 93: { 94: SIADDR = addr; 95: if(addr ->siscr != 0) 96: dualsi++; /* dual port controller */ 97: if((addr->sierr & (SIERR_ERR | SIERR_CNT)) == (SIERR_ERR | SIERR_CNT)) 98: dualsi++; 99: return(1); 100: } 101: SIADDR = (struct sidevice *) NULL; 102: return(0); 103: } 104: 105: siopen(dev, flag) 106: dev_t dev; 107: int flag; 108: { 109: register int unit; 110: 111: unit = minor(dev) & 077; 112: if (unit >= (NSI << 3) || !SIADDR) 113: return (ENXIO); 114: return (0); 115: } 116: 117: sistrategy(bp) 118: register struct buf *bp; 119: { 120: register struct buf *dp; 121: register int unit; 122: long bn; 123: int s; 124: 125: unit = minor(bp->b_dev) & 077; 126: if (unit >= (NSI << 3) || (SIADDR == (struct sidevice *) NULL)) { 127: bp->b_error = ENXIO; 128: goto errexit; 129: } 130: if (bp->b_blkno < 0 || 131: (bn = bp->b_blkno) + (long) ((bp->b_bcount + 511) >> 9) 132: > rm5_sizes[unit & 07].nblocks) { 133: bp->b_error = EINVAL; 134: errexit: 135: bp->b_flags |= B_ERROR; 136: iodone(bp); 137: return; 138: } 139: mapalloc(bp); 140: bp->b_cylin = bn / (SI_NSECT * SI_NTRAC) + rm5_sizes[unit & 07].cyloff; 141: unit = dkunit(bp->b_dev); 142: dp = &siutab[unit]; 143: s = splbio(); 144: disksort(dp, bp); 145: if (dp->b_active == 0) { 146: siustart(unit); 147: if (sitab.b_active == 0) 148: sistart(); 149: } 150: splx(s); 151: } 152: 153: /* 154: * Unit start routine. 155: * Seek the drive to where the data is 156: * and then generate another interrupt 157: * to actually start the transfer. 158: * If there is only one drive on the controller 159: * or we are very close to the data, don't 160: * bother with the search. If called after 161: * searching once, don't bother to look 162: * where we are, just queue for transfer (to avoid 163: * positioning forever without transferring). 164: */ 165: siustart(unit) 166: register unit; 167: { 168: register struct sidevice *siaddr = SIADDR; 169: register struct buf *dp; 170: struct buf *bp; 171: daddr_t bn; 172: int sn, cn, csn; 173: 174: if (unit >= NSI) 175: return; 176: #ifdef UCB_METER 177: if (si_dkn >= 0) 178: dk_busy &= ~(1 << (si_dkn + unit)); 179: #endif 180: dp = &siutab[unit]; 181: if ((bp = dp->b_actf) == NULL) 182: return; 183: /* 184: * If we have already positioned this drive, 185: * then just put it on the ready queue. 186: */ 187: if (dp->b_active) 188: goto done; 189: dp->b_active++; 190: 191: #if NSI > 1 192: /* 193: * If drive is not ready, forget about positioning. 194: */ 195: if(dualsi) 196: getsi(); 197: if ((siaddr->sissr >> (unit*4)) & SISSR_NRDY) 198: goto done; 199: 200: /* 201: * Figure out where this transfer is going to 202: * and see if we are close enough to justify not searching. 203: */ 204: bn = bp->b_blkno; 205: cn = bp->b_cylin; 206: sn = bn % (SI_NSECT * SI_NTRAC); 207: sn = (sn + SI_NSECT) % SI_NSECT; 208: 209: if (sicc[unit] == cn) 210: goto done; 211: 212: search: 213: siaddr->sisar = (unit << 10) | cn; 214: if(dualsi) 215: SIADDR->siscr = 0; 216: sicc[unit] = cn; 217: #ifdef UCB_METER 218: /* 219: * Mark unit busy for iostat. 220: */ 221: if (si_dkn >= 0) { 222: int dkn = si_dkn + unit; 223: 224: dk_busy |= 1<<dkn; 225: dk_seek[dkn]++; 226: } 227: #endif 228: return; 229: #endif NSI > 1 230: 231: done: 232: /* 233: * Device is ready to go. 234: * Put it on the ready queue for the controller. 235: */ 236: dp->b_forw = NULL; 237: if (sitab.b_actf == NULL) 238: sitab.b_actf = dp; 239: else 240: sitab.b_actl->b_forw = dp; 241: sitab.b_actl = dp; 242: } 243: 244: /* 245: * Start up a transfer on a drive. 246: */ 247: sistart() 248: { 249: register struct sidevice *siaddr = SIADDR; 250: register struct buf *bp; 251: register unit; 252: struct buf *dp; 253: daddr_t bn; 254: int dn, sn, tn, cn; 255: int offset; 256: 257: loop: 258: /* 259: * Pull a request off the controller queue. 260: */ 261: if ((dp = sitab.b_actf) == NULL) 262: return; 263: if ((bp = dp->b_actf) == NULL) { 264: sitab.b_actf = dp->b_forw; 265: goto loop; 266: } 267: /* 268: * Mark controller busy and 269: * determine destination of this request. 270: */ 271: sitab.b_active++; 272: unit = minor(bp->b_dev) & 077; 273: dn = dkunit(bp->b_dev); 274: bn = bp->b_blkno; 275: cn = bn / (SI_NSECT * SI_NTRAC) + rm5_sizes[unit & 07].cyloff; 276: sn = bn % (SI_NSECT * SI_NTRAC); 277: tn = sn / SI_NSECT; 278: sn = sn % SI_NSECT; 279: 280: /* 281: * Check that drive is ready. 282: */ 283: if(dualsi) 284: getsi(); 285: if ((siaddr->sissr >> (dn*4)) & SISSR_NRDY) { 286: sitab.b_active = 0; 287: sitab.b_errcnt = 0; 288: dp->b_actf = bp->av_forw; 289: bp->b_flags |= B_ERROR; 290: iodone(bp); 291: goto loop; 292: } 293: 294: offset = 0; 295: if (sitab.b_errcnt >= 16 && (bp->b_flags & B_READ)) 296: offset = si_offset[sitab.b_errcnt & 1]; 297: 298: siaddr->sipcr = (dn << 10) | cn; 299: siaddr->sihsr = (tn << 5) + sn; 300: siaddr->simar = bp->b_un.b_addr; 301: siaddr->siwcr = bp->b_bcount >> 1; 302: /* 303: * Warning: unit is being used as a temporary. 304: */ 305: unit = offset | ((bp->b_xmem & 3) << 4) | SI_IE | SI_GO; 306: if (bp->b_flags & B_READ) 307: unit |= SI_READ; 308: else 309: unit |= SI_WRITE; 310: siaddr->sicnr = unit; 311: 312: #ifdef UCB_METER 313: if (si_dkn >= 0) { 314: int dkn = si_dkn + NSI; 315: 316: dk_busy |= 1<<dkn; 317: dk_seek[dkn]++; 318: dk_wds[dkn] += bp->b_bcount >> 6; 319: } 320: #endif 321: } 322: 323: /* 324: * Handle a disk interrupt. 325: */ 326: siintr() 327: { 328: register struct sidevice *siaddr = SIADDR; 329: register struct buf *dp; 330: register unit; 331: struct buf *bp; 332: int ss, ssr; 333: 334: ssr = siaddr->sissr; 335: if (sitab.b_active) { 336: #ifdef UCB_METER 337: if (si_dkn >= 0) 338: dk_busy &= ~(1 << (si_dkn + NSI)); 339: #endif 340: /* 341: * Get device and block structures. 342: */ 343: dp = sitab.b_actf; 344: bp = dp->b_actf; 345: unit = dkunit(bp->b_dev); 346: /* 347: * Check for and process errors. 348: */ 349: if (siaddr->sierr & SIERR_ERR) { 350: /* 351: * After 18 retries (16 without offset and 352: * 2 with offset positioning), give up. 353: */ 354: if (++sitab.b_errcnt > 18) { 355: bp->b_flags |= B_ERROR; 356: harderr(bp, "si"); 357: printf("cnr=%b err=%b\n", siaddr->sicnr, 358: SI_BITS, siaddr->sierr, SIERR_BITS); 359: } else 360: sitab.b_active = 0; 361: 362: siaddr->sicnr = SI_RESET; 363: if(dualsi) 364: getsi(); 365: siaddr->sicnr = SI_IE; 366: 367: sicc[unit] = -1; 368: } 369: 370: if(dualsi) 371: SIADDR->siscr = 0; 372: if (sitab.b_active) { 373: sitab.b_active = 0; 374: sitab.b_errcnt = 0; 375: sitab.b_actf = dp->b_forw; 376: dp->b_active = 0; 377: dp->b_actf = bp->av_forw; 378: bp->b_resid = ~siaddr->siwcr; 379: if (bp->b_resid == 0xffff) bp->b_resid = 0; 380: bp->b_resid <<= 1; 381: iodone(bp); 382: if (dp->b_actf) 383: siustart(unit); 384: } 385: } 386: 387: for (unit = 0; unit < NSI; unit++) { 388: ss = (ssr >> unit*4) & 017; 389: if (ss & (SISSR_BUSY|SISSR_ERR)) { 390: harderr(bp, "si"); 391: printf("ssr=%b err=%b\n", ssr, 392: SISSR_BITS, siaddr->sierr, SIERR_BITS); 393: sicc[unit] = -1; 394: siustart(unit); 395: } 396: else if (ss & SISSR_DONE) 397: siustart(unit); 398: } 399: 400: sistart(); 401: } 402: 403: #ifdef SI_DUMP 404: /* 405: * Dump routine for SI 9500 406: * Dumps from dumplo to end of memory/end of disk section for minor(dev). 407: */ 408: 409: #define DBSIZE 16 /* number of blocks to write */ 410: 411: sidump(dev) 412: dev_t dev; 413: { 414: register struct sidevice *siaddr = SIADDR; 415: daddr_t bn, dumpsize; 416: long paddr; 417: register count; 418: register struct ubmap *ubp; 419: int cn, tn, sn, unit; 420: 421: unit = minor(dev) >> 3; 422: if ((bdevsw[major(dev)].d_strategy != sistrategy) /* paranoia */ 423: || unit > NSI) 424: return(EINVAL); 425: dumpsize = rm5_sizes[dev & 07].nblocks; 426: if ((dumplo < 0) || (dumplo >= dumpsize)) 427: return(EINVAL); 428: dumpsize -= dumplo; 429: 430: /* 431: * reset the 9500 432: */ 433: siaddr->sicnr = SI_RESET; 434: ubp = &UBMAP[0]; 435: for (paddr = 0L; dumpsize > 0; dumpsize -= count) { 436: count = dumpsize>DBSIZE? DBSIZE: dumpsize; 437: bn = dumplo + (paddr >> PGSHIFT); 438: cn = (bn/(SI_NSECT*SI_NTRAC)) + rm5_sizes[minor(dev)&07].cyloff; 439: sn = bn % (SI_NSECT * SI_NTRAC); 440: tn = sn / SI_NSECT; 441: sn = sn % SI_NSECT; 442: if(dualsi) 443: getsi(); 444: siaddr->sipcr = (unit << 10) | cn; 445: siaddr->sihsr = (tn << 5) + sn; 446: siaddr->siwcr = count << (PGSHIFT-1); 447: 448: if (ubmap) { 449: ubp->ub_lo = loint(paddr); 450: ubp->ub_hi = hiint(paddr); 451: siaddr->simar = 0; 452: siaddr->sicnr = SI_WRITE | SI_GO; 453: } 454: else 455: { 456: siaddr->simar = loint(paddr); 457: siaddr->sicnr = SI_WRITE|SI_GO|((paddr >> 8)&(03 << 4)); 458: } 459: while (!(siaddr->sicnr & SI_DONE)) 460: ; 461: if (siaddr->sierr & SIERR_ERR) { 462: if (siaddr->sierr & SIERR_TIMO) 463: return(0); /* made it to end of memory */ 464: return(EIO); 465: } 466: paddr += (DBSIZE << PGSHIFT); 467: } 468: return(0); /* filled disk minor dev */ 469: } 470: #endif SI_DUMP 471: 472: getsi() 473: { 474: register struct sidevice *siaddr = SIADDR; 475: 476: while(!(siaddr->siscr & 0200)) 477: { 478: siaddr->sicnr = SI_RESET; 479: siaddr->siscr = 1; 480: } 481: } 482: 483: /* 484: * Simple minded but effective. Likely none of these are still around or in use. 485: */ 486: daddr_t 487: sisize(dev) 488: dev_t dev; 489: { 490: 491: return(rm5_sizes[dev & 07].nblocks); 492: } 493: #endif NSI