1*269Sbill #define UTRACE 2268Sbill int asdel = 500; 3267Sbill int csdel3 = 100; 4*269Sbill /* 10/14/12 3.5 06/19/80 */ 5264Sbill 6264Sbill /* 7264Sbill * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery. 8264Sbill * 9266Sbill * NB: This device is very sensitive: be aware that the code is the way 10266Sbill * it is for good reason and that there are delay loops here which may 11266Sbill * have to be lengthened if your processor is faster and which should 12266Sbill * probably be shortened if your processor is slower. 13266Sbill * 14264Sbill * This driver has been tested on a SC-11B Controller, configured 15264Sbill * with the following internal switch settings: 16264Sbill * SW1-1 5/19 surfaces (off, 19 surfaces on Ampex 9300) 17264Sbill * SW1-2 chksum enable (off, checksum disabled) 18264Sbill * SW1-3 volume select (off, 815 cylinders) 19264Sbill * SW1-4 sector select (on, 32 sectors) 20264Sbill * SW1-5 unused (off) 21264Sbill * SW1-6 port select (on, single port) 22264Sbill * SW1-7 npr delay (off, disable) 23264Sbill * SW1-8 ecc test mode (off, disable) 24264Sbill * and top mounted switches: 25264Sbill * SW2-1 extend opcodes (off=open, disable) 26264Sbill * SW2-2 extend diag (off=open, disable) 27264Sbill * SW2-3 4 wd dma burst (off=open, disable) 28264Sbill * SW2-4 unused (off=open) 29264Sbill * 30264Sbill * The controller transfers data much more rapidly with SW2-3 set, 31264Sbill * but we have previously experienced problems with it set this way. 32264Sbill * We intend to try this again in the near future. 33264Sbill * 34264Sbill * wnj June 14, 1980 35264Sbill */ 36264Sbill 37264Sbill #include "../h/param.h" 38264Sbill #include "../h/systm.h" 39264Sbill #include "../h/buf.h" 40264Sbill #include "../h/conf.h" 41264Sbill #include "../h/dir.h" 42264Sbill #include "../h/user.h" 43264Sbill #include "../h/map.h" 44264Sbill #include "../h/mba.h" 45264Sbill #include "../h/mtpr.h" 46264Sbill #include "../h/pte.h" 47264Sbill #include "../h/uba.h" 48264Sbill #include "../h/vm.h" 49264Sbill 50264Sbill /* 51264Sbill * Define number of drives, and range of sampling information to be used. 52264Sbill * 53264Sbill * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats, 54264Sbill * and DK_N+NUP gathers controller transferring stats. 55264Sbill * 56264Sbill * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive. 57264Sbill * If DK_NMAX is yet smaller, some drives are not monitored. 58264Sbill */ 59264Sbill #define DK_N 1 60264Sbill #define DK_NMAX 2 61264Sbill 62264Sbill #define ushort unsigned short 63264Sbill 64264Sbill struct device 65264Sbill { 66264Sbill ushort upcs1; /* control and status register 1 */ 67264Sbill short upwc; /* word count register */ 68264Sbill ushort upba; /* UNIBUS address register */ 69264Sbill ushort upda; /* desired address register */ 70264Sbill ushort upcs2; /* control and status register 2 */ 71264Sbill ushort upds; /* drive Status */ 72264Sbill ushort uper1; /* error register 1 */ 73264Sbill ushort upas; /* attention summary */ 74264Sbill ushort upla; /* look ahead */ 75264Sbill ushort updb; /* data buffer */ 76264Sbill ushort upmr; /* maintenance */ 77264Sbill ushort updt; /* drive type */ 78264Sbill ushort upsn; /* serial number */ 79264Sbill ushort upof; /* offset register */ 80264Sbill ushort updc; /* desired cylinder address register */ 81264Sbill ushort upcc; /* current cylinder */ 82264Sbill ushort uper2; /* error register 2 */ 83264Sbill ushort uper3; /* error register 3 */ 84264Sbill ushort upec1; /* burst error bit position */ 85264Sbill ushort upec2; /* burst error bit pattern */ 86264Sbill }; 87264Sbill 88264Sbill #define UPADDR ((struct device *)(UBA0_DEV + 0176700)) 89264Sbill 90264Sbill #define NUP 2 /* Number of drives this installation */ 91264Sbill 92264Sbill #define NSECT 32 93264Sbill #define NTRAC 19 94264Sbill 95264Sbill /* 96264Sbill * Constants controlling on-cylinder SEARCH usage. 97264Sbill * 98264Sbill * We assume that it takes SDIST sectors of time to set up a transfer. 99264Sbill * If a drive is on-cylinder, and between SDIST and SDIST+RDIST sectors 100264Sbill * from the first sector to be transferred, then we just perform the 101264Sbill * transfer. SDIST represents interrupt latency, RDIST the amount 102264Sbill * of rotation which is tolerable to avoid another interrupt. 103264Sbill */ 104266Sbill #define SDIST 3 /* 2-3 sectors 1-1.5 msec */ 105266Sbill #define RDIST 6 /* 5-6 sectors 2.5-3 msec */ 106264Sbill 107264Sbill /* 108264Sbill * To fill a 300M drive: 109264Sbill * A is designed to be used as a root. 110264Sbill * B is suitable for a swap area. 111264Sbill * H is the primary storage area. 112264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 113264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 114264Sbill * covers the whole drive and can be used for pack-pack copying. 115264Sbill */ 116264Sbill struct size 117264Sbill { 118264Sbill daddr_t nblocks; 119264Sbill int cyloff; 120264Sbill } up_sizes[8] = { 121264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 122264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 123264Sbill 494912, 0, /* C=cyl 0 thru 814 */ 124264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 125264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 126264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 127264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 128264Sbill 445664, 82, /* H=cyl 82 thru 814 */ 129264Sbill /* Later, and more safely for H area... 130264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 131264Sbill }; 132264Sbill 133264Sbill /* 134264Sbill * The following defines are used in offset positioning 135264Sbill * when trying to recover disk errors, with the constants being 136264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 137264Sbill * tried (this makes sense only during read, in any case.) 138264Sbill * 139264Sbill * ARE ALL THESE IMPLEMENTED ON 9300? 140264Sbill */ 141264Sbill #define P400 020 142264Sbill #define M400 0220 143264Sbill #define P800 040 144264Sbill #define M800 0240 145264Sbill #define P1200 060 146264Sbill #define M1200 0260 147264Sbill #define HCI 020000 148264Sbill 149264Sbill int up_offset[16] = 150264Sbill { 151264Sbill P400, M400, P400, M400, 152264Sbill P800, M800, P800, M800, 153264Sbill P1200, M1200, P1200, M1200, 154264Sbill 0, 0, 0, 0, 155264Sbill }; 156264Sbill 157264Sbill /* 158264Sbill * Each drive has a table uputab[i]. On this table are sorted the 159264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 160264Sbill * In the upustart() routine, each drive is independently advanced 161264Sbill * until it is on the desired cylinder for the next transfer and near 162264Sbill * the desired sector. The drive is then chained onto the uptab 163264Sbill * table, and the transfer is initiated by the upstart() routine. 164264Sbill * When the transfer is completed the driver reinvokes the upustart() 165264Sbill * routine to set up the next transfer. 166264Sbill */ 167264Sbill struct buf uptab; 168264Sbill struct buf uputab[NUP]; 169264Sbill 170264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 171264Sbill 172264Sbill /* Drive commands, placed in upcs1 */ 173264Sbill #define GO 01 /* Go bit, set in all commands */ 174264Sbill #define PRESET 020 /* Preset drive at init or after errors */ 175264Sbill #define OFFSET 014 /* Offset heads to try to recover error */ 176264Sbill #define RTC 016 /* Return to center-line after OFFSET */ 177264Sbill #define SEARCH 030 /* Search for cylinder+sector */ 178264Sbill #define RECAL 06 /* Recalibrate, needed after seek error */ 179264Sbill #define DCLR 010 /* Drive clear, after error */ 180264Sbill #define WCOM 060 /* Write */ 181264Sbill #define RCOM 070 /* Read */ 182264Sbill 183264Sbill /* Other bits of upcs1 */ 184264Sbill #define IE 0100 /* Controller wide interrupt enable */ 185264Sbill #define TRE 040000 /* Transfer error */ 186266Sbill #define RDY 020 /* Transfer terminated */ 187264Sbill 188264Sbill /* Drive status bits of upds */ 189264Sbill #define PIP 020000 /* Positioning in progress */ 190264Sbill #define ERR 040000 /* Error has occurred, DCLR necessary */ 191264Sbill #define VV 0100 /* Volume is valid, set by PRESET */ 192264Sbill #define DPR 0400 /* Drive has been preset */ 193264Sbill #define MOL 010000 /* Drive is online, heads loaded, etc */ 194264Sbill #define DRY 0200 /* Drive ready */ 195264Sbill 196264Sbill /* Bits of uper1 */ 197264Sbill #define DCK 0100000 /* Ecc error occurred */ 198264Sbill #define ECH 0100 /* Ecc error was unrecoverable */ 199264Sbill #define WLE 04000 /* Attempt to write read-only drive */ 200264Sbill 201264Sbill /* Bits of upof; the offset bits above are also in this register */ 202264Sbill #define FMT22 010000 /* 16 bits/word, must be always set */ 203264Sbill 204264Sbill #define b_cylin b_resid 205264Sbill 206264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 207264Sbill /* 208264Sbill * The EMULEX controller balks if accessed quickly after 209264Sbill * certain operations. The exact timing has not yet been 210264Sbill * determined, but delays are known to be needed when changing 211264Sbill * the selected drive (by writing in upcs2), and thought to be 212264Sbill * needed after operations like PRESET and DCLR. The following 213264Sbill * variables control the delay, DELAY(n) is approximately n usec. 214264Sbill */ 215264Sbill int idelay = 500; /* Delay after PRESET or DCLR */ 216268Sbill int sdelay = 150; /* Delay after selecting drive in upcs2 */ 217264Sbill 218264Sbill #define DELAY(N) { register int d; d = N; while (--d > 0); } 219264Sbill 220264Sbill int nwaitcs2; /* How many sdelay loops ? */ 221264Sbill int neasycs2; /* How many sdelay loops not needed ? */ 222264Sbill 223264Sbill #ifdef INTRLVE 224264Sbill daddr_t dkblock(); 225264Sbill #endif 226264Sbill 227264Sbill /* 228264Sbill * Queue an i/o request for a drive, checking first that it is in range. 229264Sbill * 230264Sbill * A unit start is issued if the drive is inactive, causing 231264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 232264Sbill * already nearly on the money and the controller is not transferring 233264Sbill * we kick it to start the transfer. 234264Sbill */ 235264Sbill upstrategy(bp) 236264Sbill register struct buf *bp; 237264Sbill { 238264Sbill register struct buf *dp; 239264Sbill register unit, xunit; 240264Sbill long sz, bn; 241264Sbill 242264Sbill xunit = minor(bp->b_dev) & 077; 243264Sbill sz = bp->b_bcount; 244264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 245264Sbill unit = dkunit(bp); 246264Sbill if (unit >= NUP || 247264Sbill bp->b_blkno < 0 || 248264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 249264Sbill bp->b_flags |= B_ERROR; 250264Sbill iodone(bp); 251264Sbill return; 252264Sbill } 253264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 254264Sbill dp = &uputab[unit]; 255264Sbill (void) spl5(); 256264Sbill disksort(dp, bp); 257*269Sbill #ifdef UTRACE 258*269Sbill ttime(); 259*269Sbill trace("upstrat bn %d unit %d\n", bp->b_blkno, unit); 260*269Sbill #endif 261264Sbill if (dp->b_active == 0) { 262268Sbill (void) upustart(unit); 263264Sbill if (uptab.b_actf && uptab.b_active == 0) 264268Sbill (void) upstart(); 265264Sbill } 266264Sbill (void) spl0(); 267264Sbill } 268264Sbill 269264Sbill /* 270264Sbill * Start activity on specified drive; called when drive is inactive 271264Sbill * and new transfer request arrives and also when upas indicates that 272264Sbill * a SEARCH command is complete. 273264Sbill */ 274264Sbill upustart(unit) 275264Sbill register unit; 276264Sbill { 277264Sbill register struct buf *bp, *dp; 278264Sbill register struct device *upaddr = UPADDR; 279264Sbill daddr_t bn; 280264Sbill int sn, cn, csn; 281268Sbill int didie = 0; 282264Sbill 283266Sbill if (unit >= NUP) 284268Sbill goto out; 285*269Sbill #ifdef UTRACE 286*269Sbill ttime(); 287*269Sbill trace("upustart %d active %d", unit, uputab[unit].b_active); 288*269Sbill #endif 289266Sbill /* 290266Sbill * Whether or not it was before, this unit is no longer busy. 291266Sbill * Check to see if there is (still or now) a request in this 292266Sbill * drives queue, and if there is, select this unit. 293266Sbill */ 294264Sbill if (unit+DK_N <= DK_NMAX) 295264Sbill dk_busy &= ~(1<<(unit+DK_N)); 296264Sbill dp = &uputab[unit]; 297266Sbill if ((bp = dp->b_actf) == NULL) 298268Sbill goto out; 299264Sbill if ((upaddr->upcs2 & 07) != unit) { 300264Sbill upaddr->upcs2 = unit; 301264Sbill DELAY(sdelay); 302264Sbill nwaitcs2++; 303264Sbill } else 304264Sbill neasycs2++; 305266Sbill /* 306266Sbill * If we have changed packs or just initialized, 307266Sbill * the the volume will not be valid; if so, clear 308266Sbill * the drive, preset it and put in 16bit/word mode. 309266Sbill */ 310266Sbill if ((upaddr->upds & VV) == 0) { 311*269Sbill #ifdef UTRACE 312*269Sbill trace(" not VV"); 313*269Sbill #endif 314266Sbill upaddr->upcs1 = IE|DCLR|GO; 315266Sbill DELAY(idelay); 316264Sbill upaddr->upcs1 = IE|PRESET|GO; 317264Sbill DELAY(idelay); 318264Sbill upaddr->upof = FMT22; 319268Sbill didie = 1; 320264Sbill } 321264Sbill /* 322266Sbill * We are called from upstrategy when a new request arrives 323266Sbill * if we are not already active (with dp->b_active == 0), 324266Sbill * and we then set dp->b_active to 1 if we are to SEARCH 325266Sbill * for the desired cylinder, or 2 if we are on-cylinder. 326266Sbill * If we SEARCH then we will later be called from upintr() 327266Sbill * when the search is complete, and will link this disk onto 328266Sbill * the uptab. We then set dp->b_active to 2 so that upintr() 329266Sbill * will not call us again. 330266Sbill * 331266Sbill * NB: Other drives clear the bit in the attention status 332266Sbill * (i.e. upas) register corresponding to the drive when they 333266Sbill * place the drive on the ready (i.e. uptab) queue. This does 334266Sbill * not work with the Emulex, as the controller hangs the UBA 335266Sbill * of the VAX shortly after the upas register is set, for 336266Sbill * reasons unknown. This only occurs in multi-spindle configurations, 337266Sbill * but to avoid the problem we use the fact that dp->b_active is 338266Sbill * 2 to replace the clearing of the upas bit. 339264Sbill */ 340266Sbill if (dp->b_active) 341264Sbill goto done; 342266Sbill dp->b_active = 1; 343264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 344266Sbill goto done; /* Will redetect error in upstart() soon */ 345264Sbill 346266Sbill /* 347266Sbill * Do enough of the disk address decoding to determine 348266Sbill * which cylinder and sector the request is on. 349266Sbill * Then compute the number of the sector SDIST sectors before 350266Sbill * the one where the transfer is to start, this being the 351266Sbill * point where we wish to attempt to begin the transfer, 352266Sbill * allowing approximately SDIST/2 msec for interrupt latency 353266Sbill * and preparation of the request. 354266Sbill * 355266Sbill * If we are on the correct cylinder and the desired sector 356266Sbill * lies between SDIST and SDIST+RDIST sectors ahead of us, then 357266Sbill * we don't bother to SEARCH but just begin the transfer asap. 358266Sbill */ 359264Sbill bn = dkblock(bp); 360264Sbill cn = bp->b_cylin; 361264Sbill sn = bn%(NSECT*NTRAC); 362264Sbill sn = (sn+NSECT-SDIST)%NSECT; 363264Sbill 364266Sbill if (cn - upaddr->updc) 365266Sbill goto search; /* Not on-cylinder */ 366264Sbill csn = (upaddr->upla>>6) - sn - 1; 367266Sbill if (csn < 0) 368264Sbill csn += NSECT; 369266Sbill if (csn > NSECT-RDIST) 370264Sbill goto done; 371264Sbill 372264Sbill search: 373*269Sbill #ifdef UTRACE 374*269Sbill trace(" search %d@%d to %d@%d", upaddr->updc, (upaddr->upla>>6), 375*269Sbill cn, sn); 376*269Sbill #endif 377264Sbill upaddr->updc = cn; 378264Sbill upaddr->upda = sn; 379264Sbill upaddr->upcs1 = IE|SEARCH|GO; 380268Sbill didie = 1; 381266Sbill /* 382266Sbill * Mark this unit busy. 383266Sbill */ 384264Sbill unit += DK_N; 385264Sbill if (unit <= DK_NMAX) { 386264Sbill dk_busy |= 1<<unit; 387264Sbill dk_numb[unit]++; 388264Sbill } 389268Sbill goto out; 390264Sbill 391264Sbill done: 392266Sbill /* 393266Sbill * This unit is ready to go. Make active == 2 so 394266Sbill * we won't get called again (by upintr() because upas&(1<<unit)) 395266Sbill * and link us onto the chain of ready disks. 396266Sbill */ 397*269Sbill #ifdef UTRACE 398*269Sbill trace(" done"); 399*269Sbill #endif 400266Sbill dp->b_active = 2; 401264Sbill dp->b_forw = NULL; 402266Sbill if (uptab.b_actf == NULL) 403264Sbill uptab.b_actf = dp; 404264Sbill else 405264Sbill uptab.b_actl->b_forw = dp; 406264Sbill uptab.b_actl = dp; 407268Sbill 408268Sbill out: 409*269Sbill #ifdef UTRACE 410*269Sbill trace("\n"); 411*269Sbill #endif 412268Sbill return (didie); 413264Sbill } 414264Sbill 415264Sbill /* 416264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 417264Sbill */ 418264Sbill upstart() 419264Sbill { 420264Sbill register struct buf *bp, *dp; 421264Sbill register unit; 422264Sbill register struct device *upaddr; 423264Sbill daddr_t bn; 424266Sbill int dn, sn, tn, cn, cmd; 425264Sbill 426264Sbill loop: 427*269Sbill #ifdef UTRACE 428*269Sbill ttime(); 429*269Sbill trace("upstart"); 430*269Sbill #endif 431266Sbill /* 432266Sbill * Pick a drive off the queue of ready drives, and 433266Sbill * perform the first transfer on its queue. 434266Sbill * 435266Sbill * Looping here is completely for the sake of drives which 436266Sbill * are not present and on-line, for which we completely clear the 437266Sbill * request queue. 438266Sbill */ 439*269Sbill if ((dp = uptab.b_actf) == NULL) { 440*269Sbill #ifdef UTRACE 441*269Sbill trace("\n"); 442*269Sbill #endif 443268Sbill return (0); 444*269Sbill } 445264Sbill if ((bp = dp->b_actf) == NULL) { 446264Sbill uptab.b_actf = dp->b_forw; 447264Sbill goto loop; 448264Sbill } 449266Sbill /* 450266Sbill * Mark the controller busy, and multi-part disk address. 451266Sbill * Select the unit on which the i/o is to take place. 452266Sbill */ 453264Sbill uptab.b_active++; 454264Sbill unit = minor(bp->b_dev) & 077; 455264Sbill dn = dkunit(bp); 456264Sbill bn = dkblock(bp); 457264Sbill cn = up_sizes[unit&07].cyloff; 458264Sbill cn += bn/(NSECT*NTRAC); 459264Sbill sn = bn%(NSECT*NTRAC); 460264Sbill tn = sn/NSECT; 461266Sbill sn %= NSECT; 462264Sbill upaddr = UPADDR; 463*269Sbill #ifdef UTRACE 464*269Sbill trace(" unit %d", dn); 465*269Sbill #endif 466264Sbill if ((upaddr->upcs2 & 07) != dn) { 467*269Sbill #ifdef UTRACE 468*269Sbill trace(" select"); 469*269Sbill #endif 470264Sbill upaddr->upcs2 = dn; 471264Sbill DELAY(sdelay); 472264Sbill nwaitcs2++; 473264Sbill } else 474264Sbill neasycs2++; 475266Sbill up_ubinfo = ubasetup(bp, 1); /* In a funny place for delay... */ 476266Sbill /* 477266Sbill * If drive is not present and on-line, then 478266Sbill * get rid of this with an error and loop to get 479266Sbill * rid of the rest of its queued requests. 480266Sbill * (Then on to any other ready drives.) 481266Sbill */ 482264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 483*269Sbill #ifdef UTRACE 484*269Sbill trace(" !(DPR && MOL)"); 485*269Sbill #endif 486264Sbill uptab.b_active = 0; 487264Sbill uptab.b_errcnt = 0; 488264Sbill dp->b_actf = bp->av_forw; 489266Sbill dp->b_active = 0; 490264Sbill bp->b_flags |= B_ERROR; 491264Sbill iodone(bp); 492266Sbill ubafree(up_ubinfo), up_ubinfo = 0; /* A funny place ... */ 493264Sbill goto loop; 494264Sbill } 495266Sbill /* 496266Sbill * If this is a retry, then with the 16'th retry we 497266Sbill * begin to try offsetting the heads to recover the data. 498266Sbill */ 499266Sbill if (uptab.b_errcnt >= 16) { 500*269Sbill #ifdef UTRACE 501*269Sbill trace(" offset"); 502*269Sbill #endif 503264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 504266Sbill upaddr->upcs1 = IE|OFFSET|GO; 505264Sbill DELAY(idelay); 506266Sbill while (upaddr->upds & PIP) 507264Sbill DELAY(25); 508264Sbill } 509266Sbill /* 510266Sbill * Now set up the transfer, retrieving the high 511266Sbill * 2 bits of the UNIBUS address from the information 512266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 513266Sbill */ 514*269Sbill #ifdef UTRACE 515*269Sbill trace(" %s %d.%d@%d cnt %d ba %x\n", 516*269Sbill (bp->b_flags&B_READ) ? "read" : "write", 517*269Sbill cn, tn, sn, bp->b_bcount, up_ubinfo & 0x3ffff); 518*269Sbill #endif 519264Sbill upaddr->updc = cn; 520264Sbill upaddr->upda = (tn << 8) + sn; 521264Sbill upaddr->upba = up_ubinfo; 522264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 523266Sbill cmd = (up_ubinfo >> 8) & 0x300; 524264Sbill if (bp->b_flags & B_READ) 525266Sbill cmd |= IE|RCOM|GO; 526264Sbill else 527266Sbill cmd |= IE|WCOM|GO; 528266Sbill upaddr->upcs1 = cmd; 529268Sbill #ifdef notdef 530267Sbill if (csdel3) DELAY(csdel3); 531268Sbill #endif 532266Sbill /* 533266Sbill * This is a controller busy situation. 534266Sbill * Record in dk slot NUP+DK_N (after last drive) 535266Sbill * unless there aren't that many slots reserved for 536266Sbill * us in which case we record this as a drive busy 537266Sbill * (if there is room for that). 538266Sbill */ 539264Sbill unit = dn+DK_N; 540264Sbill if (NUP+DK_N == DK_NMAX) 541264Sbill unit = NUP+DK_N; 542264Sbill if (unit <= DK_NMAX) { 543264Sbill dk_busy |= 1<<unit; 544264Sbill dk_numb[unit]++; 545264Sbill dk_wds[unit] += bp->b_bcount>>6; 546264Sbill } 547268Sbill return (1); 548264Sbill } 549264Sbill 550264Sbill /* 551264Sbill * Handle a device interrupt. 552264Sbill * 553264Sbill * If the transferring drive needs attention, service it 554264Sbill * retrying on error or beginning next transfer. 555264Sbill * Service all other ready drives, calling ustart to transfer 556264Sbill * their blocks to the ready queue in uptab, and then restart 557264Sbill * the controller if there is anything to do. 558264Sbill */ 559264Sbill upintr() 560264Sbill { 561264Sbill register struct buf *bp, *dp; 562264Sbill register unit; 563264Sbill register struct device *upaddr = UPADDR; 564264Sbill int as = upaddr->upas & 0377; 565268Sbill int needie = 1; 566264Sbill 567*269Sbill #ifdef UTRACE 568*269Sbill ttime(); 569*269Sbill trace("upintr as %d act %d %d %d;", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active); 570*269Sbill #endif 571266Sbill if (uptab.b_active) { 572266Sbill /* 573266Sbill * The drive is transferring, thus the hardware 574266Sbill * (say the designers) will only interrupt when the transfer 575266Sbill * completes; check for it anyways. 576266Sbill */ 577266Sbill if ((upaddr->upcs1 & RDY) == 0) { 578*269Sbill #ifdef UTRACE 579*269Sbill trace(" !RDY"); 580*269Sbill #endif 581267Sbill printf("!RDY in upintr: cs1 %o\n", upaddr->upcs1); 582267Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active); 583*269Sbill } 584266Sbill /* 585266Sbill * Mark controller or drive not busy, and check for an 586266Sbill * error condition which may have resulted from the transfer. 587266Sbill */ 588264Sbill dp = uptab.b_actf; 589264Sbill bp = dp->b_actf; 590264Sbill unit = dkunit(bp); 591264Sbill if (DK_N+NUP == DK_NMAX) 592264Sbill dk_busy &= ~(1<<(DK_N+NUP)); 593264Sbill else if (DK_N+unit <= DK_NMAX) 594264Sbill dk_busy &= ~(1<<(DK_N+unit)); 595264Sbill if (upaddr->upcs1 & TRE) { 596*269Sbill #ifdef UTRACE 597*269Sbill trace(" TRE"); 598*269Sbill #endif 599266Sbill /* 600266Sbill * An error occurred, indeed. Select this unit 601266Sbill * to get at the drive status (a SEARCH may have 602266Sbill * intervened to change the selected unit), and 603266Sbill * wait for the command which caused the interrupt 604266Sbill * to complete (DRY). 605266Sbill * 606266Sbill * WHY IS THE WAIT NECESSARY? 607266Sbill */ 608264Sbill if ((upaddr->upcs2 & 07) != unit) { 609264Sbill upaddr->upcs2 = unit; 610264Sbill DELAY(sdelay); 611264Sbill nwaitcs2++; 612264Sbill } else 613264Sbill neasycs2++; 614266Sbill while ((upaddr->upds & DRY) == 0) 615264Sbill DELAY(25); 616266Sbill /* 617266Sbill * After 28 retries (16 w/o servo offsets, and then 618266Sbill * 12 with servo offsets), or if we encountered 619266Sbill * an error because the drive is write-protected, 620266Sbill * give up. Print an error message on the last 2 621266Sbill * retries before a hard failure. 622266Sbill */ 623266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 624264Sbill bp->b_flags |= B_ERROR; 625264Sbill else 626266Sbill uptab.b_active = 0; /* To force retry */ 627266Sbill if (uptab.b_errcnt > 27) 628264Sbill deverror(bp, upaddr->upcs2, upaddr->uper1); 629266Sbill /* 630266Sbill * If this was a correctible ECC error, let upecc 631266Sbill * do the dirty work to correct it. If upecc 632266Sbill * starts another READ for the rest of the data 633266Sbill * then it returns 1 (having set uptab.b_active). 634266Sbill * Otherwise we are done and fall through to 635266Sbill * finish up. 636266Sbill */ 637266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 638266Sbill return; 639266Sbill /* 640266Sbill * Clear the drive and, every 4 retries, recalibrate 641266Sbill * to hopefully help clear up seek positioning problems. 642266Sbill */ 643264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 644264Sbill DELAY(idelay); 645268Sbill needie = 0; 646266Sbill if ((uptab.b_errcnt&07) == 4) { 647264Sbill upaddr->upcs1 = RECAL|GO|IE; 648264Sbill DELAY(idelay); 649264Sbill while(upaddr->upds & PIP) 650264Sbill DELAY(25); 651264Sbill } 652264Sbill } 653266Sbill /* 654266Sbill * If we are still noted as active, then no 655266Sbill * (further) retries are necessary. 656266Sbill * 657266Sbill * Make sure the correct unit is selected, 658266Sbill * return it to centerline if necessary, and mark 659266Sbill * this i/o complete, starting the next transfer 660266Sbill * on this drive with the upustart routine (if any). 661266Sbill */ 662266Sbill if (uptab.b_active) { 663*269Sbill #ifdef UTRACE 664*269Sbill trace(" unit %d", unit); 665*269Sbill #endif 666266Sbill if ((upaddr->upcs2 & 07) != unit) { 667*269Sbill #ifdef UTRACE 668*269Sbill trace(" select"); 669*269Sbill #endif 670266Sbill upaddr->upcs2 = unit; 671266Sbill DELAY(sdelay); 672266Sbill nwaitcs2++; 673266Sbill } else 674266Sbill neasycs2++; 675266Sbill if (uptab.b_errcnt >= 16) { 676*269Sbill #ifdef UTRACE 677*269Sbill trace(" rtc"); 678*269Sbill #endif 679266Sbill upaddr->upcs1 = RTC|GO|IE; 680264Sbill DELAY(idelay); 681266Sbill while (upaddr->upds & PIP) 682264Sbill DELAY(25); 683268Sbill needie = 0; 684264Sbill } 685264Sbill uptab.b_active = 0; 686264Sbill uptab.b_errcnt = 0; 687264Sbill uptab.b_actf = dp->b_forw; 688264Sbill dp->b_active = 0; 689264Sbill dp->b_errcnt = 0; 690264Sbill dp->b_actf = bp->av_forw; 691266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 692264Sbill iodone(bp); 693264Sbill if(dp->b_actf) 694268Sbill if (upustart(unit)) 695268Sbill needie = 0; 696264Sbill } 697264Sbill as &= ~(1<<unit); 698264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 699266Sbill } 700266Sbill #ifndef notdef 701266Sbill else { 702264Sbill if (upaddr->upcs1 & TRE) { 703*269Sbill #ifdef UTRACE 704*269Sbill trace(" TRE"); 705*269Sbill #endif 706264Sbill upaddr->upcs1 = TRE; 707264Sbill DELAY(idelay); 708264Sbill } 709264Sbill } 710266Sbill #endif 711266Sbill /* 712266Sbill * If we have a unit with an outstanding SEARCH, 713266Sbill * and the hardware indicates the unit requires attention, 714266Sbill * the bring the drive to the ready queue. 715266Sbill * Finally, if the controller is not transferring 716266Sbill * start it if any drives are now ready to transfer. 717266Sbill */ 718*269Sbill #ifdef UTRACE 719*269Sbill trace("\n"); 720*269Sbill #endif 721266Sbill for (unit = 0; unit < NUP; unit++) 722266Sbill if (as & (1<<unit)) 723267Sbill if (uputab[unit].b_active == 1) { 724267Sbill upaddr->upas = 1<<unit; 725*269Sbill #ifdef UTRACE 726*269Sbill trace("as clear %d\n", unit); 727*269Sbill #endif 728268Sbill if (asdel) DELAY(asdel); 729268Sbill if (upustart(unit)) 730268Sbill needie = 0; 731267Sbill } else { 732266Sbill upaddr->upas = 1<<unit; 733*269Sbill #ifdef UTRACE 734*269Sbill trace("spurious as clear %d\n", unit); 735*269Sbill #endif 736266Sbill DELAY(1000); 737266Sbill } 738266Sbill if (uptab.b_actf && uptab.b_active == 0) 739268Sbill if (upstart()) 740268Sbill needie = 0; 741266Sbill out: 742*269Sbill if (needie) { 743*269Sbill #ifdef UTRACE 744*269Sbill trace("upintr set IE\n"); 745*269Sbill #endif 746266Sbill upaddr->upcs1 = IE; 747*269Sbill } 748264Sbill } 749264Sbill 750264Sbill upread(dev) 751264Sbill { 752264Sbill 753264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 754264Sbill } 755264Sbill 756264Sbill upwrite(dev) 757264Sbill { 758264Sbill 759264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 760264Sbill } 761264Sbill 762266Sbill /* 763266Sbill * Correct an ECC error, and restart the i/o to complete 764266Sbill * the transfer if necessary. This is quite complicated because 765266Sbill * the transfer may be going to an odd memory address base and/or 766266Sbill * across a page boundary. 767266Sbill */ 768264Sbill upecc(up, bp) 769264Sbill register struct device *up; 770264Sbill register struct buf *bp; 771264Sbill { 772264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 773266Sbill register int i; 774264Sbill caddr_t addr; 775266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 776264Sbill int bn, cn, tn, sn; 777264Sbill 778264Sbill /* 779266Sbill * Npf is the number of sectors transferred before the sector 780266Sbill * containing the ECC error, and reg is the UBA register 781266Sbill * mapping (the first part of) the transfer. 782266Sbill * O is offset within a memory page of the first byte transferred. 783264Sbill */ 784266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 785266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 786264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 787264Sbill printf("%D ", bp->b_blkno+npf); 788264Sbill prdev("ECC", bp->b_dev); 789264Sbill mask = up->upec2; 790264Sbill if (mask == 0) { 791266Sbill up->upof = FMT22; /* == RTC ???? */ 792264Sbill DELAY(idelay); 793264Sbill return (0); 794264Sbill } 795266Sbill /* 796266Sbill * Flush the buffered data path, and compute the 797266Sbill * byte and bit position of the error. The variable i 798266Sbill * is the byte offset in the transfer, the variable byte 799266Sbill * is the offset from a page boundary in main memory. 800266Sbill */ 801266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 802266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 803266Sbill bit = i&07; 804266Sbill i = (i&~07)>>3; 805264Sbill byte = i + o; 806266Sbill /* 807266Sbill * Correct while possible bits remain of mask. Since mask 808266Sbill * contains 11 bits, we continue while the bit offset is > -11. 809266Sbill * Also watch out for end of this block and the end of the whole 810266Sbill * transfer. 811266Sbill */ 812266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 813266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 814266Sbill (byte & PGOFSET); 815266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 816266Sbill byte++; 817266Sbill i++; 818266Sbill bit -= 8; 819264Sbill } 820266Sbill uptab.b_active++; /* Either complete or continuing... */ 821264Sbill if (up->upwc == 0) 822264Sbill return (0); 823266Sbill /* 824266Sbill * Have to continue the transfer... clear the drive, 825266Sbill * and compute the position where the transfer is to continue. 826266Sbill * We have completed npf+1 sectors of the transfer already; 827266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 828266Sbill */ 829266Sbill up->upcs1 = TRE|IE|DCLR|GO; 830264Sbill DELAY(idelay); 831264Sbill bn = dkblock(bp); 832264Sbill cn = bp->b_cylin; 833266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 834264Sbill tn = sn/NSECT; 835264Sbill sn %= NSECT; 836266Sbill cn += tn/NTRAC; 837266Sbill tn %= NTRAC; 838264Sbill up->updc = cn; 839266Sbill up->upda = (tn << 8) | sn; 840266Sbill ubaddr = (int)ptob(reg+1) + o; 841266Sbill up->upba = ubaddr; 842266Sbill cmd = (ubaddr >> 8) & 0x300; 843266Sbill cmd |= IE|GO|RCOM; 844266Sbill up->upcs1 = cmd; 845264Sbill return (1); 846264Sbill } 847