1*2379Swnj /* up.c 4.12 02/08/81 */ 2264Sbill 31937Swnj #include "up.h" 41809Sbill #if NUP > 0 5264Sbill /* 6885Sbill * UNIBUS disk driver with overlapped seeks and ECC recovery. 7264Sbill */ 81756Sbill #define DELAY(N) { register int d; d = N; while (--d > 0); } 9264Sbill 10264Sbill #include "../h/param.h" 11264Sbill #include "../h/systm.h" 12308Sbill #include "../h/dk.h" 13264Sbill #include "../h/buf.h" 14264Sbill #include "../h/conf.h" 15264Sbill #include "../h/dir.h" 16264Sbill #include "../h/user.h" 17264Sbill #include "../h/map.h" 18420Sbill #include "../h/pte.h" 19264Sbill #include "../h/mba.h" 20264Sbill #include "../h/mtpr.h" 21264Sbill #include "../h/uba.h" 22264Sbill #include "../h/vm.h" 23*2379Swnj #include "../h/cmap.h" 24264Sbill 25*2379Swnj #include "../h/upreg.h" 26264Sbill 27275Sbill /* 28275Sbill * Software extension to the upas register, so we can 29275Sbill * postpone starting SEARCH commands until the controller 30275Sbill * is not transferring. 31275Sbill */ 32341Sbill int upsoftas; 33275Sbill 34275Sbill /* 35275Sbill * If upseek then we don't issue SEARCH commands but rather just 36275Sbill * settle for a SEEK to the correct cylinder. 37275Sbill */ 38275Sbill int upseek; 39275Sbill 40264Sbill #define NSECT 32 41264Sbill #define NTRAC 19 42264Sbill 43264Sbill /* 44264Sbill * Constants controlling on-cylinder SEARCH usage. 45264Sbill * 46308Sbill * upSDIST/2 msec time needed to start transfer 47308Sbill * upRDIST/2 msec tolerable rotational latency when on-cylinder 48275Sbill * 49308Sbill * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST 50275Sbill * and in the driver then we take it as it is. Otherwise we do a SEARCH 51308Sbill * requesting an interrupt upSDIST sectors in advance. 52264Sbill */ 531592Sbill #define _upSDIST 2 /* 1.0 msec */ 541592Sbill #define _upRDIST 4 /* 2.0 msec */ 55264Sbill 56308Sbill int upSDIST = _upSDIST; 57308Sbill int upRDIST = _upRDIST; 58275Sbill 59264Sbill /* 60264Sbill * To fill a 300M drive: 61264Sbill * A is designed to be used as a root. 62264Sbill * B is suitable for a swap area. 63264Sbill * H is the primary storage area. 64264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 65264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 66264Sbill * covers the whole drive and can be used for pack-pack copying. 671756Sbill * 681756Sbill * Note: sizes here are for AMPEX drives with 815 cylinders. 691756Sbill * CDC drives can make the F,G, and H areas larger as they have 823 cylinders. 70264Sbill */ 71264Sbill struct size 72264Sbill { 73264Sbill daddr_t nblocks; 74264Sbill int cyloff; 75264Sbill } up_sizes[8] = { 76264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 77264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 78341Sbill 495520, 0, /* C=cyl 0 thru 814 */ 79264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 80264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 81264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 82264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 83264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 84264Sbill }; 85264Sbill 86264Sbill /* 87264Sbill * The following defines are used in offset positioning 88264Sbill * when trying to recover disk errors, with the constants being 89264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 90264Sbill * tried (this makes sense only during read, in any case.) 91264Sbill * 921756Sbill * NB: Not all drives/controllers emulate all of these. 93264Sbill */ 94264Sbill #define P400 020 95264Sbill #define M400 0220 96264Sbill #define P800 040 97264Sbill #define M800 0240 98264Sbill #define P1200 060 99264Sbill #define M1200 0260 100264Sbill #define HCI 020000 101264Sbill 102264Sbill int up_offset[16] = 103264Sbill { 104264Sbill P400, M400, P400, M400, 105264Sbill P800, M800, P800, M800, 106264Sbill P1200, M1200, P1200, M1200, 107264Sbill 0, 0, 0, 0, 108264Sbill }; 109264Sbill 110264Sbill /* 111264Sbill * Each drive has a table uputab[i]. On this table are sorted the 112264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 113264Sbill * In the upustart() routine, each drive is independently advanced 114264Sbill * until it is on the desired cylinder for the next transfer and near 115264Sbill * the desired sector. The drive is then chained onto the uptab 116264Sbill * table, and the transfer is initiated by the upstart() routine. 117264Sbill * When the transfer is completed the driver reinvokes the upustart() 118264Sbill * routine to set up the next transfer. 119264Sbill */ 120264Sbill struct buf uptab; 121264Sbill struct buf uputab[NUP]; 122264Sbill 123264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 124264Sbill 125264Sbill #define b_cylin b_resid 126264Sbill 127264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 128264Sbill 129313Sbill int up_wticks; /* Ticks waiting for interrupt */ 130313Sbill int upwstart; /* Have started guardian */ 131313Sbill int upwatch(); 132313Sbill 133264Sbill #ifdef INTRLVE 134264Sbill daddr_t dkblock(); 135264Sbill #endif 136264Sbill 137264Sbill /* 138264Sbill * Queue an i/o request for a drive, checking first that it is in range. 139264Sbill * 140264Sbill * A unit start is issued if the drive is inactive, causing 141264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 142264Sbill * already nearly on the money and the controller is not transferring 143264Sbill * we kick it to start the transfer. 144264Sbill */ 145264Sbill upstrategy(bp) 146264Sbill register struct buf *bp; 147264Sbill { 148264Sbill register struct buf *dp; 149264Sbill register unit, xunit; 150264Sbill long sz, bn; 151264Sbill 152313Sbill if (upwstart == 0) { 1531783Sbill timeout(upwatch, (caddr_t)0, HZ); 154313Sbill upwstart++; 155313Sbill } 156264Sbill xunit = minor(bp->b_dev) & 077; 157264Sbill sz = bp->b_bcount; 158264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 159264Sbill unit = dkunit(bp); 160264Sbill if (unit >= NUP || 161264Sbill bp->b_blkno < 0 || 162264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 163264Sbill bp->b_flags |= B_ERROR; 164264Sbill iodone(bp); 165264Sbill return; 166264Sbill } 1671945Swnj if (UPDK_N+unit <= UPDK_NMAX) 1681945Swnj dk_mspw[UPDK_N+unit] = .0000020345; 169264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 170264Sbill dp = &uputab[unit]; 171264Sbill (void) spl5(); 172264Sbill disksort(dp, bp); 173264Sbill if (dp->b_active == 0) { 174268Sbill (void) upustart(unit); 175264Sbill if (uptab.b_actf && uptab.b_active == 0) 176268Sbill (void) upstart(); 177264Sbill } 178264Sbill (void) spl0(); 179264Sbill } 180264Sbill 181264Sbill /* 182264Sbill * Start activity on specified drive; called when drive is inactive 183264Sbill * and new transfer request arrives and also when upas indicates that 184264Sbill * a SEARCH command is complete. 185264Sbill */ 186264Sbill upustart(unit) 187264Sbill register unit; 188264Sbill { 189264Sbill register struct buf *bp, *dp; 190264Sbill register struct device *upaddr = UPADDR; 191264Sbill daddr_t bn; 192264Sbill int sn, cn, csn; 193268Sbill int didie = 0; 194264Sbill 195275Sbill /* 196275Sbill * Other drivers tend to say something like 197275Sbill * upaddr->upcs1 = IE; 198275Sbill * upaddr->upas = 1<<unit; 1991756Sbill * here, but some controllers will cancel a command 200275Sbill * happens to be sitting in the cs1 if you clear the go 2011756Sbill * bit by storing there (so the first is not safe). 202275Sbill * 203275Sbill * Thus we keep careful track of when we re-enable IE 204275Sbill * after an interrupt and do it only if we didn't issue 205275Sbill * a command which re-enabled it as a matter of course. 206275Sbill * We clear bits in upas in the interrupt routine, when 207275Sbill * no transfers are active. 208275Sbill */ 209266Sbill if (unit >= NUP) 210268Sbill goto out; 2111945Swnj if (unit+UPDK_N <= UPDK_NMAX) 2121945Swnj dk_busy &= ~(1<<(unit+UPDK_N)); 213264Sbill dp = &uputab[unit]; 214266Sbill if ((bp = dp->b_actf) == NULL) 215268Sbill goto out; 216275Sbill /* 2171756Sbill * Most controllers don't start SEARCH commands when transfers are 2181756Sbill * in progress. In fact, some tend to get confused when given 219275Sbill * SEARCH'es during transfers, generating interrupts with neither 220275Sbill * RDY nor a bit in the upas register. Thus we defer 221275Sbill * until an interrupt when a transfer is pending. 222275Sbill */ 223275Sbill if (uptab.b_active) { 224341Sbill upsoftas |= 1<<unit; 225275Sbill return (0); 226275Sbill } 227276Sbill if (dp->b_active) 228276Sbill goto done; 229276Sbill dp->b_active = 1; 2301756Sbill if ((upaddr->upcs2 & 07) != unit) 231264Sbill upaddr->upcs2 = unit; 232266Sbill /* 233266Sbill * If we have changed packs or just initialized, 234275Sbill * then the volume will not be valid; if so, clear 235266Sbill * the drive, preset it and put in 16bit/word mode. 236266Sbill */ 237266Sbill if ((upaddr->upds & VV) == 0) { 238266Sbill upaddr->upcs1 = IE|DCLR|GO; 239264Sbill upaddr->upcs1 = IE|PRESET|GO; 240264Sbill upaddr->upof = FMT22; 241268Sbill didie = 1; 242264Sbill } 243264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 244275Sbill goto done; 2452298Skre 2462298Skre #if NUP > 1 247266Sbill /* 248266Sbill * Do enough of the disk address decoding to determine 249266Sbill * which cylinder and sector the request is on. 250266Sbill * If we are on the correct cylinder and the desired sector 251308Sbill * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then 252266Sbill * we don't bother to SEARCH but just begin the transfer asap. 253308Sbill * Otherwise ask for a interrupt upSDIST sectors ahead. 254266Sbill */ 255264Sbill bn = dkblock(bp); 256264Sbill cn = bp->b_cylin; 257264Sbill sn = bn%(NSECT*NTRAC); 258308Sbill sn = (sn+NSECT-upSDIST)%NSECT; 259264Sbill 260266Sbill if (cn - upaddr->updc) 261266Sbill goto search; /* Not on-cylinder */ 262275Sbill else if (upseek) 263275Sbill goto done; /* Ok just to be on-cylinder */ 264264Sbill csn = (upaddr->upla>>6) - sn - 1; 265266Sbill if (csn < 0) 266264Sbill csn += NSECT; 267308Sbill if (csn > NSECT-upRDIST) 268264Sbill goto done; 269264Sbill 270264Sbill search: 271264Sbill upaddr->updc = cn; 272275Sbill if (upseek) 273275Sbill upaddr->upcs1 = IE|SEEK|GO; 274275Sbill else { 275275Sbill upaddr->upda = sn; 276275Sbill upaddr->upcs1 = IE|SEARCH|GO; 277275Sbill } 278268Sbill didie = 1; 279266Sbill /* 280266Sbill * Mark this unit busy. 281266Sbill */ 2821945Swnj unit += UPDK_N; 2831945Swnj if (unit <= UPDK_NMAX) { 284264Sbill dk_busy |= 1<<unit; 2851412Sbill dk_seek[unit]++; 286264Sbill } 287268Sbill goto out; 2882298Skre #endif 289264Sbill 290264Sbill done: 291266Sbill /* 292275Sbill * This unit is ready to go so 293275Sbill * link it onto the chain of ready disks. 294266Sbill */ 295264Sbill dp->b_forw = NULL; 296266Sbill if (uptab.b_actf == NULL) 297264Sbill uptab.b_actf = dp; 298264Sbill else 299264Sbill uptab.b_actl->b_forw = dp; 300264Sbill uptab.b_actl = dp; 301268Sbill 302268Sbill out: 303268Sbill return (didie); 304264Sbill } 305264Sbill 306264Sbill /* 307264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 308264Sbill */ 309264Sbill upstart() 310264Sbill { 311264Sbill register struct buf *bp, *dp; 312264Sbill register unit; 313264Sbill register struct device *upaddr; 314264Sbill daddr_t bn; 315266Sbill int dn, sn, tn, cn, cmd; 316264Sbill 317264Sbill loop: 318266Sbill /* 319266Sbill * Pick a drive off the queue of ready drives, and 320266Sbill * perform the first transfer on its queue. 321266Sbill * 322266Sbill * Looping here is completely for the sake of drives which 323266Sbill * are not present and on-line, for which we completely clear the 324266Sbill * request queue. 325266Sbill */ 326273Sbill if ((dp = uptab.b_actf) == NULL) 327268Sbill return (0); 328264Sbill if ((bp = dp->b_actf) == NULL) { 329264Sbill uptab.b_actf = dp->b_forw; 330264Sbill goto loop; 331264Sbill } 332266Sbill /* 333266Sbill * Mark the controller busy, and multi-part disk address. 334266Sbill * Select the unit on which the i/o is to take place. 335266Sbill */ 336264Sbill uptab.b_active++; 337264Sbill unit = minor(bp->b_dev) & 077; 338264Sbill dn = dkunit(bp); 339264Sbill bn = dkblock(bp); 340264Sbill cn = up_sizes[unit&07].cyloff; 341264Sbill cn += bn/(NSECT*NTRAC); 342264Sbill sn = bn%(NSECT*NTRAC); 343264Sbill tn = sn/NSECT; 344266Sbill sn %= NSECT; 345264Sbill upaddr = UPADDR; 3461756Sbill if ((upaddr->upcs2 & 07) != dn) 347264Sbill upaddr->upcs2 = dn; 3481756Sbill up_ubinfo = ubasetup(bp, 1); 349266Sbill /* 350266Sbill * If drive is not present and on-line, then 351266Sbill * get rid of this with an error and loop to get 352266Sbill * rid of the rest of its queued requests. 353266Sbill * (Then on to any other ready drives.) 354266Sbill */ 355264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 356893Sbill printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds); 357893Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 358893Sbill printf("-- hard\n"); 359893Sbill uptab.b_active = 0; 360893Sbill uptab.b_errcnt = 0; 361893Sbill dp->b_actf = bp->av_forw; 362893Sbill dp->b_active = 0; 363893Sbill bp->b_flags |= B_ERROR; 364893Sbill iodone(bp); 365893Sbill /* A funny place to do this ... */ 3662053Swnj ubarelse(&up_ubinfo); 367893Sbill goto loop; 368893Sbill } 369893Sbill printf("-- came back\n"); 370264Sbill } 371266Sbill /* 372266Sbill * If this is a retry, then with the 16'th retry we 373266Sbill * begin to try offsetting the heads to recover the data. 374266Sbill */ 3752298Skre if (uptab.b_errcnt >= 16 && (bp->b_flags&B_READ) != 0) { 376264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 377266Sbill upaddr->upcs1 = IE|OFFSET|GO; 378266Sbill while (upaddr->upds & PIP) 379264Sbill DELAY(25); 380264Sbill } 381266Sbill /* 382266Sbill * Now set up the transfer, retrieving the high 383266Sbill * 2 bits of the UNIBUS address from the information 384266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 385266Sbill */ 386264Sbill upaddr->updc = cn; 387264Sbill upaddr->upda = (tn << 8) + sn; 388264Sbill upaddr->upba = up_ubinfo; 389264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 390266Sbill cmd = (up_ubinfo >> 8) & 0x300; 391264Sbill if (bp->b_flags & B_READ) 392266Sbill cmd |= IE|RCOM|GO; 393264Sbill else 394266Sbill cmd |= IE|WCOM|GO; 395266Sbill upaddr->upcs1 = cmd; 396266Sbill /* 397266Sbill * This is a controller busy situation. 3981945Swnj * Record in dk slot NUP+UPDK_N (after last drive) 399266Sbill * unless there aren't that many slots reserved for 400266Sbill * us in which case we record this as a drive busy 401266Sbill * (if there is room for that). 402266Sbill */ 4031945Swnj unit = dn+UPDK_N; 4041945Swnj if (unit <= UPDK_NMAX) { 405264Sbill dk_busy |= 1<<unit; 4061412Sbill dk_xfer[unit]++; 407264Sbill dk_wds[unit] += bp->b_bcount>>6; 408264Sbill } 409268Sbill return (1); 410264Sbill } 411264Sbill 412264Sbill /* 413264Sbill * Handle a device interrupt. 414264Sbill * 415264Sbill * If the transferring drive needs attention, service it 416264Sbill * retrying on error or beginning next transfer. 417264Sbill * Service all other ready drives, calling ustart to transfer 418264Sbill * their blocks to the ready queue in uptab, and then restart 419264Sbill * the controller if there is anything to do. 420264Sbill */ 421264Sbill upintr() 422264Sbill { 423264Sbill register struct buf *bp, *dp; 424264Sbill register unit; 425264Sbill register struct device *upaddr = UPADDR; 426264Sbill int as = upaddr->upas & 0377; 427341Sbill int oupsoftas; 428268Sbill int needie = 1; 429264Sbill 430276Sbill (void) spl6(); 431313Sbill up_wticks = 0; 432266Sbill if (uptab.b_active) { 433266Sbill /* 434266Sbill * The drive is transferring, thus the hardware 435266Sbill * (say the designers) will only interrupt when the transfer 436266Sbill * completes; check for it anyways. 437266Sbill */ 438266Sbill if ((upaddr->upcs1 & RDY) == 0) { 439272Sbill printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, 440272Sbill upaddr->upds, upaddr->upwc); 441341Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, 442341Sbill uputab[0].b_active, uputab[1].b_active); 443269Sbill } 444266Sbill /* 4451412Sbill * Mark drive not busy, and check for an 446266Sbill * error condition which may have resulted from the transfer. 447266Sbill */ 448264Sbill dp = uptab.b_actf; 449264Sbill bp = dp->b_actf; 450264Sbill unit = dkunit(bp); 4511945Swnj if (UPDK_N+unit <= UPDK_NMAX) 4521945Swnj dk_busy &= ~(1<<(UPDK_N+unit)); 4531756Sbill if ((upaddr->upcs2 & 07) != unit) 454275Sbill upaddr->upcs2 = unit; 455885Sbill if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) { 4561829Sbill int cs2; 457266Sbill /* 458266Sbill * An error occurred, indeed. Select this unit 459266Sbill * to get at the drive status (a SEARCH may have 460266Sbill * intervened to change the selected unit), and 461266Sbill * wait for the command which caused the interrupt 462266Sbill * to complete (DRY). 463266Sbill */ 464266Sbill while ((upaddr->upds & DRY) == 0) 465264Sbill DELAY(25); 466266Sbill /* 467266Sbill * After 28 retries (16 w/o servo offsets, and then 468266Sbill * 12 with servo offsets), or if we encountered 469266Sbill * an error because the drive is write-protected, 470266Sbill * give up. Print an error message on the last 2 471266Sbill * retries before a hard failure. 472266Sbill */ 473266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 474264Sbill bp->b_flags |= B_ERROR; 475264Sbill else 476266Sbill uptab.b_active = 0; /* To force retry */ 477266Sbill if (uptab.b_errcnt > 27) 4781829Sbill cs2 = (int)upaddr->upcs2; 4791783Sbill deverror(bp, (int)upaddr->upcs2, 4801783Sbill (int)upaddr->uper1); 481266Sbill /* 482266Sbill * If this was a correctible ECC error, let upecc 483266Sbill * do the dirty work to correct it. If upecc 484266Sbill * starts another READ for the rest of the data 485266Sbill * then it returns 1 (having set uptab.b_active). 486266Sbill * Otherwise we are done and fall through to 487266Sbill * finish up. 488266Sbill */ 489266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 490266Sbill return; 491266Sbill /* 492266Sbill * Clear the drive and, every 4 retries, recalibrate 493266Sbill * to hopefully help clear up seek positioning problems. 494266Sbill */ 495264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 496268Sbill needie = 0; 497266Sbill if ((uptab.b_errcnt&07) == 4) { 498264Sbill upaddr->upcs1 = RECAL|GO|IE; 499264Sbill while(upaddr->upds & PIP) 500264Sbill DELAY(25); 501264Sbill } 5021829Sbill if (uptab.b_errcnt == 28 && cs2&(NEM|MXF)) { 5031829Sbill printf("FLAKEY UP "); 5041829Sbill ubareset(); 5051829Sbill return; 5061829Sbill } 507264Sbill } 508266Sbill /* 509266Sbill * If we are still noted as active, then no 510266Sbill * (further) retries are necessary. 511266Sbill * 512266Sbill * Make sure the correct unit is selected, 513266Sbill * return it to centerline if necessary, and mark 514266Sbill * this i/o complete, starting the next transfer 515266Sbill * on this drive with the upustart routine (if any). 516266Sbill */ 517266Sbill if (uptab.b_active) { 518266Sbill if (uptab.b_errcnt >= 16) { 519266Sbill upaddr->upcs1 = RTC|GO|IE; 520266Sbill while (upaddr->upds & PIP) 521264Sbill DELAY(25); 522268Sbill needie = 0; 523264Sbill } 524264Sbill uptab.b_active = 0; 525264Sbill uptab.b_errcnt = 0; 526264Sbill uptab.b_actf = dp->b_forw; 527264Sbill dp->b_active = 0; 528264Sbill dp->b_errcnt = 0; 529264Sbill dp->b_actf = bp->av_forw; 530266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 531275Sbill if (bp->b_resid) 532341Sbill printf("resid %d ds %o er? %o %o %o\n", 533341Sbill bp->b_resid, upaddr->upds, 534275Sbill upaddr->uper1, upaddr->uper2, upaddr->uper3); 535264Sbill iodone(bp); 536264Sbill if(dp->b_actf) 537268Sbill if (upustart(unit)) 538268Sbill needie = 0; 539264Sbill } 540264Sbill as &= ~(1<<unit); 541341Sbill upsoftas &= ~(1<<unit); 5422053Swnj ubarelse(&up_ubinfo); 543273Sbill } else { 5441756Sbill if (upaddr->upcs1 & TRE) 545264Sbill upaddr->upcs1 = TRE; 546264Sbill } 547266Sbill /* 548266Sbill * If we have a unit with an outstanding SEARCH, 549266Sbill * and the hardware indicates the unit requires attention, 550266Sbill * the bring the drive to the ready queue. 551266Sbill * Finally, if the controller is not transferring 552266Sbill * start it if any drives are now ready to transfer. 553266Sbill */ 554341Sbill as |= upsoftas; 555341Sbill oupsoftas = upsoftas; 556341Sbill upsoftas = 0; 557266Sbill for (unit = 0; unit < NUP; unit++) 558341Sbill if ((as|oupsoftas) & (1<<unit)) { 5591756Sbill if (as & (1<<unit)) 560267Sbill upaddr->upas = 1<<unit; 561273Sbill if (upustart(unit)) 562273Sbill needie = 0; 563273Sbill } 564266Sbill if (uptab.b_actf && uptab.b_active == 0) 565268Sbill if (upstart()) 566268Sbill needie = 0; 567275Sbill if (needie) 568266Sbill upaddr->upcs1 = IE; 569264Sbill } 570264Sbill 571264Sbill upread(dev) 572264Sbill { 573264Sbill 574264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 575264Sbill } 576264Sbill 577264Sbill upwrite(dev) 578264Sbill { 579264Sbill 580264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 581264Sbill } 582264Sbill 583266Sbill /* 584266Sbill * Correct an ECC error, and restart the i/o to complete 585266Sbill * the transfer if necessary. This is quite complicated because 586266Sbill * the transfer may be going to an odd memory address base and/or 587266Sbill * across a page boundary. 588266Sbill */ 589264Sbill upecc(up, bp) 590264Sbill register struct device *up; 591264Sbill register struct buf *bp; 592264Sbill { 593264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 594266Sbill register int i; 595264Sbill caddr_t addr; 596266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 597264Sbill int bn, cn, tn, sn; 598264Sbill 599264Sbill /* 600266Sbill * Npf is the number of sectors transferred before the sector 601266Sbill * containing the ECC error, and reg is the UBA register 602266Sbill * mapping (the first part of) the transfer. 603266Sbill * O is offset within a memory page of the first byte transferred. 604264Sbill */ 605266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 606266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 607264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 608264Sbill printf("%D ", bp->b_blkno+npf); 609264Sbill prdev("ECC", bp->b_dev); 610264Sbill mask = up->upec2; 611264Sbill if (mask == 0) { 612266Sbill up->upof = FMT22; /* == RTC ???? */ 613264Sbill return (0); 614264Sbill } 615266Sbill /* 616266Sbill * Flush the buffered data path, and compute the 617266Sbill * byte and bit position of the error. The variable i 618266Sbill * is the byte offset in the transfer, the variable byte 619266Sbill * is the offset from a page boundary in main memory. 620266Sbill */ 621266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 622266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 623266Sbill bit = i&07; 624266Sbill i = (i&~07)>>3; 625264Sbill byte = i + o; 626266Sbill /* 627266Sbill * Correct while possible bits remain of mask. Since mask 628266Sbill * contains 11 bits, we continue while the bit offset is > -11. 629266Sbill * Also watch out for end of this block and the end of the whole 630266Sbill * transfer. 631266Sbill */ 632266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 633266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 634266Sbill (byte & PGOFSET); 635266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 636266Sbill byte++; 637266Sbill i++; 638266Sbill bit -= 8; 639264Sbill } 640266Sbill uptab.b_active++; /* Either complete or continuing... */ 641264Sbill if (up->upwc == 0) 642264Sbill return (0); 643266Sbill /* 644266Sbill * Have to continue the transfer... clear the drive, 645266Sbill * and compute the position where the transfer is to continue. 646266Sbill * We have completed npf+1 sectors of the transfer already; 647266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 648266Sbill */ 649266Sbill up->upcs1 = TRE|IE|DCLR|GO; 650264Sbill bn = dkblock(bp); 651264Sbill cn = bp->b_cylin; 652266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 653264Sbill tn = sn/NSECT; 654264Sbill sn %= NSECT; 655266Sbill cn += tn/NTRAC; 656266Sbill tn %= NTRAC; 657264Sbill up->updc = cn; 658266Sbill up->upda = (tn << 8) | sn; 659266Sbill ubaddr = (int)ptob(reg+1) + o; 660266Sbill up->upba = ubaddr; 661266Sbill cmd = (ubaddr >> 8) & 0x300; 662266Sbill cmd |= IE|GO|RCOM; 663266Sbill up->upcs1 = cmd; 664264Sbill return (1); 665264Sbill } 666286Sbill 667286Sbill /* 668286Sbill * Reset driver after UBA init. 669286Sbill * Cancel software state of all pending transfers 670286Sbill * and restart all units and the controller. 671286Sbill */ 672286Sbill upreset() 673286Sbill { 674286Sbill int unit; 675286Sbill 676286Sbill printf(" up"); 6771829Sbill DELAY(15000000); /* give it time to self-test */ 678286Sbill uptab.b_active = 0; 679286Sbill uptab.b_actf = uptab.b_actl = 0; 680286Sbill if (up_ubinfo) { 681286Sbill printf("<%d>", (up_ubinfo>>28)&0xf); 6822053Swnj ubarelse(&up_ubinfo); 683286Sbill } 684313Sbill UPADDR->upcs2 = CLR; /* clear controller */ 685286Sbill for (unit = 0; unit < NUP; unit++) { 686286Sbill uputab[unit].b_active = 0; 687286Sbill (void) upustart(unit); 688286Sbill } 689286Sbill (void) upstart(); 690286Sbill } 691313Sbill 692313Sbill /* 693313Sbill * Wake up every second and if an interrupt is pending 694313Sbill * but nothing has happened increment a counter. 695313Sbill * If nothing happens for 20 seconds, reset the controller 696313Sbill * and begin anew. 697313Sbill */ 698313Sbill upwatch() 699313Sbill { 700313Sbill int i; 701313Sbill 7021783Sbill timeout(upwatch, (caddr_t)0, HZ); 703313Sbill if (uptab.b_active == 0) { 704313Sbill for (i = 0; i < NUP; i++) 705313Sbill if (uputab[i].b_active) 706313Sbill goto active; 707313Sbill up_wticks = 0; /* idling */ 708313Sbill return; 709313Sbill } 710313Sbill active: 711313Sbill up_wticks++; 712313Sbill if (up_wticks >= 20) { 713313Sbill up_wticks = 0; 714313Sbill printf("LOST INTERRUPT RESET"); 715313Sbill upreset(); 716313Sbill printf("\n"); 717313Sbill } 718313Sbill } 719*2379Swnj 720*2379Swnj #define DBSIZE 20 721*2379Swnj 722*2379Swnj updump(dev) 723*2379Swnj dev_t dev; 724*2379Swnj { 725*2379Swnj struct device *upaddr; 726*2379Swnj char *start; 727*2379Swnj int num, blk, unit, nsect, ntrak, nspc; 728*2379Swnj struct size *sizes; 729*2379Swnj #if VAX==780 730*2379Swnj register struct uba_regs *up = (struct uba_regs *)PHYSUBA0; 731*2379Swnj register short *rp; 732*2379Swnj int bdp; 733*2379Swnj 734*2379Swnj up->uba_cr = ADINIT; 735*2379Swnj up->uba_cr = IFS|BRIE|USEFIE|SUEFIE; 736*2379Swnj while ((up->uba_cnfgr & UBIC) == 0) 737*2379Swnj ; 7381809Sbill #endif 739*2379Swnj DELAY(1000000); 740*2379Swnj while ((UPADDR->upcs1&DVA) == 0) 741*2379Swnj ; 742*2379Swnj num = maxfree; 743*2379Swnj start = 0; 744*2379Swnj unit = minor(dev) >> 3; 745*2379Swnj if (unit >= NUP) { 746*2379Swnj printf("bad unit\n"); 747*2379Swnj return (-1); 748*2379Swnj } 749*2379Swnj upaddr = UPPHYS; 750*2379Swnj upaddr->upcs2 = unit; 751*2379Swnj if ((upaddr->upds & VV) == 0) { 752*2379Swnj upaddr->upcs1 = DCLR|GO; 753*2379Swnj upaddr->upcs1 = PRESET|GO; 754*2379Swnj upaddr->upof = FMT22; 755*2379Swnj } 756*2379Swnj if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 757*2379Swnj printf("up !DPR || !MOL\n"); 758*2379Swnj return (-1); 759*2379Swnj } 760*2379Swnj nsect = NSECT; ntrak = NTRAC; sizes = up_sizes; 761*2379Swnj if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks) { 762*2379Swnj printf("dumplo+num, sizes %d %d\n", dumplo+num, sizes[minor(dev)&07].nblocks); 763*2379Swnj return (-1); 764*2379Swnj } 765*2379Swnj nspc = nsect * ntrak; 766*2379Swnj while (num > 0) { 767*2379Swnj register struct pte *io; 768*2379Swnj register int i; 769*2379Swnj int cn, sn, tn; 770*2379Swnj daddr_t bn; 771*2379Swnj 772*2379Swnj blk = num > DBSIZE ? DBSIZE : num; 773*2379Swnj bdp = 1; /* trick pcc */ 774*2379Swnj ((struct uba_regs *)PHYSUBA0)->uba_dpr[bdp] |= BNE; 775*2379Swnj io = ((struct uba_regs *)PHYSUBA0)->uba_map; 776*2379Swnj for (i = 0; i < blk; i++) 777*2379Swnj *(int *)io++ = (btop(start)+i) | (1<<21) | MRV; 778*2379Swnj *(int *)io = 0; 779*2379Swnj bn = dumplo + btop(start); 780*2379Swnj cn = bn/nspc + sizes[minor(dev)&07].cyloff; 781*2379Swnj sn = bn%nspc; 782*2379Swnj tn = sn/nsect; 783*2379Swnj sn = sn%nsect; 784*2379Swnj upaddr->updc = cn; 785*2379Swnj rp = (short *) &upaddr->upda; 786*2379Swnj *rp = (tn << 8) + sn; 787*2379Swnj *--rp = 0; 788*2379Swnj *--rp = -blk*NBPG / sizeof (short); 789*2379Swnj *--rp = GO|WCOM; 790*2379Swnj do { 791*2379Swnj DELAY(25); 792*2379Swnj } while ((upaddr->upcs1 & RDY) == 0); 793*2379Swnj if (upaddr->upcs1&ERR) { 794*2379Swnj printf("up dump dsk err: (%d,%d,%d) cs1=%x, er1=%x\n", 795*2379Swnj cn, tn, sn, upaddr->upcs1, upaddr->uper1); 796*2379Swnj return (-1); 797*2379Swnj } 798*2379Swnj start += blk*NBPG; 799*2379Swnj num -= blk; 800*2379Swnj } 801*2379Swnj bdp = 1; /* crud to fool c compiler */ 802*2379Swnj ((struct uba_regs *)PHYSUBA0)->uba_dpr[bdp] |= BNE; 803*2379Swnj return (0); 804*2379Swnj } 8051902Swnj #endif 806