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