1*885Sbill /* up.c 3.21 09/16/80 */ 2264Sbill 3264Sbill /* 4*885Sbill * UNIBUS disk driver with overlapped seeks and ECC recovery. 5264Sbill * 6*885Sbill * This driver works marginally on an Emulex SC-11B controller with rev 7*885Sbill * level J microcode, defining: 8*885Sbill * int olducode = 1; 9*885Sbill * to force CPU stalling delays. 10266Sbill * 11*885Sbill * It has worked with no delays and no problems on a prototype 12*885Sbill * SC-21 controller. Emulex intends to upgrade all SC-11s on VAXes to SC-21s. 13*885Sbill * You should get a SC-21 to replace any SC-11 on a VAX. 14*885Sbill * 15*885Sbill * SC-11B Controller 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) 27341Sbill * SW2-3 4 wd dma burst (on=closed, enable) 28264Sbill * SW2-4 unused (off=open) 29264Sbill */ 30264Sbill 31264Sbill #include "../h/param.h" 32264Sbill #include "../h/systm.h" 33308Sbill #include "../h/dk.h" 34264Sbill #include "../h/buf.h" 35264Sbill #include "../h/conf.h" 36264Sbill #include "../h/dir.h" 37264Sbill #include "../h/user.h" 38264Sbill #include "../h/map.h" 39420Sbill #include "../h/pte.h" 40264Sbill #include "../h/mba.h" 41264Sbill #include "../h/mtpr.h" 42264Sbill #include "../h/uba.h" 43264Sbill #include "../h/vm.h" 44264Sbill 45264Sbill /* 46264Sbill * Define number of drives, and range of sampling information to be used. 47264Sbill * 48264Sbill * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats, 49264Sbill * and DK_N+NUP gathers controller transferring stats. 50264Sbill * 51264Sbill * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive. 52264Sbill * If DK_NMAX is yet smaller, some drives are not monitored. 53264Sbill */ 54308Sbill #define DK_N 2 55308Sbill #define DK_NMAX 3 56264Sbill 57264Sbill #define ushort unsigned short 58264Sbill 59264Sbill struct device 60264Sbill { 61264Sbill ushort upcs1; /* control and status register 1 */ 62264Sbill short upwc; /* word count register */ 63264Sbill ushort upba; /* UNIBUS address register */ 64264Sbill ushort upda; /* desired address register */ 65264Sbill ushort upcs2; /* control and status register 2 */ 66264Sbill ushort upds; /* drive Status */ 67264Sbill ushort uper1; /* error register 1 */ 68264Sbill ushort upas; /* attention summary */ 69264Sbill ushort upla; /* look ahead */ 70264Sbill ushort updb; /* data buffer */ 71264Sbill ushort upmr; /* maintenance */ 72264Sbill ushort updt; /* drive type */ 73264Sbill ushort upsn; /* serial number */ 74264Sbill ushort upof; /* offset register */ 75264Sbill ushort updc; /* desired cylinder address register */ 76264Sbill ushort upcc; /* current cylinder */ 77264Sbill ushort uper2; /* error register 2 */ 78264Sbill ushort uper3; /* error register 3 */ 79264Sbill ushort upec1; /* burst error bit position */ 80264Sbill ushort upec2; /* burst error bit pattern */ 81264Sbill }; 82264Sbill 83275Sbill /* 84275Sbill * Software extension to the upas register, so we can 85275Sbill * postpone starting SEARCH commands until the controller 86275Sbill * is not transferring. 87275Sbill */ 88341Sbill int upsoftas; 89275Sbill 90275Sbill /* 91275Sbill * If upseek then we don't issue SEARCH commands but rather just 92275Sbill * settle for a SEEK to the correct cylinder. 93275Sbill */ 94275Sbill int upseek; 95275Sbill 96264Sbill #define UPADDR ((struct device *)(UBA0_DEV + 0176700)) 97264Sbill 98264Sbill #define NUP 2 /* Number of drives this installation */ 99264Sbill 100264Sbill #define NSECT 32 101264Sbill #define NTRAC 19 102264Sbill 103264Sbill /* 104264Sbill * Constants controlling on-cylinder SEARCH usage. 105264Sbill * 106308Sbill * upSDIST/2 msec time needed to start transfer 107308Sbill * upRDIST/2 msec tolerable rotational latency when on-cylinder 108275Sbill * 109308Sbill * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST 110275Sbill * and in the driver then we take it as it is. Otherwise we do a SEARCH 111308Sbill * requesting an interrupt upSDIST sectors in advance. 112264Sbill */ 113308Sbill #define _upSDIST 6 /* 3.0 msec */ 114308Sbill #define _upRDIST 6 /* 3.0 msec */ 115264Sbill 116308Sbill int upSDIST = _upSDIST; 117308Sbill int upRDIST = _upRDIST; 118275Sbill 119264Sbill /* 120264Sbill * To fill a 300M drive: 121264Sbill * A is designed to be used as a root. 122264Sbill * B is suitable for a swap area. 123264Sbill * H is the primary storage area. 124264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 125264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 126264Sbill * covers the whole drive and can be used for pack-pack copying. 127264Sbill */ 128264Sbill struct size 129264Sbill { 130264Sbill daddr_t nblocks; 131264Sbill int cyloff; 132264Sbill } up_sizes[8] = { 133264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 134264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 135341Sbill 495520, 0, /* C=cyl 0 thru 814 */ 136264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 137264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 138264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 139264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 140264Sbill 445664, 82, /* H=cyl 82 thru 814 */ 141264Sbill /* Later, and more safely for H area... 142264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 143264Sbill }; 144264Sbill 145264Sbill /* 146264Sbill * The following defines are used in offset positioning 147264Sbill * when trying to recover disk errors, with the constants being 148264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 149264Sbill * tried (this makes sense only during read, in any case.) 150264Sbill * 151341Sbill * NOT ALL OF THESE ARE IMPLEMENTED ON 9300!?! 152264Sbill */ 153264Sbill #define P400 020 154264Sbill #define M400 0220 155264Sbill #define P800 040 156264Sbill #define M800 0240 157264Sbill #define P1200 060 158264Sbill #define M1200 0260 159264Sbill #define HCI 020000 160264Sbill 161264Sbill int up_offset[16] = 162264Sbill { 163264Sbill P400, M400, P400, M400, 164264Sbill P800, M800, P800, M800, 165264Sbill P1200, M1200, P1200, M1200, 166264Sbill 0, 0, 0, 0, 167264Sbill }; 168264Sbill 169264Sbill /* 170264Sbill * Each drive has a table uputab[i]. On this table are sorted the 171264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 172264Sbill * In the upustart() routine, each drive is independently advanced 173264Sbill * until it is on the desired cylinder for the next transfer and near 174264Sbill * the desired sector. The drive is then chained onto the uptab 175264Sbill * table, and the transfer is initiated by the upstart() routine. 176264Sbill * When the transfer is completed the driver reinvokes the upustart() 177264Sbill * routine to set up the next transfer. 178264Sbill */ 179264Sbill struct buf uptab; 180264Sbill struct buf uputab[NUP]; 181264Sbill 182264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 183264Sbill 184264Sbill /* Drive commands, placed in upcs1 */ 185264Sbill #define GO 01 /* Go bit, set in all commands */ 186264Sbill #define PRESET 020 /* Preset drive at init or after errors */ 187264Sbill #define OFFSET 014 /* Offset heads to try to recover error */ 188264Sbill #define RTC 016 /* Return to center-line after OFFSET */ 189264Sbill #define SEARCH 030 /* Search for cylinder+sector */ 190275Sbill #define SEEK 04 /* Seek to cylinder */ 191264Sbill #define RECAL 06 /* Recalibrate, needed after seek error */ 192264Sbill #define DCLR 010 /* Drive clear, after error */ 193264Sbill #define WCOM 060 /* Write */ 194264Sbill #define RCOM 070 /* Read */ 195264Sbill 196264Sbill /* Other bits of upcs1 */ 197264Sbill #define IE 0100 /* Controller wide interrupt enable */ 198264Sbill #define TRE 040000 /* Transfer error */ 199345Sbill #define RDY 0200 /* Transfer terminated */ 200264Sbill 201264Sbill /* Drive status bits of upds */ 202264Sbill #define PIP 020000 /* Positioning in progress */ 203264Sbill #define ERR 040000 /* Error has occurred, DCLR necessary */ 204264Sbill #define VV 0100 /* Volume is valid, set by PRESET */ 205264Sbill #define DPR 0400 /* Drive has been preset */ 206264Sbill #define MOL 010000 /* Drive is online, heads loaded, etc */ 207264Sbill #define DRY 0200 /* Drive ready */ 208264Sbill 209313Sbill /* Bits of upcs2 */ 210313Sbill #define CLR 040 /* Controller clear */ 211264Sbill /* Bits of uper1 */ 212264Sbill #define DCK 0100000 /* Ecc error occurred */ 213264Sbill #define ECH 0100 /* Ecc error was unrecoverable */ 214264Sbill #define WLE 04000 /* Attempt to write read-only drive */ 215264Sbill 216264Sbill /* Bits of upof; the offset bits above are also in this register */ 217264Sbill #define FMT22 010000 /* 16 bits/word, must be always set */ 218264Sbill 219264Sbill #define b_cylin b_resid 220264Sbill 221264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 222264Sbill /* 223264Sbill * The EMULEX controller balks if accessed quickly after 224341Sbill * certain operations. With rev J delays seem to be needed only 225341Sbill * when selecting a new unit, and in drive initialization type 226341Sbill * like PRESET and DCLR. The following variables control the delay 227341Sbill * DELAY(n) is approximately n usec. 228264Sbill */ 229367Sbill int olducode = 1; 230264Sbill int idelay = 500; /* Delay after PRESET or DCLR */ 231367Sbill int osdelay = 150; /* Old delay after selecting drive in upcs2 */ 232367Sbill int ordelay = 100; /* Old delay after SEARCH */ 233367Sbill int oasdel = 100; /* Old delay after clearing bit in upas */ 234367Sbill int nsdelay = 25; 235264Sbill 236264Sbill #define DELAY(N) { register int d; d = N; while (--d > 0); } 237264Sbill 238264Sbill int nwaitcs2; /* How many sdelay loops ? */ 239264Sbill int neasycs2; /* How many sdelay loops not needed ? */ 240264Sbill 241313Sbill int up_wticks; /* Ticks waiting for interrupt */ 242313Sbill int upwstart; /* Have started guardian */ 243313Sbill int upwatch(); 244313Sbill 245264Sbill #ifdef INTRLVE 246264Sbill daddr_t dkblock(); 247264Sbill #endif 248264Sbill 249264Sbill /* 250264Sbill * Queue an i/o request for a drive, checking first that it is in range. 251264Sbill * 252264Sbill * A unit start is issued if the drive is inactive, causing 253264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 254264Sbill * already nearly on the money and the controller is not transferring 255264Sbill * we kick it to start the transfer. 256264Sbill */ 257264Sbill upstrategy(bp) 258264Sbill register struct buf *bp; 259264Sbill { 260264Sbill register struct buf *dp; 261264Sbill register unit, xunit; 262264Sbill long sz, bn; 263264Sbill 264313Sbill if (upwstart == 0) { 265313Sbill timeout((caddr_t)upwatch, 0, HZ); 266313Sbill upwstart++; 267313Sbill } 268264Sbill xunit = minor(bp->b_dev) & 077; 269264Sbill sz = bp->b_bcount; 270264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 271264Sbill unit = dkunit(bp); 272264Sbill if (unit >= NUP || 273264Sbill bp->b_blkno < 0 || 274264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 275264Sbill bp->b_flags |= B_ERROR; 276264Sbill iodone(bp); 277264Sbill return; 278264Sbill } 279264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 280264Sbill dp = &uputab[unit]; 281264Sbill (void) spl5(); 282264Sbill disksort(dp, bp); 283264Sbill if (dp->b_active == 0) { 284268Sbill (void) upustart(unit); 285264Sbill if (uptab.b_actf && uptab.b_active == 0) 286268Sbill (void) upstart(); 287264Sbill } 288264Sbill (void) spl0(); 289264Sbill } 290264Sbill 291264Sbill /* 292264Sbill * Start activity on specified drive; called when drive is inactive 293264Sbill * and new transfer request arrives and also when upas indicates that 294264Sbill * a SEARCH command is complete. 295264Sbill */ 296264Sbill upustart(unit) 297264Sbill register unit; 298264Sbill { 299264Sbill register struct buf *bp, *dp; 300264Sbill register struct device *upaddr = UPADDR; 301264Sbill daddr_t bn; 302264Sbill int sn, cn, csn; 303268Sbill int didie = 0; 304264Sbill 305275Sbill /* 306275Sbill * Other drivers tend to say something like 307275Sbill * upaddr->upcs1 = IE; 308275Sbill * upaddr->upas = 1<<unit; 309275Sbill * here, but the SC-11B will cancel a command which 310275Sbill * happens to be sitting in the cs1 if you clear the go 311275Sbill * bit by storing there (so the first is not safe), 312275Sbill * and it also does not like being bothered with operations 313275Sbill * such as clearing upas when a transfer is active (as 314275Sbill * it may well be.) 315275Sbill * 316275Sbill * Thus we keep careful track of when we re-enable IE 317275Sbill * after an interrupt and do it only if we didn't issue 318275Sbill * a command which re-enabled it as a matter of course. 319275Sbill * We clear bits in upas in the interrupt routine, when 320275Sbill * no transfers are active. 321275Sbill */ 322266Sbill if (unit >= NUP) 323268Sbill goto out; 324264Sbill if (unit+DK_N <= DK_NMAX) 325264Sbill dk_busy &= ~(1<<(unit+DK_N)); 326264Sbill dp = &uputab[unit]; 327266Sbill if ((bp = dp->b_actf) == NULL) 328268Sbill goto out; 329275Sbill /* 330275Sbill * The SC-11B doesn't start SEARCH commands when transfers are 331275Sbill * in progress. In fact, it tends to get confused when given 332275Sbill * SEARCH'es during transfers, generating interrupts with neither 333275Sbill * RDY nor a bit in the upas register. Thus we defer 334275Sbill * until an interrupt when a transfer is pending. 335275Sbill */ 336275Sbill if (uptab.b_active) { 337341Sbill upsoftas |= 1<<unit; 338275Sbill return (0); 339275Sbill } 340276Sbill if (dp->b_active) 341276Sbill goto done; 342276Sbill dp->b_active = 1; 343264Sbill if ((upaddr->upcs2 & 07) != unit) { 344264Sbill upaddr->upcs2 = unit; 345367Sbill DELAY(olducode ? osdelay : nsdelay); 346264Sbill nwaitcs2++; 347264Sbill } else 348264Sbill neasycs2++; 349266Sbill /* 350266Sbill * If we have changed packs or just initialized, 351275Sbill * then the volume will not be valid; if so, clear 352266Sbill * the drive, preset it and put in 16bit/word mode. 353266Sbill */ 354266Sbill if ((upaddr->upds & VV) == 0) { 355266Sbill upaddr->upcs1 = IE|DCLR|GO; 356266Sbill DELAY(idelay); 357264Sbill upaddr->upcs1 = IE|PRESET|GO; 358264Sbill DELAY(idelay); 359264Sbill upaddr->upof = FMT22; 360268Sbill didie = 1; 361264Sbill } 362264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 363275Sbill goto done; 364266Sbill /* 365266Sbill * Do enough of the disk address decoding to determine 366266Sbill * which cylinder and sector the request is on. 367266Sbill * If we are on the correct cylinder and the desired sector 368308Sbill * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then 369266Sbill * we don't bother to SEARCH but just begin the transfer asap. 370308Sbill * Otherwise ask for a interrupt upSDIST sectors ahead. 371266Sbill */ 372264Sbill bn = dkblock(bp); 373264Sbill cn = bp->b_cylin; 374264Sbill sn = bn%(NSECT*NTRAC); 375308Sbill sn = (sn+NSECT-upSDIST)%NSECT; 376264Sbill 377266Sbill if (cn - upaddr->updc) 378266Sbill goto search; /* Not on-cylinder */ 379275Sbill else if (upseek) 380275Sbill goto done; /* Ok just to be on-cylinder */ 381264Sbill csn = (upaddr->upla>>6) - sn - 1; 382266Sbill if (csn < 0) 383264Sbill csn += NSECT; 384308Sbill if (csn > NSECT-upRDIST) 385264Sbill goto done; 386264Sbill 387264Sbill search: 388264Sbill upaddr->updc = cn; 389275Sbill if (upseek) 390275Sbill upaddr->upcs1 = IE|SEEK|GO; 391275Sbill else { 392275Sbill upaddr->upda = sn; 393275Sbill upaddr->upcs1 = IE|SEARCH|GO; 394275Sbill } 395268Sbill didie = 1; 396266Sbill /* 397266Sbill * Mark this unit busy. 398266Sbill */ 399264Sbill unit += DK_N; 400345Sbill if (unit <= DK_NMAX && DK_N+NUP <= DK_NMAX) { 401264Sbill dk_busy |= 1<<unit; 402264Sbill dk_numb[unit]++; 403264Sbill } 404367Sbill if (olducode) 405367Sbill DELAY(ordelay); 406268Sbill goto out; 407264Sbill 408264Sbill done: 409266Sbill /* 410275Sbill * This unit is ready to go so 411275Sbill * link it onto the chain of ready disks. 412266Sbill */ 413264Sbill dp->b_forw = NULL; 414266Sbill if (uptab.b_actf == NULL) 415264Sbill uptab.b_actf = dp; 416264Sbill else 417264Sbill uptab.b_actl->b_forw = dp; 418264Sbill uptab.b_actl = dp; 419268Sbill 420268Sbill out: 421268Sbill return (didie); 422264Sbill } 423264Sbill 424264Sbill /* 425264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 426264Sbill */ 427264Sbill upstart() 428264Sbill { 429264Sbill register struct buf *bp, *dp; 430264Sbill register unit; 431264Sbill register struct device *upaddr; 432264Sbill daddr_t bn; 433266Sbill int dn, sn, tn, cn, cmd; 434264Sbill 435264Sbill loop: 436266Sbill /* 437266Sbill * Pick a drive off the queue of ready drives, and 438266Sbill * perform the first transfer on its queue. 439266Sbill * 440266Sbill * Looping here is completely for the sake of drives which 441266Sbill * are not present and on-line, for which we completely clear the 442266Sbill * request queue. 443266Sbill */ 444273Sbill if ((dp = uptab.b_actf) == NULL) 445268Sbill return (0); 446264Sbill if ((bp = dp->b_actf) == NULL) { 447264Sbill uptab.b_actf = dp->b_forw; 448264Sbill goto loop; 449264Sbill } 450266Sbill /* 451266Sbill * Mark the controller busy, and multi-part disk address. 452266Sbill * Select the unit on which the i/o is to take place. 453266Sbill */ 454264Sbill uptab.b_active++; 455264Sbill unit = minor(bp->b_dev) & 077; 456264Sbill dn = dkunit(bp); 457264Sbill bn = dkblock(bp); 458264Sbill cn = up_sizes[unit&07].cyloff; 459264Sbill cn += bn/(NSECT*NTRAC); 460264Sbill sn = bn%(NSECT*NTRAC); 461264Sbill tn = sn/NSECT; 462266Sbill sn %= NSECT; 463264Sbill upaddr = UPADDR; 464264Sbill if ((upaddr->upcs2 & 07) != dn) { 465264Sbill upaddr->upcs2 = dn; 466275Sbill /* DELAY(sdelay); Provided by ubasetup() */ 467264Sbill nwaitcs2++; 468264Sbill } else 469264Sbill neasycs2++; 470275Sbill up_ubinfo = ubasetup(bp, 1); /* Providing delay */ 471266Sbill /* 472266Sbill * If drive is not present and on-line, then 473266Sbill * get rid of this with an error and loop to get 474266Sbill * rid of the rest of its queued requests. 475266Sbill * (Then on to any other ready drives.) 476266Sbill */ 477264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 478275Sbill printf("!DPR || !MOL, unit %d, ds %o\n", dn, upaddr->upds); 479264Sbill uptab.b_active = 0; 480264Sbill uptab.b_errcnt = 0; 481264Sbill dp->b_actf = bp->av_forw; 482266Sbill dp->b_active = 0; 483264Sbill bp->b_flags |= B_ERROR; 484264Sbill iodone(bp); 485266Sbill ubafree(up_ubinfo), up_ubinfo = 0; /* A funny place ... */ 486264Sbill goto loop; 487264Sbill } 488266Sbill /* 489266Sbill * If this is a retry, then with the 16'th retry we 490266Sbill * begin to try offsetting the heads to recover the data. 491266Sbill */ 492266Sbill if (uptab.b_errcnt >= 16) { 493264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 494266Sbill upaddr->upcs1 = IE|OFFSET|GO; 495264Sbill DELAY(idelay); 496266Sbill while (upaddr->upds & PIP) 497264Sbill DELAY(25); 498264Sbill } 499266Sbill /* 500266Sbill * Now set up the transfer, retrieving the high 501266Sbill * 2 bits of the UNIBUS address from the information 502266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 503266Sbill */ 504264Sbill upaddr->updc = cn; 505264Sbill upaddr->upda = (tn << 8) + sn; 506264Sbill upaddr->upba = up_ubinfo; 507264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 508266Sbill cmd = (up_ubinfo >> 8) & 0x300; 509264Sbill if (bp->b_flags & B_READ) 510266Sbill cmd |= IE|RCOM|GO; 511264Sbill else 512266Sbill cmd |= IE|WCOM|GO; 513266Sbill upaddr->upcs1 = cmd; 514266Sbill /* 515266Sbill * This is a controller busy situation. 516266Sbill * Record in dk slot NUP+DK_N (after last drive) 517266Sbill * unless there aren't that many slots reserved for 518266Sbill * us in which case we record this as a drive busy 519266Sbill * (if there is room for that). 520266Sbill */ 521264Sbill unit = dn+DK_N; 522264Sbill if (NUP+DK_N == DK_NMAX) 523264Sbill unit = NUP+DK_N; 524264Sbill if (unit <= DK_NMAX) { 525264Sbill dk_busy |= 1<<unit; 526264Sbill dk_numb[unit]++; 527264Sbill dk_wds[unit] += bp->b_bcount>>6; 528264Sbill } 529268Sbill return (1); 530264Sbill } 531264Sbill 532264Sbill /* 533264Sbill * Handle a device interrupt. 534264Sbill * 535264Sbill * If the transferring drive needs attention, service it 536264Sbill * retrying on error or beginning next transfer. 537264Sbill * Service all other ready drives, calling ustart to transfer 538264Sbill * their blocks to the ready queue in uptab, and then restart 539264Sbill * the controller if there is anything to do. 540264Sbill */ 541264Sbill upintr() 542264Sbill { 543264Sbill register struct buf *bp, *dp; 544264Sbill register unit; 545264Sbill register struct device *upaddr = UPADDR; 546264Sbill int as = upaddr->upas & 0377; 547341Sbill int oupsoftas; 548268Sbill int needie = 1; 549264Sbill 550276Sbill (void) spl6(); 551313Sbill up_wticks = 0; 552266Sbill if (uptab.b_active) { 553266Sbill /* 554266Sbill * The drive is transferring, thus the hardware 555266Sbill * (say the designers) will only interrupt when the transfer 556266Sbill * completes; check for it anyways. 557266Sbill */ 558266Sbill if ((upaddr->upcs1 & RDY) == 0) { 559272Sbill printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, 560272Sbill upaddr->upds, upaddr->upwc); 561341Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, 562341Sbill uputab[0].b_active, uputab[1].b_active); 563269Sbill } 564266Sbill /* 565266Sbill * Mark controller or drive not busy, and check for an 566266Sbill * error condition which may have resulted from the transfer. 567266Sbill */ 568264Sbill dp = uptab.b_actf; 569264Sbill bp = dp->b_actf; 570264Sbill unit = dkunit(bp); 571264Sbill if (DK_N+NUP == DK_NMAX) 572264Sbill dk_busy &= ~(1<<(DK_N+NUP)); 573264Sbill else if (DK_N+unit <= DK_NMAX) 574264Sbill dk_busy &= ~(1<<(DK_N+unit)); 575275Sbill if ((upaddr->upcs2 & 07) != unit) { 576275Sbill upaddr->upcs2 = unit; 577367Sbill DELAY(olducode ? osdelay : nsdelay); 578275Sbill nwaitcs2++; 579275Sbill } else 580275Sbill neasycs2++; 581*885Sbill if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) { 582266Sbill /* 583266Sbill * An error occurred, indeed. Select this unit 584266Sbill * to get at the drive status (a SEARCH may have 585266Sbill * intervened to change the selected unit), and 586266Sbill * wait for the command which caused the interrupt 587266Sbill * to complete (DRY). 588266Sbill */ 589266Sbill while ((upaddr->upds & DRY) == 0) 590264Sbill DELAY(25); 591266Sbill /* 592266Sbill * After 28 retries (16 w/o servo offsets, and then 593266Sbill * 12 with servo offsets), or if we encountered 594266Sbill * an error because the drive is write-protected, 595266Sbill * give up. Print an error message on the last 2 596266Sbill * retries before a hard failure. 597266Sbill */ 598266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 599264Sbill bp->b_flags |= B_ERROR; 600264Sbill else 601266Sbill uptab.b_active = 0; /* To force retry */ 602266Sbill if (uptab.b_errcnt > 27) 603264Sbill deverror(bp, upaddr->upcs2, upaddr->uper1); 604266Sbill /* 605266Sbill * If this was a correctible ECC error, let upecc 606266Sbill * do the dirty work to correct it. If upecc 607266Sbill * starts another READ for the rest of the data 608266Sbill * then it returns 1 (having set uptab.b_active). 609266Sbill * Otherwise we are done and fall through to 610266Sbill * finish up. 611266Sbill */ 612266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 613266Sbill return; 614266Sbill /* 615266Sbill * Clear the drive and, every 4 retries, recalibrate 616266Sbill * to hopefully help clear up seek positioning problems. 617266Sbill */ 618264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 619264Sbill DELAY(idelay); 620268Sbill needie = 0; 621266Sbill if ((uptab.b_errcnt&07) == 4) { 622264Sbill upaddr->upcs1 = RECAL|GO|IE; 623264Sbill DELAY(idelay); 624264Sbill while(upaddr->upds & PIP) 625264Sbill DELAY(25); 626264Sbill } 627264Sbill } 628266Sbill /* 629266Sbill * If we are still noted as active, then no 630266Sbill * (further) retries are necessary. 631266Sbill * 632266Sbill * Make sure the correct unit is selected, 633266Sbill * return it to centerline if necessary, and mark 634266Sbill * this i/o complete, starting the next transfer 635266Sbill * on this drive with the upustart routine (if any). 636266Sbill */ 637266Sbill if (uptab.b_active) { 638266Sbill if (uptab.b_errcnt >= 16) { 639266Sbill upaddr->upcs1 = RTC|GO|IE; 640264Sbill DELAY(idelay); 641266Sbill while (upaddr->upds & PIP) 642264Sbill DELAY(25); 643268Sbill needie = 0; 644264Sbill } 645264Sbill uptab.b_active = 0; 646264Sbill uptab.b_errcnt = 0; 647264Sbill uptab.b_actf = dp->b_forw; 648264Sbill dp->b_active = 0; 649264Sbill dp->b_errcnt = 0; 650264Sbill dp->b_actf = bp->av_forw; 651266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 652275Sbill if (bp->b_resid) 653341Sbill printf("resid %d ds %o er? %o %o %o\n", 654341Sbill bp->b_resid, upaddr->upds, 655275Sbill upaddr->uper1, upaddr->uper2, upaddr->uper3); 656264Sbill iodone(bp); 657264Sbill if(dp->b_actf) 658268Sbill if (upustart(unit)) 659268Sbill needie = 0; 660264Sbill } 661264Sbill as &= ~(1<<unit); 662341Sbill upsoftas &= ~(1<<unit); 663264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 664273Sbill } else { 665264Sbill if (upaddr->upcs1 & TRE) { 666264Sbill upaddr->upcs1 = TRE; 667264Sbill DELAY(idelay); 668264Sbill } 669264Sbill } 670266Sbill /* 671266Sbill * If we have a unit with an outstanding SEARCH, 672266Sbill * and the hardware indicates the unit requires attention, 673266Sbill * the bring the drive to the ready queue. 674266Sbill * Finally, if the controller is not transferring 675266Sbill * start it if any drives are now ready to transfer. 676266Sbill */ 677341Sbill as |= upsoftas; 678341Sbill oupsoftas = upsoftas; 679341Sbill upsoftas = 0; 680266Sbill for (unit = 0; unit < NUP; unit++) 681341Sbill if ((as|oupsoftas) & (1<<unit)) { 682273Sbill if (as & (1<<unit)) { 683267Sbill upaddr->upas = 1<<unit; 684367Sbill if (olducode) 685367Sbill DELAY(oasdel); 686272Sbill } 687273Sbill if (upustart(unit)) 688273Sbill needie = 0; 689273Sbill } 690266Sbill if (uptab.b_actf && uptab.b_active == 0) 691268Sbill if (upstart()) 692268Sbill needie = 0; 693266Sbill out: 694275Sbill if (needie) 695266Sbill upaddr->upcs1 = IE; 696264Sbill } 697264Sbill 698264Sbill upread(dev) 699264Sbill { 700264Sbill 701264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 702264Sbill } 703264Sbill 704264Sbill upwrite(dev) 705264Sbill { 706264Sbill 707264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 708264Sbill } 709264Sbill 710266Sbill /* 711266Sbill * Correct an ECC error, and restart the i/o to complete 712266Sbill * the transfer if necessary. This is quite complicated because 713266Sbill * the transfer may be going to an odd memory address base and/or 714266Sbill * across a page boundary. 715266Sbill */ 716264Sbill upecc(up, bp) 717264Sbill register struct device *up; 718264Sbill register struct buf *bp; 719264Sbill { 720264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 721266Sbill register int i; 722264Sbill caddr_t addr; 723266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 724264Sbill int bn, cn, tn, sn; 725264Sbill 726264Sbill /* 727266Sbill * Npf is the number of sectors transferred before the sector 728266Sbill * containing the ECC error, and reg is the UBA register 729266Sbill * mapping (the first part of) the transfer. 730266Sbill * O is offset within a memory page of the first byte transferred. 731264Sbill */ 732266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 733266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 734264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 735264Sbill printf("%D ", bp->b_blkno+npf); 736264Sbill prdev("ECC", bp->b_dev); 737264Sbill mask = up->upec2; 738264Sbill if (mask == 0) { 739266Sbill up->upof = FMT22; /* == RTC ???? */ 740264Sbill DELAY(idelay); 741264Sbill return (0); 742264Sbill } 743266Sbill /* 744266Sbill * Flush the buffered data path, and compute the 745266Sbill * byte and bit position of the error. The variable i 746266Sbill * is the byte offset in the transfer, the variable byte 747266Sbill * is the offset from a page boundary in main memory. 748266Sbill */ 749266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 750266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 751266Sbill bit = i&07; 752266Sbill i = (i&~07)>>3; 753264Sbill byte = i + o; 754266Sbill /* 755266Sbill * Correct while possible bits remain of mask. Since mask 756266Sbill * contains 11 bits, we continue while the bit offset is > -11. 757266Sbill * Also watch out for end of this block and the end of the whole 758266Sbill * transfer. 759266Sbill */ 760266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 761266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 762266Sbill (byte & PGOFSET); 763266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 764266Sbill byte++; 765266Sbill i++; 766266Sbill bit -= 8; 767264Sbill } 768266Sbill uptab.b_active++; /* Either complete or continuing... */ 769264Sbill if (up->upwc == 0) 770264Sbill return (0); 771266Sbill /* 772266Sbill * Have to continue the transfer... clear the drive, 773266Sbill * and compute the position where the transfer is to continue. 774266Sbill * We have completed npf+1 sectors of the transfer already; 775266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 776266Sbill */ 777266Sbill up->upcs1 = TRE|IE|DCLR|GO; 778264Sbill DELAY(idelay); 779264Sbill bn = dkblock(bp); 780264Sbill cn = bp->b_cylin; 781266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 782264Sbill tn = sn/NSECT; 783264Sbill sn %= NSECT; 784266Sbill cn += tn/NTRAC; 785266Sbill tn %= NTRAC; 786264Sbill up->updc = cn; 787266Sbill up->upda = (tn << 8) | sn; 788266Sbill ubaddr = (int)ptob(reg+1) + o; 789266Sbill up->upba = ubaddr; 790266Sbill cmd = (ubaddr >> 8) & 0x300; 791266Sbill cmd |= IE|GO|RCOM; 792266Sbill up->upcs1 = cmd; 793264Sbill return (1); 794264Sbill } 795286Sbill 796286Sbill /* 797286Sbill * Reset driver after UBA init. 798286Sbill * Cancel software state of all pending transfers 799286Sbill * and restart all units and the controller. 800286Sbill */ 801286Sbill upreset() 802286Sbill { 803286Sbill int unit; 804286Sbill 805286Sbill printf(" up"); 806286Sbill uptab.b_active = 0; 807286Sbill uptab.b_actf = uptab.b_actl = 0; 808286Sbill if (DK_N+NUP == DK_NMAX) 809286Sbill dk_busy &= ~(1<<(DK_N+NUP)); 810286Sbill if (up_ubinfo) { 811286Sbill printf("<%d>", (up_ubinfo>>28)&0xf); 812286Sbill ubafree(up_ubinfo), up_ubinfo = 0; 813286Sbill } 814313Sbill UPADDR->upcs2 = CLR; /* clear controller */ 815313Sbill DELAY(idelay); 816286Sbill for (unit = 0; unit < NUP; unit++) { 817286Sbill uputab[unit].b_active = 0; 818286Sbill (void) upustart(unit); 819286Sbill } 820286Sbill (void) upstart(); 821286Sbill } 822313Sbill 823313Sbill /* 824313Sbill * Wake up every second and if an interrupt is pending 825313Sbill * but nothing has happened increment a counter. 826313Sbill * If nothing happens for 20 seconds, reset the controller 827313Sbill * and begin anew. 828313Sbill */ 829313Sbill upwatch() 830313Sbill { 831313Sbill int i; 832313Sbill 833313Sbill timeout((caddr_t)upwatch, 0, HZ); 834313Sbill if (uptab.b_active == 0) { 835313Sbill for (i = 0; i < NUP; i++) 836313Sbill if (uputab[i].b_active) 837313Sbill goto active; 838313Sbill up_wticks = 0; /* idling */ 839313Sbill return; 840313Sbill } 841313Sbill active: 842313Sbill up_wticks++; 843313Sbill if (up_wticks >= 20) { 844313Sbill up_wticks = 0; 845313Sbill printf("LOST INTERRUPT RESET"); 846313Sbill upreset(); 847313Sbill printf("\n"); 848313Sbill } 849313Sbill } 850