1*270Sbill int trc = -1; 2*270Sbill int trcw = 0; 3*270Sbill #define D(i) if (trc&(1<<i)) { if (trcw&(1<<i)) DELAY(1000); } else 4*270Sbill int csdel0 = 0; 5*270Sbill int csdel1 = 1000; 6*270Sbill int csdel2 = 2000; 7268Sbill int asdel = 500; 8267Sbill int csdel3 = 100; 9*270Sbill /* 10/14/12 3.6 06/19/80 */ 10264Sbill 11264Sbill /* 12264Sbill * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery. 13264Sbill * 14266Sbill * NB: This device is very sensitive: be aware that the code is the way 15266Sbill * it is for good reason and that there are delay loops here which may 16266Sbill * have to be lengthened if your processor is faster and which should 17266Sbill * probably be shortened if your processor is slower. 18266Sbill * 19264Sbill * This driver has been tested on a SC-11B Controller, configured 20264Sbill * with the following internal switch settings: 21264Sbill * SW1-1 5/19 surfaces (off, 19 surfaces on Ampex 9300) 22264Sbill * SW1-2 chksum enable (off, checksum disabled) 23264Sbill * SW1-3 volume select (off, 815 cylinders) 24264Sbill * SW1-4 sector select (on, 32 sectors) 25264Sbill * SW1-5 unused (off) 26264Sbill * SW1-6 port select (on, single port) 27264Sbill * SW1-7 npr delay (off, disable) 28264Sbill * SW1-8 ecc test mode (off, disable) 29264Sbill * and top mounted switches: 30264Sbill * SW2-1 extend opcodes (off=open, disable) 31264Sbill * SW2-2 extend diag (off=open, disable) 32264Sbill * SW2-3 4 wd dma burst (off=open, disable) 33264Sbill * SW2-4 unused (off=open) 34264Sbill * 35264Sbill * The controller transfers data much more rapidly with SW2-3 set, 36264Sbill * but we have previously experienced problems with it set this way. 37264Sbill * We intend to try this again in the near future. 38264Sbill * 39264Sbill * wnj June 14, 1980 40264Sbill */ 41264Sbill 42264Sbill #include "../h/param.h" 43264Sbill #include "../h/systm.h" 44264Sbill #include "../h/buf.h" 45264Sbill #include "../h/conf.h" 46264Sbill #include "../h/dir.h" 47264Sbill #include "../h/user.h" 48264Sbill #include "../h/map.h" 49264Sbill #include "../h/mba.h" 50264Sbill #include "../h/mtpr.h" 51264Sbill #include "../h/pte.h" 52264Sbill #include "../h/uba.h" 53264Sbill #include "../h/vm.h" 54264Sbill 55264Sbill /* 56264Sbill * Define number of drives, and range of sampling information to be used. 57264Sbill * 58264Sbill * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats, 59264Sbill * and DK_N+NUP gathers controller transferring stats. 60264Sbill * 61264Sbill * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive. 62264Sbill * If DK_NMAX is yet smaller, some drives are not monitored. 63264Sbill */ 64264Sbill #define DK_N 1 65264Sbill #define DK_NMAX 2 66264Sbill 67264Sbill #define ushort unsigned short 68264Sbill 69264Sbill struct device 70264Sbill { 71264Sbill ushort upcs1; /* control and status register 1 */ 72264Sbill short upwc; /* word count register */ 73264Sbill ushort upba; /* UNIBUS address register */ 74264Sbill ushort upda; /* desired address register */ 75264Sbill ushort upcs2; /* control and status register 2 */ 76264Sbill ushort upds; /* drive Status */ 77264Sbill ushort uper1; /* error register 1 */ 78264Sbill ushort upas; /* attention summary */ 79264Sbill ushort upla; /* look ahead */ 80264Sbill ushort updb; /* data buffer */ 81264Sbill ushort upmr; /* maintenance */ 82264Sbill ushort updt; /* drive type */ 83264Sbill ushort upsn; /* serial number */ 84264Sbill ushort upof; /* offset register */ 85264Sbill ushort updc; /* desired cylinder address register */ 86264Sbill ushort upcc; /* current cylinder */ 87264Sbill ushort uper2; /* error register 2 */ 88264Sbill ushort uper3; /* error register 3 */ 89264Sbill ushort upec1; /* burst error bit position */ 90264Sbill ushort upec2; /* burst error bit pattern */ 91264Sbill }; 92264Sbill 93264Sbill #define UPADDR ((struct device *)(UBA0_DEV + 0176700)) 94264Sbill 95264Sbill #define NUP 2 /* Number of drives this installation */ 96264Sbill 97264Sbill #define NSECT 32 98264Sbill #define NTRAC 19 99264Sbill 100264Sbill /* 101264Sbill * Constants controlling on-cylinder SEARCH usage. 102264Sbill * 103264Sbill * We assume that it takes SDIST sectors of time to set up a transfer. 104264Sbill * If a drive is on-cylinder, and between SDIST and SDIST+RDIST sectors 105264Sbill * from the first sector to be transferred, then we just perform the 106264Sbill * transfer. SDIST represents interrupt latency, RDIST the amount 107264Sbill * of rotation which is tolerable to avoid another interrupt. 108264Sbill */ 109266Sbill #define SDIST 3 /* 2-3 sectors 1-1.5 msec */ 110266Sbill #define RDIST 6 /* 5-6 sectors 2.5-3 msec */ 111264Sbill 112264Sbill /* 113264Sbill * To fill a 300M drive: 114264Sbill * A is designed to be used as a root. 115264Sbill * B is suitable for a swap area. 116264Sbill * H is the primary storage area. 117264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 118264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 119264Sbill * covers the whole drive and can be used for pack-pack copying. 120264Sbill */ 121264Sbill struct size 122264Sbill { 123264Sbill daddr_t nblocks; 124264Sbill int cyloff; 125264Sbill } up_sizes[8] = { 126264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 127264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 128264Sbill 494912, 0, /* C=cyl 0 thru 814 */ 129264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 130264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 131264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 132264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 133264Sbill 445664, 82, /* H=cyl 82 thru 814 */ 134264Sbill /* Later, and more safely for H area... 135264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 136264Sbill }; 137264Sbill 138264Sbill /* 139264Sbill * The following defines are used in offset positioning 140264Sbill * when trying to recover disk errors, with the constants being 141264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 142264Sbill * tried (this makes sense only during read, in any case.) 143264Sbill * 144264Sbill * ARE ALL THESE IMPLEMENTED ON 9300? 145264Sbill */ 146264Sbill #define P400 020 147264Sbill #define M400 0220 148264Sbill #define P800 040 149264Sbill #define M800 0240 150264Sbill #define P1200 060 151264Sbill #define M1200 0260 152264Sbill #define HCI 020000 153264Sbill 154264Sbill int up_offset[16] = 155264Sbill { 156264Sbill P400, M400, P400, M400, 157264Sbill P800, M800, P800, M800, 158264Sbill P1200, M1200, P1200, M1200, 159264Sbill 0, 0, 0, 0, 160264Sbill }; 161264Sbill 162264Sbill /* 163264Sbill * Each drive has a table uputab[i]. On this table are sorted the 164264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 165264Sbill * In the upustart() routine, each drive is independently advanced 166264Sbill * until it is on the desired cylinder for the next transfer and near 167264Sbill * the desired sector. The drive is then chained onto the uptab 168264Sbill * table, and the transfer is initiated by the upstart() routine. 169264Sbill * When the transfer is completed the driver reinvokes the upustart() 170264Sbill * routine to set up the next transfer. 171264Sbill */ 172264Sbill struct buf uptab; 173264Sbill struct buf uputab[NUP]; 174264Sbill 175264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 176264Sbill 177264Sbill /* Drive commands, placed in upcs1 */ 178264Sbill #define GO 01 /* Go bit, set in all commands */ 179264Sbill #define PRESET 020 /* Preset drive at init or after errors */ 180264Sbill #define OFFSET 014 /* Offset heads to try to recover error */ 181264Sbill #define RTC 016 /* Return to center-line after OFFSET */ 182264Sbill #define SEARCH 030 /* Search for cylinder+sector */ 183264Sbill #define RECAL 06 /* Recalibrate, needed after seek error */ 184264Sbill #define DCLR 010 /* Drive clear, after error */ 185264Sbill #define WCOM 060 /* Write */ 186264Sbill #define RCOM 070 /* Read */ 187264Sbill 188264Sbill /* Other bits of upcs1 */ 189264Sbill #define IE 0100 /* Controller wide interrupt enable */ 190264Sbill #define TRE 040000 /* Transfer error */ 191266Sbill #define RDY 020 /* Transfer terminated */ 192264Sbill 193264Sbill /* Drive status bits of upds */ 194264Sbill #define PIP 020000 /* Positioning in progress */ 195264Sbill #define ERR 040000 /* Error has occurred, DCLR necessary */ 196264Sbill #define VV 0100 /* Volume is valid, set by PRESET */ 197264Sbill #define DPR 0400 /* Drive has been preset */ 198264Sbill #define MOL 010000 /* Drive is online, heads loaded, etc */ 199264Sbill #define DRY 0200 /* Drive ready */ 200264Sbill 201264Sbill /* Bits of uper1 */ 202264Sbill #define DCK 0100000 /* Ecc error occurred */ 203264Sbill #define ECH 0100 /* Ecc error was unrecoverable */ 204264Sbill #define WLE 04000 /* Attempt to write read-only drive */ 205264Sbill 206264Sbill /* Bits of upof; the offset bits above are also in this register */ 207264Sbill #define FMT22 010000 /* 16 bits/word, must be always set */ 208264Sbill 209264Sbill #define b_cylin b_resid 210264Sbill 211264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 212264Sbill /* 213264Sbill * The EMULEX controller balks if accessed quickly after 214264Sbill * certain operations. The exact timing has not yet been 215264Sbill * determined, but delays are known to be needed when changing 216264Sbill * the selected drive (by writing in upcs2), and thought to be 217264Sbill * needed after operations like PRESET and DCLR. The following 218264Sbill * variables control the delay, DELAY(n) is approximately n usec. 219264Sbill */ 220264Sbill int idelay = 500; /* Delay after PRESET or DCLR */ 221268Sbill int sdelay = 150; /* Delay after selecting drive in upcs2 */ 222264Sbill 223264Sbill #define DELAY(N) { register int d; d = N; while (--d > 0); } 224264Sbill 225264Sbill int nwaitcs2; /* How many sdelay loops ? */ 226264Sbill int neasycs2; /* How many sdelay loops not needed ? */ 227264Sbill 228264Sbill #ifdef INTRLVE 229264Sbill daddr_t dkblock(); 230264Sbill #endif 231264Sbill 232264Sbill /* 233264Sbill * Queue an i/o request for a drive, checking first that it is in range. 234264Sbill * 235264Sbill * A unit start is issued if the drive is inactive, causing 236264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 237264Sbill * already nearly on the money and the controller is not transferring 238264Sbill * we kick it to start the transfer. 239264Sbill */ 240264Sbill upstrategy(bp) 241264Sbill register struct buf *bp; 242264Sbill { 243264Sbill register struct buf *dp; 244264Sbill register unit, xunit; 245264Sbill long sz, bn; 246264Sbill 247264Sbill xunit = minor(bp->b_dev) & 077; 248264Sbill sz = bp->b_bcount; 249264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 250264Sbill unit = dkunit(bp); 251264Sbill if (unit >= NUP || 252264Sbill bp->b_blkno < 0 || 253264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 254264Sbill bp->b_flags |= B_ERROR; 255264Sbill iodone(bp); 256264Sbill return; 257264Sbill } 258264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 259264Sbill dp = &uputab[unit]; 260264Sbill (void) spl5(); 261264Sbill disksort(dp, bp); 262269Sbill #ifdef UTRACE 263*270Sbill D(1) ttime(); 264*270Sbill D(2) trace("upstrat bn %d unit %d\n", bp->b_blkno, unit); 265269Sbill #endif 266264Sbill if (dp->b_active == 0) { 267268Sbill (void) upustart(unit); 268264Sbill if (uptab.b_actf && uptab.b_active == 0) 269268Sbill (void) upstart(); 270264Sbill } 271264Sbill (void) spl0(); 272264Sbill } 273264Sbill 274264Sbill /* 275264Sbill * Start activity on specified drive; called when drive is inactive 276264Sbill * and new transfer request arrives and also when upas indicates that 277264Sbill * a SEARCH command is complete. 278264Sbill */ 279264Sbill upustart(unit) 280264Sbill register unit; 281264Sbill { 282264Sbill register struct buf *bp, *dp; 283264Sbill register struct device *upaddr = UPADDR; 284264Sbill daddr_t bn; 285264Sbill int sn, cn, csn; 286268Sbill int didie = 0; 287264Sbill 288266Sbill if (unit >= NUP) 289268Sbill goto out; 290269Sbill #ifdef UTRACE 291*270Sbill D(3) ttime(); 292*270Sbill D(4) trace("upustart %d active %d", unit, uputab[unit].b_active); 293269Sbill #endif 294266Sbill /* 295266Sbill * Whether or not it was before, this unit is no longer busy. 296266Sbill * Check to see if there is (still or now) a request in this 297266Sbill * drives queue, and if there is, select this unit. 298266Sbill */ 299264Sbill if (unit+DK_N <= DK_NMAX) 300264Sbill dk_busy &= ~(1<<(unit+DK_N)); 301264Sbill dp = &uputab[unit]; 302266Sbill if ((bp = dp->b_actf) == NULL) 303268Sbill goto out; 304264Sbill if ((upaddr->upcs2 & 07) != unit) { 305264Sbill upaddr->upcs2 = unit; 306264Sbill DELAY(sdelay); 307264Sbill nwaitcs2++; 308264Sbill } else 309264Sbill neasycs2++; 310266Sbill /* 311266Sbill * If we have changed packs or just initialized, 312266Sbill * the the volume will not be valid; if so, clear 313266Sbill * the drive, preset it and put in 16bit/word mode. 314266Sbill */ 315266Sbill if ((upaddr->upds & VV) == 0) { 316269Sbill #ifdef UTRACE 317*270Sbill D(5) trace(" not VV"); 318269Sbill #endif 319266Sbill upaddr->upcs1 = IE|DCLR|GO; 320266Sbill DELAY(idelay); 321264Sbill upaddr->upcs1 = IE|PRESET|GO; 322264Sbill DELAY(idelay); 323264Sbill upaddr->upof = FMT22; 324268Sbill didie = 1; 325264Sbill } 326264Sbill /* 327266Sbill * We are called from upstrategy when a new request arrives 328266Sbill * if we are not already active (with dp->b_active == 0), 329266Sbill * and we then set dp->b_active to 1 if we are to SEARCH 330266Sbill * for the desired cylinder, or 2 if we are on-cylinder. 331266Sbill * If we SEARCH then we will later be called from upintr() 332266Sbill * when the search is complete, and will link this disk onto 333266Sbill * the uptab. We then set dp->b_active to 2 so that upintr() 334266Sbill * will not call us again. 335266Sbill * 336266Sbill * NB: Other drives clear the bit in the attention status 337266Sbill * (i.e. upas) register corresponding to the drive when they 338266Sbill * place the drive on the ready (i.e. uptab) queue. This does 339266Sbill * not work with the Emulex, as the controller hangs the UBA 340266Sbill * of the VAX shortly after the upas register is set, for 341266Sbill * reasons unknown. This only occurs in multi-spindle configurations, 342266Sbill * but to avoid the problem we use the fact that dp->b_active is 343266Sbill * 2 to replace the clearing of the upas bit. 344264Sbill */ 345266Sbill if (dp->b_active) 346264Sbill goto done; 347266Sbill dp->b_active = 1; 348264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 349266Sbill goto done; /* Will redetect error in upstart() soon */ 350264Sbill 351266Sbill /* 352266Sbill * Do enough of the disk address decoding to determine 353266Sbill * which cylinder and sector the request is on. 354266Sbill * Then compute the number of the sector SDIST sectors before 355266Sbill * the one where the transfer is to start, this being the 356266Sbill * point where we wish to attempt to begin the transfer, 357266Sbill * allowing approximately SDIST/2 msec for interrupt latency 358266Sbill * and preparation of the request. 359266Sbill * 360266Sbill * If we are on the correct cylinder and the desired sector 361266Sbill * lies between SDIST and SDIST+RDIST sectors ahead of us, then 362266Sbill * we don't bother to SEARCH but just begin the transfer asap. 363266Sbill */ 364264Sbill bn = dkblock(bp); 365264Sbill cn = bp->b_cylin; 366264Sbill sn = bn%(NSECT*NTRAC); 367264Sbill sn = (sn+NSECT-SDIST)%NSECT; 368264Sbill 369266Sbill if (cn - upaddr->updc) 370266Sbill goto search; /* Not on-cylinder */ 371264Sbill csn = (upaddr->upla>>6) - sn - 1; 372266Sbill if (csn < 0) 373264Sbill csn += NSECT; 374266Sbill if (csn > NSECT-RDIST) 375264Sbill goto done; 376264Sbill 377264Sbill search: 378269Sbill #ifdef UTRACE 379*270Sbill D(6) trace(" search %d@%d to %d@%d", upaddr->updc, (upaddr->upla>>6), 380269Sbill cn, sn); 381269Sbill #endif 382264Sbill upaddr->updc = cn; 383264Sbill upaddr->upda = sn; 384264Sbill upaddr->upcs1 = IE|SEARCH|GO; 385268Sbill didie = 1; 386266Sbill /* 387266Sbill * Mark this unit busy. 388266Sbill */ 389264Sbill unit += DK_N; 390264Sbill if (unit <= DK_NMAX) { 391264Sbill dk_busy |= 1<<unit; 392264Sbill dk_numb[unit]++; 393264Sbill } 394*270Sbill if (csdel0) DELAY(csdel0); 395268Sbill goto out; 396264Sbill 397264Sbill done: 398266Sbill /* 399266Sbill * This unit is ready to go. Make active == 2 so 400266Sbill * we won't get called again (by upintr() because upas&(1<<unit)) 401266Sbill * and link us onto the chain of ready disks. 402266Sbill */ 403269Sbill #ifdef UTRACE 404*270Sbill D(7) trace(" done"); 405269Sbill #endif 406266Sbill dp->b_active = 2; 407264Sbill dp->b_forw = NULL; 408266Sbill if (uptab.b_actf == NULL) 409264Sbill uptab.b_actf = dp; 410264Sbill else 411264Sbill uptab.b_actl->b_forw = dp; 412264Sbill uptab.b_actl = dp; 413268Sbill 414268Sbill out: 415*270Sbill if (csdel1) DELAY(csdel1); 416269Sbill #ifdef UTRACE 417*270Sbill D(8) trace("\n"); 418269Sbill #endif 419268Sbill return (didie); 420264Sbill } 421264Sbill 422264Sbill /* 423264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 424264Sbill */ 425264Sbill upstart() 426264Sbill { 427264Sbill register struct buf *bp, *dp; 428264Sbill register unit; 429264Sbill register struct device *upaddr; 430264Sbill daddr_t bn; 431266Sbill int dn, sn, tn, cn, cmd; 432264Sbill 433264Sbill loop: 434*270Sbill if (csdel2) DELAY(csdel2); 435269Sbill #ifdef UTRACE 436*270Sbill D(9) ttime(); 437*270Sbill D(10) trace("upstart"); 438269Sbill #endif 439266Sbill /* 440266Sbill * Pick a drive off the queue of ready drives, and 441266Sbill * perform the first transfer on its queue. 442266Sbill * 443266Sbill * Looping here is completely for the sake of drives which 444266Sbill * are not present and on-line, for which we completely clear the 445266Sbill * request queue. 446266Sbill */ 447269Sbill if ((dp = uptab.b_actf) == NULL) { 448269Sbill #ifdef UTRACE 449*270Sbill D(11) trace("\n"); 450269Sbill #endif 451268Sbill return (0); 452269Sbill } 453264Sbill if ((bp = dp->b_actf) == NULL) { 454264Sbill uptab.b_actf = dp->b_forw; 455264Sbill goto loop; 456264Sbill } 457266Sbill /* 458266Sbill * Mark the controller busy, and multi-part disk address. 459266Sbill * Select the unit on which the i/o is to take place. 460266Sbill */ 461264Sbill uptab.b_active++; 462264Sbill unit = minor(bp->b_dev) & 077; 463264Sbill dn = dkunit(bp); 464264Sbill bn = dkblock(bp); 465264Sbill cn = up_sizes[unit&07].cyloff; 466264Sbill cn += bn/(NSECT*NTRAC); 467264Sbill sn = bn%(NSECT*NTRAC); 468264Sbill tn = sn/NSECT; 469266Sbill sn %= NSECT; 470264Sbill upaddr = UPADDR; 471269Sbill #ifdef UTRACE 472*270Sbill D(12) trace(" unit %d", dn); 473269Sbill #endif 474264Sbill if ((upaddr->upcs2 & 07) != dn) { 475269Sbill #ifdef UTRACE 476*270Sbill D(13) trace(" select"); 477269Sbill #endif 478264Sbill upaddr->upcs2 = dn; 479264Sbill DELAY(sdelay); 480264Sbill nwaitcs2++; 481264Sbill } else 482264Sbill neasycs2++; 483266Sbill up_ubinfo = ubasetup(bp, 1); /* In a funny place for delay... */ 484266Sbill /* 485266Sbill * If drive is not present and on-line, then 486266Sbill * get rid of this with an error and loop to get 487266Sbill * rid of the rest of its queued requests. 488266Sbill * (Then on to any other ready drives.) 489266Sbill */ 490264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 491269Sbill #ifdef UTRACE 492*270Sbill D(14) trace(" !(DPR && MOL)"); 493269Sbill #endif 494264Sbill uptab.b_active = 0; 495264Sbill uptab.b_errcnt = 0; 496264Sbill dp->b_actf = bp->av_forw; 497266Sbill dp->b_active = 0; 498264Sbill bp->b_flags |= B_ERROR; 499264Sbill iodone(bp); 500266Sbill ubafree(up_ubinfo), up_ubinfo = 0; /* A funny place ... */ 501264Sbill goto loop; 502264Sbill } 503266Sbill /* 504266Sbill * If this is a retry, then with the 16'th retry we 505266Sbill * begin to try offsetting the heads to recover the data. 506266Sbill */ 507266Sbill if (uptab.b_errcnt >= 16) { 508269Sbill #ifdef UTRACE 509*270Sbill D(15) trace(" offset"); 510269Sbill #endif 511264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 512266Sbill upaddr->upcs1 = IE|OFFSET|GO; 513264Sbill DELAY(idelay); 514266Sbill while (upaddr->upds & PIP) 515264Sbill DELAY(25); 516264Sbill } 517266Sbill /* 518266Sbill * Now set up the transfer, retrieving the high 519266Sbill * 2 bits of the UNIBUS address from the information 520266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 521266Sbill */ 522269Sbill #ifdef UTRACE 523*270Sbill D(16) trace(" %s %d.%d@%d cnt %d ba %x\n", 524269Sbill (bp->b_flags&B_READ) ? "read" : "write", 525269Sbill cn, tn, sn, bp->b_bcount, up_ubinfo & 0x3ffff); 526269Sbill #endif 527264Sbill upaddr->updc = cn; 528264Sbill upaddr->upda = (tn << 8) + sn; 529264Sbill upaddr->upba = up_ubinfo; 530264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 531266Sbill cmd = (up_ubinfo >> 8) & 0x300; 532264Sbill if (bp->b_flags & B_READ) 533266Sbill cmd |= IE|RCOM|GO; 534264Sbill else 535266Sbill cmd |= IE|WCOM|GO; 536266Sbill upaddr->upcs1 = cmd; 537268Sbill #ifdef notdef 538267Sbill if (csdel3) DELAY(csdel3); 539268Sbill #endif 540266Sbill /* 541266Sbill * This is a controller busy situation. 542266Sbill * Record in dk slot NUP+DK_N (after last drive) 543266Sbill * unless there aren't that many slots reserved for 544266Sbill * us in which case we record this as a drive busy 545266Sbill * (if there is room for that). 546266Sbill */ 547264Sbill unit = dn+DK_N; 548264Sbill if (NUP+DK_N == DK_NMAX) 549264Sbill unit = NUP+DK_N; 550264Sbill if (unit <= DK_NMAX) { 551264Sbill dk_busy |= 1<<unit; 552264Sbill dk_numb[unit]++; 553264Sbill dk_wds[unit] += bp->b_bcount>>6; 554264Sbill } 555268Sbill return (1); 556264Sbill } 557264Sbill 558264Sbill /* 559264Sbill * Handle a device interrupt. 560264Sbill * 561264Sbill * If the transferring drive needs attention, service it 562264Sbill * retrying on error or beginning next transfer. 563264Sbill * Service all other ready drives, calling ustart to transfer 564264Sbill * their blocks to the ready queue in uptab, and then restart 565264Sbill * the controller if there is anything to do. 566264Sbill */ 567264Sbill upintr() 568264Sbill { 569264Sbill register struct buf *bp, *dp; 570264Sbill register unit; 571264Sbill register struct device *upaddr = UPADDR; 572264Sbill int as = upaddr->upas & 0377; 573268Sbill int needie = 1; 574264Sbill 575269Sbill #ifdef UTRACE 576*270Sbill D(17) ttime(); 577*270Sbill D(18) trace("upintr as %d act %d %d %d;", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active); 578269Sbill #endif 579266Sbill if (uptab.b_active) { 580266Sbill /* 581266Sbill * The drive is transferring, thus the hardware 582266Sbill * (say the designers) will only interrupt when the transfer 583266Sbill * completes; check for it anyways. 584266Sbill */ 585266Sbill if ((upaddr->upcs1 & RDY) == 0) { 586269Sbill #ifdef UTRACE 587*270Sbill D(19) trace(" !RDY"); 588269Sbill #endif 589267Sbill printf("!RDY in upintr: cs1 %o\n", upaddr->upcs1); 590267Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active); 591269Sbill } 592266Sbill /* 593266Sbill * Mark controller or drive not busy, and check for an 594266Sbill * error condition which may have resulted from the transfer. 595266Sbill */ 596264Sbill dp = uptab.b_actf; 597264Sbill bp = dp->b_actf; 598264Sbill unit = dkunit(bp); 599264Sbill if (DK_N+NUP == DK_NMAX) 600264Sbill dk_busy &= ~(1<<(DK_N+NUP)); 601264Sbill else if (DK_N+unit <= DK_NMAX) 602264Sbill dk_busy &= ~(1<<(DK_N+unit)); 603264Sbill if (upaddr->upcs1 & TRE) { 604269Sbill #ifdef UTRACE 605*270Sbill D(20) trace(" TRE"); 606269Sbill #endif 607266Sbill /* 608266Sbill * An error occurred, indeed. Select this unit 609266Sbill * to get at the drive status (a SEARCH may have 610266Sbill * intervened to change the selected unit), and 611266Sbill * wait for the command which caused the interrupt 612266Sbill * to complete (DRY). 613266Sbill * 614266Sbill * WHY IS THE WAIT NECESSARY? 615266Sbill */ 616264Sbill if ((upaddr->upcs2 & 07) != unit) { 617264Sbill upaddr->upcs2 = unit; 618264Sbill DELAY(sdelay); 619264Sbill nwaitcs2++; 620264Sbill } else 621264Sbill neasycs2++; 622266Sbill while ((upaddr->upds & DRY) == 0) 623264Sbill DELAY(25); 624266Sbill /* 625266Sbill * After 28 retries (16 w/o servo offsets, and then 626266Sbill * 12 with servo offsets), or if we encountered 627266Sbill * an error because the drive is write-protected, 628266Sbill * give up. Print an error message on the last 2 629266Sbill * retries before a hard failure. 630266Sbill */ 631266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 632264Sbill bp->b_flags |= B_ERROR; 633264Sbill else 634266Sbill uptab.b_active = 0; /* To force retry */ 635266Sbill if (uptab.b_errcnt > 27) 636264Sbill deverror(bp, upaddr->upcs2, upaddr->uper1); 637266Sbill /* 638266Sbill * If this was a correctible ECC error, let upecc 639266Sbill * do the dirty work to correct it. If upecc 640266Sbill * starts another READ for the rest of the data 641266Sbill * then it returns 1 (having set uptab.b_active). 642266Sbill * Otherwise we are done and fall through to 643266Sbill * finish up. 644266Sbill */ 645266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 646266Sbill return; 647266Sbill /* 648266Sbill * Clear the drive and, every 4 retries, recalibrate 649266Sbill * to hopefully help clear up seek positioning problems. 650266Sbill */ 651264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 652264Sbill DELAY(idelay); 653268Sbill needie = 0; 654266Sbill if ((uptab.b_errcnt&07) == 4) { 655264Sbill upaddr->upcs1 = RECAL|GO|IE; 656264Sbill DELAY(idelay); 657264Sbill while(upaddr->upds & PIP) 658264Sbill DELAY(25); 659264Sbill } 660264Sbill } 661266Sbill /* 662266Sbill * If we are still noted as active, then no 663266Sbill * (further) retries are necessary. 664266Sbill * 665266Sbill * Make sure the correct unit is selected, 666266Sbill * return it to centerline if necessary, and mark 667266Sbill * this i/o complete, starting the next transfer 668266Sbill * on this drive with the upustart routine (if any). 669266Sbill */ 670266Sbill if (uptab.b_active) { 671269Sbill #ifdef UTRACE 672*270Sbill D(21) trace(" unit %d", unit); 673269Sbill #endif 674266Sbill if ((upaddr->upcs2 & 07) != unit) { 675269Sbill #ifdef UTRACE 676*270Sbill D(22) trace(" select"); 677269Sbill #endif 678266Sbill upaddr->upcs2 = unit; 679266Sbill DELAY(sdelay); 680266Sbill nwaitcs2++; 681266Sbill } else 682266Sbill neasycs2++; 683266Sbill if (uptab.b_errcnt >= 16) { 684269Sbill #ifdef UTRACE 685*270Sbill D(23) trace(" rtc"); 686269Sbill #endif 687266Sbill upaddr->upcs1 = RTC|GO|IE; 688264Sbill DELAY(idelay); 689266Sbill while (upaddr->upds & PIP) 690264Sbill DELAY(25); 691268Sbill needie = 0; 692264Sbill } 693264Sbill uptab.b_active = 0; 694264Sbill uptab.b_errcnt = 0; 695264Sbill uptab.b_actf = dp->b_forw; 696264Sbill dp->b_active = 0; 697264Sbill dp->b_errcnt = 0; 698264Sbill dp->b_actf = bp->av_forw; 699266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 700264Sbill iodone(bp); 701264Sbill if(dp->b_actf) 702268Sbill if (upustart(unit)) 703268Sbill needie = 0; 704264Sbill } 705264Sbill as &= ~(1<<unit); 706264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 707266Sbill } 708266Sbill #ifndef notdef 709266Sbill else { 710264Sbill if (upaddr->upcs1 & TRE) { 711269Sbill #ifdef UTRACE 712*270Sbill D(24) trace(" TRE"); 713269Sbill #endif 714264Sbill upaddr->upcs1 = TRE; 715264Sbill DELAY(idelay); 716264Sbill } 717264Sbill } 718266Sbill #endif 719266Sbill /* 720266Sbill * If we have a unit with an outstanding SEARCH, 721266Sbill * and the hardware indicates the unit requires attention, 722266Sbill * the bring the drive to the ready queue. 723266Sbill * Finally, if the controller is not transferring 724266Sbill * start it if any drives are now ready to transfer. 725266Sbill */ 726269Sbill #ifdef UTRACE 727*270Sbill D(25) trace("\n"); 728269Sbill #endif 729266Sbill for (unit = 0; unit < NUP; unit++) 730266Sbill if (as & (1<<unit)) 731267Sbill if (uputab[unit].b_active == 1) { 732267Sbill upaddr->upas = 1<<unit; 733269Sbill #ifdef UTRACE 734*270Sbill D(26) trace("as clear %d\n", unit); 735269Sbill #endif 736268Sbill if (asdel) DELAY(asdel); 737268Sbill if (upustart(unit)) 738268Sbill needie = 0; 739267Sbill } else { 740266Sbill upaddr->upas = 1<<unit; 741269Sbill #ifdef UTRACE 742*270Sbill D(27) trace("spurious as clear %d\n", unit); 743269Sbill #endif 744266Sbill DELAY(1000); 745266Sbill } 746266Sbill if (uptab.b_actf && uptab.b_active == 0) 747268Sbill if (upstart()) 748268Sbill needie = 0; 749266Sbill out: 750269Sbill if (needie) { 751269Sbill #ifdef UTRACE 752*270Sbill D(28) trace("upintr set IE\n"); 753269Sbill #endif 754266Sbill upaddr->upcs1 = IE; 755269Sbill } 756264Sbill } 757264Sbill 758264Sbill upread(dev) 759264Sbill { 760264Sbill 761264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 762264Sbill } 763264Sbill 764264Sbill upwrite(dev) 765264Sbill { 766264Sbill 767264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 768264Sbill } 769264Sbill 770266Sbill /* 771266Sbill * Correct an ECC error, and restart the i/o to complete 772266Sbill * the transfer if necessary. This is quite complicated because 773266Sbill * the transfer may be going to an odd memory address base and/or 774266Sbill * across a page boundary. 775266Sbill */ 776264Sbill upecc(up, bp) 777264Sbill register struct device *up; 778264Sbill register struct buf *bp; 779264Sbill { 780264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 781266Sbill register int i; 782264Sbill caddr_t addr; 783266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 784264Sbill int bn, cn, tn, sn; 785264Sbill 786264Sbill /* 787266Sbill * Npf is the number of sectors transferred before the sector 788266Sbill * containing the ECC error, and reg is the UBA register 789266Sbill * mapping (the first part of) the transfer. 790266Sbill * O is offset within a memory page of the first byte transferred. 791264Sbill */ 792266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 793266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 794264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 795264Sbill printf("%D ", bp->b_blkno+npf); 796264Sbill prdev("ECC", bp->b_dev); 797264Sbill mask = up->upec2; 798264Sbill if (mask == 0) { 799266Sbill up->upof = FMT22; /* == RTC ???? */ 800264Sbill DELAY(idelay); 801264Sbill return (0); 802264Sbill } 803266Sbill /* 804266Sbill * Flush the buffered data path, and compute the 805266Sbill * byte and bit position of the error. The variable i 806266Sbill * is the byte offset in the transfer, the variable byte 807266Sbill * is the offset from a page boundary in main memory. 808266Sbill */ 809266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 810266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 811266Sbill bit = i&07; 812266Sbill i = (i&~07)>>3; 813264Sbill byte = i + o; 814266Sbill /* 815266Sbill * Correct while possible bits remain of mask. Since mask 816266Sbill * contains 11 bits, we continue while the bit offset is > -11. 817266Sbill * Also watch out for end of this block and the end of the whole 818266Sbill * transfer. 819266Sbill */ 820266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 821266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 822266Sbill (byte & PGOFSET); 823266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 824266Sbill byte++; 825266Sbill i++; 826266Sbill bit -= 8; 827264Sbill } 828266Sbill uptab.b_active++; /* Either complete or continuing... */ 829264Sbill if (up->upwc == 0) 830264Sbill return (0); 831266Sbill /* 832266Sbill * Have to continue the transfer... clear the drive, 833266Sbill * and compute the position where the transfer is to continue. 834266Sbill * We have completed npf+1 sectors of the transfer already; 835266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 836266Sbill */ 837266Sbill up->upcs1 = TRE|IE|DCLR|GO; 838264Sbill DELAY(idelay); 839264Sbill bn = dkblock(bp); 840264Sbill cn = bp->b_cylin; 841266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 842264Sbill tn = sn/NSECT; 843264Sbill sn %= NSECT; 844266Sbill cn += tn/NTRAC; 845266Sbill tn %= NTRAC; 846264Sbill up->updc = cn; 847266Sbill up->upda = (tn << 8) | sn; 848266Sbill ubaddr = (int)ptob(reg+1) + o; 849266Sbill up->upba = ubaddr; 850266Sbill cmd = (ubaddr >> 8) & 0x300; 851266Sbill cmd |= IE|GO|RCOM; 852266Sbill up->upcs1 = cmd; 853264Sbill return (1); 854264Sbill } 855