| 1 | /* %H% 3.12 %G% */ |
| 2 | |
| 3 | #define spl5 spl6 |
| 4 | /* |
| 5 | * Emulex UNIBUS disk driver with overlapped seeks and ECC recovery. |
| 6 | * |
| 7 | * NB: This device is very sensitive: be aware that the code is the way |
| 8 | * it is for good reason and that there are delay loops here which may |
| 9 | * have to be lengthened if your processor is faster and which should |
| 10 | * probably be shortened if your processor is slower. |
| 11 | * |
| 12 | * This driver has been tested on a SC-11B Controller, configured |
| 13 | * with the following internal switch settings: |
| 14 | * SW1-1 5/19 surfaces (off, 19 surfaces on Ampex 9300) |
| 15 | * SW1-2 chksum enable (off, checksum disabled) |
| 16 | * SW1-3 volume select (off, 815 cylinders) |
| 17 | * SW1-4 sector select (on, 32 sectors) |
| 18 | * SW1-5 unused (off) |
| 19 | * SW1-6 port select (on, single port) |
| 20 | * SW1-7 npr delay (off, disable) |
| 21 | * SW1-8 ecc test mode (off, disable) |
| 22 | * and top mounted switches: |
| 23 | * SW2-1 extend opcodes (off=open, disable) |
| 24 | * SW2-2 extend diag (off=open, disable) |
| 25 | * SW2-3 4 wd dma burst (off=open, disable) |
| 26 | * SW2-4 unused (off=open) |
| 27 | * |
| 28 | * The controller transfers data much more rapidly with SW2-3 set, |
| 29 | * but we have previously experienced problems with it set this way. |
| 30 | * We intend to try this again in the near future. |
| 31 | * |
| 32 | * wnj June 14, 1980 |
| 33 | */ |
| 34 | |
| 35 | #include "../h/param.h" |
| 36 | #include "../h/systm.h" |
| 37 | #include "../h/buf.h" |
| 38 | #include "../h/conf.h" |
| 39 | #include "../h/dir.h" |
| 40 | #include "../h/user.h" |
| 41 | #include "../h/map.h" |
| 42 | #include "../h/mba.h" |
| 43 | #include "../h/mtpr.h" |
| 44 | #include "../h/pte.h" |
| 45 | #include "../h/uba.h" |
| 46 | #include "../h/vm.h" |
| 47 | |
| 48 | /* |
| 49 | * Define number of drives, and range of sampling information to be used. |
| 50 | * |
| 51 | * Normally, DK_N .. DK_N+NUP-1 gather individual drive stats, |
| 52 | * and DK_N+NUP gathers controller transferring stats. |
| 53 | * |
| 54 | * If DK_N+NUP > DK_NMAX, then transfer stats are divided per drive. |
| 55 | * If DK_NMAX is yet smaller, some drives are not monitored. |
| 56 | */ |
| 57 | #define DK_N 1 |
| 58 | #define DK_NMAX 2 |
| 59 | |
| 60 | #define ushort unsigned short |
| 61 | |
| 62 | struct device |
| 63 | { |
| 64 | ushort upcs1; /* control and status register 1 */ |
| 65 | short upwc; /* word count register */ |
| 66 | ushort upba; /* UNIBUS address register */ |
| 67 | ushort upda; /* desired address register */ |
| 68 | ushort upcs2; /* control and status register 2 */ |
| 69 | ushort upds; /* drive Status */ |
| 70 | ushort uper1; /* error register 1 */ |
| 71 | ushort upas; /* attention summary */ |
| 72 | ushort upla; /* look ahead */ |
| 73 | ushort updb; /* data buffer */ |
| 74 | ushort upmr; /* maintenance */ |
| 75 | ushort updt; /* drive type */ |
| 76 | ushort upsn; /* serial number */ |
| 77 | ushort upof; /* offset register */ |
| 78 | ushort updc; /* desired cylinder address register */ |
| 79 | ushort upcc; /* current cylinder */ |
| 80 | ushort uper2; /* error register 2 */ |
| 81 | ushort uper3; /* error register 3 */ |
| 82 | ushort upec1; /* burst error bit position */ |
| 83 | ushort upec2; /* burst error bit pattern */ |
| 84 | }; |
| 85 | |
| 86 | /* |
| 87 | * Software extension to the upas register, so we can |
| 88 | * postpone starting SEARCH commands until the controller |
| 89 | * is not transferring. |
| 90 | */ |
| 91 | int softas; |
| 92 | |
| 93 | /* |
| 94 | * If upseek then we don't issue SEARCH commands but rather just |
| 95 | * settle for a SEEK to the correct cylinder. |
| 96 | */ |
| 97 | int upseek; |
| 98 | |
| 99 | #define UPADDR ((struct device *)(UBA0_DEV + 0176700)) |
| 100 | |
| 101 | #define NUP 2 /* Number of drives this installation */ |
| 102 | |
| 103 | #define NSECT 32 |
| 104 | #define NTRAC 19 |
| 105 | |
| 106 | /* |
| 107 | * Constants controlling on-cylinder SEARCH usage. |
| 108 | * |
| 109 | * SDIST/2 msec time needed to start transfer |
| 110 | * IDIST/2 msec slop for interrupt latency |
| 111 | * RDIST/2 msec tolerable rotational latency when on-cylinder |
| 112 | * |
| 113 | * If we are no closer than SDIST sectors and no further than SDIST+RDIST |
| 114 | * and in the driver then we take it as it is. Otherwise we do a SEARCH |
| 115 | * requesting an interrupt SDIST+IDIST sectors in advance. |
| 116 | */ |
| 117 | #define _SDIST 6 /* 3.0 msec */ |
| 118 | #define _RDIST 6 /* 2.5 msec */ |
| 119 | #define _IDIST 1 /* 0.5 msec */ |
| 120 | |
| 121 | int SDIST = _SDIST; |
| 122 | int RDIST = _RDIST; |
| 123 | int IDIST = _IDIST; |
| 124 | |
| 125 | /* |
| 126 | * To fill a 300M drive: |
| 127 | * A is designed to be used as a root. |
| 128 | * B is suitable for a swap area. |
| 129 | * H is the primary storage area. |
| 130 | * On systems with RP06'es, we normally use only 291346 blocks of the H |
| 131 | * area, and use DEF or G to cover the rest of the drive. The C system |
| 132 | * covers the whole drive and can be used for pack-pack copying. |
| 133 | */ |
| 134 | struct size |
| 135 | { |
| 136 | daddr_t nblocks; |
| 137 | int cyloff; |
| 138 | } up_sizes[8] = { |
| 139 | 15884, 0, /* A=cyl 0 thru 26 */ |
| 140 | 33440, 27, /* B=cyl 27 thru 81 */ |
| 141 | 494912, 0, /* C=cyl 0 thru 814 */ |
| 142 | 15884, 562, /* D=cyl 562 thru 588 */ |
| 143 | 55936, 589, /* E=cyl 589 thru 680 */ |
| 144 | 81472, 681, /* F=cyl 681 thru 814 */ |
| 145 | 153824, 562, /* G=cyl 562 thru 814 */ |
| 146 | 445664, 82, /* H=cyl 82 thru 814 */ |
| 147 | /* Later, and more safely for H area... |
| 148 | 291346, 82, /* H=cyl 82 thru 561 */ |
| 149 | }; |
| 150 | |
| 151 | /* |
| 152 | * The following defines are used in offset positioning |
| 153 | * when trying to recover disk errors, with the constants being |
| 154 | * +/- microinches. Note that header compare inhibit (HCI) is not |
| 155 | * tried (this makes sense only during read, in any case.) |
| 156 | * |
| 157 | * ARE ALL THESE IMPLEMENTED ON 9300? |
| 158 | */ |
| 159 | #define P400 020 |
| 160 | #define M400 0220 |
| 161 | #define P800 040 |
| 162 | #define M800 0240 |
| 163 | #define P1200 060 |
| 164 | #define M1200 0260 |
| 165 | #define HCI 020000 |
| 166 | |
| 167 | int up_offset[16] = |
| 168 | { |
| 169 | P400, M400, P400, M400, |
| 170 | P800, M800, P800, M800, |
| 171 | P1200, M1200, P1200, M1200, |
| 172 | 0, 0, 0, 0, |
| 173 | }; |
| 174 | |
| 175 | /* |
| 176 | * Each drive has a table uputab[i]. On this table are sorted the |
| 177 | * pending requests implementing an elevator algorithm (see dsort.c.) |
| 178 | * In the upustart() routine, each drive is independently advanced |
| 179 | * until it is on the desired cylinder for the next transfer and near |
| 180 | * the desired sector. The drive is then chained onto the uptab |
| 181 | * table, and the transfer is initiated by the upstart() routine. |
| 182 | * When the transfer is completed the driver reinvokes the upustart() |
| 183 | * routine to set up the next transfer. |
| 184 | */ |
| 185 | struct buf uptab; |
| 186 | struct buf uputab[NUP]; |
| 187 | |
| 188 | struct buf rupbuf; /* Buffer for raw i/o */ |
| 189 | |
| 190 | /* Drive commands, placed in upcs1 */ |
| 191 | #define GO 01 /* Go bit, set in all commands */ |
| 192 | #define PRESET 020 /* Preset drive at init or after errors */ |
| 193 | #define OFFSET 014 /* Offset heads to try to recover error */ |
| 194 | #define RTC 016 /* Return to center-line after OFFSET */ |
| 195 | #define SEARCH 030 /* Search for cylinder+sector */ |
| 196 | #define SEEK 04 /* Seek to cylinder */ |
| 197 | #define RECAL 06 /* Recalibrate, needed after seek error */ |
| 198 | #define DCLR 010 /* Drive clear, after error */ |
| 199 | #define WCOM 060 /* Write */ |
| 200 | #define RCOM 070 /* Read */ |
| 201 | |
| 202 | /* Other bits of upcs1 */ |
| 203 | #define IE 0100 /* Controller wide interrupt enable */ |
| 204 | #define TRE 040000 /* Transfer error */ |
| 205 | #define RDY 020 /* Transfer terminated */ |
| 206 | |
| 207 | /* Drive status bits of upds */ |
| 208 | #define PIP 020000 /* Positioning in progress */ |
| 209 | #define ERR 040000 /* Error has occurred, DCLR necessary */ |
| 210 | #define VV 0100 /* Volume is valid, set by PRESET */ |
| 211 | #define DPR 0400 /* Drive has been preset */ |
| 212 | #define MOL 010000 /* Drive is online, heads loaded, etc */ |
| 213 | #define DRY 0200 /* Drive ready */ |
| 214 | |
| 215 | /* Bits of uper1 */ |
| 216 | #define DCK 0100000 /* Ecc error occurred */ |
| 217 | #define ECH 0100 /* Ecc error was unrecoverable */ |
| 218 | #define WLE 04000 /* Attempt to write read-only drive */ |
| 219 | |
| 220 | /* Bits of upof; the offset bits above are also in this register */ |
| 221 | #define FMT22 010000 /* 16 bits/word, must be always set */ |
| 222 | |
| 223 | #define b_cylin b_resid |
| 224 | |
| 225 | int up_ubinfo; /* Information about UBA usage saved here */ |
| 226 | /* |
| 227 | * The EMULEX controller balks if accessed quickly after |
| 228 | * certain operations. The exact timing has not yet been |
| 229 | * determined, but delays are known to be needed when changing |
| 230 | * the selected drive (by writing in upcs2), and thought to be |
| 231 | * needed after operations like PRESET and DCLR. The following |
| 232 | * variables control the delay, DELAY(n) is approximately n usec. |
| 233 | */ |
| 234 | int idelay = 500; /* Delay after PRESET or DCLR */ |
| 235 | int sdelay = 150; /* Delay after selecting drive in upcs2 */ |
| 236 | int rdelay = 100; /* Delay after SEARCH */ |
| 237 | int asdel = 100; /* Delay after clearing bit in upas */ |
| 238 | |
| 239 | int csdel2 = 0; /* ??? Delay in upstart ??? */ |
| 240 | |
| 241 | #define DELAY(N) { register int d; d = N; while (--d > 0); } |
| 242 | |
| 243 | int nwaitcs2; /* How many sdelay loops ? */ |
| 244 | int neasycs2; /* How many sdelay loops not needed ? */ |
| 245 | |
| 246 | #ifdef INTRLVE |
| 247 | daddr_t dkblock(); |
| 248 | #endif |
| 249 | |
| 250 | /* |
| 251 | * Queue an i/o request for a drive, checking first that it is in range. |
| 252 | * |
| 253 | * A unit start is issued if the drive is inactive, causing |
| 254 | * a SEARCH for the correct cylinder/sector. If the drive is |
| 255 | * already nearly on the money and the controller is not transferring |
| 256 | * we kick it to start the transfer. |
| 257 | */ |
| 258 | upstrategy(bp) |
| 259 | register struct buf *bp; |
| 260 | { |
| 261 | register struct buf *dp; |
| 262 | register unit, xunit; |
| 263 | long sz, bn; |
| 264 | |
| 265 | xunit = minor(bp->b_dev) & 077; |
| 266 | sz = bp->b_bcount; |
| 267 | sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ |
| 268 | unit = dkunit(bp); |
| 269 | if (unit >= NUP || |
| 270 | bp->b_blkno < 0 || |
| 271 | (bn = dkblock(bp))+sz > up_sizes[xunit&07].nblocks) { |
| 272 | bp->b_flags |= B_ERROR; |
| 273 | iodone(bp); |
| 274 | return; |
| 275 | } |
| 276 | bp->b_cylin = bn/(NSECT*NTRAC) + up_sizes[xunit&07].cyloff; |
| 277 | dp = &uputab[unit]; |
| 278 | (void) spl5(); |
| 279 | disksort(dp, bp); |
| 280 | if (dp->b_active == 0) { |
| 281 | (void) upustart(unit); |
| 282 | if (uptab.b_actf && uptab.b_active == 0) |
| 283 | (void) upstart(); |
| 284 | } |
| 285 | (void) spl0(); |
| 286 | } |
| 287 | |
| 288 | /* |
| 289 | * Start activity on specified drive; called when drive is inactive |
| 290 | * and new transfer request arrives and also when upas indicates that |
| 291 | * a SEARCH command is complete. |
| 292 | */ |
| 293 | upustart(unit) |
| 294 | register unit; |
| 295 | { |
| 296 | register struct buf *bp, *dp; |
| 297 | register struct device *upaddr = UPADDR; |
| 298 | daddr_t bn; |
| 299 | int sn, cn, csn; |
| 300 | int didie = 0; |
| 301 | |
| 302 | /* |
| 303 | * Other drivers tend to say something like |
| 304 | * upaddr->upcs1 = IE; |
| 305 | * upaddr->upas = 1<<unit; |
| 306 | * here, but the SC-11B will cancel a command which |
| 307 | * happens to be sitting in the cs1 if you clear the go |
| 308 | * bit by storing there (so the first is not safe), |
| 309 | * and it also does not like being bothered with operations |
| 310 | * such as clearing upas when a transfer is active (as |
| 311 | * it may well be.) |
| 312 | * |
| 313 | * Thus we keep careful track of when we re-enable IE |
| 314 | * after an interrupt and do it only if we didn't issue |
| 315 | * a command which re-enabled it as a matter of course. |
| 316 | * We clear bits in upas in the interrupt routine, when |
| 317 | * no transfers are active. |
| 318 | */ |
| 319 | if (unit >= NUP) |
| 320 | goto out; |
| 321 | if (unit+DK_N <= DK_NMAX) |
| 322 | dk_busy &= ~(1<<(unit+DK_N)); |
| 323 | dp = &uputab[unit]; |
| 324 | if ((bp = dp->b_actf) == NULL) |
| 325 | goto out; |
| 326 | /* |
| 327 | * The SC-11B doesn't start SEARCH commands when transfers are |
| 328 | * in progress. In fact, it tends to get confused when given |
| 329 | * SEARCH'es during transfers, generating interrupts with neither |
| 330 | * RDY nor a bit in the upas register. Thus we defer |
| 331 | * until an interrupt when a transfer is pending. |
| 332 | */ |
| 333 | if (uptab.b_active) { |
| 334 | softas |= 1<<unit; |
| 335 | return (0); |
| 336 | } |
| 337 | if (dp->b_active) |
| 338 | goto done; |
| 339 | dp->b_active = 1; |
| 340 | if ((upaddr->upcs2 & 07) != unit) { |
| 341 | upaddr->upcs2 = unit; |
| 342 | DELAY(sdelay); |
| 343 | nwaitcs2++; |
| 344 | } else |
| 345 | neasycs2++; |
| 346 | /* |
| 347 | * If we have changed packs or just initialized, |
| 348 | * then the volume will not be valid; if so, clear |
| 349 | * the drive, preset it and put in 16bit/word mode. |
| 350 | */ |
| 351 | if ((upaddr->upds & VV) == 0) { |
| 352 | upaddr->upcs1 = IE|DCLR|GO; |
| 353 | DELAY(idelay); |
| 354 | upaddr->upcs1 = IE|PRESET|GO; |
| 355 | DELAY(idelay); |
| 356 | upaddr->upof = FMT22; |
| 357 | printf("VV done ds %o, er? %o %o %o\n", upaddr->upds, upaddr->uper1, upaddr->uper2, upaddr->uper3); |
| 358 | didie = 1; |
| 359 | } |
| 360 | if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) |
| 361 | goto done; |
| 362 | /* |
| 363 | * Do enough of the disk address decoding to determine |
| 364 | * which cylinder and sector the request is on. |
| 365 | * If we are on the correct cylinder and the desired sector |
| 366 | * lies between SDIST and SDIST+RDIST sectors ahead of us, then |
| 367 | * we don't bother to SEARCH but just begin the transfer asap. |
| 368 | * Otherwise ask for a interrupt SDIST+IDIST sectors ahead. |
| 369 | */ |
| 370 | bn = dkblock(bp); |
| 371 | cn = bp->b_cylin; |
| 372 | sn = bn%(NSECT*NTRAC); |
| 373 | sn = (sn+NSECT-SDIST)%NSECT; |
| 374 | |
| 375 | if (cn - upaddr->updc) |
| 376 | goto search; /* Not on-cylinder */ |
| 377 | else if (upseek) |
| 378 | goto done; /* Ok just to be on-cylinder */ |
| 379 | csn = (upaddr->upla>>6) - sn - 1; |
| 380 | if (csn < 0) |
| 381 | csn += NSECT; |
| 382 | if (csn > NSECT-RDIST) |
| 383 | goto done; |
| 384 | |
| 385 | search: |
| 386 | upaddr->updc = cn; |
| 387 | if (upseek) |
| 388 | upaddr->upcs1 = IE|SEEK|GO; |
| 389 | else { |
| 390 | upaddr->upda = sn; |
| 391 | upaddr->upcs1 = IE|SEARCH|GO; |
| 392 | } |
| 393 | didie = 1; |
| 394 | /* |
| 395 | * Mark this unit busy. |
| 396 | */ |
| 397 | unit += DK_N; |
| 398 | if (unit <= DK_NMAX) { |
| 399 | dk_busy |= 1<<unit; |
| 400 | dk_numb[unit]++; |
| 401 | } |
| 402 | DELAY(rdelay); |
| 403 | goto out; |
| 404 | |
| 405 | done: |
| 406 | /* |
| 407 | * This unit is ready to go so |
| 408 | * link it onto the chain of ready disks. |
| 409 | */ |
| 410 | dp->b_forw = NULL; |
| 411 | if (uptab.b_actf == NULL) |
| 412 | uptab.b_actf = dp; |
| 413 | else |
| 414 | uptab.b_actl->b_forw = dp; |
| 415 | uptab.b_actl = dp; |
| 416 | |
| 417 | out: |
| 418 | return (didie); |
| 419 | } |
| 420 | |
| 421 | /* |
| 422 | * Start a transfer; call from top level at spl5() or on interrupt. |
| 423 | */ |
| 424 | upstart() |
| 425 | { |
| 426 | register struct buf *bp, *dp; |
| 427 | register unit; |
| 428 | register struct device *upaddr; |
| 429 | daddr_t bn; |
| 430 | int dn, sn, tn, cn, cmd; |
| 431 | |
| 432 | loop: |
| 433 | if (csdel2) DELAY(csdel2); |
| 434 | /* |
| 435 | * Pick a drive off the queue of ready drives, and |
| 436 | * perform the first transfer on its queue. |
| 437 | * |
| 438 | * Looping here is completely for the sake of drives which |
| 439 | * are not present and on-line, for which we completely clear the |
| 440 | * request queue. |
| 441 | */ |
| 442 | if ((dp = uptab.b_actf) == NULL) |
| 443 | return (0); |
| 444 | if ((bp = dp->b_actf) == NULL) { |
| 445 | uptab.b_actf = dp->b_forw; |
| 446 | goto loop; |
| 447 | } |
| 448 | /* |
| 449 | * Mark the controller busy, and multi-part disk address. |
| 450 | * Select the unit on which the i/o is to take place. |
| 451 | */ |
| 452 | uptab.b_active++; |
| 453 | unit = minor(bp->b_dev) & 077; |
| 454 | dn = dkunit(bp); |
| 455 | bn = dkblock(bp); |
| 456 | cn = up_sizes[unit&07].cyloff; |
| 457 | cn += bn/(NSECT*NTRAC); |
| 458 | sn = bn%(NSECT*NTRAC); |
| 459 | tn = sn/NSECT; |
| 460 | sn %= NSECT; |
| 461 | upaddr = UPADDR; |
| 462 | if ((upaddr->upcs2 & 07) != dn) { |
| 463 | upaddr->upcs2 = dn; |
| 464 | /* DELAY(sdelay); Provided by ubasetup() */ |
| 465 | nwaitcs2++; |
| 466 | } else |
| 467 | neasycs2++; |
| 468 | up_ubinfo = ubasetup(bp, 1); /* Providing delay */ |
| 469 | /* |
| 470 | * If drive is not present and on-line, then |
| 471 | * get rid of this with an error and loop to get |
| 472 | * rid of the rest of its queued requests. |
| 473 | * (Then on to any other ready drives.) |
| 474 | */ |
| 475 | if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { |
| 476 | printf("!DPR || !MOL, unit %d, ds %o\n", dn, upaddr->upds); |
| 477 | uptab.b_active = 0; |
| 478 | uptab.b_errcnt = 0; |
| 479 | dp->b_actf = bp->av_forw; |
| 480 | dp->b_active = 0; |
| 481 | bp->b_flags |= B_ERROR; |
| 482 | iodone(bp); |
| 483 | ubafree(up_ubinfo), up_ubinfo = 0; /* A funny place ... */ |
| 484 | goto loop; |
| 485 | } |
| 486 | /* |
| 487 | * If this is a retry, then with the 16'th retry we |
| 488 | * begin to try offsetting the heads to recover the data. |
| 489 | */ |
| 490 | if (uptab.b_errcnt >= 16) { |
| 491 | upaddr->upof = up_offset[uptab.b_errcnt & 017] | FMT22; |
| 492 | upaddr->upcs1 = IE|OFFSET|GO; |
| 493 | DELAY(idelay); |
| 494 | while (upaddr->upds & PIP) |
| 495 | DELAY(25); |
| 496 | } |
| 497 | /* |
| 498 | * Now set up the transfer, retrieving the high |
| 499 | * 2 bits of the UNIBUS address from the information |
| 500 | * returned by ubasetup() for the cs1 register bits 8 and 9. |
| 501 | */ |
| 502 | upaddr->updc = cn; |
| 503 | upaddr->upda = (tn << 8) + sn; |
| 504 | upaddr->upba = up_ubinfo; |
| 505 | upaddr->upwc = -bp->b_bcount / sizeof (short); |
| 506 | cmd = (up_ubinfo >> 8) & 0x300; |
| 507 | if (bp->b_flags & B_READ) |
| 508 | cmd |= IE|RCOM|GO; |
| 509 | else |
| 510 | cmd |= IE|WCOM|GO; |
| 511 | upaddr->upcs1 = cmd; |
| 512 | /* |
| 513 | * This is a controller busy situation. |
| 514 | * Record in dk slot NUP+DK_N (after last drive) |
| 515 | * unless there aren't that many slots reserved for |
| 516 | * us in which case we record this as a drive busy |
| 517 | * (if there is room for that). |
| 518 | */ |
| 519 | unit = dn+DK_N; |
| 520 | if (NUP+DK_N == DK_NMAX) |
| 521 | unit = NUP+DK_N; |
| 522 | if (unit <= DK_NMAX) { |
| 523 | dk_busy |= 1<<unit; |
| 524 | dk_numb[unit]++; |
| 525 | dk_wds[unit] += bp->b_bcount>>6; |
| 526 | } |
| 527 | return (1); |
| 528 | } |
| 529 | |
| 530 | /* |
| 531 | * Handle a device interrupt. |
| 532 | * |
| 533 | * If the transferring drive needs attention, service it |
| 534 | * retrying on error or beginning next transfer. |
| 535 | * Service all other ready drives, calling ustart to transfer |
| 536 | * their blocks to the ready queue in uptab, and then restart |
| 537 | * the controller if there is anything to do. |
| 538 | */ |
| 539 | upintr() |
| 540 | { |
| 541 | register struct buf *bp, *dp; |
| 542 | register unit; |
| 543 | register struct device *upaddr = UPADDR; |
| 544 | int as = upaddr->upas & 0377; |
| 545 | int osoftas; |
| 546 | int needie = 1; |
| 547 | |
| 548 | (void) spl6(); |
| 549 | if (uptab.b_active) { |
| 550 | /* |
| 551 | * The drive is transferring, thus the hardware |
| 552 | * (say the designers) will only interrupt when the transfer |
| 553 | * completes; check for it anyways. |
| 554 | */ |
| 555 | if ((upaddr->upcs1 & RDY) == 0) { |
| 556 | printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, |
| 557 | upaddr->upds, upaddr->upwc); |
| 558 | printf("as=%d act %d %d %d\n", as, uptab.b_active, uputab[0].b_active, uputab[1].b_active); |
| 559 | } |
| 560 | /* |
| 561 | * Mark controller or drive not busy, and check for an |
| 562 | * error condition which may have resulted from the transfer. |
| 563 | */ |
| 564 | dp = uptab.b_actf; |
| 565 | bp = dp->b_actf; |
| 566 | unit = dkunit(bp); |
| 567 | if (DK_N+NUP == DK_NMAX) |
| 568 | dk_busy &= ~(1<<(DK_N+NUP)); |
| 569 | else if (DK_N+unit <= DK_NMAX) |
| 570 | dk_busy &= ~(1<<(DK_N+unit)); |
| 571 | if ((upaddr->upcs2 & 07) != unit) { |
| 572 | upaddr->upcs2 = unit; |
| 573 | DELAY(sdelay); |
| 574 | nwaitcs2++; |
| 575 | } else |
| 576 | neasycs2++; |
| 577 | if (upaddr->upds & ERR) { |
| 578 | /* |
| 579 | * An error occurred, indeed. Select this unit |
| 580 | * to get at the drive status (a SEARCH may have |
| 581 | * intervened to change the selected unit), and |
| 582 | * wait for the command which caused the interrupt |
| 583 | * to complete (DRY). |
| 584 | */ |
| 585 | while ((upaddr->upds & DRY) == 0) |
| 586 | DELAY(25); |
| 587 | /* |
| 588 | * After 28 retries (16 w/o servo offsets, and then |
| 589 | * 12 with servo offsets), or if we encountered |
| 590 | * an error because the drive is write-protected, |
| 591 | * give up. Print an error message on the last 2 |
| 592 | * retries before a hard failure. |
| 593 | */ |
| 594 | if (++uptab.b_errcnt > 28 || upaddr->uper1&WLE) |
| 595 | bp->b_flags |= B_ERROR; |
| 596 | else |
| 597 | uptab.b_active = 0; /* To force retry */ |
| 598 | if (uptab.b_errcnt > 27) |
| 599 | deverror(bp, upaddr->upcs2, upaddr->uper1); |
| 600 | /* |
| 601 | * If this was a correctible ECC error, let upecc |
| 602 | * do the dirty work to correct it. If upecc |
| 603 | * starts another READ for the rest of the data |
| 604 | * then it returns 1 (having set uptab.b_active). |
| 605 | * Otherwise we are done and fall through to |
| 606 | * finish up. |
| 607 | */ |
| 608 | if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(upaddr, bp)) |
| 609 | return; |
| 610 | /* |
| 611 | * Clear the drive and, every 4 retries, recalibrate |
| 612 | * to hopefully help clear up seek positioning problems. |
| 613 | */ |
| 614 | upaddr->upcs1 = TRE|IE|DCLR|GO; |
| 615 | DELAY(idelay); |
| 616 | needie = 0; |
| 617 | if ((uptab.b_errcnt&07) == 4) { |
| 618 | upaddr->upcs1 = RECAL|GO|IE; |
| 619 | DELAY(idelay); |
| 620 | while(upaddr->upds & PIP) |
| 621 | DELAY(25); |
| 622 | } |
| 623 | } |
| 624 | /* |
| 625 | * If we are still noted as active, then no |
| 626 | * (further) retries are necessary. |
| 627 | * |
| 628 | * Make sure the correct unit is selected, |
| 629 | * return it to centerline if necessary, and mark |
| 630 | * this i/o complete, starting the next transfer |
| 631 | * on this drive with the upustart routine (if any). |
| 632 | */ |
| 633 | if (uptab.b_active) { |
| 634 | if (uptab.b_errcnt >= 16) { |
| 635 | upaddr->upcs1 = RTC|GO|IE; |
| 636 | DELAY(idelay); |
| 637 | while (upaddr->upds & PIP) |
| 638 | DELAY(25); |
| 639 | needie = 0; |
| 640 | } |
| 641 | uptab.b_active = 0; |
| 642 | uptab.b_errcnt = 0; |
| 643 | uptab.b_actf = dp->b_forw; |
| 644 | dp->b_active = 0; |
| 645 | dp->b_errcnt = 0; |
| 646 | dp->b_actf = bp->av_forw; |
| 647 | bp->b_resid = (-upaddr->upwc * sizeof(short)); |
| 648 | if (bp->b_resid) |
| 649 | printf("resid %d ds %o er? %o %o %o\n", bp->b_resid, upaddr->upds, |
| 650 | upaddr->uper1, upaddr->uper2, upaddr->uper3); |
| 651 | iodone(bp); |
| 652 | if(dp->b_actf) |
| 653 | if (upustart(unit)) |
| 654 | needie = 0; |
| 655 | } |
| 656 | as &= ~(1<<unit); |
| 657 | softas &= ~(1<<unit); |
| 658 | ubafree(up_ubinfo), up_ubinfo = 0; |
| 659 | } else { |
| 660 | if (upaddr->upcs1 & TRE) { |
| 661 | upaddr->upcs1 = TRE; |
| 662 | DELAY(idelay); |
| 663 | } |
| 664 | } |
| 665 | /* |
| 666 | * If we have a unit with an outstanding SEARCH, |
| 667 | * and the hardware indicates the unit requires attention, |
| 668 | * the bring the drive to the ready queue. |
| 669 | * Finally, if the controller is not transferring |
| 670 | * start it if any drives are now ready to transfer. |
| 671 | */ |
| 672 | as |= softas; |
| 673 | osoftas = softas; |
| 674 | softas = 0; |
| 675 | for (unit = 0; unit < NUP; unit++) |
| 676 | if ((as|osoftas) & (1<<unit)) { |
| 677 | if (as & (1<<unit)) { |
| 678 | upaddr->upas = 1<<unit; |
| 679 | if (asdel) DELAY(asdel); |
| 680 | } |
| 681 | if (upustart(unit)) |
| 682 | needie = 0; |
| 683 | } |
| 684 | if (uptab.b_actf && uptab.b_active == 0) |
| 685 | if (upstart()) |
| 686 | needie = 0; |
| 687 | out: |
| 688 | if (needie) |
| 689 | upaddr->upcs1 = IE; |
| 690 | } |
| 691 | |
| 692 | upread(dev) |
| 693 | { |
| 694 | |
| 695 | physio(upstrategy, &rupbuf, dev, B_READ, minphys); |
| 696 | } |
| 697 | |
| 698 | upwrite(dev) |
| 699 | { |
| 700 | |
| 701 | physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); |
| 702 | } |
| 703 | |
| 704 | /* |
| 705 | * Correct an ECC error, and restart the i/o to complete |
| 706 | * the transfer if necessary. This is quite complicated because |
| 707 | * the transfer may be going to an odd memory address base and/or |
| 708 | * across a page boundary. |
| 709 | */ |
| 710 | upecc(up, bp) |
| 711 | register struct device *up; |
| 712 | register struct buf *bp; |
| 713 | { |
| 714 | struct uba_regs *ubp = (struct uba_regs *)UBA0; |
| 715 | register int i; |
| 716 | caddr_t addr; |
| 717 | int reg, bit, byte, npf, mask, o, cmd, ubaddr; |
| 718 | int bn, cn, tn, sn; |
| 719 | |
| 720 | /* |
| 721 | * Npf is the number of sectors transferred before the sector |
| 722 | * containing the ECC error, and reg is the UBA register |
| 723 | * mapping (the first part of) the transfer. |
| 724 | * O is offset within a memory page of the first byte transferred. |
| 725 | */ |
| 726 | npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; |
| 727 | reg = btop(up_ubinfo&0x3ffff) + npf; |
| 728 | o = (int)bp->b_un.b_addr & PGOFSET; |
| 729 | printf("%D ", bp->b_blkno+npf); |
| 730 | prdev("ECC", bp->b_dev); |
| 731 | mask = up->upec2; |
| 732 | if (mask == 0) { |
| 733 | up->upof = FMT22; /* == RTC ???? */ |
| 734 | DELAY(idelay); |
| 735 | return (0); |
| 736 | } |
| 737 | /* |
| 738 | * Flush the buffered data path, and compute the |
| 739 | * byte and bit position of the error. The variable i |
| 740 | * is the byte offset in the transfer, the variable byte |
| 741 | * is the offset from a page boundary in main memory. |
| 742 | */ |
| 743 | ubp->uba_dpr[(up_ubinfo>>28)&0x0f] |= BNE; |
| 744 | i = up->upec1 - 1; /* -1 makes 0 origin */ |
| 745 | bit = i&07; |
| 746 | i = (i&~07)>>3; |
| 747 | byte = i + o; |
| 748 | /* |
| 749 | * Correct while possible bits remain of mask. Since mask |
| 750 | * contains 11 bits, we continue while the bit offset is > -11. |
| 751 | * Also watch out for end of this block and the end of the whole |
| 752 | * transfer. |
| 753 | */ |
| 754 | while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { |
| 755 | addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ |
| 756 | (byte & PGOFSET); |
| 757 | putmemc(addr, getmemc(addr)^(mask<<bit)); |
| 758 | byte++; |
| 759 | i++; |
| 760 | bit -= 8; |
| 761 | } |
| 762 | uptab.b_active++; /* Either complete or continuing... */ |
| 763 | if (up->upwc == 0) |
| 764 | return (0); |
| 765 | /* |
| 766 | * Have to continue the transfer... clear the drive, |
| 767 | * and compute the position where the transfer is to continue. |
| 768 | * We have completed npf+1 sectors of the transfer already; |
| 769 | * restart at offset o of next sector (i.e. in UBA register reg+1). |
| 770 | */ |
| 771 | up->upcs1 = TRE|IE|DCLR|GO; |
| 772 | DELAY(idelay); |
| 773 | bn = dkblock(bp); |
| 774 | cn = bp->b_cylin; |
| 775 | sn = bn%(NSECT*NTRAC) + npf + 1; |
| 776 | tn = sn/NSECT; |
| 777 | sn %= NSECT; |
| 778 | cn += tn/NTRAC; |
| 779 | tn %= NTRAC; |
| 780 | up->updc = cn; |
| 781 | up->upda = (tn << 8) | sn; |
| 782 | ubaddr = (int)ptob(reg+1) + o; |
| 783 | up->upba = ubaddr; |
| 784 | cmd = (ubaddr >> 8) & 0x300; |
| 785 | cmd |= IE|GO|RCOM; |
| 786 | up->upcs1 = cmd; |
| 787 | return (1); |
| 788 | } |