1*1412Sbill /* up.c 3.25 10/13/80 */ 2264Sbill 3264Sbill /* 4885Sbill * UNIBUS disk driver with overlapped seeks and ECC recovery. 5264Sbill * 6885Sbill * This driver works marginally on an Emulex SC-11B controller with rev 7885Sbill * level J microcode, defining: 8885Sbill * int olducode = 1; 9885Sbill * to force CPU stalling delays. 10266Sbill * 11885Sbill * It has worked with no delays and no problems on a prototype 12885Sbill * SC-21 controller. Emulex intends to upgrade all SC-11s on VAXes to SC-21s. 13885Sbill * You should get a SC-21 to replace any SC-11 on a VAX. 14885Sbill * 15885Sbill * 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 /* 46*1412Sbill * Drive stats are gathered in dk_*[DK_N].. dk_*[DK_NMAX] 47264Sbill */ 48308Sbill #define DK_N 2 49308Sbill #define DK_NMAX 3 50264Sbill 51264Sbill #define ushort unsigned short 52264Sbill 53264Sbill struct device 54264Sbill { 55264Sbill ushort upcs1; /* control and status register 1 */ 56264Sbill short upwc; /* word count register */ 57264Sbill ushort upba; /* UNIBUS address register */ 58264Sbill ushort upda; /* desired address register */ 59264Sbill ushort upcs2; /* control and status register 2 */ 60264Sbill ushort upds; /* drive Status */ 61264Sbill ushort uper1; /* error register 1 */ 62264Sbill ushort upas; /* attention summary */ 63264Sbill ushort upla; /* look ahead */ 64264Sbill ushort updb; /* data buffer */ 65264Sbill ushort upmr; /* maintenance */ 66264Sbill ushort updt; /* drive type */ 67264Sbill ushort upsn; /* serial number */ 68264Sbill ushort upof; /* offset register */ 69264Sbill ushort updc; /* desired cylinder address register */ 70264Sbill ushort upcc; /* current cylinder */ 71264Sbill ushort uper2; /* error register 2 */ 72264Sbill ushort uper3; /* error register 3 */ 73264Sbill ushort upec1; /* burst error bit position */ 74264Sbill ushort upec2; /* burst error bit pattern */ 75264Sbill }; 76264Sbill 77275Sbill /* 78275Sbill * Software extension to the upas register, so we can 79275Sbill * postpone starting SEARCH commands until the controller 80275Sbill * is not transferring. 81275Sbill */ 82341Sbill int upsoftas; 83275Sbill 84275Sbill /* 85275Sbill * If upseek then we don't issue SEARCH commands but rather just 86275Sbill * settle for a SEEK to the correct cylinder. 87275Sbill */ 88275Sbill int upseek; 89275Sbill 90264Sbill #define UPADDR ((struct device *)(UBA0_DEV + 0176700)) 91264Sbill 92264Sbill #define NUP 2 /* Number of drives this installation */ 93264Sbill 94264Sbill #define NSECT 32 95264Sbill #define NTRAC 19 96264Sbill 97264Sbill /* 98264Sbill * Constants controlling on-cylinder SEARCH usage. 99264Sbill * 100308Sbill * upSDIST/2 msec time needed to start transfer 101308Sbill * upRDIST/2 msec tolerable rotational latency when on-cylinder 102275Sbill * 103308Sbill * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST 104275Sbill * and in the driver then we take it as it is. Otherwise we do a SEARCH 105308Sbill * requesting an interrupt upSDIST sectors in advance. 106264Sbill */ 107308Sbill #define _upSDIST 6 /* 3.0 msec */ 108308Sbill #define _upRDIST 6 /* 3.0 msec */ 109264Sbill 110308Sbill int upSDIST = _upSDIST; 111308Sbill int upRDIST = _upRDIST; 112275Sbill 113264Sbill /* 114264Sbill * To fill a 300M drive: 115264Sbill * A is designed to be used as a root. 116264Sbill * B is suitable for a swap area. 117264Sbill * H is the primary storage area. 118264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 119264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 120264Sbill * covers the whole drive and can be used for pack-pack copying. 121264Sbill */ 122264Sbill struct size 123264Sbill { 124264Sbill daddr_t nblocks; 125264Sbill int cyloff; 126264Sbill } up_sizes[8] = { 127264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 128264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 129341Sbill 495520, 0, /* C=cyl 0 thru 814 */ 130264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 131264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 132264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 133264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 134264Sbill 445664, 82, /* H=cyl 82 thru 814 */ 135264Sbill /* Later, and more safely for H area... 136264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 137264Sbill }; 138264Sbill 139264Sbill /* 140264Sbill * The following defines are used in offset positioning 141264Sbill * when trying to recover disk errors, with the constants being 142264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 143264Sbill * tried (this makes sense only during read, in any case.) 144264Sbill * 145341Sbill * NOT ALL OF THESE ARE IMPLEMENTED ON 9300!?! 146264Sbill */ 147264Sbill #define P400 020 148264Sbill #define M400 0220 149264Sbill #define P800 040 150264Sbill #define M800 0240 151264Sbill #define P1200 060 152264Sbill #define M1200 0260 153264Sbill #define HCI 020000 154264Sbill 155264Sbill int up_offset[16] = 156264Sbill { 157264Sbill P400, M400, P400, M400, 158264Sbill P800, M800, P800, M800, 159264Sbill P1200, M1200, P1200, M1200, 160264Sbill 0, 0, 0, 0, 161264Sbill }; 162264Sbill 163264Sbill /* 164264Sbill * Each drive has a table uputab[i]. On this table are sorted the 165264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 166264Sbill * In the upustart() routine, each drive is independently advanced 167264Sbill * until it is on the desired cylinder for the next transfer and near 168264Sbill * the desired sector. The drive is then chained onto the uptab 169264Sbill * table, and the transfer is initiated by the upstart() routine. 170264Sbill * When the transfer is completed the driver reinvokes the upustart() 171264Sbill * routine to set up the next transfer. 172264Sbill */ 173264Sbill struct buf uptab; 174264Sbill struct buf uputab[NUP]; 175264Sbill 176264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 177264Sbill 178264Sbill /* Drive commands, placed in upcs1 */ 179264Sbill #define GO 01 /* Go bit, set in all commands */ 180264Sbill #define PRESET 020 /* Preset drive at init or after errors */ 181264Sbill #define OFFSET 014 /* Offset heads to try to recover error */ 182264Sbill #define RTC 016 /* Return to center-line after OFFSET */ 183264Sbill #define SEARCH 030 /* Search for cylinder+sector */ 184275Sbill #define SEEK 04 /* Seek to cylinder */ 185264Sbill #define RECAL 06 /* Recalibrate, needed after seek error */ 186264Sbill #define DCLR 010 /* Drive clear, after error */ 187264Sbill #define WCOM 060 /* Write */ 188264Sbill #define RCOM 070 /* Read */ 189264Sbill 190264Sbill /* Other bits of upcs1 */ 191264Sbill #define IE 0100 /* Controller wide interrupt enable */ 192264Sbill #define TRE 040000 /* Transfer error */ 193345Sbill #define RDY 0200 /* Transfer terminated */ 194264Sbill 195264Sbill /* Drive status bits of upds */ 196264Sbill #define PIP 020000 /* Positioning in progress */ 197264Sbill #define ERR 040000 /* Error has occurred, DCLR necessary */ 198264Sbill #define VV 0100 /* Volume is valid, set by PRESET */ 199264Sbill #define DPR 0400 /* Drive has been preset */ 200264Sbill #define MOL 010000 /* Drive is online, heads loaded, etc */ 201264Sbill #define DRY 0200 /* Drive ready */ 202264Sbill 203313Sbill /* Bits of upcs2 */ 204313Sbill #define CLR 040 /* Controller clear */ 205264Sbill /* Bits of uper1 */ 206264Sbill #define DCK 0100000 /* Ecc error occurred */ 207264Sbill #define ECH 0100 /* Ecc error was unrecoverable */ 208264Sbill #define WLE 04000 /* Attempt to write read-only drive */ 209264Sbill 210264Sbill /* Bits of upof; the offset bits above are also in this register */ 211264Sbill #define FMT22 010000 /* 16 bits/word, must be always set */ 212264Sbill 213264Sbill #define b_cylin b_resid 214264Sbill 215264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 216264Sbill /* 217264Sbill * The EMULEX controller balks if accessed quickly after 218341Sbill * certain operations. With rev J delays seem to be needed only 219341Sbill * when selecting a new unit, and in drive initialization type 220341Sbill * like PRESET and DCLR. The following variables control the delay 221341Sbill * DELAY(n) is approximately n usec. 222264Sbill */ 223367Sbill int olducode = 1; 224264Sbill int idelay = 500; /* Delay after PRESET or DCLR */ 225367Sbill int osdelay = 150; /* Old delay after selecting drive in upcs2 */ 226367Sbill int ordelay = 100; /* Old delay after SEARCH */ 227367Sbill int oasdel = 100; /* Old delay after clearing bit in upas */ 228367Sbill int nsdelay = 25; 229264Sbill 230264Sbill #define DELAY(N) { register int d; d = N; while (--d > 0); } 231264Sbill 232264Sbill int nwaitcs2; /* How many sdelay loops ? */ 233264Sbill int neasycs2; /* How many sdelay loops not needed ? */ 234264Sbill 235313Sbill int up_wticks; /* Ticks waiting for interrupt */ 236313Sbill int upwstart; /* Have started guardian */ 237313Sbill int upwatch(); 238313Sbill 239264Sbill #ifdef INTRLVE 240264Sbill daddr_t dkblock(); 241264Sbill #endif 242264Sbill 243264Sbill /* 244264Sbill * Queue an i/o request for a drive, checking first that it is in range. 245264Sbill * 246264Sbill * A unit start is issued if the drive is inactive, causing 247264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 248264Sbill * already nearly on the money and the controller is not transferring 249264Sbill * we kick it to start the transfer. 250264Sbill */ 251264Sbill upstrategy(bp) 252264Sbill register struct buf *bp; 253264Sbill { 254264Sbill register struct buf *dp; 255264Sbill register unit, xunit; 256264Sbill long sz, bn; 257264Sbill 258313Sbill if (upwstart == 0) { 259313Sbill timeout((caddr_t)upwatch, 0, HZ); 260313Sbill upwstart++; 261313Sbill } 262264Sbill xunit = minor(bp->b_dev) & 077; 263264Sbill sz = bp->b_bcount; 264264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 265264Sbill unit = dkunit(bp); 266264Sbill if (unit >= NUP || 267264Sbill bp->b_blkno < 0 || 268264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 269264Sbill bp->b_flags |= B_ERROR; 270264Sbill iodone(bp); 271264Sbill return; 272264Sbill } 273*1412Sbill if (DK_N+unit <= DK_NMAX) 274*1412Sbill dk_mspw[DK_N+unit] = .0000020345; 275264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 276264Sbill dp = &uputab[unit]; 277264Sbill (void) spl5(); 278264Sbill disksort(dp, bp); 279264Sbill if (dp->b_active == 0) { 280268Sbill (void) upustart(unit); 281264Sbill if (uptab.b_actf && uptab.b_active == 0) 282268Sbill (void) upstart(); 283264Sbill } 284264Sbill (void) spl0(); 285264Sbill } 286264Sbill 287264Sbill /* 288264Sbill * Start activity on specified drive; called when drive is inactive 289264Sbill * and new transfer request arrives and also when upas indicates that 290264Sbill * a SEARCH command is complete. 291264Sbill */ 292264Sbill upustart(unit) 293264Sbill register unit; 294264Sbill { 295264Sbill register struct buf *bp, *dp; 296264Sbill register struct device *upaddr = UPADDR; 297264Sbill daddr_t bn; 298264Sbill int sn, cn, csn; 299268Sbill int didie = 0; 300264Sbill 301275Sbill /* 302275Sbill * Other drivers tend to say something like 303275Sbill * upaddr->upcs1 = IE; 304275Sbill * upaddr->upas = 1<<unit; 305275Sbill * here, but the SC-11B will cancel a command which 306275Sbill * happens to be sitting in the cs1 if you clear the go 307275Sbill * bit by storing there (so the first is not safe), 308275Sbill * and it also does not like being bothered with operations 309275Sbill * such as clearing upas when a transfer is active (as 310275Sbill * it may well be.) 311275Sbill * 312275Sbill * Thus we keep careful track of when we re-enable IE 313275Sbill * after an interrupt and do it only if we didn't issue 314275Sbill * a command which re-enabled it as a matter of course. 315275Sbill * We clear bits in upas in the interrupt routine, when 316275Sbill * no transfers are active. 317275Sbill */ 318266Sbill if (unit >= NUP) 319268Sbill goto out; 320264Sbill if (unit+DK_N <= DK_NMAX) 321264Sbill dk_busy &= ~(1<<(unit+DK_N)); 322264Sbill dp = &uputab[unit]; 323266Sbill if ((bp = dp->b_actf) == NULL) 324268Sbill goto out; 325275Sbill /* 326275Sbill * The SC-11B doesn't start SEARCH commands when transfers are 327275Sbill * in progress. In fact, it tends to get confused when given 328275Sbill * SEARCH'es during transfers, generating interrupts with neither 329275Sbill * RDY nor a bit in the upas register. Thus we defer 330275Sbill * until an interrupt when a transfer is pending. 331275Sbill */ 332275Sbill if (uptab.b_active) { 333341Sbill upsoftas |= 1<<unit; 334275Sbill return (0); 335275Sbill } 336276Sbill if (dp->b_active) 337276Sbill goto done; 338276Sbill dp->b_active = 1; 339264Sbill if ((upaddr->upcs2 & 07) != unit) { 340264Sbill upaddr->upcs2 = unit; 341367Sbill DELAY(olducode ? osdelay : nsdelay); 342264Sbill nwaitcs2++; 343264Sbill } else 344264Sbill neasycs2++; 345266Sbill /* 346266Sbill * If we have changed packs or just initialized, 347275Sbill * then the volume will not be valid; if so, clear 348266Sbill * the drive, preset it and put in 16bit/word mode. 349266Sbill */ 350266Sbill if ((upaddr->upds & VV) == 0) { 351266Sbill upaddr->upcs1 = IE|DCLR|GO; 352266Sbill DELAY(idelay); 353264Sbill upaddr->upcs1 = IE|PRESET|GO; 354264Sbill DELAY(idelay); 355264Sbill upaddr->upof = FMT22; 356268Sbill didie = 1; 357264Sbill } 358264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 359275Sbill goto done; 360266Sbill /* 361266Sbill * Do enough of the disk address decoding to determine 362266Sbill * which cylinder and sector the request is on. 363266Sbill * If we are on the correct cylinder and the desired sector 364308Sbill * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then 365266Sbill * we don't bother to SEARCH but just begin the transfer asap. 366308Sbill * Otherwise ask for a interrupt upSDIST sectors ahead. 367266Sbill */ 368264Sbill bn = dkblock(bp); 369264Sbill cn = bp->b_cylin; 370264Sbill sn = bn%(NSECT*NTRAC); 371308Sbill sn = (sn+NSECT-upSDIST)%NSECT; 372264Sbill 373266Sbill if (cn - upaddr->updc) 374266Sbill goto search; /* Not on-cylinder */ 375275Sbill else if (upseek) 376275Sbill goto done; /* Ok just to be on-cylinder */ 377264Sbill csn = (upaddr->upla>>6) - sn - 1; 378266Sbill if (csn < 0) 379264Sbill csn += NSECT; 380308Sbill if (csn > NSECT-upRDIST) 381264Sbill goto done; 382264Sbill 383264Sbill search: 384264Sbill upaddr->updc = cn; 385275Sbill if (upseek) 386275Sbill upaddr->upcs1 = IE|SEEK|GO; 387275Sbill else { 388275Sbill upaddr->upda = sn; 389275Sbill upaddr->upcs1 = IE|SEARCH|GO; 390275Sbill } 391268Sbill didie = 1; 392266Sbill /* 393266Sbill * Mark this unit busy. 394266Sbill */ 395264Sbill unit += DK_N; 396*1412Sbill if (unit <= DK_NMAX) { 397264Sbill dk_busy |= 1<<unit; 398*1412Sbill dk_seek[unit]++; 399264Sbill } 400367Sbill if (olducode) 401367Sbill DELAY(ordelay); 402268Sbill goto out; 403264Sbill 404264Sbill done: 405266Sbill /* 406275Sbill * This unit is ready to go so 407275Sbill * link it onto the chain of ready disks. 408266Sbill */ 409264Sbill dp->b_forw = NULL; 410266Sbill if (uptab.b_actf == NULL) 411264Sbill uptab.b_actf = dp; 412264Sbill else 413264Sbill uptab.b_actl->b_forw = dp; 414264Sbill uptab.b_actl = dp; 415268Sbill 416268Sbill out: 417268Sbill return (didie); 418264Sbill } 419264Sbill 420264Sbill /* 421264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 422264Sbill */ 423264Sbill upstart() 424264Sbill { 425264Sbill register struct buf *bp, *dp; 426264Sbill register unit; 427264Sbill register struct device *upaddr; 428264Sbill daddr_t bn; 429266Sbill int dn, sn, tn, cn, cmd; 430264Sbill 431264Sbill loop: 432266Sbill /* 433266Sbill * Pick a drive off the queue of ready drives, and 434266Sbill * perform the first transfer on its queue. 435266Sbill * 436266Sbill * Looping here is completely for the sake of drives which 437266Sbill * are not present and on-line, for which we completely clear the 438266Sbill * request queue. 439266Sbill */ 440273Sbill if ((dp = uptab.b_actf) == NULL) 441268Sbill return (0); 442264Sbill if ((bp = dp->b_actf) == NULL) { 443264Sbill uptab.b_actf = dp->b_forw; 444264Sbill goto loop; 445264Sbill } 446266Sbill /* 447266Sbill * Mark the controller busy, and multi-part disk address. 448266Sbill * Select the unit on which the i/o is to take place. 449266Sbill */ 450264Sbill uptab.b_active++; 451264Sbill unit = minor(bp->b_dev) & 077; 452264Sbill dn = dkunit(bp); 453264Sbill bn = dkblock(bp); 454264Sbill cn = up_sizes[unit&07].cyloff; 455264Sbill cn += bn/(NSECT*NTRAC); 456264Sbill sn = bn%(NSECT*NTRAC); 457264Sbill tn = sn/NSECT; 458266Sbill sn %= NSECT; 459264Sbill upaddr = UPADDR; 460264Sbill if ((upaddr->upcs2 & 07) != dn) { 461264Sbill upaddr->upcs2 = dn; 462275Sbill /* DELAY(sdelay); Provided by ubasetup() */ 463264Sbill nwaitcs2++; 464264Sbill } else 465264Sbill neasycs2++; 466275Sbill up_ubinfo = ubasetup(bp, 1); /* Providing delay */ 467266Sbill /* 468266Sbill * If drive is not present and on-line, then 469266Sbill * get rid of this with an error and loop to get 470266Sbill * rid of the rest of its queued requests. 471266Sbill * (Then on to any other ready drives.) 472266Sbill */ 473264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 474893Sbill printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds); 475893Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 476893Sbill printf("-- hard\n"); 477893Sbill uptab.b_active = 0; 478893Sbill uptab.b_errcnt = 0; 479893Sbill dp->b_actf = bp->av_forw; 480893Sbill dp->b_active = 0; 481893Sbill bp->b_flags |= B_ERROR; 482893Sbill iodone(bp); 483893Sbill /* A funny place to do this ... */ 484893Sbill ubafree(up_ubinfo), up_ubinfo = 0; 485893Sbill goto loop; 486893Sbill } 487893Sbill printf("-- came back\n"); 488264Sbill } 489266Sbill /* 490266Sbill * If this is a retry, then with the 16'th retry we 491266Sbill * begin to try offsetting the heads to recover the data. 492266Sbill */ 493924Sbill if (uptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) { 494264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 495266Sbill upaddr->upcs1 = IE|OFFSET|GO; 496264Sbill DELAY(idelay); 497266Sbill while (upaddr->upds & PIP) 498264Sbill DELAY(25); 499264Sbill } 500266Sbill /* 501266Sbill * Now set up the transfer, retrieving the high 502266Sbill * 2 bits of the UNIBUS address from the information 503266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 504266Sbill */ 505264Sbill upaddr->updc = cn; 506264Sbill upaddr->upda = (tn << 8) + sn; 507264Sbill upaddr->upba = up_ubinfo; 508264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 509266Sbill cmd = (up_ubinfo >> 8) & 0x300; 510264Sbill if (bp->b_flags & B_READ) 511266Sbill cmd |= IE|RCOM|GO; 512264Sbill else 513266Sbill cmd |= IE|WCOM|GO; 514266Sbill upaddr->upcs1 = cmd; 515266Sbill /* 516266Sbill * This is a controller busy situation. 517266Sbill * Record in dk slot NUP+DK_N (after last drive) 518266Sbill * unless there aren't that many slots reserved for 519266Sbill * us in which case we record this as a drive busy 520266Sbill * (if there is room for that). 521266Sbill */ 522264Sbill unit = dn+DK_N; 523264Sbill if (unit <= DK_NMAX) { 524264Sbill dk_busy |= 1<<unit; 525*1412Sbill dk_xfer[unit]++; 526264Sbill dk_wds[unit] += bp->b_bcount>>6; 527264Sbill } 528268Sbill return (1); 529264Sbill } 530264Sbill 531264Sbill /* 532264Sbill * Handle a device interrupt. 533264Sbill * 534264Sbill * If the transferring drive needs attention, service it 535264Sbill * retrying on error or beginning next transfer. 536264Sbill * Service all other ready drives, calling ustart to transfer 537264Sbill * their blocks to the ready queue in uptab, and then restart 538264Sbill * the controller if there is anything to do. 539264Sbill */ 540264Sbill upintr() 541264Sbill { 542264Sbill register struct buf *bp, *dp; 543264Sbill register unit; 544264Sbill register struct device *upaddr = UPADDR; 545264Sbill int as = upaddr->upas & 0377; 546341Sbill int oupsoftas; 547268Sbill int needie = 1; 548264Sbill 549276Sbill (void) spl6(); 550313Sbill up_wticks = 0; 551266Sbill if (uptab.b_active) { 552266Sbill /* 553266Sbill * The drive is transferring, thus the hardware 554266Sbill * (say the designers) will only interrupt when the transfer 555266Sbill * completes; check for it anyways. 556266Sbill */ 557266Sbill if ((upaddr->upcs1 & RDY) == 0) { 558272Sbill printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, 559272Sbill upaddr->upds, upaddr->upwc); 560341Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, 561341Sbill uputab[0].b_active, uputab[1].b_active); 562269Sbill } 563266Sbill /* 564*1412Sbill * Mark drive not busy, and check for an 565266Sbill * error condition which may have resulted from the transfer. 566266Sbill */ 567264Sbill dp = uptab.b_actf; 568264Sbill bp = dp->b_actf; 569264Sbill unit = dkunit(bp); 570*1412Sbill if (DK_N+unit <= DK_NMAX) 571264Sbill dk_busy &= ~(1<<(DK_N+unit)); 572275Sbill if ((upaddr->upcs2 & 07) != unit) { 573275Sbill upaddr->upcs2 = unit; 574367Sbill DELAY(olducode ? osdelay : nsdelay); 575275Sbill nwaitcs2++; 576275Sbill } else 577275Sbill neasycs2++; 578885Sbill if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) { 579266Sbill /* 580266Sbill * An error occurred, indeed. Select this unit 581266Sbill * to get at the drive status (a SEARCH may have 582266Sbill * intervened to change the selected unit), and 583266Sbill * wait for the command which caused the interrupt 584266Sbill * to complete (DRY). 585266Sbill */ 586266Sbill while ((upaddr->upds & DRY) == 0) 587264Sbill DELAY(25); 588266Sbill /* 589266Sbill * After 28 retries (16 w/o servo offsets, and then 590266Sbill * 12 with servo offsets), or if we encountered 591266Sbill * an error because the drive is write-protected, 592266Sbill * give up. Print an error message on the last 2 593266Sbill * retries before a hard failure. 594266Sbill */ 595266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 596264Sbill bp->b_flags |= B_ERROR; 597264Sbill else 598266Sbill uptab.b_active = 0; /* To force retry */ 599266Sbill if (uptab.b_errcnt > 27) 600264Sbill deverror(bp, upaddr->upcs2, upaddr->uper1); 601266Sbill /* 602266Sbill * If this was a correctible ECC error, let upecc 603266Sbill * do the dirty work to correct it. If upecc 604266Sbill * starts another READ for the rest of the data 605266Sbill * then it returns 1 (having set uptab.b_active). 606266Sbill * Otherwise we are done and fall through to 607266Sbill * finish up. 608266Sbill */ 609266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 610266Sbill return; 611266Sbill /* 612266Sbill * Clear the drive and, every 4 retries, recalibrate 613266Sbill * to hopefully help clear up seek positioning problems. 614266Sbill */ 615264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 616264Sbill DELAY(idelay); 617268Sbill needie = 0; 618266Sbill if ((uptab.b_errcnt&07) == 4) { 619264Sbill upaddr->upcs1 = RECAL|GO|IE; 620264Sbill DELAY(idelay); 621264Sbill while(upaddr->upds & PIP) 622264Sbill DELAY(25); 623264Sbill } 624264Sbill } 625266Sbill /* 626266Sbill * If we are still noted as active, then no 627266Sbill * (further) retries are necessary. 628266Sbill * 629266Sbill * Make sure the correct unit is selected, 630266Sbill * return it to centerline if necessary, and mark 631266Sbill * this i/o complete, starting the next transfer 632266Sbill * on this drive with the upustart routine (if any). 633266Sbill */ 634266Sbill if (uptab.b_active) { 635266Sbill if (uptab.b_errcnt >= 16) { 636266Sbill upaddr->upcs1 = RTC|GO|IE; 637264Sbill DELAY(idelay); 638266Sbill while (upaddr->upds & PIP) 639264Sbill DELAY(25); 640268Sbill needie = 0; 641264Sbill } 642264Sbill uptab.b_active = 0; 643264Sbill uptab.b_errcnt = 0; 644264Sbill uptab.b_actf = dp->b_forw; 645264Sbill dp->b_active = 0; 646264Sbill dp->b_errcnt = 0; 647264Sbill dp->b_actf = bp->av_forw; 648266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 649275Sbill if (bp->b_resid) 650341Sbill printf("resid %d ds %o er? %o %o %o\n", 651341Sbill bp->b_resid, upaddr->upds, 652275Sbill upaddr->uper1, upaddr->uper2, upaddr->uper3); 653264Sbill iodone(bp); 654264Sbill if(dp->b_actf) 655268Sbill if (upustart(unit)) 656268Sbill needie = 0; 657264Sbill } 658264Sbill as &= ~(1<<unit); 659341Sbill upsoftas &= ~(1<<unit); 660264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 661273Sbill } else { 662264Sbill if (upaddr->upcs1 & TRE) { 663264Sbill upaddr->upcs1 = TRE; 664264Sbill DELAY(idelay); 665264Sbill } 666264Sbill } 667266Sbill /* 668266Sbill * If we have a unit with an outstanding SEARCH, 669266Sbill * and the hardware indicates the unit requires attention, 670266Sbill * the bring the drive to the ready queue. 671266Sbill * Finally, if the controller is not transferring 672266Sbill * start it if any drives are now ready to transfer. 673266Sbill */ 674341Sbill as |= upsoftas; 675341Sbill oupsoftas = upsoftas; 676341Sbill upsoftas = 0; 677266Sbill for (unit = 0; unit < NUP; unit++) 678341Sbill if ((as|oupsoftas) & (1<<unit)) { 679273Sbill if (as & (1<<unit)) { 680267Sbill upaddr->upas = 1<<unit; 681367Sbill if (olducode) 682367Sbill DELAY(oasdel); 683272Sbill } 684273Sbill if (upustart(unit)) 685273Sbill needie = 0; 686273Sbill } 687266Sbill if (uptab.b_actf && uptab.b_active == 0) 688268Sbill if (upstart()) 689268Sbill needie = 0; 690266Sbill out: 691275Sbill if (needie) 692266Sbill upaddr->upcs1 = IE; 693264Sbill } 694264Sbill 695264Sbill upread(dev) 696264Sbill { 697264Sbill 698264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 699264Sbill } 700264Sbill 701264Sbill upwrite(dev) 702264Sbill { 703264Sbill 704264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 705264Sbill } 706264Sbill 707266Sbill /* 708266Sbill * Correct an ECC error, and restart the i/o to complete 709266Sbill * the transfer if necessary. This is quite complicated because 710266Sbill * the transfer may be going to an odd memory address base and/or 711266Sbill * across a page boundary. 712266Sbill */ 713264Sbill upecc(up, bp) 714264Sbill register struct device *up; 715264Sbill register struct buf *bp; 716264Sbill { 717264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 718266Sbill register int i; 719264Sbill caddr_t addr; 720266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 721264Sbill int bn, cn, tn, sn; 722264Sbill 723264Sbill /* 724266Sbill * Npf is the number of sectors transferred before the sector 725266Sbill * containing the ECC error, and reg is the UBA register 726266Sbill * mapping (the first part of) the transfer. 727266Sbill * O is offset within a memory page of the first byte transferred. 728264Sbill */ 729266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 730266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 731264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 732264Sbill printf("%D ", bp->b_blkno+npf); 733264Sbill prdev("ECC", bp->b_dev); 734264Sbill mask = up->upec2; 735264Sbill if (mask == 0) { 736266Sbill up->upof = FMT22; /* == RTC ???? */ 737264Sbill DELAY(idelay); 738264Sbill return (0); 739264Sbill } 740266Sbill /* 741266Sbill * Flush the buffered data path, and compute the 742266Sbill * byte and bit position of the error. The variable i 743266Sbill * is the byte offset in the transfer, the variable byte 744266Sbill * is the offset from a page boundary in main memory. 745266Sbill */ 746266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 747266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 748266Sbill bit = i&07; 749266Sbill i = (i&~07)>>3; 750264Sbill byte = i + o; 751266Sbill /* 752266Sbill * Correct while possible bits remain of mask. Since mask 753266Sbill * contains 11 bits, we continue while the bit offset is > -11. 754266Sbill * Also watch out for end of this block and the end of the whole 755266Sbill * transfer. 756266Sbill */ 757266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 758266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 759266Sbill (byte & PGOFSET); 760266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 761266Sbill byte++; 762266Sbill i++; 763266Sbill bit -= 8; 764264Sbill } 765266Sbill uptab.b_active++; /* Either complete or continuing... */ 766264Sbill if (up->upwc == 0) 767264Sbill return (0); 768266Sbill /* 769266Sbill * Have to continue the transfer... clear the drive, 770266Sbill * and compute the position where the transfer is to continue. 771266Sbill * We have completed npf+1 sectors of the transfer already; 772266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 773266Sbill */ 774266Sbill up->upcs1 = TRE|IE|DCLR|GO; 775264Sbill DELAY(idelay); 776264Sbill bn = dkblock(bp); 777264Sbill cn = bp->b_cylin; 778266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 779264Sbill tn = sn/NSECT; 780264Sbill sn %= NSECT; 781266Sbill cn += tn/NTRAC; 782266Sbill tn %= NTRAC; 783264Sbill up->updc = cn; 784266Sbill up->upda = (tn << 8) | sn; 785266Sbill ubaddr = (int)ptob(reg+1) + o; 786266Sbill up->upba = ubaddr; 787266Sbill cmd = (ubaddr >> 8) & 0x300; 788266Sbill cmd |= IE|GO|RCOM; 789266Sbill up->upcs1 = cmd; 790264Sbill return (1); 791264Sbill } 792286Sbill 793286Sbill /* 794286Sbill * Reset driver after UBA init. 795286Sbill * Cancel software state of all pending transfers 796286Sbill * and restart all units and the controller. 797286Sbill */ 798286Sbill upreset() 799286Sbill { 800286Sbill int unit; 801286Sbill 802286Sbill printf(" up"); 803286Sbill uptab.b_active = 0; 804286Sbill uptab.b_actf = uptab.b_actl = 0; 805286Sbill if (up_ubinfo) { 806286Sbill printf("<%d>", (up_ubinfo>>28)&0xf); 807286Sbill ubafree(up_ubinfo), up_ubinfo = 0; 808286Sbill } 809313Sbill UPADDR->upcs2 = CLR; /* clear controller */ 810313Sbill DELAY(idelay); 811286Sbill for (unit = 0; unit < NUP; unit++) { 812286Sbill uputab[unit].b_active = 0; 813286Sbill (void) upustart(unit); 814286Sbill } 815286Sbill (void) upstart(); 816286Sbill } 817313Sbill 818313Sbill /* 819313Sbill * Wake up every second and if an interrupt is pending 820313Sbill * but nothing has happened increment a counter. 821313Sbill * If nothing happens for 20 seconds, reset the controller 822313Sbill * and begin anew. 823313Sbill */ 824313Sbill upwatch() 825313Sbill { 826313Sbill int i; 827313Sbill 828313Sbill timeout((caddr_t)upwatch, 0, HZ); 829313Sbill if (uptab.b_active == 0) { 830313Sbill for (i = 0; i < NUP; i++) 831313Sbill if (uputab[i].b_active) 832313Sbill goto active; 833313Sbill up_wticks = 0; /* idling */ 834313Sbill return; 835313Sbill } 836313Sbill active: 837313Sbill up_wticks++; 838313Sbill if (up_wticks >= 20) { 839313Sbill up_wticks = 0; 840313Sbill printf("LOST INTERRUPT RESET"); 841313Sbill upreset(); 842313Sbill printf("\n"); 843313Sbill } 844313Sbill } 845