1*1829Sbill /* up.c 4.4 11/22/80 */ 2264Sbill 31563Sbill #include "../conf/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" 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 */ 175*1829Sbill #define MXF 01000 176*1829Sbill #define NEM 04000 177*1829Sbill 178264Sbill /* Bits of uper1 */ 179264Sbill #define DCK 0100000 /* Ecc error occurred */ 180264Sbill #define ECH 0100 /* Ecc error was unrecoverable */ 181264Sbill #define WLE 04000 /* Attempt to write read-only drive */ 182264Sbill 183264Sbill /* Bits of upof; the offset bits above are also in this register */ 184264Sbill #define FMT22 010000 /* 16 bits/word, must be always set */ 185264Sbill 186264Sbill #define b_cylin b_resid 187264Sbill 188264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 189264Sbill 190313Sbill int up_wticks; /* Ticks waiting for interrupt */ 191313Sbill int upwstart; /* Have started guardian */ 192313Sbill int upwatch(); 193313Sbill 194264Sbill #ifdef INTRLVE 195264Sbill daddr_t dkblock(); 196264Sbill #endif 197264Sbill 198264Sbill /* 199264Sbill * Queue an i/o request for a drive, checking first that it is in range. 200264Sbill * 201264Sbill * A unit start is issued if the drive is inactive, causing 202264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 203264Sbill * already nearly on the money and the controller is not transferring 204264Sbill * we kick it to start the transfer. 205264Sbill */ 206264Sbill upstrategy(bp) 207264Sbill register struct buf *bp; 208264Sbill { 209264Sbill register struct buf *dp; 210264Sbill register unit, xunit; 211264Sbill long sz, bn; 212264Sbill 213313Sbill if (upwstart == 0) { 2141783Sbill timeout(upwatch, (caddr_t)0, HZ); 215313Sbill upwstart++; 216313Sbill } 217264Sbill xunit = minor(bp->b_dev) & 077; 218264Sbill sz = bp->b_bcount; 219264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 220264Sbill unit = dkunit(bp); 221264Sbill if (unit >= NUP || 222264Sbill bp->b_blkno < 0 || 223264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 224264Sbill bp->b_flags |= B_ERROR; 225264Sbill iodone(bp); 226264Sbill return; 227264Sbill } 2281412Sbill if (DK_N+unit <= DK_NMAX) 2291412Sbill dk_mspw[DK_N+unit] = .0000020345; 230264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 231264Sbill dp = &uputab[unit]; 232264Sbill (void) spl5(); 233264Sbill disksort(dp, bp); 234264Sbill if (dp->b_active == 0) { 235268Sbill (void) upustart(unit); 236264Sbill if (uptab.b_actf && uptab.b_active == 0) 237268Sbill (void) upstart(); 238264Sbill } 239264Sbill (void) spl0(); 240264Sbill } 241264Sbill 242264Sbill /* 243264Sbill * Start activity on specified drive; called when drive is inactive 244264Sbill * and new transfer request arrives and also when upas indicates that 245264Sbill * a SEARCH command is complete. 246264Sbill */ 247264Sbill upustart(unit) 248264Sbill register unit; 249264Sbill { 250264Sbill register struct buf *bp, *dp; 251264Sbill register struct device *upaddr = UPADDR; 252264Sbill daddr_t bn; 253264Sbill int sn, cn, csn; 254268Sbill int didie = 0; 255264Sbill 256275Sbill /* 257275Sbill * Other drivers tend to say something like 258275Sbill * upaddr->upcs1 = IE; 259275Sbill * upaddr->upas = 1<<unit; 2601756Sbill * here, but some controllers will cancel a command 261275Sbill * happens to be sitting in the cs1 if you clear the go 2621756Sbill * bit by storing there (so the first is not safe). 263275Sbill * 264275Sbill * Thus we keep careful track of when we re-enable IE 265275Sbill * after an interrupt and do it only if we didn't issue 266275Sbill * a command which re-enabled it as a matter of course. 267275Sbill * We clear bits in upas in the interrupt routine, when 268275Sbill * no transfers are active. 269275Sbill */ 270266Sbill if (unit >= NUP) 271268Sbill goto out; 272264Sbill if (unit+DK_N <= DK_NMAX) 273264Sbill dk_busy &= ~(1<<(unit+DK_N)); 274264Sbill dp = &uputab[unit]; 275266Sbill if ((bp = dp->b_actf) == NULL) 276268Sbill goto out; 277275Sbill /* 2781756Sbill * Most controllers don't start SEARCH commands when transfers are 2791756Sbill * in progress. In fact, some tend to get confused when given 280275Sbill * SEARCH'es during transfers, generating interrupts with neither 281275Sbill * RDY nor a bit in the upas register. Thus we defer 282275Sbill * until an interrupt when a transfer is pending. 283275Sbill */ 284275Sbill if (uptab.b_active) { 285341Sbill upsoftas |= 1<<unit; 286275Sbill return (0); 287275Sbill } 288276Sbill if (dp->b_active) 289276Sbill goto done; 290276Sbill dp->b_active = 1; 2911756Sbill if ((upaddr->upcs2 & 07) != unit) 292264Sbill upaddr->upcs2 = unit; 293266Sbill /* 294266Sbill * If we have changed packs or just initialized, 295275Sbill * then the volume will not be valid; if so, clear 296266Sbill * the drive, preset it and put in 16bit/word mode. 297266Sbill */ 298266Sbill if ((upaddr->upds & VV) == 0) { 299266Sbill upaddr->upcs1 = IE|DCLR|GO; 300264Sbill upaddr->upcs1 = IE|PRESET|GO; 301264Sbill upaddr->upof = FMT22; 302268Sbill didie = 1; 303264Sbill } 304264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 305275Sbill goto done; 306266Sbill /* 307266Sbill * Do enough of the disk address decoding to determine 308266Sbill * which cylinder and sector the request is on. 309266Sbill * If we are on the correct cylinder and the desired sector 310308Sbill * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then 311266Sbill * we don't bother to SEARCH but just begin the transfer asap. 312308Sbill * Otherwise ask for a interrupt upSDIST sectors ahead. 313266Sbill */ 314264Sbill bn = dkblock(bp); 315264Sbill cn = bp->b_cylin; 316264Sbill sn = bn%(NSECT*NTRAC); 317308Sbill sn = (sn+NSECT-upSDIST)%NSECT; 318264Sbill 319266Sbill if (cn - upaddr->updc) 320266Sbill goto search; /* Not on-cylinder */ 321275Sbill else if (upseek) 322275Sbill goto done; /* Ok just to be on-cylinder */ 323264Sbill csn = (upaddr->upla>>6) - sn - 1; 324266Sbill if (csn < 0) 325264Sbill csn += NSECT; 326308Sbill if (csn > NSECT-upRDIST) 327264Sbill goto done; 328264Sbill 329264Sbill search: 330264Sbill upaddr->updc = cn; 331275Sbill if (upseek) 332275Sbill upaddr->upcs1 = IE|SEEK|GO; 333275Sbill else { 334275Sbill upaddr->upda = sn; 335275Sbill upaddr->upcs1 = IE|SEARCH|GO; 336275Sbill } 337268Sbill didie = 1; 338266Sbill /* 339266Sbill * Mark this unit busy. 340266Sbill */ 341264Sbill unit += DK_N; 3421412Sbill if (unit <= DK_NMAX) { 343264Sbill dk_busy |= 1<<unit; 3441412Sbill dk_seek[unit]++; 345264Sbill } 346268Sbill goto out; 347264Sbill 348264Sbill done: 349266Sbill /* 350275Sbill * This unit is ready to go so 351275Sbill * link it onto the chain of ready disks. 352266Sbill */ 353264Sbill dp->b_forw = NULL; 354266Sbill if (uptab.b_actf == NULL) 355264Sbill uptab.b_actf = dp; 356264Sbill else 357264Sbill uptab.b_actl->b_forw = dp; 358264Sbill uptab.b_actl = dp; 359268Sbill 360268Sbill out: 361268Sbill return (didie); 362264Sbill } 363264Sbill 364264Sbill /* 365264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 366264Sbill */ 367264Sbill upstart() 368264Sbill { 369264Sbill register struct buf *bp, *dp; 370264Sbill register unit; 371264Sbill register struct device *upaddr; 372264Sbill daddr_t bn; 373266Sbill int dn, sn, tn, cn, cmd; 374264Sbill 375264Sbill loop: 376266Sbill /* 377266Sbill * Pick a drive off the queue of ready drives, and 378266Sbill * perform the first transfer on its queue. 379266Sbill * 380266Sbill * Looping here is completely for the sake of drives which 381266Sbill * are not present and on-line, for which we completely clear the 382266Sbill * request queue. 383266Sbill */ 384273Sbill if ((dp = uptab.b_actf) == NULL) 385268Sbill return (0); 386264Sbill if ((bp = dp->b_actf) == NULL) { 387264Sbill uptab.b_actf = dp->b_forw; 388264Sbill goto loop; 389264Sbill } 390266Sbill /* 391266Sbill * Mark the controller busy, and multi-part disk address. 392266Sbill * Select the unit on which the i/o is to take place. 393266Sbill */ 394264Sbill uptab.b_active++; 395264Sbill unit = minor(bp->b_dev) & 077; 396264Sbill dn = dkunit(bp); 397264Sbill bn = dkblock(bp); 398264Sbill cn = up_sizes[unit&07].cyloff; 399264Sbill cn += bn/(NSECT*NTRAC); 400264Sbill sn = bn%(NSECT*NTRAC); 401264Sbill tn = sn/NSECT; 402266Sbill sn %= NSECT; 403264Sbill upaddr = UPADDR; 4041756Sbill if ((upaddr->upcs2 & 07) != dn) 405264Sbill upaddr->upcs2 = dn; 4061756Sbill up_ubinfo = ubasetup(bp, 1); 407266Sbill /* 408266Sbill * If drive is not present and on-line, then 409266Sbill * get rid of this with an error and loop to get 410266Sbill * rid of the rest of its queued requests. 411266Sbill * (Then on to any other ready drives.) 412266Sbill */ 413264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 414893Sbill printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds); 415893Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 416893Sbill printf("-- hard\n"); 417893Sbill uptab.b_active = 0; 418893Sbill uptab.b_errcnt = 0; 419893Sbill dp->b_actf = bp->av_forw; 420893Sbill dp->b_active = 0; 421893Sbill bp->b_flags |= B_ERROR; 422893Sbill iodone(bp); 423893Sbill /* A funny place to do this ... */ 424893Sbill ubafree(up_ubinfo), up_ubinfo = 0; 425893Sbill goto loop; 426893Sbill } 427893Sbill printf("-- came back\n"); 428264Sbill } 429266Sbill /* 430266Sbill * If this is a retry, then with the 16'th retry we 431266Sbill * begin to try offsetting the heads to recover the data. 432266Sbill */ 433924Sbill if (uptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) { 434264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 435266Sbill upaddr->upcs1 = IE|OFFSET|GO; 436266Sbill while (upaddr->upds & PIP) 437264Sbill DELAY(25); 438264Sbill } 439266Sbill /* 440266Sbill * Now set up the transfer, retrieving the high 441266Sbill * 2 bits of the UNIBUS address from the information 442266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 443266Sbill */ 444264Sbill upaddr->updc = cn; 445264Sbill upaddr->upda = (tn << 8) + sn; 446264Sbill upaddr->upba = up_ubinfo; 447264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 448266Sbill cmd = (up_ubinfo >> 8) & 0x300; 449264Sbill if (bp->b_flags & B_READ) 450266Sbill cmd |= IE|RCOM|GO; 451264Sbill else 452266Sbill cmd |= IE|WCOM|GO; 453266Sbill upaddr->upcs1 = cmd; 454266Sbill /* 455266Sbill * This is a controller busy situation. 456266Sbill * Record in dk slot NUP+DK_N (after last drive) 457266Sbill * unless there aren't that many slots reserved for 458266Sbill * us in which case we record this as a drive busy 459266Sbill * (if there is room for that). 460266Sbill */ 461264Sbill unit = dn+DK_N; 462264Sbill if (unit <= DK_NMAX) { 463264Sbill dk_busy |= 1<<unit; 4641412Sbill dk_xfer[unit]++; 465264Sbill dk_wds[unit] += bp->b_bcount>>6; 466264Sbill } 467268Sbill return (1); 468264Sbill } 469264Sbill 470264Sbill /* 471264Sbill * Handle a device interrupt. 472264Sbill * 473264Sbill * If the transferring drive needs attention, service it 474264Sbill * retrying on error or beginning next transfer. 475264Sbill * Service all other ready drives, calling ustart to transfer 476264Sbill * their blocks to the ready queue in uptab, and then restart 477264Sbill * the controller if there is anything to do. 478264Sbill */ 479264Sbill upintr() 480264Sbill { 481264Sbill register struct buf *bp, *dp; 482264Sbill register unit; 483264Sbill register struct device *upaddr = UPADDR; 484264Sbill int as = upaddr->upas & 0377; 485341Sbill int oupsoftas; 486268Sbill int needie = 1; 487264Sbill 488276Sbill (void) spl6(); 489313Sbill up_wticks = 0; 490266Sbill if (uptab.b_active) { 491266Sbill /* 492266Sbill * The drive is transferring, thus the hardware 493266Sbill * (say the designers) will only interrupt when the transfer 494266Sbill * completes; check for it anyways. 495266Sbill */ 496266Sbill if ((upaddr->upcs1 & RDY) == 0) { 497272Sbill printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, 498272Sbill upaddr->upds, upaddr->upwc); 499341Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, 500341Sbill uputab[0].b_active, uputab[1].b_active); 501269Sbill } 502266Sbill /* 5031412Sbill * Mark drive not busy, and check for an 504266Sbill * error condition which may have resulted from the transfer. 505266Sbill */ 506264Sbill dp = uptab.b_actf; 507264Sbill bp = dp->b_actf; 508264Sbill unit = dkunit(bp); 5091412Sbill if (DK_N+unit <= DK_NMAX) 510264Sbill dk_busy &= ~(1<<(DK_N+unit)); 5111756Sbill if ((upaddr->upcs2 & 07) != unit) 512275Sbill upaddr->upcs2 = unit; 513885Sbill if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) { 514*1829Sbill int cs2; 515266Sbill /* 516266Sbill * An error occurred, indeed. Select this unit 517266Sbill * to get at the drive status (a SEARCH may have 518266Sbill * intervened to change the selected unit), and 519266Sbill * wait for the command which caused the interrupt 520266Sbill * to complete (DRY). 521266Sbill */ 522266Sbill while ((upaddr->upds & DRY) == 0) 523264Sbill DELAY(25); 524266Sbill /* 525266Sbill * After 28 retries (16 w/o servo offsets, and then 526266Sbill * 12 with servo offsets), or if we encountered 527266Sbill * an error because the drive is write-protected, 528266Sbill * give up. Print an error message on the last 2 529266Sbill * retries before a hard failure. 530266Sbill */ 531266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 532264Sbill bp->b_flags |= B_ERROR; 533264Sbill else 534266Sbill uptab.b_active = 0; /* To force retry */ 535266Sbill if (uptab.b_errcnt > 27) 536*1829Sbill cs2 = (int)upaddr->upcs2; 5371783Sbill deverror(bp, (int)upaddr->upcs2, 5381783Sbill (int)upaddr->uper1); 539266Sbill /* 540266Sbill * If this was a correctible ECC error, let upecc 541266Sbill * do the dirty work to correct it. If upecc 542266Sbill * starts another READ for the rest of the data 543266Sbill * then it returns 1 (having set uptab.b_active). 544266Sbill * Otherwise we are done and fall through to 545266Sbill * finish up. 546266Sbill */ 547266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 548266Sbill return; 549266Sbill /* 550266Sbill * Clear the drive and, every 4 retries, recalibrate 551266Sbill * to hopefully help clear up seek positioning problems. 552266Sbill */ 553264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 554268Sbill needie = 0; 555266Sbill if ((uptab.b_errcnt&07) == 4) { 556264Sbill upaddr->upcs1 = RECAL|GO|IE; 557264Sbill while(upaddr->upds & PIP) 558264Sbill DELAY(25); 559264Sbill } 560*1829Sbill if (uptab.b_errcnt == 28 && cs2&(NEM|MXF)) { 561*1829Sbill printf("FLAKEY UP "); 562*1829Sbill ubareset(); 563*1829Sbill return; 564*1829Sbill } 565264Sbill } 566266Sbill /* 567266Sbill * If we are still noted as active, then no 568266Sbill * (further) retries are necessary. 569266Sbill * 570266Sbill * Make sure the correct unit is selected, 571266Sbill * return it to centerline if necessary, and mark 572266Sbill * this i/o complete, starting the next transfer 573266Sbill * on this drive with the upustart routine (if any). 574266Sbill */ 575266Sbill if (uptab.b_active) { 576266Sbill if (uptab.b_errcnt >= 16) { 577266Sbill upaddr->upcs1 = RTC|GO|IE; 578266Sbill while (upaddr->upds & PIP) 579264Sbill DELAY(25); 580268Sbill needie = 0; 581264Sbill } 582264Sbill uptab.b_active = 0; 583264Sbill uptab.b_errcnt = 0; 584264Sbill uptab.b_actf = dp->b_forw; 585264Sbill dp->b_active = 0; 586264Sbill dp->b_errcnt = 0; 587264Sbill dp->b_actf = bp->av_forw; 588266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 589275Sbill if (bp->b_resid) 590341Sbill printf("resid %d ds %o er? %o %o %o\n", 591341Sbill bp->b_resid, upaddr->upds, 592275Sbill upaddr->uper1, upaddr->uper2, upaddr->uper3); 593264Sbill iodone(bp); 594264Sbill if(dp->b_actf) 595268Sbill if (upustart(unit)) 596268Sbill needie = 0; 597264Sbill } 598264Sbill as &= ~(1<<unit); 599341Sbill upsoftas &= ~(1<<unit); 600264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 601273Sbill } else { 6021756Sbill if (upaddr->upcs1 & TRE) 603264Sbill upaddr->upcs1 = TRE; 604264Sbill } 605266Sbill /* 606266Sbill * If we have a unit with an outstanding SEARCH, 607266Sbill * and the hardware indicates the unit requires attention, 608266Sbill * the bring the drive to the ready queue. 609266Sbill * Finally, if the controller is not transferring 610266Sbill * start it if any drives are now ready to transfer. 611266Sbill */ 612341Sbill as |= upsoftas; 613341Sbill oupsoftas = upsoftas; 614341Sbill upsoftas = 0; 615266Sbill for (unit = 0; unit < NUP; unit++) 616341Sbill if ((as|oupsoftas) & (1<<unit)) { 6171756Sbill if (as & (1<<unit)) 618267Sbill upaddr->upas = 1<<unit; 619273Sbill if (upustart(unit)) 620273Sbill needie = 0; 621273Sbill } 622266Sbill if (uptab.b_actf && uptab.b_active == 0) 623268Sbill if (upstart()) 624268Sbill needie = 0; 625275Sbill if (needie) 626266Sbill upaddr->upcs1 = IE; 627264Sbill } 628264Sbill 629264Sbill upread(dev) 630264Sbill { 631264Sbill 632264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 633264Sbill } 634264Sbill 635264Sbill upwrite(dev) 636264Sbill { 637264Sbill 638264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 639264Sbill } 640264Sbill 641266Sbill /* 642266Sbill * Correct an ECC error, and restart the i/o to complete 643266Sbill * the transfer if necessary. This is quite complicated because 644266Sbill * the transfer may be going to an odd memory address base and/or 645266Sbill * across a page boundary. 646266Sbill */ 647264Sbill upecc(up, bp) 648264Sbill register struct device *up; 649264Sbill register struct buf *bp; 650264Sbill { 651264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 652266Sbill register int i; 653264Sbill caddr_t addr; 654266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 655264Sbill int bn, cn, tn, sn; 656264Sbill 657264Sbill /* 658266Sbill * Npf is the number of sectors transferred before the sector 659266Sbill * containing the ECC error, and reg is the UBA register 660266Sbill * mapping (the first part of) the transfer. 661266Sbill * O is offset within a memory page of the first byte transferred. 662264Sbill */ 663266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 664266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 665264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 666264Sbill printf("%D ", bp->b_blkno+npf); 667264Sbill prdev("ECC", bp->b_dev); 668264Sbill mask = up->upec2; 669264Sbill if (mask == 0) { 670266Sbill up->upof = FMT22; /* == RTC ???? */ 671264Sbill return (0); 672264Sbill } 673266Sbill /* 674266Sbill * Flush the buffered data path, and compute the 675266Sbill * byte and bit position of the error. The variable i 676266Sbill * is the byte offset in the transfer, the variable byte 677266Sbill * is the offset from a page boundary in main memory. 678266Sbill */ 679266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 680266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 681266Sbill bit = i&07; 682266Sbill i = (i&~07)>>3; 683264Sbill byte = i + o; 684266Sbill /* 685266Sbill * Correct while possible bits remain of mask. Since mask 686266Sbill * contains 11 bits, we continue while the bit offset is > -11. 687266Sbill * Also watch out for end of this block and the end of the whole 688266Sbill * transfer. 689266Sbill */ 690266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 691266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 692266Sbill (byte & PGOFSET); 693266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 694266Sbill byte++; 695266Sbill i++; 696266Sbill bit -= 8; 697264Sbill } 698266Sbill uptab.b_active++; /* Either complete or continuing... */ 699264Sbill if (up->upwc == 0) 700264Sbill return (0); 701266Sbill /* 702266Sbill * Have to continue the transfer... clear the drive, 703266Sbill * and compute the position where the transfer is to continue. 704266Sbill * We have completed npf+1 sectors of the transfer already; 705266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 706266Sbill */ 707266Sbill up->upcs1 = TRE|IE|DCLR|GO; 708264Sbill bn = dkblock(bp); 709264Sbill cn = bp->b_cylin; 710266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 711264Sbill tn = sn/NSECT; 712264Sbill sn %= NSECT; 713266Sbill cn += tn/NTRAC; 714266Sbill tn %= NTRAC; 715264Sbill up->updc = cn; 716266Sbill up->upda = (tn << 8) | sn; 717266Sbill ubaddr = (int)ptob(reg+1) + o; 718266Sbill up->upba = ubaddr; 719266Sbill cmd = (ubaddr >> 8) & 0x300; 720266Sbill cmd |= IE|GO|RCOM; 721266Sbill up->upcs1 = cmd; 722264Sbill return (1); 723264Sbill } 724286Sbill 725286Sbill /* 726286Sbill * Reset driver after UBA init. 727286Sbill * Cancel software state of all pending transfers 728286Sbill * and restart all units and the controller. 729286Sbill */ 730286Sbill upreset() 731286Sbill { 732286Sbill int unit; 733286Sbill 734286Sbill printf(" up"); 735*1829Sbill DELAY(15000000); /* give it time to self-test */ 736286Sbill uptab.b_active = 0; 737286Sbill uptab.b_actf = uptab.b_actl = 0; 738286Sbill if (up_ubinfo) { 739286Sbill printf("<%d>", (up_ubinfo>>28)&0xf); 740286Sbill ubafree(up_ubinfo), up_ubinfo = 0; 741286Sbill } 742313Sbill UPADDR->upcs2 = CLR; /* clear controller */ 743286Sbill for (unit = 0; unit < NUP; unit++) { 744286Sbill uputab[unit].b_active = 0; 745286Sbill (void) upustart(unit); 746286Sbill } 747286Sbill (void) upstart(); 748286Sbill } 749313Sbill 750313Sbill /* 751313Sbill * Wake up every second and if an interrupt is pending 752313Sbill * but nothing has happened increment a counter. 753313Sbill * If nothing happens for 20 seconds, reset the controller 754313Sbill * and begin anew. 755313Sbill */ 756313Sbill upwatch() 757313Sbill { 758313Sbill int i; 759313Sbill 7601783Sbill timeout(upwatch, (caddr_t)0, HZ); 761313Sbill if (uptab.b_active == 0) { 762313Sbill for (i = 0; i < NUP; i++) 763313Sbill if (uputab[i].b_active) 764313Sbill goto active; 765313Sbill up_wticks = 0; /* idling */ 766313Sbill return; 767313Sbill } 768313Sbill active: 769313Sbill up_wticks++; 770313Sbill if (up_wticks >= 20) { 771313Sbill up_wticks = 0; 772313Sbill printf("LOST INTERRUPT RESET"); 773313Sbill upreset(); 774313Sbill printf("\n"); 775313Sbill } 776313Sbill } 7771809Sbill #endif 778