1*1563Sbill /* up.c 3.26 10/19/80 */ 2264Sbill 3*1563Sbill #include "../conf/up.h" 4264Sbill /* 5885Sbill * UNIBUS disk driver with overlapped seeks and ECC recovery. 6264Sbill * 7885Sbill * This driver works marginally on an Emulex SC-11B controller with rev 8885Sbill * level J microcode, defining: 9885Sbill * int olducode = 1; 10885Sbill * to force CPU stalling delays. 11266Sbill * 12885Sbill * It has worked with no delays and no problems on a prototype 13885Sbill * SC-21 controller. Emulex intends to upgrade all SC-11s on VAXes to SC-21s. 14885Sbill * You should get a SC-21 to replace any SC-11 on a VAX. 15885Sbill * 16885Sbill * SC-11B Controller switch settings: 17264Sbill * SW1-1 5/19 surfaces (off, 19 surfaces on Ampex 9300) 18264Sbill * SW1-2 chksum enable (off, checksum disabled) 19264Sbill * SW1-3 volume select (off, 815 cylinders) 20264Sbill * SW1-4 sector select (on, 32 sectors) 21264Sbill * SW1-5 unused (off) 22264Sbill * SW1-6 port select (on, single port) 23264Sbill * SW1-7 npr delay (off, disable) 24264Sbill * SW1-8 ecc test mode (off, disable) 25264Sbill * and top mounted switches: 26264Sbill * SW2-1 extend opcodes (off=open, disable) 27264Sbill * SW2-2 extend diag (off=open, disable) 28341Sbill * SW2-3 4 wd dma burst (on=closed, enable) 29264Sbill * SW2-4 unused (off=open) 30264Sbill */ 31264Sbill 32264Sbill #include "../h/param.h" 33264Sbill #include "../h/systm.h" 34308Sbill #include "../h/dk.h" 35264Sbill #include "../h/buf.h" 36264Sbill #include "../h/conf.h" 37264Sbill #include "../h/dir.h" 38264Sbill #include "../h/user.h" 39264Sbill #include "../h/map.h" 40420Sbill #include "../h/pte.h" 41264Sbill #include "../h/mba.h" 42264Sbill #include "../h/mtpr.h" 43264Sbill #include "../h/uba.h" 44264Sbill #include "../h/vm.h" 45264Sbill 46264Sbill #define ushort unsigned short 47264Sbill 48264Sbill struct device 49264Sbill { 50264Sbill ushort upcs1; /* control and status register 1 */ 51264Sbill short upwc; /* word count register */ 52264Sbill ushort upba; /* UNIBUS address register */ 53264Sbill ushort upda; /* desired address register */ 54264Sbill ushort upcs2; /* control and status register 2 */ 55264Sbill ushort upds; /* drive Status */ 56264Sbill ushort uper1; /* error register 1 */ 57264Sbill ushort upas; /* attention summary */ 58264Sbill ushort upla; /* look ahead */ 59264Sbill ushort updb; /* data buffer */ 60264Sbill ushort upmr; /* maintenance */ 61264Sbill ushort updt; /* drive type */ 62264Sbill ushort upsn; /* serial number */ 63264Sbill ushort upof; /* offset register */ 64264Sbill ushort updc; /* desired cylinder address register */ 65264Sbill ushort upcc; /* current cylinder */ 66264Sbill ushort uper2; /* error register 2 */ 67264Sbill ushort uper3; /* error register 3 */ 68264Sbill ushort upec1; /* burst error bit position */ 69264Sbill ushort upec2; /* burst error bit pattern */ 70264Sbill }; 71264Sbill 72275Sbill /* 73275Sbill * Software extension to the upas register, so we can 74275Sbill * postpone starting SEARCH commands until the controller 75275Sbill * is not transferring. 76275Sbill */ 77341Sbill int upsoftas; 78275Sbill 79275Sbill /* 80275Sbill * If upseek then we don't issue SEARCH commands but rather just 81275Sbill * settle for a SEEK to the correct cylinder. 82275Sbill */ 83275Sbill int upseek; 84275Sbill 85264Sbill #define NSECT 32 86264Sbill #define NTRAC 19 87264Sbill 88264Sbill /* 89264Sbill * Constants controlling on-cylinder SEARCH usage. 90264Sbill * 91308Sbill * upSDIST/2 msec time needed to start transfer 92308Sbill * upRDIST/2 msec tolerable rotational latency when on-cylinder 93275Sbill * 94308Sbill * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST 95275Sbill * and in the driver then we take it as it is. Otherwise we do a SEARCH 96308Sbill * requesting an interrupt upSDIST sectors in advance. 97264Sbill */ 98308Sbill #define _upSDIST 6 /* 3.0 msec */ 99308Sbill #define _upRDIST 6 /* 3.0 msec */ 100264Sbill 101308Sbill int upSDIST = _upSDIST; 102308Sbill int upRDIST = _upRDIST; 103275Sbill 104264Sbill /* 105264Sbill * To fill a 300M drive: 106264Sbill * A is designed to be used as a root. 107264Sbill * B is suitable for a swap area. 108264Sbill * H is the primary storage area. 109264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 110264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 111264Sbill * covers the whole drive and can be used for pack-pack copying. 112264Sbill */ 113264Sbill struct size 114264Sbill { 115264Sbill daddr_t nblocks; 116264Sbill int cyloff; 117264Sbill } up_sizes[8] = { 118264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 119264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 120341Sbill 495520, 0, /* C=cyl 0 thru 814 */ 121264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 122264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 123264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 124264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 125264Sbill 445664, 82, /* H=cyl 82 thru 814 */ 126264Sbill /* Later, and more safely for H area... 127264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 128264Sbill }; 129264Sbill 130264Sbill /* 131264Sbill * The following defines are used in offset positioning 132264Sbill * when trying to recover disk errors, with the constants being 133264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 134264Sbill * tried (this makes sense only during read, in any case.) 135264Sbill * 136341Sbill * NOT ALL OF THESE ARE IMPLEMENTED ON 9300!?! 137264Sbill */ 138264Sbill #define P400 020 139264Sbill #define M400 0220 140264Sbill #define P800 040 141264Sbill #define M800 0240 142264Sbill #define P1200 060 143264Sbill #define M1200 0260 144264Sbill #define HCI 020000 145264Sbill 146264Sbill int up_offset[16] = 147264Sbill { 148264Sbill P400, M400, P400, M400, 149264Sbill P800, M800, P800, M800, 150264Sbill P1200, M1200, P1200, M1200, 151264Sbill 0, 0, 0, 0, 152264Sbill }; 153264Sbill 154264Sbill /* 155264Sbill * Each drive has a table uputab[i]. On this table are sorted the 156264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 157264Sbill * In the upustart() routine, each drive is independently advanced 158264Sbill * until it is on the desired cylinder for the next transfer and near 159264Sbill * the desired sector. The drive is then chained onto the uptab 160264Sbill * table, and the transfer is initiated by the upstart() routine. 161264Sbill * When the transfer is completed the driver reinvokes the upustart() 162264Sbill * routine to set up the next transfer. 163264Sbill */ 164264Sbill struct buf uptab; 165264Sbill struct buf uputab[NUP]; 166264Sbill 167264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 168264Sbill 169264Sbill /* Drive commands, placed in upcs1 */ 170264Sbill #define GO 01 /* Go bit, set in all commands */ 171264Sbill #define PRESET 020 /* Preset drive at init or after errors */ 172264Sbill #define OFFSET 014 /* Offset heads to try to recover error */ 173264Sbill #define RTC 016 /* Return to center-line after OFFSET */ 174264Sbill #define SEARCH 030 /* Search for cylinder+sector */ 175275Sbill #define SEEK 04 /* Seek to cylinder */ 176264Sbill #define RECAL 06 /* Recalibrate, needed after seek error */ 177264Sbill #define DCLR 010 /* Drive clear, after error */ 178264Sbill #define WCOM 060 /* Write */ 179264Sbill #define RCOM 070 /* Read */ 180264Sbill 181264Sbill /* Other bits of upcs1 */ 182264Sbill #define IE 0100 /* Controller wide interrupt enable */ 183264Sbill #define TRE 040000 /* Transfer error */ 184345Sbill #define RDY 0200 /* Transfer terminated */ 185264Sbill 186264Sbill /* Drive status bits of upds */ 187264Sbill #define PIP 020000 /* Positioning in progress */ 188264Sbill #define ERR 040000 /* Error has occurred, DCLR necessary */ 189264Sbill #define VV 0100 /* Volume is valid, set by PRESET */ 190264Sbill #define DPR 0400 /* Drive has been preset */ 191264Sbill #define MOL 010000 /* Drive is online, heads loaded, etc */ 192264Sbill #define DRY 0200 /* Drive ready */ 193264Sbill 194313Sbill /* Bits of upcs2 */ 195313Sbill #define CLR 040 /* Controller clear */ 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 209341Sbill * certain operations. With rev J delays seem to be needed only 210341Sbill * when selecting a new unit, and in drive initialization type 211341Sbill * like PRESET and DCLR. The following variables control the delay 212341Sbill * DELAY(n) is approximately n usec. 213264Sbill */ 214367Sbill int olducode = 1; 215264Sbill int idelay = 500; /* Delay after PRESET or DCLR */ 216367Sbill int osdelay = 150; /* Old delay after selecting drive in upcs2 */ 217367Sbill int ordelay = 100; /* Old delay after SEARCH */ 218367Sbill int oasdel = 100; /* Old delay after clearing bit in upas */ 219367Sbill int nsdelay = 25; 220264Sbill 221264Sbill #define DELAY(N) { register int d; d = N; while (--d > 0); } 222264Sbill 223264Sbill int nwaitcs2; /* How many sdelay loops ? */ 224264Sbill int neasycs2; /* How many sdelay loops not needed ? */ 225264Sbill 226313Sbill int up_wticks; /* Ticks waiting for interrupt */ 227313Sbill int upwstart; /* Have started guardian */ 228313Sbill int upwatch(); 229313Sbill 230264Sbill #ifdef INTRLVE 231264Sbill daddr_t dkblock(); 232264Sbill #endif 233264Sbill 234264Sbill /* 235264Sbill * Queue an i/o request for a drive, checking first that it is in range. 236264Sbill * 237264Sbill * A unit start is issued if the drive is inactive, causing 238264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 239264Sbill * already nearly on the money and the controller is not transferring 240264Sbill * we kick it to start the transfer. 241264Sbill */ 242264Sbill upstrategy(bp) 243264Sbill register struct buf *bp; 244264Sbill { 245264Sbill register struct buf *dp; 246264Sbill register unit, xunit; 247264Sbill long sz, bn; 248264Sbill 249313Sbill if (upwstart == 0) { 250313Sbill timeout((caddr_t)upwatch, 0, HZ); 251313Sbill upwstart++; 252313Sbill } 253264Sbill xunit = minor(bp->b_dev) & 077; 254264Sbill sz = bp->b_bcount; 255264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 256264Sbill unit = dkunit(bp); 257264Sbill if (unit >= NUP || 258264Sbill bp->b_blkno < 0 || 259264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 260264Sbill bp->b_flags |= B_ERROR; 261264Sbill iodone(bp); 262264Sbill return; 263264Sbill } 2641412Sbill if (DK_N+unit <= DK_NMAX) 2651412Sbill dk_mspw[DK_N+unit] = .0000020345; 266264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 267264Sbill dp = &uputab[unit]; 268264Sbill (void) spl5(); 269264Sbill disksort(dp, bp); 270264Sbill if (dp->b_active == 0) { 271268Sbill (void) upustart(unit); 272264Sbill if (uptab.b_actf && uptab.b_active == 0) 273268Sbill (void) upstart(); 274264Sbill } 275264Sbill (void) spl0(); 276264Sbill } 277264Sbill 278264Sbill /* 279264Sbill * Start activity on specified drive; called when drive is inactive 280264Sbill * and new transfer request arrives and also when upas indicates that 281264Sbill * a SEARCH command is complete. 282264Sbill */ 283264Sbill upustart(unit) 284264Sbill register unit; 285264Sbill { 286264Sbill register struct buf *bp, *dp; 287264Sbill register struct device *upaddr = UPADDR; 288264Sbill daddr_t bn; 289264Sbill int sn, cn, csn; 290268Sbill int didie = 0; 291264Sbill 292275Sbill /* 293275Sbill * Other drivers tend to say something like 294275Sbill * upaddr->upcs1 = IE; 295275Sbill * upaddr->upas = 1<<unit; 296275Sbill * here, but the SC-11B will cancel a command which 297275Sbill * happens to be sitting in the cs1 if you clear the go 298275Sbill * bit by storing there (so the first is not safe), 299275Sbill * and it also does not like being bothered with operations 300275Sbill * such as clearing upas when a transfer is active (as 301275Sbill * it may well be.) 302275Sbill * 303275Sbill * Thus we keep careful track of when we re-enable IE 304275Sbill * after an interrupt and do it only if we didn't issue 305275Sbill * a command which re-enabled it as a matter of course. 306275Sbill * We clear bits in upas in the interrupt routine, when 307275Sbill * no transfers are active. 308275Sbill */ 309266Sbill if (unit >= NUP) 310268Sbill goto out; 311264Sbill if (unit+DK_N <= DK_NMAX) 312264Sbill dk_busy &= ~(1<<(unit+DK_N)); 313264Sbill dp = &uputab[unit]; 314266Sbill if ((bp = dp->b_actf) == NULL) 315268Sbill goto out; 316275Sbill /* 317275Sbill * The SC-11B doesn't start SEARCH commands when transfers are 318275Sbill * in progress. In fact, it tends to get confused when given 319275Sbill * SEARCH'es during transfers, generating interrupts with neither 320275Sbill * RDY nor a bit in the upas register. Thus we defer 321275Sbill * until an interrupt when a transfer is pending. 322275Sbill */ 323275Sbill if (uptab.b_active) { 324341Sbill upsoftas |= 1<<unit; 325275Sbill return (0); 326275Sbill } 327276Sbill if (dp->b_active) 328276Sbill goto done; 329276Sbill dp->b_active = 1; 330264Sbill if ((upaddr->upcs2 & 07) != unit) { 331264Sbill upaddr->upcs2 = unit; 332367Sbill DELAY(olducode ? osdelay : nsdelay); 333264Sbill nwaitcs2++; 334264Sbill } else 335264Sbill neasycs2++; 336266Sbill /* 337266Sbill * If we have changed packs or just initialized, 338275Sbill * then the volume will not be valid; if so, clear 339266Sbill * the drive, preset it and put in 16bit/word mode. 340266Sbill */ 341266Sbill if ((upaddr->upds & VV) == 0) { 342266Sbill upaddr->upcs1 = IE|DCLR|GO; 343266Sbill DELAY(idelay); 344264Sbill upaddr->upcs1 = IE|PRESET|GO; 345264Sbill DELAY(idelay); 346264Sbill upaddr->upof = FMT22; 347268Sbill didie = 1; 348264Sbill } 349264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 350275Sbill goto done; 351266Sbill /* 352266Sbill * Do enough of the disk address decoding to determine 353266Sbill * which cylinder and sector the request is on. 354266Sbill * If we are on the correct cylinder and the desired sector 355308Sbill * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then 356266Sbill * we don't bother to SEARCH but just begin the transfer asap. 357308Sbill * Otherwise ask for a interrupt upSDIST sectors ahead. 358266Sbill */ 359264Sbill bn = dkblock(bp); 360264Sbill cn = bp->b_cylin; 361264Sbill sn = bn%(NSECT*NTRAC); 362308Sbill sn = (sn+NSECT-upSDIST)%NSECT; 363264Sbill 364266Sbill if (cn - upaddr->updc) 365266Sbill goto search; /* Not on-cylinder */ 366275Sbill else if (upseek) 367275Sbill goto done; /* Ok just to be on-cylinder */ 368264Sbill csn = (upaddr->upla>>6) - sn - 1; 369266Sbill if (csn < 0) 370264Sbill csn += NSECT; 371308Sbill if (csn > NSECT-upRDIST) 372264Sbill goto done; 373264Sbill 374264Sbill search: 375264Sbill upaddr->updc = cn; 376275Sbill if (upseek) 377275Sbill upaddr->upcs1 = IE|SEEK|GO; 378275Sbill else { 379275Sbill upaddr->upda = sn; 380275Sbill upaddr->upcs1 = IE|SEARCH|GO; 381275Sbill } 382268Sbill didie = 1; 383266Sbill /* 384266Sbill * Mark this unit busy. 385266Sbill */ 386264Sbill unit += DK_N; 3871412Sbill if (unit <= DK_NMAX) { 388264Sbill dk_busy |= 1<<unit; 3891412Sbill dk_seek[unit]++; 390264Sbill } 391367Sbill if (olducode) 392367Sbill DELAY(ordelay); 393268Sbill goto out; 394264Sbill 395264Sbill done: 396266Sbill /* 397275Sbill * This unit is ready to go so 398275Sbill * link it onto the chain of ready disks. 399266Sbill */ 400264Sbill dp->b_forw = NULL; 401266Sbill if (uptab.b_actf == NULL) 402264Sbill uptab.b_actf = dp; 403264Sbill else 404264Sbill uptab.b_actl->b_forw = dp; 405264Sbill uptab.b_actl = dp; 406268Sbill 407268Sbill out: 408268Sbill return (didie); 409264Sbill } 410264Sbill 411264Sbill /* 412264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 413264Sbill */ 414264Sbill upstart() 415264Sbill { 416264Sbill register struct buf *bp, *dp; 417264Sbill register unit; 418264Sbill register struct device *upaddr; 419264Sbill daddr_t bn; 420266Sbill int dn, sn, tn, cn, cmd; 421264Sbill 422264Sbill loop: 423266Sbill /* 424266Sbill * Pick a drive off the queue of ready drives, and 425266Sbill * perform the first transfer on its queue. 426266Sbill * 427266Sbill * Looping here is completely for the sake of drives which 428266Sbill * are not present and on-line, for which we completely clear the 429266Sbill * request queue. 430266Sbill */ 431273Sbill if ((dp = uptab.b_actf) == NULL) 432268Sbill return (0); 433264Sbill if ((bp = dp->b_actf) == NULL) { 434264Sbill uptab.b_actf = dp->b_forw; 435264Sbill goto loop; 436264Sbill } 437266Sbill /* 438266Sbill * Mark the controller busy, and multi-part disk address. 439266Sbill * Select the unit on which the i/o is to take place. 440266Sbill */ 441264Sbill uptab.b_active++; 442264Sbill unit = minor(bp->b_dev) & 077; 443264Sbill dn = dkunit(bp); 444264Sbill bn = dkblock(bp); 445264Sbill cn = up_sizes[unit&07].cyloff; 446264Sbill cn += bn/(NSECT*NTRAC); 447264Sbill sn = bn%(NSECT*NTRAC); 448264Sbill tn = sn/NSECT; 449266Sbill sn %= NSECT; 450264Sbill upaddr = UPADDR; 451264Sbill if ((upaddr->upcs2 & 07) != dn) { 452264Sbill upaddr->upcs2 = dn; 453275Sbill /* DELAY(sdelay); Provided by ubasetup() */ 454264Sbill nwaitcs2++; 455264Sbill } else 456264Sbill neasycs2++; 457275Sbill up_ubinfo = ubasetup(bp, 1); /* Providing delay */ 458266Sbill /* 459266Sbill * If drive is not present and on-line, then 460266Sbill * get rid of this with an error and loop to get 461266Sbill * rid of the rest of its queued requests. 462266Sbill * (Then on to any other ready drives.) 463266Sbill */ 464264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 465893Sbill printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds); 466893Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 467893Sbill printf("-- hard\n"); 468893Sbill uptab.b_active = 0; 469893Sbill uptab.b_errcnt = 0; 470893Sbill dp->b_actf = bp->av_forw; 471893Sbill dp->b_active = 0; 472893Sbill bp->b_flags |= B_ERROR; 473893Sbill iodone(bp); 474893Sbill /* A funny place to do this ... */ 475893Sbill ubafree(up_ubinfo), up_ubinfo = 0; 476893Sbill goto loop; 477893Sbill } 478893Sbill printf("-- came back\n"); 479264Sbill } 480266Sbill /* 481266Sbill * If this is a retry, then with the 16'th retry we 482266Sbill * begin to try offsetting the heads to recover the data. 483266Sbill */ 484924Sbill if (uptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) { 485264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 486266Sbill upaddr->upcs1 = IE|OFFSET|GO; 487264Sbill DELAY(idelay); 488266Sbill while (upaddr->upds & PIP) 489264Sbill DELAY(25); 490264Sbill } 491266Sbill /* 492266Sbill * Now set up the transfer, retrieving the high 493266Sbill * 2 bits of the UNIBUS address from the information 494266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 495266Sbill */ 496264Sbill upaddr->updc = cn; 497264Sbill upaddr->upda = (tn << 8) + sn; 498264Sbill upaddr->upba = up_ubinfo; 499264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 500266Sbill cmd = (up_ubinfo >> 8) & 0x300; 501264Sbill if (bp->b_flags & B_READ) 502266Sbill cmd |= IE|RCOM|GO; 503264Sbill else 504266Sbill cmd |= IE|WCOM|GO; 505266Sbill upaddr->upcs1 = cmd; 506266Sbill /* 507266Sbill * This is a controller busy situation. 508266Sbill * Record in dk slot NUP+DK_N (after last drive) 509266Sbill * unless there aren't that many slots reserved for 510266Sbill * us in which case we record this as a drive busy 511266Sbill * (if there is room for that). 512266Sbill */ 513264Sbill unit = dn+DK_N; 514264Sbill if (unit <= DK_NMAX) { 515264Sbill dk_busy |= 1<<unit; 5161412Sbill dk_xfer[unit]++; 517264Sbill dk_wds[unit] += bp->b_bcount>>6; 518264Sbill } 519268Sbill return (1); 520264Sbill } 521264Sbill 522264Sbill /* 523264Sbill * Handle a device interrupt. 524264Sbill * 525264Sbill * If the transferring drive needs attention, service it 526264Sbill * retrying on error or beginning next transfer. 527264Sbill * Service all other ready drives, calling ustart to transfer 528264Sbill * their blocks to the ready queue in uptab, and then restart 529264Sbill * the controller if there is anything to do. 530264Sbill */ 531264Sbill upintr() 532264Sbill { 533264Sbill register struct buf *bp, *dp; 534264Sbill register unit; 535264Sbill register struct device *upaddr = UPADDR; 536264Sbill int as = upaddr->upas & 0377; 537341Sbill int oupsoftas; 538268Sbill int needie = 1; 539264Sbill 540276Sbill (void) spl6(); 541313Sbill up_wticks = 0; 542266Sbill if (uptab.b_active) { 543266Sbill /* 544266Sbill * The drive is transferring, thus the hardware 545266Sbill * (say the designers) will only interrupt when the transfer 546266Sbill * completes; check for it anyways. 547266Sbill */ 548266Sbill if ((upaddr->upcs1 & RDY) == 0) { 549272Sbill printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, 550272Sbill upaddr->upds, upaddr->upwc); 551341Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, 552341Sbill uputab[0].b_active, uputab[1].b_active); 553269Sbill } 554266Sbill /* 5551412Sbill * Mark drive not busy, and check for an 556266Sbill * error condition which may have resulted from the transfer. 557266Sbill */ 558264Sbill dp = uptab.b_actf; 559264Sbill bp = dp->b_actf; 560264Sbill unit = dkunit(bp); 5611412Sbill if (DK_N+unit <= DK_NMAX) 562264Sbill dk_busy &= ~(1<<(DK_N+unit)); 563275Sbill if ((upaddr->upcs2 & 07) != unit) { 564275Sbill upaddr->upcs2 = unit; 565367Sbill DELAY(olducode ? osdelay : nsdelay); 566275Sbill nwaitcs2++; 567275Sbill } else 568275Sbill neasycs2++; 569885Sbill if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) { 570266Sbill /* 571266Sbill * An error occurred, indeed. Select this unit 572266Sbill * to get at the drive status (a SEARCH may have 573266Sbill * intervened to change the selected unit), and 574266Sbill * wait for the command which caused the interrupt 575266Sbill * to complete (DRY). 576266Sbill */ 577266Sbill while ((upaddr->upds & DRY) == 0) 578264Sbill DELAY(25); 579266Sbill /* 580266Sbill * After 28 retries (16 w/o servo offsets, and then 581266Sbill * 12 with servo offsets), or if we encountered 582266Sbill * an error because the drive is write-protected, 583266Sbill * give up. Print an error message on the last 2 584266Sbill * retries before a hard failure. 585266Sbill */ 586266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 587264Sbill bp->b_flags |= B_ERROR; 588264Sbill else 589266Sbill uptab.b_active = 0; /* To force retry */ 590266Sbill if (uptab.b_errcnt > 27) 591264Sbill deverror(bp, upaddr->upcs2, upaddr->uper1); 592266Sbill /* 593266Sbill * If this was a correctible ECC error, let upecc 594266Sbill * do the dirty work to correct it. If upecc 595266Sbill * starts another READ for the rest of the data 596266Sbill * then it returns 1 (having set uptab.b_active). 597266Sbill * Otherwise we are done and fall through to 598266Sbill * finish up. 599266Sbill */ 600266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 601266Sbill return; 602266Sbill /* 603266Sbill * Clear the drive and, every 4 retries, recalibrate 604266Sbill * to hopefully help clear up seek positioning problems. 605266Sbill */ 606264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 607264Sbill DELAY(idelay); 608268Sbill needie = 0; 609266Sbill if ((uptab.b_errcnt&07) == 4) { 610264Sbill upaddr->upcs1 = RECAL|GO|IE; 611264Sbill DELAY(idelay); 612264Sbill while(upaddr->upds & PIP) 613264Sbill DELAY(25); 614264Sbill } 615264Sbill } 616266Sbill /* 617266Sbill * If we are still noted as active, then no 618266Sbill * (further) retries are necessary. 619266Sbill * 620266Sbill * Make sure the correct unit is selected, 621266Sbill * return it to centerline if necessary, and mark 622266Sbill * this i/o complete, starting the next transfer 623266Sbill * on this drive with the upustart routine (if any). 624266Sbill */ 625266Sbill if (uptab.b_active) { 626266Sbill if (uptab.b_errcnt >= 16) { 627266Sbill upaddr->upcs1 = RTC|GO|IE; 628264Sbill DELAY(idelay); 629266Sbill while (upaddr->upds & PIP) 630264Sbill DELAY(25); 631268Sbill needie = 0; 632264Sbill } 633264Sbill uptab.b_active = 0; 634264Sbill uptab.b_errcnt = 0; 635264Sbill uptab.b_actf = dp->b_forw; 636264Sbill dp->b_active = 0; 637264Sbill dp->b_errcnt = 0; 638264Sbill dp->b_actf = bp->av_forw; 639266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 640275Sbill if (bp->b_resid) 641341Sbill printf("resid %d ds %o er? %o %o %o\n", 642341Sbill bp->b_resid, upaddr->upds, 643275Sbill upaddr->uper1, upaddr->uper2, upaddr->uper3); 644264Sbill iodone(bp); 645264Sbill if(dp->b_actf) 646268Sbill if (upustart(unit)) 647268Sbill needie = 0; 648264Sbill } 649264Sbill as &= ~(1<<unit); 650341Sbill upsoftas &= ~(1<<unit); 651264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 652273Sbill } else { 653264Sbill if (upaddr->upcs1 & TRE) { 654264Sbill upaddr->upcs1 = TRE; 655264Sbill DELAY(idelay); 656264Sbill } 657264Sbill } 658266Sbill /* 659266Sbill * If we have a unit with an outstanding SEARCH, 660266Sbill * and the hardware indicates the unit requires attention, 661266Sbill * the bring the drive to the ready queue. 662266Sbill * Finally, if the controller is not transferring 663266Sbill * start it if any drives are now ready to transfer. 664266Sbill */ 665341Sbill as |= upsoftas; 666341Sbill oupsoftas = upsoftas; 667341Sbill upsoftas = 0; 668266Sbill for (unit = 0; unit < NUP; unit++) 669341Sbill if ((as|oupsoftas) & (1<<unit)) { 670273Sbill if (as & (1<<unit)) { 671267Sbill upaddr->upas = 1<<unit; 672367Sbill if (olducode) 673367Sbill DELAY(oasdel); 674272Sbill } 675273Sbill if (upustart(unit)) 676273Sbill needie = 0; 677273Sbill } 678266Sbill if (uptab.b_actf && uptab.b_active == 0) 679268Sbill if (upstart()) 680268Sbill needie = 0; 681266Sbill out: 682275Sbill if (needie) 683266Sbill upaddr->upcs1 = IE; 684264Sbill } 685264Sbill 686264Sbill upread(dev) 687264Sbill { 688264Sbill 689264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 690264Sbill } 691264Sbill 692264Sbill upwrite(dev) 693264Sbill { 694264Sbill 695264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 696264Sbill } 697264Sbill 698266Sbill /* 699266Sbill * Correct an ECC error, and restart the i/o to complete 700266Sbill * the transfer if necessary. This is quite complicated because 701266Sbill * the transfer may be going to an odd memory address base and/or 702266Sbill * across a page boundary. 703266Sbill */ 704264Sbill upecc(up, bp) 705264Sbill register struct device *up; 706264Sbill register struct buf *bp; 707264Sbill { 708264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 709266Sbill register int i; 710264Sbill caddr_t addr; 711266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 712264Sbill int bn, cn, tn, sn; 713264Sbill 714264Sbill /* 715266Sbill * Npf is the number of sectors transferred before the sector 716266Sbill * containing the ECC error, and reg is the UBA register 717266Sbill * mapping (the first part of) the transfer. 718266Sbill * O is offset within a memory page of the first byte transferred. 719264Sbill */ 720266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 721266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 722264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 723264Sbill printf("%D ", bp->b_blkno+npf); 724264Sbill prdev("ECC", bp->b_dev); 725264Sbill mask = up->upec2; 726264Sbill if (mask == 0) { 727266Sbill up->upof = FMT22; /* == RTC ???? */ 728264Sbill DELAY(idelay); 729264Sbill return (0); 730264Sbill } 731266Sbill /* 732266Sbill * Flush the buffered data path, and compute the 733266Sbill * byte and bit position of the error. The variable i 734266Sbill * is the byte offset in the transfer, the variable byte 735266Sbill * is the offset from a page boundary in main memory. 736266Sbill */ 737266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 738266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 739266Sbill bit = i&07; 740266Sbill i = (i&~07)>>3; 741264Sbill byte = i + o; 742266Sbill /* 743266Sbill * Correct while possible bits remain of mask. Since mask 744266Sbill * contains 11 bits, we continue while the bit offset is > -11. 745266Sbill * Also watch out for end of this block and the end of the whole 746266Sbill * transfer. 747266Sbill */ 748266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 749266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 750266Sbill (byte & PGOFSET); 751266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 752266Sbill byte++; 753266Sbill i++; 754266Sbill bit -= 8; 755264Sbill } 756266Sbill uptab.b_active++; /* Either complete or continuing... */ 757264Sbill if (up->upwc == 0) 758264Sbill return (0); 759266Sbill /* 760266Sbill * Have to continue the transfer... clear the drive, 761266Sbill * and compute the position where the transfer is to continue. 762266Sbill * We have completed npf+1 sectors of the transfer already; 763266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 764266Sbill */ 765266Sbill up->upcs1 = TRE|IE|DCLR|GO; 766264Sbill DELAY(idelay); 767264Sbill bn = dkblock(bp); 768264Sbill cn = bp->b_cylin; 769266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 770264Sbill tn = sn/NSECT; 771264Sbill sn %= NSECT; 772266Sbill cn += tn/NTRAC; 773266Sbill tn %= NTRAC; 774264Sbill up->updc = cn; 775266Sbill up->upda = (tn << 8) | sn; 776266Sbill ubaddr = (int)ptob(reg+1) + o; 777266Sbill up->upba = ubaddr; 778266Sbill cmd = (ubaddr >> 8) & 0x300; 779266Sbill cmd |= IE|GO|RCOM; 780266Sbill up->upcs1 = cmd; 781264Sbill return (1); 782264Sbill } 783286Sbill 784286Sbill /* 785286Sbill * Reset driver after UBA init. 786286Sbill * Cancel software state of all pending transfers 787286Sbill * and restart all units and the controller. 788286Sbill */ 789286Sbill upreset() 790286Sbill { 791286Sbill int unit; 792286Sbill 793286Sbill printf(" up"); 794286Sbill uptab.b_active = 0; 795286Sbill uptab.b_actf = uptab.b_actl = 0; 796286Sbill if (up_ubinfo) { 797286Sbill printf("<%d>", (up_ubinfo>>28)&0xf); 798286Sbill ubafree(up_ubinfo), up_ubinfo = 0; 799286Sbill } 800313Sbill UPADDR->upcs2 = CLR; /* clear controller */ 801313Sbill DELAY(idelay); 802286Sbill for (unit = 0; unit < NUP; unit++) { 803286Sbill uputab[unit].b_active = 0; 804286Sbill (void) upustart(unit); 805286Sbill } 806286Sbill (void) upstart(); 807286Sbill } 808313Sbill 809313Sbill /* 810313Sbill * Wake up every second and if an interrupt is pending 811313Sbill * but nothing has happened increment a counter. 812313Sbill * If nothing happens for 20 seconds, reset the controller 813313Sbill * and begin anew. 814313Sbill */ 815313Sbill upwatch() 816313Sbill { 817313Sbill int i; 818313Sbill 819313Sbill timeout((caddr_t)upwatch, 0, HZ); 820313Sbill if (uptab.b_active == 0) { 821313Sbill for (i = 0; i < NUP; i++) 822313Sbill if (uputab[i].b_active) 823313Sbill goto active; 824313Sbill up_wticks = 0; /* idling */ 825313Sbill return; 826313Sbill } 827313Sbill active: 828313Sbill up_wticks++; 829313Sbill if (up_wticks >= 20) { 830313Sbill up_wticks = 0; 831313Sbill printf("LOST INTERRUPT RESET"); 832313Sbill upreset(); 833313Sbill printf("\n"); 834313Sbill } 835313Sbill } 836