cleanup
[unix-history] / usr / src / sys / vax / stand / up.c
CommitLineData
7522de24 1/* up.c 4.7 83/02/17 */
258a5c40 2
0839bdeb
SL
3/*
4 * UNIBUS peripheral standalone driver
5 * with ECC correction and bad block forwarding.
6 * Also supports header operation and write
7 * check for data and/or header.
8 */
b8ca3ab9 9
258a5c40
SL
10#include "../h/param.h"
11#include "../h/inode.h"
12#include "../h/fs.h"
13#include "../h/dkbad.h"
14#include "../h/vmmac.h"
15
16#include "../vax/pte.h"
17#include "../vaxuba/upreg.h"
18#include "../vaxuba/ubareg.h"
19
0839bdeb 20#include "saio.h"
258a5c40
SL
21#include "savax.h"
22
b8ca3ab9
HS
23#define MAXBADDESC 126 /* max number of bad sectors recorded */
24#define SECTSIZ 512 /* sector size in bytes */
25#define HDRSIZ 4 /* number of bytes in sector header */
31fa9244 26#define MAXECC 5 /* max # bad bits allowed on ecc w/ F_ECCLM */
b8ca3ab9 27
258a5c40 28u_short ubastd[] = { 0776700 };
0839bdeb 29
258a5c40
SL
30char up_gottype[MAXNUBA*8] = { 0 };
31char up_type[MAXNUBA*8] = { 0 };
31fa9244
SL
32short up9300_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 };
33short up9766_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 };
258a5c40
SL
34short fj_off[] = { 0, 50, 0, -1, -1, -1, -1, 155 };
35/* this is called upam instead of am because hp.c has a similar array */
36short upam_off[] = { 0, 32, 0, 668, 723, 778, 668, 98 };
0839bdeb 37
31fa9244 38#define NUPTYPES 4
b8ca3ab9 39struct st upst[NUPTYPES] = {
31fa9244
SL
40 32, 19, 32*19, 815, up9300_off, /* 9300 */
41 32, 19, 32*19, 823, up9766_off, /* 9766 */
0839bdeb
SL
42 32, 10, 32*10, 823, fj_off, /* Fuji 160 */
43 32, 16, 32*16, 1024, upam_off, /* Capricorn */
258a5c40 44};
0839bdeb 45
258a5c40
SL
46u_char up_offset[16] = {
47 UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400,
48 UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800,
49 UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200,
50 0, 0, 0, 0
51};
52
0839bdeb 53struct dkbad upbad[MAXNUBA*8]; /* bad sector table */
b8ca3ab9 54int sectsiz; /* real sector size */
0839bdeb 55
258a5c40
SL
56upopen(io)
57 register struct iob *io;
58{
b8ca3ab9 59 register unit = io->i_unit;
0839bdeb 60 register struct updevice *upaddr;
31fa9244 61 register struct st *st;
258a5c40 62
0839bdeb
SL
63 if (io->i_boff < 0 || io->i_boff > 7 || st->off[io->i_boff] == -1)
64 _stop("up bad unit");
b8ca3ab9 65 upaddr = (struct updevice *)ubamem(unit, ubastd[0]);
3ab40097 66 while ((upaddr->upcs1 & UP_DVA) == 0)
258a5c40 67 ;
b8ca3ab9 68 if (up_gottype[unit] == 0) {
0839bdeb
SL
69 register int i;
70 struct iob tio;
71
31fa9244
SL
72 up_type[unit] = upmaptype(unit, upaddr);
73 if (up_type[unit] < 0)
0839bdeb 74 _stop("unknown drive type");
31fa9244 75 st = &upst[up_type[unit]];
0839bdeb
SL
76 /*
77 * Read in the bad sector table:
78 * copy the contents of the io structure
79 * to tio for use during the bb pointer
80 * read operation.
81 */
82 tio = *io;
83 tio.i_bn = st->nspc * st->ncyl - st->nsect;
258a5c40 84 tio.i_ma = (char *)&upbad[tio.i_unit];
960ade39 85 tio.i_cc = sizeof (struct dkbad);
0839bdeb
SL
86 tio.i_flgs |= F_RDDATA;
87 for (i = 0; i < 5; i++) {
960ade39 88 if (upstrategy(&tio, READ) == sizeof (struct dkbad))
0839bdeb 89 break;
258a5c40
SL
90 tio.i_bn += 2;
91 }
92 if (i == 5) {
0839bdeb 93 printf("Unable to read bad sector table\n");
b8ca3ab9
HS
94 for (i = 0; i < MAXBADDESC; i++) {
95 upbad[unit].bt_bad[i].bt_cyl = -1;
96 upbad[unit].bt_bad[i].bt_trksec = -1;
258a5c40
SL
97 }
98 }
b8ca3ab9 99 up_gottype[unit] = 1;
258a5c40 100 }
258a5c40 101 io->i_boff = st->off[io->i_boff] * st->nspc;
0839bdeb 102 io->i_flgs &= ~F_TYPEMASK;
258a5c40
SL
103}
104
31fa9244
SL
105upmaptype(unit, upaddr)
106 int unit;
107 register struct updevice *upaddr;
108{
109 register struct st *st;
110 int type = -1;
111
112 upaddr->upcs1 = 0;
113 upaddr->upcs2 = unit % 8;
114 upaddr->uphr = UPHR_MAXTRAK;
115 for (st = upst; st < &upst[NUPTYPES]; st++)
116 if (upaddr->uphr == st->ntrak - 1) {
117 type = st - upst;
118 break;
119 }
120 if (type < 0)
121 printf("up%d: uphr=%x\n", unit, upaddr->uphr);
122 if (type == 0) {
123 upaddr->uphr = UPHR_MAXCYL;
124 if (upaddr->uphr == 822) /* CDC 9766 */
125 type++;
126 }
127 upaddr->upcs2 = UPCS2_CLR;
128 return (type);
129}
130
258a5c40
SL
131upstrategy(io, func)
132 register struct iob *io;
133{
b8ca3ab9
HS
134 int cn, tn, sn;
135 register unit = io->i_unit;
258a5c40
SL
136 daddr_t bn;
137 int recal, info, waitdry;
138 register struct updevice *upaddr =
b8ca3ab9
HS
139 (struct updevice *)ubamem(unit, ubastd[0]);
140 register struct st *st = &upst[up_type[unit]];
258a5c40 141
b8ca3ab9 142 sectsiz = SECTSIZ;
3ab40097 143 if (io->i_flgs & (F_HDR|F_HCHECK))
b8ca3ab9 144 sectsiz += HDRSIZ;
258a5c40
SL
145 upaddr->upcs2 = unit;
146 if ((upaddr->upds & UPDS_VV) == 0) {
147 upaddr->upcs1 = UP_DCLR|UP_GO;
148 upaddr->upcs1 = UP_PRESET|UP_GO;
149 upaddr->upof = UPOF_FMT22;
150 }
3ab40097 151 if ((upaddr->upds & UPDS_DREADY) == 0)
258a5c40
SL
152 _stop("up not ready");
153 info = ubasetup(io, 1);
154 upaddr->upwc = -io->i_cc / sizeof (short);
155 upaddr->upba = info;
3ab40097
SL
156 recal = 0;
157 io->i_errcnt = 0;
158
960ade39 159restart:
3ab40097 160 bn = io->i_bn + (io->i_cc + upaddr->upwc * sizeof(short)) / sectsiz;
0839bdeb
SL
161 while((upaddr->upds & UPDS_DRY) == 0)
162 ;
b8ca3ab9
HS
163 if (upstart(io, bn) != 0) {
164 ubafree(io, info);
258a5c40 165 return (-1);
b8ca3ab9 166 }
258a5c40
SL
167 do {
168 DELAY(25);
169 } while ((upaddr->upcs1 & UP_RDY) == 0);
3ab40097
SL
170 /*
171 * If transfer has completed, free UNIBUS
172 * resources and return transfer size.
173 */
174 if ((upaddr->upds&UPDS_ERR) == 0 && (upaddr->upcs1&UP_TRE) == 0) {
b8ca3ab9 175 ubafree(io, info);
3ab40097 176 return (io->i_cc);
b8ca3ab9 177 }
258a5c40
SL
178#ifdef LOGALLERRS
179 printf("uper: (c,t,s)=(%d,%d,%d) cs2=%b er1=%b er2=%b wc=%x\n",
3ab40097
SL
180 upaddr->updc, upaddr->upda>>8, (upaddr->upda&0x1f-1),
181 upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
182 UPER1_BITS, upaddr->uper2, UPER2_BITS,-upaddr->upwc);
258a5c40
SL
183#endif
184 waitdry = 0;
b8ca3ab9 185 while ((upaddr->upds & UPDS_DRY) == 0 && ++waitdry < sectsiz)
0839bdeb 186 DELAY(5);
3ab40097
SL
187 if (upaddr->uper1&UPER1_WLE) {
188 /*
189 * Give up on write locked devices immediately.
190 */
191 printf("up%d: write locked\n", unit);
192 return (-1);
193 }
258a5c40
SL
194 if (++io->i_errcnt > 27) {
195 /*
196 * After 28 retries (16 without offset, and
197 * 12 with offset positioning) give up.
198 */
0839bdeb 199 io->i_error = EHER;
0839bdeb
SL
200 if (upaddr->upcs2 & UPCS2_WCE)
201 io->i_error = EWCK;
b8ca3ab9 202hard:
3ab40097
SL
203 bn = io->i_bn +
204 (io->i_cc + upaddr->upwc * sizeof (short)) / sectsiz;
258a5c40
SL
205 cn = bn/st->nspc;
206 sn = bn%st->nspc;
207 tn = sn/st->nsect;
208 sn = sn%st->nsect;
3ab40097
SL
209 printf(
210 "up error: (cyl,trk,sec)=(%d,%d,%d) cs2=%b er1=%b er2=%b\n",
211 cn, tn, sn,
212 upaddr->upcs2, UPCS2_BITS, upaddr->uper1,
213 UPER1_BITS, upaddr->uper2, UPER2_BITS);
258a5c40 214 upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
b8ca3ab9 215 io->i_errblk = bn;
3ab40097
SL
216 return (io->i_cc + upaddr->upwc * sizeof(short));
217 }
218 if (upaddr->uper2 & UPER2_BSE) {
219 short wc = upaddr->upwc;
220 if ((io->i_flgs&F_NBSF) == 0 && upecc(io, BSE) == 0) {
221 if (wc != upaddr->upwc)
222 printf("wc %x upwc %x\n", wc, upaddr->upwc);
258a5c40 223 goto success;
258a5c40 224 }
3ab40097
SL
225 io->i_error = EBSE;
226 goto hard;
258a5c40 227 }
3ab40097
SL
228 /*
229 * Retriable error.
230 * If a soft ecc, correct it
231 * Otherwise fall through and retry the transfer
232 */
233 if (upaddr->uper1 & UPER1_DCK) {
258a5c40 234 /*
3ab40097
SL
235 * If a write check command is active, all
236 * ecc errors give UPER1_ECH.
258a5c40 237 */
3ab40097
SL
238 if ((upaddr->uper1 & UPER1_ECH) == 0 ||
239 (upaddr->upcs2 & UPCS2_WCE)) {
240 if (upecc(io, ECC) == 0)
241 goto success;
242 io->i_error = EECC;
243 goto hard;
244 }
245 }
258a5c40
SL
246 /*
247 * Clear drive error and, every eight attempts,
248 * (starting with the fourth)
249 * recalibrate to clear the slate.
250 */
251 upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO;
960ade39 252 if ((io->i_errcnt&07) == 4 ) {
258a5c40 253 upaddr->upcs1 = UP_RECAL|UP_GO;
960ade39
HS
254 recal = 1;
255 goto restart;
258a5c40
SL
256 }
257 /*
258 * Advance recalibration finite state machine
259 * if recalibrate in progress, through
260 * RECAL
261 * SEEK
262 * OFFSET (optional)
263 * RETRY
264 */
265 switch (recal) {
266
267 case 1:
268 upaddr->updc = cn;
269 upaddr->upcs1 = UP_SEEK|UP_GO;
3ab40097 270 recal = 2;
960ade39 271 goto restart;
0839bdeb 272
258a5c40
SL
273 case 2:
274 if (io->i_errcnt < 16 || (func & READ) == 0)
275 goto donerecal;
276 upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22;
277 upaddr->upcs1 = UP_OFFSET|UP_GO;
3ab40097 278 recal = 3;
960ade39 279 goto restart;
0839bdeb 280
258a5c40
SL
281 donerecal:
282 case 3:
283 recal = 0;
258a5c40
SL
284 break;
285 }
286 /*
960ade39
HS
287 * If we were offset positioning,
288 * return to centerline.
258a5c40 289 */
960ade39
HS
290 if (io->i_errcnt >= 16) {
291 upaddr->upof = UPOF_FMT22;
292 upaddr->upcs1 = UP_RTC|UP_GO;
293 while ((upaddr->upds&UPDS_DRY) == 0)
294 DELAY(25);
258a5c40 295 }
960ade39 296 goto restart;
3ab40097 297
258a5c40 298success:
0839bdeb 299 if (upaddr->upwc != 0)
960ade39 300 goto restart;
258a5c40
SL
301 /*
302 * Release unibus
303 */
304 ubafree(io, info);
305 return (io->i_cc);
306}
307
308/*
309 * Correct an ECC error, and restart the i/o to complete
310 * the transfer if necessary. This is quite complicated because
311 * the transfer may be going to an odd memory address base and/or
312 * across a page boundary.
313 */
0839bdeb 314upecc(io, flag)
258a5c40
SL
315 register struct iob *io;
316 int flag;
317{
318 register struct updevice *up =
319 (struct updevice *)ubamem(io->i_unit, ubastd[0]);
b8ca3ab9 320 register struct st *st;
258a5c40
SL
321 register int i;
322 caddr_t addr;
6796f4df 323 int bn, twc, npf, mask, cn, tn, sn;
b8ca3ab9 324 daddr_t bbn;
258a5c40
SL
325
326 /*
327 * Npf is the number of sectors transferred before the sector
328 * containing the ECC error, bn is the current block number
329 */
b8ca3ab9
HS
330 twc = up->upwc;
331 npf = ((twc * sizeof(short)) + io->i_cc)/sectsiz;
258a5c40
SL
332#ifdef UPECCDEBUG
333 printf("npf %d mask 0x%x pos %d wc 0x%x\n",npf,mask,up->upec1,-up->upwc);
334#endif
b8ca3ab9 335 bn = io->i_bn + npf ;
258a5c40 336 st = &upst[up_type[io->i_unit]];
6796f4df
HS
337 cn = bn/st->nspc;
338 sn = bn%st->nspc;
339 tn = sn/st->nsect;
340 sn = sn%st->nsect;
258a5c40
SL
341 /*
342 * action taken depends on the flag
343 */
344 if (flag == ECC) {
b8ca3ab9 345 int bit, byte, ecccnt;
3ab40097 346
b8ca3ab9 347 ecccnt = 0;
258a5c40 348 mask = up->upec2;
960ade39 349 printf("up%d: soft ecc sn%d\n", io->i_unit, bn);
258a5c40
SL
350 /*
351 * Compute the
352 * byte and bit position of the error. The variable i
353 * is the byte offset in the transfer.
354 */
355 i = up->upec1 - 1; /* -1 makes 0 origin */
356 bit = i&07;
357 i = (i&~07)>>3;
358 byte = i;
359 up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
360 /*
361 * Correct while possible bits remain of mask. Since mask
362 * contains 11 bits, we continue while the bit offset is > -11.
363 * Also watch out for end of this block and the end of the whole
364 * transfer.
365 */
b8ca3ab9 366 while (i < sectsiz && (npf*sectsiz)+i < io->i_cc && bit > -11) {
258a5c40 367 /*
0839bdeb 368 * addr = vax base addr + (number of sectors transferred
258a5c40
SL
369 * before the error sector times the sector size)
370 * + byte number
371 */
3ab40097 372 addr = io->i_ma + (npf * sectsiz) + byte;
258a5c40 373#ifdef UPECCDEBUG
b8ca3ab9 374 printf("addr %x old: %x ",addr, (*addr&0xff));
258a5c40 375#endif
b8ca3ab9
HS
376 if ((io->i_flgs & (F_CHECK|F_HCHECK)) == 0)
377 *addr ^= (mask << bit);
258a5c40 378#ifdef UPECCDEBUG
b8ca3ab9 379 printf("new: %x\n", (*addr&0xff));
258a5c40
SL
380#endif
381 byte++;
382 i++;
383 bit -= 8;
3ab40097
SL
384 if ((io->i_flgs&F_ECCLM) && ++ecccnt > MAXECC)
385 return (1);
258a5c40 386 }
3ab40097
SL
387 return (0);
388 }
389 if (flag == BSE) {
258a5c40 390 /*
b8ca3ab9 391 * if not in bad sector table, return 1 (= hard error)
258a5c40 392 */
258a5c40 393 up->upcs1 = UP_TRE|UP_DCLR|UP_GO;
6796f4df 394 if ((bbn = isbad(&upbad[io->i_unit], cn, tn, sn)) < 0)
3ab40097 395 return (1);
258a5c40 396 bbn = st->ncyl * st->nspc -st->nsect - 1 - bbn;
b8ca3ab9 397 twc = up->upwc + sectsiz;
3ab40097 398 up->upwc = - (sectsiz / sizeof (short));
258a5c40
SL
399#ifdef UPECCDEBUG
400 printf("revector to block %d\n", bbn);
401#endif
402 /*
403 * Clear the drive & read the replacement sector.
404 * If this is in the middle of a transfer, then set up the
405 * controller registers in a normal fashion.
406 * The ub-address need not be changed.
407 */
0839bdeb 408 while (up->upcs1 & UP_RDY == 0)
258a5c40 409 ;
0839bdeb 410 if (upstart(io, bbn) != 0)
b8ca3ab9
HS
411 return (1); /* error */
412 io->i_errcnt = 0; /* success */
258a5c40
SL
413 do {
414 DELAY(25);
415 } while ( up->upcs1 & UP_RDY == 0) ;
416 if (up->upds & UPDS_ERR || up->upcs1 & UP_TRE) {
b8ca3ab9
HS
417 up->upwc = twc -sectsiz;
418 return (1);
258a5c40
SL
419 }
420 }
960ade39 421 if (twc)
258a5c40 422 up->upwc = twc;
b8ca3ab9 423 return (0);
258a5c40 424}
258a5c40
SL
425
426upstart(io, bn)
0839bdeb
SL
427 register struct iob *io;
428 daddr_t bn;
258a5c40
SL
429{
430 register struct updevice *upaddr =
0839bdeb 431 (struct updevice *)ubamem(io->i_unit, ubastd[0]);
b8ca3ab9 432 register struct st *st = &upst[up_type[io->i_unit]];
258a5c40
SL
433 int sn, tn;
434
435 sn = bn%st->nspc;
436 tn = sn/st->nsect;
437 sn %= st->nsect;
438 upaddr->updc = bn/st->nspc;
439 upaddr->upda = (tn << 8) + sn;
b8ca3ab9 440 switch (io->i_flgs & F_TYPEMASK) {
0839bdeb
SL
441
442 case F_RDDATA:
443 upaddr->upcs1 = UP_RCOM|UP_GO;
444 break;
445
446 case F_WRDATA:
447 upaddr->upcs1 = UP_WCOM|UP_GO;
258a5c40 448 break;
0839bdeb
SL
449
450 case F_HDR|F_RDDATA:
451 upaddr->upcs1 = UP_RHDR|UP_GO;
452 break;
453
454 case F_HDR|F_WRDATA:
455 upaddr->upcs1 = UP_WHDR|UP_GO;
258a5c40 456 break;
0839bdeb
SL
457
458 case F_CHECK|F_WRDATA:
459 case F_CHECK|F_RDDATA:
258a5c40
SL
460 upaddr->upcs1 = UP_WCDATA|UP_GO;
461 break;
0839bdeb
SL
462
463 case F_HCHECK|F_WRDATA:
464 case F_HCHECK|F_RDDATA:
258a5c40
SL
465 upaddr->upcs1 = UP_WCHDR|UP_GO;
466 break;
0839bdeb 467
258a5c40 468 default:
0839bdeb
SL
469 io->i_error = ECMD;
470 io->i_flgs &= ~F_TYPEMASK;
471 return (1);
258a5c40 472 }
0839bdeb 473 return (0);
258a5c40
SL
474}
475
0839bdeb
SL
476/*ARGSUSED*/
477upioctl(io, cmd, arg)
478 struct iob *io;
479 int cmd;
480 caddr_t arg;
481{
b8ca3ab9
HS
482 struct st *st = &upst[up_type[io->i_unit]], *tmp;
483
484 switch(cmd) {
485
486 case SAIODEVDATA:
487 tmp = (struct st *)arg;
488 *tmp = *st;
3ab40097 489 return (0);
b8ca3ab9 490 }
3ab40097 491 return (ECMD);
b8ca3ab9 492}