1*272Sbill int trc = -1; 2*272Sbill int trcw = 0; 3*272Sbill #define D(i) if (trc&(1<<i)) { if (trcw&(1<<i)) DELAY(1000); } else 4271Sbill int csdel0 = 30; 5*272Sbill int csdel1 = 0; 6*272Sbill int csdel2 = 0; 7268Sbill int asdel = 500; 8*272Sbill int csdel3 = 100; 9*272Sbill int softas; 10*272Sbill /* 10/14/12 3.8 06/19/80 */ 11264Sbill 12264Sbill /* 13264Sbill * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery. 14264Sbill * 15266Sbill * NB: This device is very sensitive: be aware that the code is the way 16266Sbill * it is for good reason and that there are delay loops here which may 17266Sbill * have to be lengthened if your processor is faster and which should 18266Sbill * probably be shortened if your processor is slower. 19266Sbill * 20264Sbill * This driver has been tested on a SC-11B Controller, configured 21264Sbill * with the following internal switch settings: 22264Sbill * SW1-1 5/19 surfaces (off, 19 surfaces on Ampex 9300) 23264Sbill * SW1-2 chksum enable (off, checksum disabled) 24264Sbill * SW1-3 volume select (off, 815 cylinders) 25264Sbill * SW1-4 sector select (on, 32 sectors) 26264Sbill * SW1-5 unused (off) 27264Sbill * SW1-6 port select (on, single port) 28264Sbill * SW1-7 npr delay (off, disable) 29264Sbill * SW1-8 ecc test mode (off, disable) 30264Sbill * and top mounted switches: 31264Sbill * SW2-1 extend opcodes (off=open, disable) 32264Sbill * SW2-2 extend diag (off=open, disable) 33264Sbill * SW2-3 4 wd dma burst (off=open, disable) 34264Sbill * SW2-4 unused (off=open) 35264Sbill * 36264Sbill * The controller transfers data much more rapidly with SW2-3 set, 37264Sbill * but we have previously experienced problems with it set this way. 38264Sbill * We intend to try this again in the near future. 39264Sbill * 40264Sbill * wnj June 14, 1980 41264Sbill */ 42264Sbill 43264Sbill #include "../h/param.h" 44264Sbill #include "../h/systm.h" 45264Sbill #include "../h/buf.h" 46264Sbill #include "../h/conf.h" 47264Sbill #include "../h/dir.h" 48264Sbill #include "../h/user.h" 49264Sbill #include "../h/map.h" 50264Sbill #include "../h/mba.h" 51264Sbill #include "../h/mtpr.h" 52264Sbill #include "../h/pte.h" 53264Sbill #include "../h/uba.h" 54264Sbill #include "../h/vm.h" 55264Sbill 56264Sbill /* 57264Sbill * Define number of drives, and range of sampling information to be used. 58264Sbill * 59264Sbill * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats, 60264Sbill * and DK_N+NUP gathers controller transferring stats. 61264Sbill * 62264Sbill * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive. 63264Sbill * If DK_NMAX is yet smaller, some drives are not monitored. 64264Sbill */ 65264Sbill #define DK_N 1 66264Sbill #define DK_NMAX 2 67264Sbill 68264Sbill #define ushort unsigned short 69264Sbill 70264Sbill struct device 71264Sbill { 72264Sbill ushort upcs1; /* control and status register 1 */ 73264Sbill short upwc; /* word count register */ 74264Sbill ushort upba; /* UNIBUS address register */ 75264Sbill ushort upda; /* desired address register */ 76264Sbill ushort upcs2; /* control and status register 2 */ 77264Sbill ushort upds; /* drive Status */ 78264Sbill ushort uper1; /* error register 1 */ 79264Sbill ushort upas; /* attention summary */ 80264Sbill ushort upla; /* look ahead */ 81264Sbill ushort updb; /* data buffer */ 82264Sbill ushort upmr; /* maintenance */ 83264Sbill ushort updt; /* drive type */ 84264Sbill ushort upsn; /* serial number */ 85264Sbill ushort upof; /* offset register */ 86264Sbill ushort updc; /* desired cylinder address register */ 87264Sbill ushort upcc; /* current cylinder */ 88264Sbill ushort uper2; /* error register 2 */ 89264Sbill ushort uper3; /* error register 3 */ 90264Sbill ushort upec1; /* burst error bit position */ 91264Sbill ushort upec2; /* burst error bit pattern */ 92264Sbill }; 93264Sbill 94264Sbill #define UPADDR ((struct device *)(UBA0_DEV + 0176700)) 95264Sbill 96264Sbill #define NUP 2 /* Number of drives this installation */ 97264Sbill 98264Sbill #define NSECT 32 99264Sbill #define NTRAC 19 100264Sbill 101264Sbill /* 102264Sbill * Constants controlling on-cylinder SEARCH usage. 103264Sbill * 104264Sbill * We assume that it takes SDIST sectors of time to set up a transfer. 105264Sbill * If a drive is on-cylinder, and between SDIST and SDIST+RDIST sectors 106264Sbill * from the first sector to be transferred, then we just perform the 107264Sbill * transfer. SDIST represents interrupt latency, RDIST the amount 108264Sbill * of rotation which is tolerable to avoid another interrupt. 109264Sbill */ 110266Sbill #define SDIST 3 /* 2-3 sectors 1-1.5 msec */ 111266Sbill #define RDIST 6 /* 5-6 sectors 2.5-3 msec */ 112264Sbill 113264Sbill /* 114264Sbill * To fill a 300M drive: 115264Sbill * A is designed to be used as a root. 116264Sbill * B is suitable for a swap area. 117264Sbill * H is the primary storage area. 118264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 119264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 120264Sbill * covers the whole drive and can be used for pack-pack copying. 121264Sbill */ 122264Sbill struct size 123264Sbill { 124264Sbill daddr_t nblocks; 125264Sbill int cyloff; 126264Sbill } up_sizes[8] = { 127264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 128264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 129264Sbill 494912, 0, /* C=cyl 0 thru 814 */ 130264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 131264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 132264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 133264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 134264Sbill 445664, 82, /* H=cyl 82 thru 814 */ 135264Sbill /* Later, and more safely for H area... 136264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 137264Sbill }; 138264Sbill 139264Sbill /* 140264Sbill * The following defines are used in offset positioning 141264Sbill * when trying to recover disk errors, with the constants being 142264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 143264Sbill * tried (this makes sense only during read, in any case.) 144264Sbill * 145264Sbill * ARE ALL THESE IMPLEMENTED ON 9300? 146264Sbill */ 147264Sbill #define P400 020 148264Sbill #define M400 0220 149264Sbill #define P800 040 150264Sbill #define M800 0240 151264Sbill #define P1200 060 152264Sbill #define M1200 0260 153264Sbill #define HCI 020000 154264Sbill 155264Sbill int up_offset[16] = 156264Sbill { 157264Sbill P400, M400, P400, M400, 158264Sbill P800, M800, P800, M800, 159264Sbill P1200, M1200, P1200, M1200, 160264Sbill 0, 0, 0, 0, 161264Sbill }; 162264Sbill 163264Sbill /* 164264Sbill * Each drive has a table uputab[i]. On this table are sorted the 165264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 166264Sbill * In the upustart() routine, each drive is independently advanced 167264Sbill * until it is on the desired cylinder for the next transfer and near 168264Sbill * the desired sector. The drive is then chained onto the uptab 169264Sbill * table, and the transfer is initiated by the upstart() routine. 170264Sbill * When the transfer is completed the driver reinvokes the upustart() 171264Sbill * routine to set up the next transfer. 172264Sbill */ 173264Sbill struct buf uptab; 174264Sbill struct buf uputab[NUP]; 175264Sbill 176264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 177264Sbill 178264Sbill /* Drive commands, placed in upcs1 */ 179264Sbill #define GO 01 /* Go bit, set in all commands */ 180264Sbill #define PRESET 020 /* Preset drive at init or after errors */ 181264Sbill #define OFFSET 014 /* Offset heads to try to recover error */ 182264Sbill #define RTC 016 /* Return to center-line after OFFSET */ 183264Sbill #define SEARCH 030 /* Search for cylinder+sector */ 184264Sbill #define RECAL 06 /* Recalibrate, needed after seek error */ 185264Sbill #define DCLR 010 /* Drive clear, after error */ 186264Sbill #define WCOM 060 /* Write */ 187264Sbill #define RCOM 070 /* Read */ 188264Sbill 189264Sbill /* Other bits of upcs1 */ 190264Sbill #define IE 0100 /* Controller wide interrupt enable */ 191264Sbill #define TRE 040000 /* Transfer error */ 192266Sbill #define RDY 020 /* Transfer terminated */ 193264Sbill 194264Sbill /* Drive status bits of upds */ 195264Sbill #define PIP 020000 /* Positioning in progress */ 196264Sbill #define ERR 040000 /* Error has occurred, DCLR necessary */ 197264Sbill #define VV 0100 /* Volume is valid, set by PRESET */ 198264Sbill #define DPR 0400 /* Drive has been preset */ 199264Sbill #define MOL 010000 /* Drive is online, heads loaded, etc */ 200264Sbill #define DRY 0200 /* Drive ready */ 201264Sbill 202264Sbill /* Bits of uper1 */ 203264Sbill #define DCK 0100000 /* Ecc error occurred */ 204264Sbill #define ECH 0100 /* Ecc error was unrecoverable */ 205264Sbill #define WLE 04000 /* Attempt to write read-only drive */ 206264Sbill 207264Sbill /* Bits of upof; the offset bits above are also in this register */ 208264Sbill #define FMT22 010000 /* 16 bits/word, must be always set */ 209264Sbill 210264Sbill #define b_cylin b_resid 211264Sbill 212264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 213264Sbill /* 214264Sbill * The EMULEX controller balks if accessed quickly after 215264Sbill * certain operations. The exact timing has not yet been 216264Sbill * determined, but delays are known to be needed when changing 217264Sbill * the selected drive (by writing in upcs2), and thought to be 218264Sbill * needed after operations like PRESET and DCLR. The following 219264Sbill * variables control the delay, DELAY(n) is approximately n usec. 220264Sbill */ 221264Sbill int idelay = 500; /* Delay after PRESET or DCLR */ 222268Sbill int sdelay = 150; /* Delay after selecting drive in upcs2 */ 223264Sbill 224264Sbill #define DELAY(N) { register int d; d = N; while (--d > 0); } 225264Sbill 226264Sbill int nwaitcs2; /* How many sdelay loops ? */ 227264Sbill int neasycs2; /* How many sdelay loops not needed ? */ 228264Sbill 229264Sbill #ifdef INTRLVE 230264Sbill daddr_t dkblock(); 231264Sbill #endif 232264Sbill 233264Sbill /* 234264Sbill * Queue an i/o request for a drive, checking first that it is in range. 235264Sbill * 236264Sbill * A unit start is issued if the drive is inactive, causing 237264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 238264Sbill * already nearly on the money and the controller is not transferring 239264Sbill * we kick it to start the transfer. 240264Sbill */ 241264Sbill upstrategy(bp) 242264Sbill register struct buf *bp; 243264Sbill { 244264Sbill register struct buf *dp; 245264Sbill register unit, xunit; 246264Sbill long sz, bn; 247264Sbill 248264Sbill xunit = minor(bp->b_dev) & 077; 249264Sbill sz = bp->b_bcount; 250264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 251264Sbill unit = dkunit(bp); 252264Sbill if (unit >= NUP || 253264Sbill bp->b_blkno < 0 || 254264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 255264Sbill bp->b_flags |= B_ERROR; 256264Sbill iodone(bp); 257264Sbill return; 258264Sbill } 259264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 260264Sbill dp = &uputab[unit]; 261264Sbill (void) spl5(); 262264Sbill disksort(dp, bp); 263*272Sbill #ifdef UTRACE 264*272Sbill D(1) ttime(); 265*272Sbill D(2) trace("upstrat bn %d unit %d\n", bp->b_blkno, unit); 266*272Sbill #endif 267264Sbill if (dp->b_active == 0) { 268268Sbill (void) upustart(unit); 269264Sbill if (uptab.b_actf && uptab.b_active == 0) 270268Sbill (void) upstart(); 271264Sbill } 272264Sbill (void) spl0(); 273264Sbill } 274264Sbill 275264Sbill /* 276264Sbill * Start activity on specified drive; called when drive is inactive 277264Sbill * and new transfer request arrives and also when upas indicates that 278264Sbill * a SEARCH command is complete. 279264Sbill */ 280264Sbill upustart(unit) 281264Sbill register unit; 282264Sbill { 283264Sbill register struct buf *bp, *dp; 284264Sbill register struct device *upaddr = UPADDR; 285264Sbill daddr_t bn; 286264Sbill int sn, cn, csn; 287268Sbill int didie = 0; 288264Sbill 289266Sbill if (unit >= NUP) 290268Sbill goto out; 291*272Sbill if (uptab.b_active) { 292*272Sbill softas |= 1<<unit; 293*272Sbill return; 294*272Sbill } 295*272Sbill #ifdef UTRACE 296*272Sbill D(3) ttime(); 297*272Sbill D(4) trace("upustart %d active %d", unit, uputab[unit].b_active); 298*272Sbill #endif 299266Sbill /* 300266Sbill * Whether or not it was before, this unit is no longer busy. 301266Sbill * Check to see if there is (still or now) a request in this 302266Sbill * drives queue, and if there is, select this unit. 303266Sbill */ 304264Sbill if (unit+DK_N <= DK_NMAX) 305264Sbill dk_busy &= ~(1<<(unit+DK_N)); 306264Sbill dp = &uputab[unit]; 307266Sbill if ((bp = dp->b_actf) == NULL) 308268Sbill goto out; 309264Sbill if ((upaddr->upcs2 & 07) != unit) { 310264Sbill upaddr->upcs2 = unit; 311264Sbill DELAY(sdelay); 312264Sbill nwaitcs2++; 313264Sbill } else 314264Sbill neasycs2++; 315266Sbill /* 316266Sbill * If we have changed packs or just initialized, 317266Sbill * the the volume will not be valid; if so, clear 318266Sbill * the drive, preset it and put in 16bit/word mode. 319266Sbill */ 320266Sbill if ((upaddr->upds & VV) == 0) { 321*272Sbill #ifdef UTRACE 322*272Sbill D(5) trace(" not VV"); 323*272Sbill #endif 324266Sbill upaddr->upcs1 = IE|DCLR|GO; 325266Sbill DELAY(idelay); 326264Sbill upaddr->upcs1 = IE|PRESET|GO; 327264Sbill DELAY(idelay); 328264Sbill upaddr->upof = FMT22; 329268Sbill didie = 1; 330264Sbill } 331264Sbill /* 332266Sbill * We are called from upstrategy when a new request arrives 333266Sbill * if we are not already active (with dp->b_active == 0), 334266Sbill * and we then set dp->b_active to 1 if we are to SEARCH 335266Sbill * for the desired cylinder, or 2 if we are on-cylinder. 336266Sbill * If we SEARCH then we will later be called from upintr() 337266Sbill * when the search is complete, and will link this disk onto 338266Sbill * the uptab. We then set dp->b_active to 2 so that upintr() 339266Sbill * will not call us again. 340266Sbill * 341266Sbill * NB: Other drives clear the bit in the attention status 342266Sbill * (i.e. upas) register corresponding to the drive when they 343266Sbill * place the drive on the ready (i.e. uptab) queue. This does 344266Sbill * not work with the Emulex, as the controller hangs the UBA 345266Sbill * of the VAX shortly after the upas register is set, for 346266Sbill * reasons unknown. This only occurs in multi-spindle configurations, 347266Sbill * but to avoid the problem we use the fact that dp->b_active is 348266Sbill * 2 to replace the clearing of the upas bit. 349264Sbill */ 350266Sbill if (dp->b_active) 351264Sbill goto done; 352266Sbill dp->b_active = 1; 353264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 354266Sbill goto done; /* Will redetect error in upstart() soon */ 355264Sbill 356266Sbill /* 357266Sbill * Do enough of the disk address decoding to determine 358266Sbill * which cylinder and sector the request is on. 359266Sbill * Then compute the number of the sector SDIST sectors before 360266Sbill * the one where the transfer is to start, this being the 361266Sbill * point where we wish to attempt to begin the transfer, 362266Sbill * allowing approximately SDIST/2 msec for interrupt latency 363266Sbill * and preparation of the request. 364266Sbill * 365266Sbill * If we are on the correct cylinder and the desired sector 366266Sbill * lies between SDIST and SDIST+RDIST sectors ahead of us, then 367266Sbill * we don't bother to SEARCH but just begin the transfer asap. 368266Sbill */ 369264Sbill bn = dkblock(bp); 370264Sbill cn = bp->b_cylin; 371264Sbill sn = bn%(NSECT*NTRAC); 372264Sbill sn = (sn+NSECT-SDIST)%NSECT; 373264Sbill 374266Sbill if (cn - upaddr->updc) 375266Sbill goto search; /* Not on-cylinder */ 376264Sbill csn = (upaddr->upla>>6) - sn - 1; 377266Sbill if (csn < 0) 378264Sbill csn += NSECT; 379266Sbill if (csn > NSECT-RDIST) 380264Sbill goto done; 381264Sbill 382264Sbill search: 383*272Sbill #ifdef UTRACE 384*272Sbill D(6) trace(" search %d@%d to %d@%d", upaddr->updc, (upaddr->upla>>6), 385*272Sbill cn, sn); 386*272Sbill #endif 387264Sbill upaddr->updc = cn; 388264Sbill upaddr->upda = sn; 389264Sbill upaddr->upcs1 = IE|SEARCH|GO; 390268Sbill didie = 1; 391266Sbill /* 392266Sbill * Mark this unit busy. 393266Sbill */ 394264Sbill unit += DK_N; 395264Sbill if (unit <= DK_NMAX) { 396264Sbill dk_busy |= 1<<unit; 397264Sbill dk_numb[unit]++; 398264Sbill } 399270Sbill if (csdel0) DELAY(csdel0); 400268Sbill goto out; 401264Sbill 402264Sbill done: 403266Sbill /* 404266Sbill * This unit is ready to go. Make active == 2 so 405266Sbill * we won't get called again (by upintr() because upas&(1<<unit)) 406266Sbill * and link us onto the chain of ready disks. 407266Sbill */ 408*272Sbill #ifdef UTRACE 409*272Sbill D(7) trace(" done"); 410*272Sbill #endif 411266Sbill dp->b_active = 2; 412264Sbill dp->b_forw = NULL; 413266Sbill if (uptab.b_actf == NULL) 414264Sbill uptab.b_actf = dp; 415264Sbill else 416264Sbill uptab.b_actl->b_forw = dp; 417264Sbill uptab.b_actl = dp; 418268Sbill 419268Sbill out: 420*272Sbill if (csdel1) DELAY(csdel1); 421*272Sbill #ifdef UTRACE 422*272Sbill D(8) trace("\n"); 423*272Sbill #endif 424268Sbill return (didie); 425264Sbill } 426264Sbill 427264Sbill /* 428264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 429264Sbill */ 430264Sbill upstart() 431264Sbill { 432264Sbill register struct buf *bp, *dp; 433264Sbill register unit; 434264Sbill register struct device *upaddr; 435264Sbill daddr_t bn; 436266Sbill int dn, sn, tn, cn, cmd; 437264Sbill 438264Sbill loop: 439*272Sbill if (csdel2) DELAY(csdel2); 440*272Sbill #ifdef UTRACE 441*272Sbill D(9) ttime(); 442*272Sbill D(10) trace("upstart"); 443*272Sbill #endif 444266Sbill /* 445266Sbill * Pick a drive off the queue of ready drives, and 446266Sbill * perform the first transfer on its queue. 447266Sbill * 448266Sbill * Looping here is completely for the sake of drives which 449266Sbill * are not present and on-line, for which we completely clear the 450266Sbill * request queue. 451266Sbill */ 452269Sbill if ((dp = uptab.b_actf) == NULL) { 453*272Sbill #ifdef UTRACE 454*272Sbill D(11) trace("\n"); 455*272Sbill #endif 456268Sbill return (0); 457269Sbill } 458264Sbill if ((bp = dp->b_actf) == NULL) { 459264Sbill uptab.b_actf = dp->b_forw; 460264Sbill goto loop; 461264Sbill } 462266Sbill /* 463266Sbill * Mark the controller busy, and multi-part disk address. 464266Sbill * Select the unit on which the i/o is to take place. 465266Sbill */ 466264Sbill uptab.b_active++; 467264Sbill unit = minor(bp->b_dev) & 077; 468264Sbill dn = dkunit(bp); 469264Sbill bn = dkblock(bp); 470264Sbill cn = up_sizes[unit&07].cyloff; 471264Sbill cn += bn/(NSECT*NTRAC); 472264Sbill sn = bn%(NSECT*NTRAC); 473264Sbill tn = sn/NSECT; 474266Sbill sn %= NSECT; 475264Sbill upaddr = UPADDR; 476*272Sbill #ifdef UTRACE 477*272Sbill D(12) trace(" unit %d", dn); 478*272Sbill #endif 479264Sbill if ((upaddr->upcs2 & 07) != dn) { 480*272Sbill #ifdef UTRACE 481*272Sbill D(13) trace(" select"); 482*272Sbill #endif 483264Sbill upaddr->upcs2 = dn; 484264Sbill DELAY(sdelay); 485264Sbill nwaitcs2++; 486264Sbill } else 487264Sbill neasycs2++; 488266Sbill up_ubinfo = ubasetup(bp, 1); /* In a funny place for delay... */ 489266Sbill /* 490266Sbill * If drive is not present and on-line, then 491266Sbill * get rid of this with an error and loop to get 492266Sbill * rid of the rest of its queued requests. 493266Sbill * (Then on to any other ready drives.) 494266Sbill */ 495264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 496*272Sbill #ifdef UTRACE 497*272Sbill D(14) trace(" !(DPR && MOL)"); 498*272Sbill #endif 499264Sbill uptab.b_active = 0; 500264Sbill uptab.b_errcnt = 0; 501264Sbill dp->b_actf = bp->av_forw; 502266Sbill dp->b_active = 0; 503264Sbill bp->b_flags |= B_ERROR; 504264Sbill iodone(bp); 505266Sbill ubafree(up_ubinfo), up_ubinfo = 0; /* A funny place ... */ 506264Sbill goto loop; 507264Sbill } 508266Sbill /* 509266Sbill * If this is a retry, then with the 16'th retry we 510266Sbill * begin to try offsetting the heads to recover the data. 511266Sbill */ 512266Sbill if (uptab.b_errcnt >= 16) { 513*272Sbill #ifdef UTRACE 514*272Sbill D(15) trace(" offset"); 515*272Sbill #endif 516264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 517266Sbill upaddr->upcs1 = IE|OFFSET|GO; 518264Sbill DELAY(idelay); 519266Sbill while (upaddr->upds & PIP) 520264Sbill DELAY(25); 521264Sbill } 522266Sbill /* 523266Sbill * Now set up the transfer, retrieving the high 524266Sbill * 2 bits of the UNIBUS address from the information 525266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 526266Sbill */ 527*272Sbill #ifdef UTRACE 528*272Sbill D(16) trace(" %s %d.%d@%d cnt %d ba %x\n", 529*272Sbill (bp->b_flags&B_READ) ? "read" : "write", 530*272Sbill cn, tn, sn, bp->b_bcount, up_ubinfo & 0x3ffff); 531*272Sbill #endif 532264Sbill upaddr->updc = cn; 533264Sbill upaddr->upda = (tn << 8) + sn; 534264Sbill upaddr->upba = up_ubinfo; 535264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 536266Sbill cmd = (up_ubinfo >> 8) & 0x300; 537264Sbill if (bp->b_flags & B_READ) 538266Sbill cmd |= IE|RCOM|GO; 539264Sbill else 540266Sbill cmd |= IE|WCOM|GO; 541266Sbill upaddr->upcs1 = cmd; 542*272Sbill #ifdef notdef 543*272Sbill if (csdel3) DELAY(csdel3); 544*272Sbill #endif 545266Sbill /* 546266Sbill * This is a controller busy situation. 547266Sbill * Record in dk slot NUP+DK_N (after last drive) 548266Sbill * unless there aren't that many slots reserved for 549266Sbill * us in which case we record this as a drive busy 550266Sbill * (if there is room for that). 551266Sbill */ 552264Sbill unit = dn+DK_N; 553264Sbill if (NUP+DK_N == DK_NMAX) 554264Sbill unit = NUP+DK_N; 555264Sbill if (unit <= DK_NMAX) { 556264Sbill dk_busy |= 1<<unit; 557264Sbill dk_numb[unit]++; 558264Sbill dk_wds[unit] += bp->b_bcount>>6; 559264Sbill } 560268Sbill return (1); 561264Sbill } 562264Sbill 563264Sbill /* 564264Sbill * Handle a device interrupt. 565264Sbill * 566264Sbill * If the transferring drive needs attention, service it 567264Sbill * retrying on error or beginning next transfer. 568264Sbill * Service all other ready drives, calling ustart to transfer 569264Sbill * their blocks to the ready queue in uptab, and then restart 570264Sbill * the controller if there is anything to do. 571264Sbill */ 572264Sbill upintr() 573264Sbill { 574264Sbill register struct buf *bp, *dp; 575264Sbill register unit; 576264Sbill register struct device *upaddr = UPADDR; 577264Sbill int as = upaddr->upas & 0377; 578*272Sbill int osoftas; 579268Sbill int needie = 1; 580264Sbill 581*272Sbill #ifdef UTRACE 582*272Sbill D(17) ttime(); 583*272Sbill D(18) trace("upintr as %d act %d %d %d;", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active); 584*272Sbill #endif 585266Sbill if (uptab.b_active) { 586266Sbill /* 587266Sbill * The drive is transferring, thus the hardware 588266Sbill * (say the designers) will only interrupt when the transfer 589266Sbill * completes; check for it anyways. 590266Sbill */ 591266Sbill if ((upaddr->upcs1 & RDY) == 0) { 592*272Sbill #ifdef UTRACE 593*272Sbill D(19) trace(" !RDY"); 594*272Sbill #endif 595*272Sbill printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, 596*272Sbill upaddr->upds, upaddr->upwc); 597267Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active); 598269Sbill } 599266Sbill /* 600266Sbill * Mark controller or drive not busy, and check for an 601266Sbill * error condition which may have resulted from the transfer. 602266Sbill */ 603264Sbill dp = uptab.b_actf; 604264Sbill bp = dp->b_actf; 605264Sbill unit = dkunit(bp); 606264Sbill if (DK_N+NUP == DK_NMAX) 607264Sbill dk_busy &= ~(1<<(DK_N+NUP)); 608264Sbill else if (DK_N+unit <= DK_NMAX) 609264Sbill dk_busy &= ~(1<<(DK_N+unit)); 610264Sbill if (upaddr->upcs1 & TRE) { 611*272Sbill #ifdef UTRACE 612*272Sbill D(20) trace(" TRE"); 613*272Sbill #endif 614266Sbill /* 615266Sbill * An error occurred, indeed. Select this unit 616266Sbill * to get at the drive status (a SEARCH may have 617266Sbill * intervened to change the selected unit), and 618266Sbill * wait for the command which caused the interrupt 619266Sbill * to complete (DRY). 620266Sbill * 621266Sbill * WHY IS THE WAIT NECESSARY? 622266Sbill */ 623264Sbill if ((upaddr->upcs2 & 07) != unit) { 624264Sbill upaddr->upcs2 = unit; 625264Sbill DELAY(sdelay); 626264Sbill nwaitcs2++; 627264Sbill } else 628264Sbill neasycs2++; 629266Sbill while ((upaddr->upds & DRY) == 0) 630264Sbill DELAY(25); 631266Sbill /* 632266Sbill * After 28 retries (16 w/o servo offsets, and then 633266Sbill * 12 with servo offsets), or if we encountered 634266Sbill * an error because the drive is write-protected, 635266Sbill * give up. Print an error message on the last 2 636266Sbill * retries before a hard failure. 637266Sbill */ 638266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 639264Sbill bp->b_flags |= B_ERROR; 640264Sbill else 641266Sbill uptab.b_active = 0; /* To force retry */ 642266Sbill if (uptab.b_errcnt > 27) 643264Sbill deverror(bp, upaddr->upcs2, upaddr->uper1); 644266Sbill /* 645266Sbill * If this was a correctible ECC error, let upecc 646266Sbill * do the dirty work to correct it. If upecc 647266Sbill * starts another READ for the rest of the data 648266Sbill * then it returns 1 (having set uptab.b_active). 649266Sbill * Otherwise we are done and fall through to 650266Sbill * finish up. 651266Sbill */ 652266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 653266Sbill return; 654266Sbill /* 655266Sbill * Clear the drive and, every 4 retries, recalibrate 656266Sbill * to hopefully help clear up seek positioning problems. 657266Sbill */ 658264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 659264Sbill DELAY(idelay); 660268Sbill needie = 0; 661266Sbill if ((uptab.b_errcnt&07) == 4) { 662264Sbill upaddr->upcs1 = RECAL|GO|IE; 663264Sbill DELAY(idelay); 664264Sbill while(upaddr->upds & PIP) 665264Sbill DELAY(25); 666264Sbill } 667264Sbill } 668266Sbill /* 669266Sbill * If we are still noted as active, then no 670266Sbill * (further) retries are necessary. 671266Sbill * 672266Sbill * Make sure the correct unit is selected, 673266Sbill * return it to centerline if necessary, and mark 674266Sbill * this i/o complete, starting the next transfer 675266Sbill * on this drive with the upustart routine (if any). 676266Sbill */ 677266Sbill if (uptab.b_active) { 678*272Sbill #ifdef UTRACE 679*272Sbill D(21) trace(" unit %d", unit); 680*272Sbill #endif 681266Sbill if ((upaddr->upcs2 & 07) != unit) { 682*272Sbill #ifdef UTRACE 683*272Sbill D(22) trace(" select"); 684*272Sbill #endif 685266Sbill upaddr->upcs2 = unit; 686266Sbill DELAY(sdelay); 687266Sbill nwaitcs2++; 688266Sbill } else 689266Sbill neasycs2++; 690266Sbill if (uptab.b_errcnt >= 16) { 691*272Sbill #ifdef UTRACE 692*272Sbill D(23) trace(" rtc"); 693*272Sbill #endif 694266Sbill upaddr->upcs1 = RTC|GO|IE; 695264Sbill DELAY(idelay); 696266Sbill while (upaddr->upds & PIP) 697264Sbill DELAY(25); 698268Sbill needie = 0; 699264Sbill } 700264Sbill uptab.b_active = 0; 701264Sbill uptab.b_errcnt = 0; 702264Sbill uptab.b_actf = dp->b_forw; 703264Sbill dp->b_active = 0; 704264Sbill dp->b_errcnt = 0; 705264Sbill dp->b_actf = bp->av_forw; 706266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 707264Sbill iodone(bp); 708264Sbill if(dp->b_actf) 709268Sbill if (upustart(unit)) 710268Sbill needie = 0; 711264Sbill } 712264Sbill as &= ~(1<<unit); 713*272Sbill softas &= ~(1<<unit); 714264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 715266Sbill } 716266Sbill #ifndef notdef 717266Sbill else { 718264Sbill if (upaddr->upcs1 & TRE) { 719*272Sbill #ifdef UTRACE 720*272Sbill D(24) trace(" TRE"); 721*272Sbill #endif 722264Sbill upaddr->upcs1 = TRE; 723264Sbill DELAY(idelay); 724264Sbill } 725264Sbill } 726266Sbill #endif 727266Sbill /* 728266Sbill * If we have a unit with an outstanding SEARCH, 729266Sbill * and the hardware indicates the unit requires attention, 730266Sbill * the bring the drive to the ready queue. 731266Sbill * Finally, if the controller is not transferring 732266Sbill * start it if any drives are now ready to transfer. 733266Sbill */ 734*272Sbill #ifdef UTRACE 735*272Sbill D(25) trace("\n"); 736*272Sbill #endif 737*272Sbill as |= softas; 738*272Sbill osoftas = softas; 739*272Sbill softas = 0; 740266Sbill for (unit = 0; unit < NUP; unit++) 741*272Sbill if ((as|osoftas) & (1<<unit)) 742*272Sbill /* 743267Sbill if (uputab[unit].b_active == 1) { 744*272Sbill */ 745*272Sbill { 746*272Sbill if (as & (1<<unit)) { 747267Sbill upaddr->upas = 1<<unit; 748*272Sbill #ifdef UTRACE 749*272Sbill D(26) trace("as clear %d\n", unit); 750*272Sbill #endif 751268Sbill if (asdel) DELAY(asdel); 752*272Sbill } 753268Sbill if (upustart(unit)) 754268Sbill needie = 0; 755*272Sbill } 756*272Sbill /* 757267Sbill } else { 758266Sbill upaddr->upas = 1<<unit; 759*272Sbill #ifdef UTRACE 760*272Sbill D(27) trace("spurious as clear %d\n", unit); 761*272Sbill #endif 762266Sbill DELAY(1000); 763266Sbill } 764*272Sbill */ 765266Sbill if (uptab.b_actf && uptab.b_active == 0) 766268Sbill if (upstart()) 767268Sbill needie = 0; 768266Sbill out: 769269Sbill if (needie) { 770*272Sbill #ifdef UTRACE 771*272Sbill D(28) trace("upintr set IE\n"); 772*272Sbill #endif 773266Sbill upaddr->upcs1 = IE; 774269Sbill } 775264Sbill } 776264Sbill 777264Sbill upread(dev) 778264Sbill { 779264Sbill 780264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 781264Sbill } 782264Sbill 783264Sbill upwrite(dev) 784264Sbill { 785264Sbill 786264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 787264Sbill } 788264Sbill 789266Sbill /* 790266Sbill * Correct an ECC error, and restart the i/o to complete 791266Sbill * the transfer if necessary. This is quite complicated because 792266Sbill * the transfer may be going to an odd memory address base and/or 793266Sbill * across a page boundary. 794266Sbill */ 795264Sbill upecc(up, bp) 796264Sbill register struct device *up; 797264Sbill register struct buf *bp; 798264Sbill { 799264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 800266Sbill register int i; 801264Sbill caddr_t addr; 802266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 803264Sbill int bn, cn, tn, sn; 804264Sbill 805264Sbill /* 806266Sbill * Npf is the number of sectors transferred before the sector 807266Sbill * containing the ECC error, and reg is the UBA register 808266Sbill * mapping (the first part of) the transfer. 809266Sbill * O is offset within a memory page of the first byte transferred. 810264Sbill */ 811266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 812266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 813264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 814264Sbill printf("%D ", bp->b_blkno+npf); 815264Sbill prdev("ECC", bp->b_dev); 816264Sbill mask = up->upec2; 817264Sbill if (mask == 0) { 818266Sbill up->upof = FMT22; /* == RTC ???? */ 819264Sbill DELAY(idelay); 820264Sbill return (0); 821264Sbill } 822266Sbill /* 823266Sbill * Flush the buffered data path, and compute the 824266Sbill * byte and bit position of the error. The variable i 825266Sbill * is the byte offset in the transfer, the variable byte 826266Sbill * is the offset from a page boundary in main memory. 827266Sbill */ 828266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 829266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 830266Sbill bit = i&07; 831266Sbill i = (i&~07)>>3; 832264Sbill byte = i + o; 833266Sbill /* 834266Sbill * Correct while possible bits remain of mask. Since mask 835266Sbill * contains 11 bits, we continue while the bit offset is > -11. 836266Sbill * Also watch out for end of this block and the end of the whole 837266Sbill * transfer. 838266Sbill */ 839266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 840266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 841266Sbill (byte & PGOFSET); 842266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 843266Sbill byte++; 844266Sbill i++; 845266Sbill bit -= 8; 846264Sbill } 847266Sbill uptab.b_active++; /* Either complete or continuing... */ 848264Sbill if (up->upwc == 0) 849264Sbill return (0); 850266Sbill /* 851266Sbill * Have to continue the transfer... clear the drive, 852266Sbill * and compute the position where the transfer is to continue. 853266Sbill * We have completed npf+1 sectors of the transfer already; 854266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 855266Sbill */ 856266Sbill up->upcs1 = TRE|IE|DCLR|GO; 857264Sbill DELAY(idelay); 858264Sbill bn = dkblock(bp); 859264Sbill cn = bp->b_cylin; 860266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 861264Sbill tn = sn/NSECT; 862264Sbill sn %= NSECT; 863266Sbill cn += tn/NTRAC; 864266Sbill tn %= NTRAC; 865264Sbill up->updc = cn; 866266Sbill up->upda = (tn << 8) | sn; 867266Sbill ubaddr = (int)ptob(reg+1) + o; 868266Sbill up->upba = ubaddr; 869266Sbill cmd = (ubaddr >> 8) & 0x300; 870266Sbill cmd |= IE|GO|RCOM; 871266Sbill up->upcs1 = cmd; 872264Sbill return (1); 873264Sbill } 874