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