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