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