1*1937Swnj /* up.c 4.7 12/19/80 */ 2264Sbill 3*1937Swnj #include "up.h" 41809Sbill #if NUP > 0 51933Swnj #if SC11 > 0 61933Swnj #include "../dev/up.c.SC11" 71902Swnj #else 8264Sbill /* 9885Sbill * UNIBUS disk driver with overlapped seeks and ECC recovery. 10264Sbill */ 111756Sbill #define DELAY(N) { register int d; d = N; while (--d > 0); } 12264Sbill 13264Sbill #include "../h/param.h" 14264Sbill #include "../h/systm.h" 15308Sbill #include "../h/dk.h" 16264Sbill #include "../h/buf.h" 17264Sbill #include "../h/conf.h" 18264Sbill #include "../h/dir.h" 19264Sbill #include "../h/user.h" 20264Sbill #include "../h/map.h" 21420Sbill #include "../h/pte.h" 22264Sbill #include "../h/mba.h" 23264Sbill #include "../h/mtpr.h" 24264Sbill #include "../h/uba.h" 25264Sbill #include "../h/vm.h" 26264Sbill 27264Sbill #define ushort unsigned short 28264Sbill 29264Sbill struct device 30264Sbill { 31264Sbill ushort upcs1; /* control and status register 1 */ 32264Sbill short upwc; /* word count register */ 33264Sbill ushort upba; /* UNIBUS address register */ 34264Sbill ushort upda; /* desired address register */ 35264Sbill ushort upcs2; /* control and status register 2 */ 36264Sbill ushort upds; /* drive Status */ 37264Sbill ushort uper1; /* error register 1 */ 38264Sbill ushort upas; /* attention summary */ 39264Sbill ushort upla; /* look ahead */ 40264Sbill ushort updb; /* data buffer */ 41264Sbill ushort upmr; /* maintenance */ 42264Sbill ushort updt; /* drive type */ 43264Sbill ushort upsn; /* serial number */ 44264Sbill ushort upof; /* offset register */ 45264Sbill ushort updc; /* desired cylinder address register */ 46264Sbill ushort upcc; /* current cylinder */ 47264Sbill ushort uper2; /* error register 2 */ 48264Sbill ushort uper3; /* error register 3 */ 49264Sbill ushort upec1; /* burst error bit position */ 50264Sbill ushort upec2; /* burst error bit pattern */ 51264Sbill }; 52264Sbill 53275Sbill /* 54275Sbill * Software extension to the upas register, so we can 55275Sbill * postpone starting SEARCH commands until the controller 56275Sbill * is not transferring. 57275Sbill */ 58341Sbill int upsoftas; 59275Sbill 60275Sbill /* 61275Sbill * If upseek then we don't issue SEARCH commands but rather just 62275Sbill * settle for a SEEK to the correct cylinder. 63275Sbill */ 64275Sbill int upseek; 65275Sbill 66264Sbill #define NSECT 32 67264Sbill #define NTRAC 19 68264Sbill 69264Sbill /* 70264Sbill * Constants controlling on-cylinder SEARCH usage. 71264Sbill * 72308Sbill * upSDIST/2 msec time needed to start transfer 73308Sbill * upRDIST/2 msec tolerable rotational latency when on-cylinder 74275Sbill * 75308Sbill * If we are no closer than upSDIST sectors and no further than upSDIST+upRDIST 76275Sbill * and in the driver then we take it as it is. Otherwise we do a SEARCH 77308Sbill * requesting an interrupt upSDIST sectors in advance. 78264Sbill */ 791592Sbill #define _upSDIST 2 /* 1.0 msec */ 801592Sbill #define _upRDIST 4 /* 2.0 msec */ 81264Sbill 82308Sbill int upSDIST = _upSDIST; 83308Sbill int upRDIST = _upRDIST; 84275Sbill 85264Sbill /* 86264Sbill * To fill a 300M drive: 87264Sbill * A is designed to be used as a root. 88264Sbill * B is suitable for a swap area. 89264Sbill * H is the primary storage area. 90264Sbill * On systems with RP06'es, we normally use only 291346 blocks of the H 91264Sbill * area, and use DEF or G to cover the rest of the drive. The C system 92264Sbill * covers the whole drive and can be used for pack-pack copying. 931756Sbill * 941756Sbill * Note: sizes here are for AMPEX drives with 815 cylinders. 951756Sbill * CDC drives can make the F,G, and H areas larger as they have 823 cylinders. 96264Sbill */ 97264Sbill struct size 98264Sbill { 99264Sbill daddr_t nblocks; 100264Sbill int cyloff; 101264Sbill } up_sizes[8] = { 102264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 103264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 104341Sbill 495520, 0, /* C=cyl 0 thru 814 */ 105264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 106264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 107264Sbill 81472, 681, /* F=cyl 681 thru 814 */ 108264Sbill 153824, 562, /* G=cyl 562 thru 814 */ 109264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 110264Sbill }; 111264Sbill 112264Sbill /* 113264Sbill * The following defines are used in offset positioning 114264Sbill * when trying to recover disk errors, with the constants being 115264Sbill * +/- microinches. Note that header compare inhibit (HCI) is not 116264Sbill * tried (this makes sense only during read, in any case.) 117264Sbill * 1181756Sbill * NB: Not all drives/controllers emulate all of these. 119264Sbill */ 120264Sbill #define P400 020 121264Sbill #define M400 0220 122264Sbill #define P800 040 123264Sbill #define M800 0240 124264Sbill #define P1200 060 125264Sbill #define M1200 0260 126264Sbill #define HCI 020000 127264Sbill 128264Sbill int up_offset[16] = 129264Sbill { 130264Sbill P400, M400, P400, M400, 131264Sbill P800, M800, P800, M800, 132264Sbill P1200, M1200, P1200, M1200, 133264Sbill 0, 0, 0, 0, 134264Sbill }; 135264Sbill 136264Sbill /* 137264Sbill * Each drive has a table uputab[i]. On this table are sorted the 138264Sbill * pending requests implementing an elevator algorithm (see dsort.c.) 139264Sbill * In the upustart() routine, each drive is independently advanced 140264Sbill * until it is on the desired cylinder for the next transfer and near 141264Sbill * the desired sector. The drive is then chained onto the uptab 142264Sbill * table, and the transfer is initiated by the upstart() routine. 143264Sbill * When the transfer is completed the driver reinvokes the upustart() 144264Sbill * routine to set up the next transfer. 145264Sbill */ 146264Sbill struct buf uptab; 147264Sbill struct buf uputab[NUP]; 148264Sbill 149264Sbill struct buf rupbuf; /* Buffer for raw i/o */ 150264Sbill 151264Sbill /* Drive commands, placed in upcs1 */ 152264Sbill #define GO 01 /* Go bit, set in all commands */ 153264Sbill #define PRESET 020 /* Preset drive at init or after errors */ 154264Sbill #define OFFSET 014 /* Offset heads to try to recover error */ 155264Sbill #define RTC 016 /* Return to center-line after OFFSET */ 156264Sbill #define SEARCH 030 /* Search for cylinder+sector */ 157275Sbill #define SEEK 04 /* Seek to cylinder */ 158264Sbill #define RECAL 06 /* Recalibrate, needed after seek error */ 159264Sbill #define DCLR 010 /* Drive clear, after error */ 160264Sbill #define WCOM 060 /* Write */ 161264Sbill #define RCOM 070 /* Read */ 162264Sbill 163264Sbill /* Other bits of upcs1 */ 164264Sbill #define IE 0100 /* Controller wide interrupt enable */ 165264Sbill #define TRE 040000 /* Transfer error */ 166345Sbill #define RDY 0200 /* Transfer terminated */ 167264Sbill 168264Sbill /* Drive status bits of upds */ 169264Sbill #define PIP 020000 /* Positioning in progress */ 170264Sbill #define ERR 040000 /* Error has occurred, DCLR necessary */ 171264Sbill #define VV 0100 /* Volume is valid, set by PRESET */ 172264Sbill #define DPR 0400 /* Drive has been preset */ 173264Sbill #define MOL 010000 /* Drive is online, heads loaded, etc */ 174264Sbill #define DRY 0200 /* Drive ready */ 175264Sbill 176313Sbill /* Bits of upcs2 */ 177313Sbill #define CLR 040 /* Controller clear */ 1781829Sbill #define MXF 01000 1791829Sbill #define NEM 04000 1801829Sbill 181264Sbill /* Bits of uper1 */ 182264Sbill #define DCK 0100000 /* Ecc error occurred */ 183264Sbill #define ECH 0100 /* Ecc error was unrecoverable */ 184264Sbill #define WLE 04000 /* Attempt to write read-only drive */ 185264Sbill 186264Sbill /* Bits of upof; the offset bits above are also in this register */ 187264Sbill #define FMT22 010000 /* 16 bits/word, must be always set */ 188264Sbill 189264Sbill #define b_cylin b_resid 190264Sbill 191264Sbill int up_ubinfo; /* Information about UBA usage saved here */ 192264Sbill 193313Sbill int up_wticks; /* Ticks waiting for interrupt */ 194313Sbill int upwstart; /* Have started guardian */ 195313Sbill int upwatch(); 196313Sbill 197264Sbill #ifdef INTRLVE 198264Sbill daddr_t dkblock(); 199264Sbill #endif 200264Sbill 201264Sbill /* 202264Sbill * Queue an i/o request for a drive, checking first that it is in range. 203264Sbill * 204264Sbill * A unit start is issued if the drive is inactive, causing 205264Sbill * a SEARCH for the correct cylinder/sector. If the drive is 206264Sbill * already nearly on the money and the controller is not transferring 207264Sbill * we kick it to start the transfer. 208264Sbill */ 209264Sbill upstrategy(bp) 210264Sbill register struct buf *bp; 211264Sbill { 212264Sbill register struct buf *dp; 213264Sbill register unit, xunit; 214264Sbill long sz, bn; 215264Sbill 216313Sbill if (upwstart == 0) { 2171783Sbill timeout(upwatch, (caddr_t)0, HZ); 218313Sbill upwstart++; 219313Sbill } 220264Sbill xunit = minor(bp->b_dev) & 077; 221264Sbill sz = bp->b_bcount; 222264Sbill sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ 223264Sbill unit = dkunit(bp); 224264Sbill if (unit >= NUP || 225264Sbill bp->b_blkno < 0 || 226264Sbill (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { 227264Sbill bp->b_flags |= B_ERROR; 228264Sbill iodone(bp); 229264Sbill return; 230264Sbill } 2311412Sbill if (DK_N+unit <= DK_NMAX) 2321412Sbill dk_mspw[DK_N+unit] = .0000020345; 233264Sbill bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; 234264Sbill dp = &uputab[unit]; 235264Sbill (void) spl5(); 236264Sbill disksort(dp, bp); 237264Sbill if (dp->b_active == 0) { 238268Sbill (void) upustart(unit); 239264Sbill if (uptab.b_actf && uptab.b_active == 0) 240268Sbill (void) upstart(); 241264Sbill } 242264Sbill (void) spl0(); 243264Sbill } 244264Sbill 245264Sbill /* 246264Sbill * Start activity on specified drive; called when drive is inactive 247264Sbill * and new transfer request arrives and also when upas indicates that 248264Sbill * a SEARCH command is complete. 249264Sbill */ 250264Sbill upustart(unit) 251264Sbill register unit; 252264Sbill { 253264Sbill register struct buf *bp, *dp; 254264Sbill register struct device *upaddr = UPADDR; 255264Sbill daddr_t bn; 256264Sbill int sn, cn, csn; 257268Sbill int didie = 0; 258264Sbill 259275Sbill /* 260275Sbill * Other drivers tend to say something like 261275Sbill * upaddr->upcs1 = IE; 262275Sbill * upaddr->upas = 1<<unit; 2631756Sbill * here, but some controllers will cancel a command 264275Sbill * happens to be sitting in the cs1 if you clear the go 2651756Sbill * bit by storing there (so the first is not safe). 266275Sbill * 267275Sbill * Thus we keep careful track of when we re-enable IE 268275Sbill * after an interrupt and do it only if we didn't issue 269275Sbill * a command which re-enabled it as a matter of course. 270275Sbill * We clear bits in upas in the interrupt routine, when 271275Sbill * no transfers are active. 272275Sbill */ 273266Sbill if (unit >= NUP) 274268Sbill goto out; 275264Sbill if (unit+DK_N <= DK_NMAX) 276264Sbill dk_busy &= ~(1<<(unit+DK_N)); 277264Sbill dp = &uputab[unit]; 278266Sbill if ((bp = dp->b_actf) == NULL) 279268Sbill goto out; 280275Sbill /* 2811756Sbill * Most controllers don't start SEARCH commands when transfers are 2821756Sbill * in progress. In fact, some tend to get confused when given 283275Sbill * SEARCH'es during transfers, generating interrupts with neither 284275Sbill * RDY nor a bit in the upas register. Thus we defer 285275Sbill * until an interrupt when a transfer is pending. 286275Sbill */ 287275Sbill if (uptab.b_active) { 288341Sbill upsoftas |= 1<<unit; 289275Sbill return (0); 290275Sbill } 291276Sbill if (dp->b_active) 292276Sbill goto done; 293276Sbill dp->b_active = 1; 2941756Sbill if ((upaddr->upcs2 & 07) != unit) 295264Sbill upaddr->upcs2 = unit; 296266Sbill /* 297266Sbill * If we have changed packs or just initialized, 298275Sbill * then the volume will not be valid; if so, clear 299266Sbill * the drive, preset it and put in 16bit/word mode. 300266Sbill */ 301266Sbill if ((upaddr->upds & VV) == 0) { 302266Sbill upaddr->upcs1 = IE|DCLR|GO; 303264Sbill upaddr->upcs1 = IE|PRESET|GO; 304264Sbill upaddr->upof = FMT22; 305268Sbill didie = 1; 306264Sbill } 307264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) 308275Sbill goto done; 309266Sbill /* 310266Sbill * Do enough of the disk address decoding to determine 311266Sbill * which cylinder and sector the request is on. 312266Sbill * If we are on the correct cylinder and the desired sector 313308Sbill * lies between upSDIST and upSDIST+upRDIST sectors ahead of us, then 314266Sbill * we don't bother to SEARCH but just begin the transfer asap. 315308Sbill * Otherwise ask for a interrupt upSDIST sectors ahead. 316266Sbill */ 317264Sbill bn = dkblock(bp); 318264Sbill cn = bp->b_cylin; 319264Sbill sn = bn%(NSECT*NTRAC); 320308Sbill sn = (sn+NSECT-upSDIST)%NSECT; 321264Sbill 322266Sbill if (cn - upaddr->updc) 323266Sbill goto search; /* Not on-cylinder */ 324275Sbill else if (upseek) 325275Sbill goto done; /* Ok just to be on-cylinder */ 326264Sbill csn = (upaddr->upla>>6) - sn - 1; 327266Sbill if (csn < 0) 328264Sbill csn += NSECT; 329308Sbill if (csn > NSECT-upRDIST) 330264Sbill goto done; 331264Sbill 332264Sbill search: 333264Sbill upaddr->updc = cn; 334275Sbill if (upseek) 335275Sbill upaddr->upcs1 = IE|SEEK|GO; 336275Sbill else { 337275Sbill upaddr->upda = sn; 338275Sbill upaddr->upcs1 = IE|SEARCH|GO; 339275Sbill } 340268Sbill didie = 1; 341266Sbill /* 342266Sbill * Mark this unit busy. 343266Sbill */ 344264Sbill unit += DK_N; 3451412Sbill if (unit <= DK_NMAX) { 346264Sbill dk_busy |= 1<<unit; 3471412Sbill dk_seek[unit]++; 348264Sbill } 349268Sbill goto out; 350264Sbill 351264Sbill done: 352266Sbill /* 353275Sbill * This unit is ready to go so 354275Sbill * link it onto the chain of ready disks. 355266Sbill */ 356264Sbill dp->b_forw = NULL; 357266Sbill if (uptab.b_actf == NULL) 358264Sbill uptab.b_actf = dp; 359264Sbill else 360264Sbill uptab.b_actl->b_forw = dp; 361264Sbill uptab.b_actl = dp; 362268Sbill 363268Sbill out: 364268Sbill return (didie); 365264Sbill } 366264Sbill 367264Sbill /* 368264Sbill * Start a transfer; call from top level at spl5() or on interrupt. 369264Sbill */ 370264Sbill upstart() 371264Sbill { 372264Sbill register struct buf *bp, *dp; 373264Sbill register unit; 374264Sbill register struct device *upaddr; 375264Sbill daddr_t bn; 376266Sbill int dn, sn, tn, cn, cmd; 377264Sbill 378264Sbill loop: 379266Sbill /* 380266Sbill * Pick a drive off the queue of ready drives, and 381266Sbill * perform the first transfer on its queue. 382266Sbill * 383266Sbill * Looping here is completely for the sake of drives which 384266Sbill * are not present and on-line, for which we completely clear the 385266Sbill * request queue. 386266Sbill */ 387273Sbill if ((dp = uptab.b_actf) == NULL) 388268Sbill return (0); 389264Sbill if ((bp = dp->b_actf) == NULL) { 390264Sbill uptab.b_actf = dp->b_forw; 391264Sbill goto loop; 392264Sbill } 393266Sbill /* 394266Sbill * Mark the controller busy, and multi-part disk address. 395266Sbill * Select the unit on which the i/o is to take place. 396266Sbill */ 397264Sbill uptab.b_active++; 398264Sbill unit = minor(bp->b_dev) & 077; 399264Sbill dn = dkunit(bp); 400264Sbill bn = dkblock(bp); 401264Sbill cn = up_sizes[unit&07].cyloff; 402264Sbill cn += bn/(NSECT*NTRAC); 403264Sbill sn = bn%(NSECT*NTRAC); 404264Sbill tn = sn/NSECT; 405266Sbill sn %= NSECT; 406264Sbill upaddr = UPADDR; 4071756Sbill if ((upaddr->upcs2 & 07) != dn) 408264Sbill upaddr->upcs2 = dn; 4091756Sbill up_ubinfo = ubasetup(bp, 1); 410266Sbill /* 411266Sbill * If drive is not present and on-line, then 412266Sbill * get rid of this with an error and loop to get 413266Sbill * rid of the rest of its queued requests. 414266Sbill * (Then on to any other ready drives.) 415266Sbill */ 416264Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 417893Sbill printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds); 418893Sbill if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { 419893Sbill printf("-- hard\n"); 420893Sbill uptab.b_active = 0; 421893Sbill uptab.b_errcnt = 0; 422893Sbill dp->b_actf = bp->av_forw; 423893Sbill dp->b_active = 0; 424893Sbill bp->b_flags |= B_ERROR; 425893Sbill iodone(bp); 426893Sbill /* A funny place to do this ... */ 427893Sbill ubafree(up_ubinfo), up_ubinfo = 0; 428893Sbill goto loop; 429893Sbill } 430893Sbill printf("-- came back\n"); 431264Sbill } 432266Sbill /* 433266Sbill * If this is a retry, then with the 16'th retry we 434266Sbill * begin to try offsetting the heads to recover the data. 435266Sbill */ 436924Sbill if (uptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) { 437264Sbill upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; 438266Sbill upaddr->upcs1 = IE|OFFSET|GO; 439266Sbill while (upaddr->upds & PIP) 440264Sbill DELAY(25); 441264Sbill } 442266Sbill /* 443266Sbill * Now set up the transfer, retrieving the high 444266Sbill * 2 bits of the UNIBUS address from the information 445266Sbill * returned by ubasetup() for the cs1 register bits 8 and 9. 446266Sbill */ 447264Sbill upaddr->updc = cn; 448264Sbill upaddr->upda = (tn << 8) + sn; 449264Sbill upaddr->upba = up_ubinfo; 450264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 451266Sbill cmd = (up_ubinfo >> 8) & 0x300; 452264Sbill if (bp->b_flags & B_READ) 453266Sbill cmd |= IE|RCOM|GO; 454264Sbill else 455266Sbill cmd |= IE|WCOM|GO; 456266Sbill upaddr->upcs1 = cmd; 457266Sbill /* 458266Sbill * This is a controller busy situation. 459266Sbill * Record in dk slot NUP+DK_N (after last drive) 460266Sbill * unless there aren't that many slots reserved for 461266Sbill * us in which case we record this as a drive busy 462266Sbill * (if there is room for that). 463266Sbill */ 464264Sbill unit = dn+DK_N; 465264Sbill if (unit <= DK_NMAX) { 466264Sbill dk_busy |= 1<<unit; 4671412Sbill dk_xfer[unit]++; 468264Sbill dk_wds[unit] += bp->b_bcount>>6; 469264Sbill } 470268Sbill return (1); 471264Sbill } 472264Sbill 473264Sbill /* 474264Sbill * Handle a device interrupt. 475264Sbill * 476264Sbill * If the transferring drive needs attention, service it 477264Sbill * retrying on error or beginning next transfer. 478264Sbill * Service all other ready drives, calling ustart to transfer 479264Sbill * their blocks to the ready queue in uptab, and then restart 480264Sbill * the controller if there is anything to do. 481264Sbill */ 482264Sbill upintr() 483264Sbill { 484264Sbill register struct buf *bp, *dp; 485264Sbill register unit; 486264Sbill register struct device *upaddr = UPADDR; 487264Sbill int as = upaddr->upas & 0377; 488341Sbill int oupsoftas; 489268Sbill int needie = 1; 490264Sbill 491276Sbill (void) spl6(); 492313Sbill up_wticks = 0; 493266Sbill if (uptab.b_active) { 494266Sbill /* 495266Sbill * The drive is transferring, thus the hardware 496266Sbill * (say the designers) will only interrupt when the transfer 497266Sbill * completes; check for it anyways. 498266Sbill */ 499266Sbill if ((upaddr->upcs1 & RDY) == 0) { 500272Sbill printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, 501272Sbill upaddr->upds, upaddr->upwc); 502341Sbill printf("as=%d act %d %d %d\n", as, uptab.b_active, 503341Sbill uputab[0].b_active, uputab[1].b_active); 504269Sbill } 505266Sbill /* 5061412Sbill * Mark drive not busy, and check for an 507266Sbill * error condition which may have resulted from the transfer. 508266Sbill */ 509264Sbill dp = uptab.b_actf; 510264Sbill bp = dp->b_actf; 511264Sbill unit = dkunit(bp); 5121412Sbill if (DK_N+unit <= DK_NMAX) 513264Sbill dk_busy &= ~(1<<(DK_N+unit)); 5141756Sbill if ((upaddr->upcs2 & 07) != unit) 515275Sbill upaddr->upcs2 = unit; 516885Sbill if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) { 5171829Sbill int cs2; 518266Sbill /* 519266Sbill * An error occurred, indeed. Select this unit 520266Sbill * to get at the drive status (a SEARCH may have 521266Sbill * intervened to change the selected unit), and 522266Sbill * wait for the command which caused the interrupt 523266Sbill * to complete (DRY). 524266Sbill */ 525266Sbill while ((upaddr->upds & DRY) == 0) 526264Sbill DELAY(25); 527266Sbill /* 528266Sbill * After 28 retries (16 w/o servo offsets, and then 529266Sbill * 12 with servo offsets), or if we encountered 530266Sbill * an error because the drive is write-protected, 531266Sbill * give up. Print an error message on the last 2 532266Sbill * retries before a hard failure. 533266Sbill */ 534266Sbill if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) 535264Sbill bp->b_flags |= B_ERROR; 536264Sbill else 537266Sbill uptab.b_active = 0; /* To force retry */ 538266Sbill if (uptab.b_errcnt > 27) 5391829Sbill cs2 = (int)upaddr->upcs2; 5401783Sbill deverror(bp, (int)upaddr->upcs2, 5411783Sbill (int)upaddr->uper1); 542266Sbill /* 543266Sbill * If this was a correctible ECC error, let upecc 544266Sbill * do the dirty work to correct it. If upecc 545266Sbill * starts another READ for the rest of the data 546266Sbill * then it returns 1 (having set uptab.b_active). 547266Sbill * Otherwise we are done and fall through to 548266Sbill * finish up. 549266Sbill */ 550266Sbill if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) 551266Sbill return; 552266Sbill /* 553266Sbill * Clear the drive and, every 4 retries, recalibrate 554266Sbill * to hopefully help clear up seek positioning problems. 555266Sbill */ 556264Sbill upaddr->upcs1 = TRE|IE|DCLR|GO; 557268Sbill needie = 0; 558266Sbill if ((uptab.b_errcnt&07) == 4) { 559264Sbill upaddr->upcs1 = RECAL|GO|IE; 560264Sbill while(upaddr->upds & PIP) 561264Sbill DELAY(25); 562264Sbill } 5631829Sbill if (uptab.b_errcnt == 28 && cs2&(NEM|MXF)) { 5641829Sbill printf("FLAKEY UP "); 5651829Sbill ubareset(); 5661829Sbill return; 5671829Sbill } 568264Sbill } 569266Sbill /* 570266Sbill * If we are still noted as active, then no 571266Sbill * (further) retries are necessary. 572266Sbill * 573266Sbill * Make sure the correct unit is selected, 574266Sbill * return it to centerline if necessary, and mark 575266Sbill * this i/o complete, starting the next transfer 576266Sbill * on this drive with the upustart routine (if any). 577266Sbill */ 578266Sbill if (uptab.b_active) { 579266Sbill if (uptab.b_errcnt >= 16) { 580266Sbill upaddr->upcs1 = RTC|GO|IE; 581266Sbill while (upaddr->upds & PIP) 582264Sbill DELAY(25); 583268Sbill needie = 0; 584264Sbill } 585264Sbill uptab.b_active = 0; 586264Sbill uptab.b_errcnt = 0; 587264Sbill uptab.b_actf = dp->b_forw; 588264Sbill dp->b_active = 0; 589264Sbill dp->b_errcnt = 0; 590264Sbill dp->b_actf = bp->av_forw; 591266Sbill bp->b_resid = (-upaddr->upwc * sizeof(short)); 592275Sbill if (bp->b_resid) 593341Sbill printf("resid %d ds %o er? %o %o %o\n", 594341Sbill bp->b_resid, upaddr->upds, 595275Sbill upaddr->uper1, upaddr->uper2, upaddr->uper3); 596264Sbill iodone(bp); 597264Sbill if(dp->b_actf) 598268Sbill if (upustart(unit)) 599268Sbill needie = 0; 600264Sbill } 601264Sbill as &= ~(1<<unit); 602341Sbill upsoftas &= ~(1<<unit); 603264Sbill ubafree(up_ubinfo), up_ubinfo = 0; 604273Sbill } else { 6051756Sbill if (upaddr->upcs1 & TRE) 606264Sbill upaddr->upcs1 = TRE; 607264Sbill } 608266Sbill /* 609266Sbill * If we have a unit with an outstanding SEARCH, 610266Sbill * and the hardware indicates the unit requires attention, 611266Sbill * the bring the drive to the ready queue. 612266Sbill * Finally, if the controller is not transferring 613266Sbill * start it if any drives are now ready to transfer. 614266Sbill */ 615341Sbill as |= upsoftas; 616341Sbill oupsoftas = upsoftas; 617341Sbill upsoftas = 0; 618266Sbill for (unit = 0; unit < NUP; unit++) 619341Sbill if ((as|oupsoftas) & (1<<unit)) { 6201756Sbill if (as & (1<<unit)) 621267Sbill upaddr->upas = 1<<unit; 622273Sbill if (upustart(unit)) 623273Sbill needie = 0; 624273Sbill } 625266Sbill if (uptab.b_actf && uptab.b_active == 0) 626268Sbill if (upstart()) 627268Sbill needie = 0; 628275Sbill if (needie) 629266Sbill upaddr->upcs1 = IE; 630264Sbill } 631264Sbill 632264Sbill upread(dev) 633264Sbill { 634264Sbill 635264Sbill physio(upstrategy, &rupbuf, dev, B_READ, minphys); 636264Sbill } 637264Sbill 638264Sbill upwrite(dev) 639264Sbill { 640264Sbill 641264Sbill physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); 642264Sbill } 643264Sbill 644266Sbill /* 645266Sbill * Correct an ECC error, and restart the i/o to complete 646266Sbill * the transfer if necessary. This is quite complicated because 647266Sbill * the transfer may be going to an odd memory address base and/or 648266Sbill * across a page boundary. 649266Sbill */ 650264Sbill upecc(up, bp) 651264Sbill register struct device *up; 652264Sbill register struct buf *bp; 653264Sbill { 654264Sbill struct uba_regs *ubp = (struct uba_regs *)UBA0; 655266Sbill register int i; 656264Sbill caddr_t addr; 657266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 658264Sbill int bn, cn, tn, sn; 659264Sbill 660264Sbill /* 661266Sbill * Npf is the number of sectors transferred before the sector 662266Sbill * containing the ECC error, and reg is the UBA register 663266Sbill * mapping (the first part of) the transfer. 664266Sbill * O is offset within a memory page of the first byte transferred. 665264Sbill */ 666266Sbill npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; 667266Sbill reg = btop(up_ubinfo&0x3ffff) + npf; 668264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 669264Sbill printf("%D ", bp->b_blkno+npf); 670264Sbill prdev("ECC", bp->b_dev); 671264Sbill mask = up->upec2; 672264Sbill if (mask == 0) { 673266Sbill up->upof = FMT22; /* == RTC ???? */ 674264Sbill return (0); 675264Sbill } 676266Sbill /* 677266Sbill * Flush the buffered data path, and compute the 678266Sbill * byte and bit position of the error. The variable i 679266Sbill * is the byte offset in the transfer, the variable byte 680266Sbill * is the offset from a page boundary in main memory. 681266Sbill */ 682266Sbill ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; 683266Sbill i = up->upec1 - 1; /* -1 makes 0 origin */ 684266Sbill bit = i&07; 685266Sbill i = (i&~07)>>3; 686264Sbill byte = i + o; 687266Sbill /* 688266Sbill * Correct while possible bits remain of mask. Since mask 689266Sbill * contains 11 bits, we continue while the bit offset is > -11. 690266Sbill * Also watch out for end of this block and the end of the whole 691266Sbill * transfer. 692266Sbill */ 693266Sbill while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 694266Sbill addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 695266Sbill (byte & PGOFSET); 696266Sbill putmemc(addr, getmemc(addr)^(mask<<bit)); 697266Sbill byte++; 698266Sbill i++; 699266Sbill bit -= 8; 700264Sbill } 701266Sbill uptab.b_active++; /* Either complete or continuing... */ 702264Sbill if (up->upwc == 0) 703264Sbill return (0); 704266Sbill /* 705266Sbill * Have to continue the transfer... clear the drive, 706266Sbill * and compute the position where the transfer is to continue. 707266Sbill * We have completed npf+1 sectors of the transfer already; 708266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 709266Sbill */ 710266Sbill up->upcs1 = TRE|IE|DCLR|GO; 711264Sbill bn = dkblock(bp); 712264Sbill cn = bp->b_cylin; 713266Sbill sn = bn%(NSECT*NTRAC) + npf + 1; 714264Sbill tn = sn/NSECT; 715264Sbill sn %= NSECT; 716266Sbill cn += tn/NTRAC; 717266Sbill tn %= NTRAC; 718264Sbill up->updc = cn; 719266Sbill up->upda = (tn << 8) | sn; 720266Sbill ubaddr = (int)ptob(reg+1) + o; 721266Sbill up->upba = ubaddr; 722266Sbill cmd = (ubaddr >> 8) & 0x300; 723266Sbill cmd |= IE|GO|RCOM; 724266Sbill up->upcs1 = cmd; 725264Sbill return (1); 726264Sbill } 727286Sbill 728286Sbill /* 729286Sbill * Reset driver after UBA init. 730286Sbill * Cancel software state of all pending transfers 731286Sbill * and restart all units and the controller. 732286Sbill */ 733286Sbill upreset() 734286Sbill { 735286Sbill int unit; 736286Sbill 737286Sbill printf(" up"); 7381829Sbill DELAY(15000000); /* give it time to self-test */ 739286Sbill uptab.b_active = 0; 740286Sbill uptab.b_actf = uptab.b_actl = 0; 741286Sbill if (up_ubinfo) { 742286Sbill printf("<%d>", (up_ubinfo>>28)&0xf); 743286Sbill ubafree(up_ubinfo), up_ubinfo = 0; 744286Sbill } 745313Sbill UPADDR->upcs2 = CLR; /* clear controller */ 746286Sbill for (unit = 0; unit < NUP; unit++) { 747286Sbill uputab[unit].b_active = 0; 748286Sbill (void) upustart(unit); 749286Sbill } 750286Sbill (void) upstart(); 751286Sbill } 752313Sbill 753313Sbill /* 754313Sbill * Wake up every second and if an interrupt is pending 755313Sbill * but nothing has happened increment a counter. 756313Sbill * If nothing happens for 20 seconds, reset the controller 757313Sbill * and begin anew. 758313Sbill */ 759313Sbill upwatch() 760313Sbill { 761313Sbill int i; 762313Sbill 7631783Sbill timeout(upwatch, (caddr_t)0, HZ); 764313Sbill if (uptab.b_active == 0) { 765313Sbill for (i = 0; i < NUP; i++) 766313Sbill if (uputab[i].b_active) 767313Sbill goto active; 768313Sbill up_wticks = 0; /* idling */ 769313Sbill return; 770313Sbill } 771313Sbill active: 772313Sbill up_wticks++; 773313Sbill if (up_wticks >= 20) { 774313Sbill up_wticks = 0; 775313Sbill printf("LOST INTERRUPT RESET"); 776313Sbill upreset(); 777313Sbill printf("\n"); 778313Sbill } 779313Sbill } 7801809Sbill #endif 7811902Swnj #endif 782