1*1809Sbill /* up.c 4.3 11/13/80 */ 2264Sbill 31563Sbill #include "../conf/up.h" 4*1809Sbill #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" 23264Sbill 24264Sbill #define ushort unsigned short 25264Sbill 26264Sbill struct device 27264Sbill { 28264Sbill ushort upcs1; /* control and status register 1 */ 29264Sbill short upwc; /* word count register */ 30264Sbill ushort upba; /* UNIBUS address register */ 31264Sbill ushort upda; /* desired address register */ 32264Sbill ushort upcs2; /* control and status register 2 */ 33264Sbill ushort upds; /* drive Status */ 34264Sbill ushort uper1; /* error register 1 */ 35264Sbill ushort upas; /* attention summary */ 36264Sbill ushort upla; /* look ahead */ 37264Sbill ushort updb; /* data buffer */ 38264Sbill ushort upmr; /* maintenance */ 39264Sbill ushort updt; /* drive type */ 40264Sbill ushort upsn; /* serial number */ 41264Sbill ushort upof; /* offset register */ 42264Sbill ushort updc; /* desired cylinder address register */ 43264Sbill ushort upcc; /* current cylinder */ 44264Sbill ushort uper2; /* error register 2 */ 45264Sbill ushort uper3; /* error register 3 */ 46264Sbill ushort upec1; /* burst error bit position */ 47264Sbill ushort upec2; /* burst error bit pattern */ 48264Sbill }; 49264Sbill 50275Sbill /* 51275Sbill * Software extension to the upas register, so we can 52275Sbill * postpone starting SEARCH commands until the controller 53275Sbill * is not transferring. 54275Sbill */ 55341Sbill int upsoftas; 56275Sbill 57275Sbill /* 58275Sbill * If upseek then we don't issue SEARCH commands but rather just 59275Sbill * settle for a SEEK to the correct cylinder. 60275Sbill */ 61275Sbill int upseek; 62275Sbill 63264Sbill #define NSECT 32 64264Sbill #define NTRAC 19 65264Sbill 66264Sbill /* 67264Sbill * Constants controlling on-cylinder SEARCH usage. 68264Sbill * 69308Sbill * upSDIST/2 msec time needed to start transfer 70308Sbill * upRDIST/2 msec tolerable rotational latency when on-cylinder 71275Sbill * 72308Sbill * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST 73275Sbill * and in the driver then we take it as it is. Otherwise we do a SEARCH 74308Sbill * requesting an interrupt upSDIST sectors in advance. 75264Sbill */ 761592Sbill #define _upSDIST 2 /* 1.0 msec */ 771592Sbill #define _upRDIST 4 /* 2.0 msec */ 78264Sbill 79308Sbill int upSDIST = _upSDIST; 80308Sbill int upRDIST = _upRDIST; 81275Sbill 82264Sbill /* 83264Sbill * To fill a 300M drive: 84264Sbill * A is designed to be used as a root. 85264Sbill * B is suitable for a swap area. 86264Sbill * H is the primary storage area. 87264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 88264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 89264Sbill * covers the whole drive and can be used for pack-pack copying. 901756Sbill * 911756Sbill * Note: sizes here are for AMPEX drives with 815 cylinders. 921756Sbill * CDC drives can make the F,G, and H areas larger as they have 823 cylinders. 93264Sbill */ 94264Sbill struct size 95264Sbill { 96264Sbill daddr_t nblocks; 97264Sbill int cyloff; 98264Sbill } up_sizes[8] = { 99264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 100264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 101341Sbill 495520, 0, /* C=cyl 0 thru 814 */ 102264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 103264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 104264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 105264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 106264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 107264Sbill }; 108264Sbill 109264Sbill /* 110264Sbill * The following defines are used in offset positioning 111264Sbill * when trying to recover disk errors, with the constants being 112264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 113264Sbill * tried (this makes sense only during read, in any case.) 114264Sbill * 1151756Sbill * NB: Not all drives/controllers emulate all of these. 116264Sbill */ 117264Sbill #define P400 020 118264Sbill #define M400 0220 119264Sbill #define P800 040 120264Sbill #define M800 0240 121264Sbill #define P1200 060 122264Sbill #define M1200 0260 123264Sbill #define HCI 020000 124264Sbill 125264Sbill int up_offset[16] = 126264Sbill { 127264Sbill P400, M400, P400, M400, 128264Sbill P800, M800, P800, M800, 129264Sbill P1200, M1200, P1200, M1200, 130264Sbill 0, 0, 0, 0, 131264Sbill }; 132264Sbill 133264Sbill /* 134264Sbill * Each drive has a table uputab[i]. On this table are sorted the 135264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 136264Sbill * In the upustart() routine, each drive is independently advanced 137264Sbill * until it is on the desired cylinder for the next transfer and near 138264Sbill * the desired sector. The drive is then chained onto the uptab 139264Sbill * table, and the transfer is initiated by the upstart() routine. 140264Sbill * When the transfer is completed the driver reinvokes the upustart() 141264Sbill * routine to set up the next transfer. 142264Sbill */ 143264Sbill struct buf uptab; 144264Sbill struct buf uputab[NUP]; 145264Sbill 146264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 147264Sbill 148264Sbill /* Drive commands, placed in upcs1 */ 149264Sbill #define GO 01 /* Go bit, set in all commands */ 150264Sbill #define PRESET 020 /* Preset drive at init or after errors */ 151264Sbill #define OFFSET 014 /* Offset heads to try to recover error */ 152264Sbill #define RTC 016 /* Return to center-line after OFFSET */ 153264Sbill #define SEARCH 030 /* Search for cylinder+sector */ 154275Sbill #define SEEK 04 /* Seek to cylinder */ 155264Sbill #define RECAL 06 /* Recalibrate, needed after seek error */ 156264Sbill #define DCLR 010 /* Drive clear, after error */ 157264Sbill #define WCOM 060 /* Write */ 158264Sbill #define RCOM 070 /* Read */ 159264Sbill 160264Sbill /* Other bits of upcs1 */ 161264Sbill #define IE 0100 /* Controller wide interrupt enable */ 162264Sbill #define TRE 040000 /* Transfer error */ 163345Sbill #define RDY 0200 /* Transfer terminated */ 164264Sbill 165264Sbill /* Drive status bits of upds */ 166264Sbill #define PIP 020000 /* Positioning in progress */ 167264Sbill #define ERR 040000 /* Error has occurred, DCLR necessary */ 168264Sbill #define VV 0100 /* Volume is valid, set by PRESET */ 169264Sbill #define DPR 0400 /* Drive has been preset */ 170264Sbill #define MOL 010000 /* Drive is online, heads loaded, etc */ 171264Sbill #define DRY 0200 /* Drive ready */ 172264Sbill 173313Sbill /* Bits of upcs2 */ 174313Sbill #define CLR 040 /* Controller clear */ 175264Sbill /* Bits of uper1 */ 176264Sbill #define DCK 0100000 /* Ecc error occurred */ 177264Sbill #define ECH 0100 /* Ecc error was unrecoverable */ 178264Sbill #define WLE 04000 /* Attempt to write read-only drive */ 179264Sbill 180264Sbill /* Bits of upof; the offset bits above are also in this register */ 181264Sbill #define FMT22 010000 /* 16 bits/word, must be always set */ 182264Sbill 183264Sbill #define b_cylin b_resid 184264Sbill 185264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 186264Sbill 187313Sbill int up_wticks; /* Ticks waiting for interrupt */ 188313Sbill int upwstart; /* Have started guardian */ 189313Sbill int upwatch(); 190313Sbill 191264Sbill #ifdef INTRLVE 192264Sbill daddr_t dkblock(); 193264Sbill #endif 194264Sbill 195264Sbill /* 196264Sbill * Queue an i/o request for a drive, checking first that it is in range. 197264Sbill * 198264Sbill * A unit start is issued if the drive is inactive, causing 199264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 200264Sbill * already nearly on the money and the controller is not transferring 201264Sbill * we kick it to start the transfer. 202264Sbill */ 203264Sbill upstrategy(bp) 204264Sbill register struct buf *bp; 205264Sbill { 206264Sbill register struct buf *dp; 207264Sbill register unit, xunit; 208264Sbill long sz, bn; 209264Sbill 210313Sbill if (upwstart == 0) { 2111783Sbill timeout(upwatch, (caddr_t)0, HZ); 212313Sbill upwstart++; 213313Sbill } 214264Sbill xunit = minor(bp->b_dev) & 077; 215264Sbill sz = bp->b_bcount; 216264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 217264Sbill unit = dkunit(bp); 218264Sbill if (unit >= NUP || 219264Sbill bp->b_blkno < 0 || 220264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 221264Sbill bp->b_flags |= B_ERROR; 222264Sbill iodone(bp); 223264Sbill return; 224264Sbill } 2251412Sbill if (DK_N+unit <= DK_NMAX) 2261412Sbill dk_mspw[DK_N+unit] = .0000020345; 227264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 228264Sbill dp = &uputab[unit]; 229264Sbill (void) spl5(); 230264Sbill disksort(dp, bp); 231264Sbill if (dp->b_active == 0) { 232268Sbill (void) upustart(unit); 233264Sbill if (uptab.b_actf && uptab.b_active == 0) 234268Sbill (void) upstart(); 235264Sbill } 236264Sbill (void) spl0(); 237264Sbill } 238264Sbill 239264Sbill /* 240264Sbill * Start activity on specified drive; called when drive is inactive 241264Sbill * and new transfer request arrives and also when upas indicates that 242264Sbill * a SEARCH command is complete. 243264Sbill */ 244264Sbill upustart(unit) 245264Sbill register unit; 246264Sbill { 247264Sbill register struct buf *bp, *dp; 248264Sbill register struct device *upaddr = UPADDR; 249264Sbill daddr_t bn; 250264Sbill int sn, cn, csn; 251268Sbill int didie = 0; 252264Sbill 253275Sbill /* 254275Sbill * Other drivers tend to say something like 255275Sbill * upaddr->upcs1 = IE; 256275Sbill * upaddr->upas = 1<<unit; 2571756Sbill * here, but some controllers will cancel a command 258275Sbill * happens to be sitting in the cs1 if you clear the go 2591756Sbill * bit by storing there (so the first is not safe). 260275Sbill * 261275Sbill * Thus we keep careful track of when we re-enable IE 262275Sbill * after an interrupt and do it only if we didn't issue 263275Sbill * a command which re-enabled it as a matter of course. 264275Sbill * We clear bits in upas in the interrupt routine, when 265275Sbill * no transfers are active. 266275Sbill */ 267266Sbill if (unit >= NUP) 268268Sbill goto out; 269264Sbill if (unit+DK_N <= DK_NMAX) 270264Sbill dk_busy &= ~(1<<(unit+DK_N)); 271264Sbill dp = &uputab[unit]; 272266Sbill if ((bp = dp->b_actf) == NULL) 273268Sbill goto out; 274275Sbill /* 2751756Sbill * Most controllers don't start SEARCH commands when transfers are 2761756Sbill * in progress. In fact, some tend to get confused when given 277275Sbill * SEARCH'es during transfers, generating interrupts with neither 278275Sbill * RDY nor a bit in the upas register. Thus we defer 279275Sbill * until an interrupt when a transfer is pending. 280275Sbill */ 281275Sbill if (uptab.b_active) { 282341Sbill upsoftas |= 1<<unit; 283275Sbill return (0); 284275Sbill } 285276Sbill if (dp->b_active) 286276Sbill goto done; 287276Sbill dp->b_active = 1; 2881756Sbill if ((upaddr->upcs2 & 07) != unit) 289264Sbill upaddr->upcs2 = unit; 290266Sbill /* 291266Sbill * If we have changed packs or just initialized, 292275Sbill * then the volume will not be valid; if so, clear 293266Sbill * the drive, preset it and put in 16bit/word mode. 294266Sbill */ 295266Sbill if ((upaddr->upds & VV) == 0) { 296266Sbill upaddr->upcs1 = IE|DCLR|GO; 297264Sbill upaddr->upcs1 = IE|PRESET|GO; 298264Sbill upaddr->upof = FMT22; 299268Sbill didie = 1; 300264Sbill } 301264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 302275Sbill goto done; 303266Sbill /* 304266Sbill * Do enough of the disk address decoding to determine 305266Sbill * which cylinder and sector the request is on. 306266Sbill * If we are on the correct cylinder and the desired sector 307308Sbill * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then 308266Sbill * we don't bother to SEARCH but just begin the transfer asap. 309308Sbill * Otherwise ask for a interrupt upSDIST sectors ahead. 310266Sbill */ 311264Sbill bn = dkblock(bp); 312264Sbill cn = bp->b_cylin; 313264Sbill sn = bn%(NSECT*NTRAC); 314308Sbill sn = (sn+NSECT-upSDIST)%NSECT; 315264Sbill 316266Sbill if (cn - upaddr->updc) 317266Sbill goto search; /* Not on-cylinder */ 318275Sbill else if (upseek) 319275Sbill goto done; /* Ok just to be on-cylinder */ 320264Sbill csn = (upaddr->upla>>6) - sn - 1; 321266Sbill if (csn < 0) 322264Sbill csn += NSECT; 323308Sbill if (csn > NSECT-upRDIST) 324264Sbill goto done; 325264Sbill 326264Sbill search: 327264Sbill upaddr->updc = cn; 328275Sbill if (upseek) 329275Sbill upaddr->upcs1 = IE|SEEK|GO; 330275Sbill else { 331275Sbill upaddr->upda = sn; 332275Sbill upaddr->upcs1 = IE|SEARCH|GO; 333275Sbill } 334268Sbill didie = 1; 335266Sbill /* 336266Sbill * Mark this unit busy. 337266Sbill */ 338264Sbill unit += DK_N; 3391412Sbill if (unit <= DK_NMAX) { 340264Sbill dk_busy |= 1<<unit; 3411412Sbill dk_seek[unit]++; 342264Sbill } 343268Sbill goto out; 344264Sbill 345264Sbill done: 346266Sbill /* 347275Sbill * This unit is ready to go so 348275Sbill * link it onto the chain of ready disks. 349266Sbill */ 350264Sbill dp->b_forw = NULL; 351266Sbill if (uptab.b_actf == NULL) 352264Sbill uptab.b_actf = dp; 353264Sbill else 354264Sbill uptab.b_actl->b_forw = dp; 355264Sbill uptab.b_actl = dp; 356268Sbill 357268Sbill out: 358268Sbill return (didie); 359264Sbill } 360264Sbill 361264Sbill /* 362264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 363264Sbill */ 364264Sbill upstart() 365264Sbill { 366264Sbill register struct buf *bp, *dp; 367264Sbill register unit; 368264Sbill register struct device *upaddr; 369264Sbill daddr_t bn; 370266Sbill int dn, sn, tn, cn, cmd; 371264Sbill 372264Sbill loop: 373266Sbill /* 374266Sbill * Pick a drive off the queue of ready drives, and 375266Sbill * perform the first transfer on its queue. 376266Sbill * 377266Sbill * Looping here is completely for the sake of drives which 378266Sbill * are not present and on-line, for which we completely clear the 379266Sbill * request queue. 380266Sbill */ 381273Sbill if ((dp = uptab.b_actf) == NULL) 382268Sbill return (0); 383264Sbill if ((bp = dp->b_actf) == NULL) { 384264Sbill uptab.b_actf = dp->b_forw; 385264Sbill goto loop; 386264Sbill } 387266Sbill /* 388266Sbill * Mark the controller busy, and multi-part disk address. 389266Sbill * Select the unit on which the i/o is to take place. 390266Sbill */ 391264Sbill uptab.b_active++; 392264Sbill unit = minor(bp->b_dev) & 077; 393264Sbill dn = dkunit(bp); 394264Sbill bn = dkblock(bp); 395264Sbill cn = up_sizes[unit&07].cyloff; 396264Sbill cn += bn/(NSECT*NTRAC); 397264Sbill sn = bn%(NSECT*NTRAC); 398264Sbill tn = sn/NSECT; 399266Sbill sn %= NSECT; 400264Sbill upaddr = UPADDR; 4011756Sbill if ((upaddr->upcs2 & 07) != dn) 402264Sbill upaddr->upcs2 = dn; 4031756Sbill up_ubinfo = ubasetup(bp, 1); 404266Sbill /* 405266Sbill * If drive is not present and on-line, then 406266Sbill * get rid of this with an error and loop to get 407266Sbill * rid of the rest of its queued requests. 408266Sbill * (Then on to any other ready drives.) 409266Sbill */ 410264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 411893Sbill printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds); 412893Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 413893Sbill printf("-- hard\n"); 414893Sbill uptab.b_active = 0; 415893Sbill uptab.b_errcnt = 0; 416893Sbill dp->b_actf = bp->av_forw; 417893Sbill dp->b_active = 0; 418893Sbill bp->b_flags |= B_ERROR; 419893Sbill iodone(bp); 420893Sbill /* A funny place to do this ... */ 421893Sbill ubafree(up_ubinfo), up_ubinfo = 0; 422893Sbill goto loop; 423893Sbill } 424893Sbill printf("-- came back\n"); 425264Sbill } 426266Sbill /* 427266Sbill * If this is a retry, then with the 16'th retry we 428266Sbill * begin to try offsetting the heads to recover the data. 429266Sbill */ 430924Sbill if (uptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) { 431264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 432266Sbill upaddr->upcs1 = IE|OFFSET|GO; 433266Sbill while (upaddr->upds & PIP) 434264Sbill DELAY(25); 435264Sbill } 436266Sbill /* 437266Sbill * Now set up the transfer, retrieving the high 438266Sbill * 2 bits of the UNIBUS address from the information 439266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 440266Sbill */ 441264Sbill upaddr->updc = cn; 442264Sbill upaddr->upda = (tn << 8) + sn; 443264Sbill upaddr->upba = up_ubinfo; 444264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 445266Sbill cmd = (up_ubinfo >> 8) & 0x300; 446264Sbill if (bp->b_flags & B_READ) 447266Sbill cmd |= IE|RCOM|GO; 448264Sbill else 449266Sbill cmd |= IE|WCOM|GO; 450266Sbill upaddr->upcs1 = cmd; 451266Sbill /* 452266Sbill * This is a controller busy situation. 453266Sbill * Record in dk slot NUP+DK_N (after last drive) 454266Sbill * unless there aren't that many slots reserved for 455266Sbill * us in which case we record this as a drive busy 456266Sbill * (if there is room for that). 457266Sbill */ 458264Sbill unit = dn+DK_N; 459264Sbill if (unit <= DK_NMAX) { 460264Sbill dk_busy |= 1<<unit; 4611412Sbill dk_xfer[unit]++; 462264Sbill dk_wds[unit] += bp->b_bcount>>6; 463264Sbill } 464268Sbill return (1); 465264Sbill } 466264Sbill 467264Sbill /* 468264Sbill * Handle a device interrupt. 469264Sbill * 470264Sbill * If the transferring drive needs attention, service it 471264Sbill * retrying on error or beginning next transfer. 472264Sbill * Service all other ready drives, calling ustart to transfer 473264Sbill * their blocks to the ready queue in uptab, and then restart 474264Sbill * the controller if there is anything to do. 475264Sbill */ 476264Sbill upintr() 477264Sbill { 478264Sbill register struct buf *bp, *dp; 479264Sbill register unit; 480264Sbill register struct device *upaddr = UPADDR; 481264Sbill int as = upaddr->upas & 0377; 482341Sbill int oupsoftas; 483268Sbill int needie = 1; 484264Sbill 485276Sbill (void) spl6(); 486313Sbill up_wticks = 0; 487266Sbill if (uptab.b_active) { 488266Sbill /* 489266Sbill * The drive is transferring, thus the hardware 490266Sbill * (say the designers) will only interrupt when the transfer 491266Sbill * completes; check for it anyways. 492266Sbill */ 493266Sbill if ((upaddr->upcs1 & RDY) == 0) { 494272Sbill printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, 495272Sbill upaddr->upds, upaddr->upwc); 496341Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, 497341Sbill uputab[0].b_active, uputab[1].b_active); 498269Sbill } 499266Sbill /* 5001412Sbill * Mark drive not busy, and check for an 501266Sbill * error condition which may have resulted from the transfer. 502266Sbill */ 503264Sbill dp = uptab.b_actf; 504264Sbill bp = dp->b_actf; 505264Sbill unit = dkunit(bp); 5061412Sbill if (DK_N+unit <= DK_NMAX) 507264Sbill dk_busy &= ~(1<<(DK_N+unit)); 5081756Sbill if ((upaddr->upcs2 & 07) != unit) 509275Sbill upaddr->upcs2 = unit; 510885Sbill if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) { 511266Sbill /* 512266Sbill * An error occurred, indeed. Select this unit 513266Sbill * to get at the drive status (a SEARCH may have 514266Sbill * intervened to change the selected unit), and 515266Sbill * wait for the command which caused the interrupt 516266Sbill * to complete (DRY). 517266Sbill */ 518266Sbill while ((upaddr->upds & DRY) == 0) 519264Sbill DELAY(25); 520266Sbill /* 521266Sbill * After 28 retries (16 w/o servo offsets, and then 522266Sbill * 12 with servo offsets), or if we encountered 523266Sbill * an error because the drive is write-protected, 524266Sbill * give up. Print an error message on the last 2 525266Sbill * retries before a hard failure. 526266Sbill */ 527266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 528264Sbill bp->b_flags |= B_ERROR; 529264Sbill else 530266Sbill uptab.b_active = 0; /* To force retry */ 531266Sbill if (uptab.b_errcnt > 27) 5321783Sbill deverror(bp, (int)upaddr->upcs2, 5331783Sbill (int)upaddr->uper1); 534266Sbill /* 535266Sbill * If this was a correctible ECC error, let upecc 536266Sbill * do the dirty work to correct it. If upecc 537266Sbill * starts another READ for the rest of the data 538266Sbill * then it returns 1 (having set uptab.b_active). 539266Sbill * Otherwise we are done and fall through to 540266Sbill * finish up. 541266Sbill */ 542266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 543266Sbill return; 544266Sbill /* 545266Sbill * Clear the drive and, every 4 retries, recalibrate 546266Sbill * to hopefully help clear up seek positioning problems. 547266Sbill */ 548264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 549268Sbill needie = 0; 550266Sbill if ((uptab.b_errcnt&07) == 4) { 551264Sbill upaddr->upcs1 = RECAL|GO|IE; 552264Sbill while(upaddr->upds & PIP) 553264Sbill DELAY(25); 554264Sbill } 555264Sbill } 556266Sbill /* 557266Sbill * If we are still noted as active, then no 558266Sbill * (further) retries are necessary. 559266Sbill * 560266Sbill * Make sure the correct unit is selected, 561266Sbill * return it to centerline if necessary, and mark 562266Sbill * this i/o complete, starting the next transfer 563266Sbill * on this drive with the upustart routine (if any). 564266Sbill */ 565266Sbill if (uptab.b_active) { 566266Sbill if (uptab.b_errcnt >= 16) { 567266Sbill upaddr->upcs1 = RTC|GO|IE; 568266Sbill while (upaddr->upds & PIP) 569264Sbill DELAY(25); 570268Sbill needie = 0; 571264Sbill } 572264Sbill uptab.b_active = 0; 573264Sbill uptab.b_errcnt = 0; 574264Sbill uptab.b_actf = dp->b_forw; 575264Sbill dp->b_active = 0; 576264Sbill dp->b_errcnt = 0; 577264Sbill dp->b_actf = bp->av_forw; 578266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 579275Sbill if (bp->b_resid) 580341Sbill printf("resid %d ds %o er? %o %o %o\n", 581341Sbill bp->b_resid, upaddr->upds, 582275Sbill upaddr->uper1, upaddr->uper2, upaddr->uper3); 583264Sbill iodone(bp); 584264Sbill if(dp->b_actf) 585268Sbill if (upustart(unit)) 586268Sbill needie = 0; 587264Sbill } 588264Sbill as &= ~(1<<unit); 589341Sbill upsoftas &= ~(1<<unit); 590264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 591273Sbill } else { 5921756Sbill if (upaddr->upcs1 & TRE) 593264Sbill upaddr->upcs1 = TRE; 594264Sbill } 595266Sbill /* 596266Sbill * If we have a unit with an outstanding SEARCH, 597266Sbill * and the hardware indicates the unit requires attention, 598266Sbill * the bring the drive to the ready queue. 599266Sbill * Finally, if the controller is not transferring 600266Sbill * start it if any drives are now ready to transfer. 601266Sbill */ 602341Sbill as |= upsoftas; 603341Sbill oupsoftas = upsoftas; 604341Sbill upsoftas = 0; 605266Sbill for (unit = 0; unit < NUP; unit++) 606341Sbill if ((as|oupsoftas) & (1<<unit)) { 6071756Sbill if (as & (1<<unit)) 608267Sbill upaddr->upas = 1<<unit; 609273Sbill if (upustart(unit)) 610273Sbill needie = 0; 611273Sbill } 612266Sbill if (uptab.b_actf && uptab.b_active == 0) 613268Sbill if (upstart()) 614268Sbill needie = 0; 615275Sbill if (needie) 616266Sbill upaddr->upcs1 = IE; 617264Sbill } 618264Sbill 619264Sbill upread(dev) 620264Sbill { 621264Sbill 622264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 623264Sbill } 624264Sbill 625264Sbill upwrite(dev) 626264Sbill { 627264Sbill 628264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 629264Sbill } 630264Sbill 631266Sbill /* 632266Sbill * Correct an ECC error, and restart the i/o to complete 633266Sbill * the transfer if necessary. This is quite complicated because 634266Sbill * the transfer may be going to an odd memory address base and/or 635266Sbill * across a page boundary. 636266Sbill */ 637264Sbill upecc(up, bp) 638264Sbill register struct device *up; 639264Sbill register struct buf *bp; 640264Sbill { 641264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 642266Sbill register int i; 643264Sbill caddr_t addr; 644266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 645264Sbill int bn, cn, tn, sn; 646264Sbill 647264Sbill /* 648266Sbill * Npf is the number of sectors transferred before the sector 649266Sbill * containing the ECC error, and reg is the UBA register 650266Sbill * mapping (the first part of) the transfer. 651266Sbill * O is offset within a memory page of the first byte transferred. 652264Sbill */ 653266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 654266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 655264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 656264Sbill printf("%D ", bp->b_blkno+npf); 657264Sbill prdev("ECC", bp->b_dev); 658264Sbill mask = up->upec2; 659264Sbill if (mask == 0) { 660266Sbill up->upof = FMT22; /* == RTC ???? */ 661264Sbill return (0); 662264Sbill } 663266Sbill /* 664266Sbill * Flush the buffered data path, and compute the 665266Sbill * byte and bit position of the error. The variable i 666266Sbill * is the byte offset in the transfer, the variable byte 667266Sbill * is the offset from a page boundary in main memory. 668266Sbill */ 669266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 670266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 671266Sbill bit = i&07; 672266Sbill i = (i&~07)>>3; 673264Sbill byte = i + o; 674266Sbill /* 675266Sbill * Correct while possible bits remain of mask. Since mask 676266Sbill * contains 11 bits, we continue while the bit offset is > -11. 677266Sbill * Also watch out for end of this block and the end of the whole 678266Sbill * transfer. 679266Sbill */ 680266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 681266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 682266Sbill (byte & PGOFSET); 683266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 684266Sbill byte++; 685266Sbill i++; 686266Sbill bit -= 8; 687264Sbill } 688266Sbill uptab.b_active++; /* Either complete or continuing... */ 689264Sbill if (up->upwc == 0) 690264Sbill return (0); 691266Sbill /* 692266Sbill * Have to continue the transfer... clear the drive, 693266Sbill * and compute the position where the transfer is to continue. 694266Sbill * We have completed npf+1 sectors of the transfer already; 695266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 696266Sbill */ 697266Sbill up->upcs1 = TRE|IE|DCLR|GO; 698264Sbill bn = dkblock(bp); 699264Sbill cn = bp->b_cylin; 700266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 701264Sbill tn = sn/NSECT; 702264Sbill sn %= NSECT; 703266Sbill cn += tn/NTRAC; 704266Sbill tn %= NTRAC; 705264Sbill up->updc = cn; 706266Sbill up->upda = (tn << 8) | sn; 707266Sbill ubaddr = (int)ptob(reg+1) + o; 708266Sbill up->upba = ubaddr; 709266Sbill cmd = (ubaddr >> 8) & 0x300; 710266Sbill cmd |= IE|GO|RCOM; 711266Sbill up->upcs1 = cmd; 712264Sbill return (1); 713264Sbill } 714286Sbill 715286Sbill /* 716286Sbill * Reset driver after UBA init. 717286Sbill * Cancel software state of all pending transfers 718286Sbill * and restart all units and the controller. 719286Sbill */ 720286Sbill upreset() 721286Sbill { 722286Sbill int unit; 723286Sbill 724286Sbill printf(" up"); 725286Sbill uptab.b_active = 0; 726286Sbill uptab.b_actf = uptab.b_actl = 0; 727286Sbill if (up_ubinfo) { 728286Sbill printf("<%d>", (up_ubinfo>>28)&0xf); 729286Sbill ubafree(up_ubinfo), up_ubinfo = 0; 730286Sbill } 731313Sbill UPADDR->upcs2 = CLR; /* clear controller */ 732286Sbill for (unit = 0; unit < NUP; unit++) { 733286Sbill uputab[unit].b_active = 0; 734286Sbill (void) upustart(unit); 735286Sbill } 736286Sbill (void) upstart(); 737286Sbill } 738313Sbill 739313Sbill /* 740313Sbill * Wake up every second and if an interrupt is pending 741313Sbill * but nothing has happened increment a counter. 742313Sbill * If nothing happens for 20 seconds, reset the controller 743313Sbill * and begin anew. 744313Sbill */ 745313Sbill upwatch() 746313Sbill { 747313Sbill int i; 748313Sbill 7491783Sbill timeout(upwatch, (caddr_t)0, HZ); 750313Sbill if (uptab.b_active == 0) { 751313Sbill for (i = 0; i < NUP; i++) 752313Sbill if (uputab[i].b_active) 753313Sbill goto active; 754313Sbill up_wticks = 0; /* idling */ 755313Sbill return; 756313Sbill } 757313Sbill active: 758313Sbill up_wticks++; 759313Sbill if (up_wticks >= 20) { 760313Sbill up_wticks = 0; 761313Sbill printf("LOST INTERRUPT RESET"); 762313Sbill upreset(); 763313Sbill printf("\n"); 764313Sbill } 765313Sbill } 766*1809Sbill #endif 767