1*10023Ssam /* up.c 4.2 82/12/30 */ 29974Ssam 3*10023Ssam /* 4*10023Ssam * UNIBUS peripheral standalone driver 5*10023Ssam * with ECC correction and bad block forwarding. 6*10023Ssam * Also supports header operation and write 7*10023Ssam * check for data and/or header. 8*10023Ssam */ 99974Ssam #include "../h/param.h" 109974Ssam #include "../h/inode.h" 119974Ssam #include "../h/fs.h" 129974Ssam #include "../h/dkbad.h" 139974Ssam #include "../h/vmmac.h" 149974Ssam 159974Ssam #include "../vax/pte.h" 169974Ssam #include "../vaxuba/upreg.h" 179974Ssam #include "../vaxuba/ubareg.h" 189974Ssam 19*10023Ssam #include "saio.h" 209974Ssam #include "savax.h" 219974Ssam 22*10023Ssam u_short ubastd[] = { 0776700 }; 239974Ssam 249974Ssam char up_gottype[MAXNUBA*8] = { 0 }; 259974Ssam char up_type[MAXNUBA*8] = { 0 }; 269974Ssam short up_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 }; 279974Ssam short fj_off[] = { 0, 50, 0, -1, -1, -1, -1, 155 }; 289974Ssam /* this is called upam instead of am because hp.c has a similar array */ 299974Ssam short upam_off[] = { 0, 32, 0, 668, 723, 778, 668, 98 }; 30*10023Ssam 31*10023Ssam #define NUPTYPES 3 32*10023Ssam 339974Ssam struct upst { 349974Ssam short nsect; 359974Ssam short ntrak; 369974Ssam short nspc; 379974Ssam short ncyl; 389974Ssam short *off; 39*10023Ssam } upst[NUPTYPES] = { 40*10023Ssam 32, 19, 32*19, 823, up_off, /* 9300/equiv */ 41*10023Ssam 32, 10, 32*10, 823, fj_off, /* Fuji 160 */ 42*10023Ssam 32, 16, 32*16, 1024, upam_off, /* Capricorn */ 439974Ssam }; 44*10023Ssam 459974Ssam u_char up_offset[16] = { 469974Ssam UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400, 479974Ssam UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800, 489974Ssam UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200, 499974Ssam 0, 0, 0, 0 509974Ssam }; 519974Ssam 52*10023Ssam struct dkbad upbad[MAXNUBA*8]; /* bad sector table */ 53*10023Ssam 549974Ssam upopen(io) 559974Ssam register struct iob *io; 569974Ssam { 57*10023Ssam register struct updevice *upaddr; 589974Ssam register struct upst *st; 599974Ssam 60*10023Ssam if (io->i_boff < 0 || io->i_boff > 7 || st->off[io->i_boff] == -1) 61*10023Ssam _stop("up bad unit"); 62*10023Ssam upaddr = (struct updevice *)ubamem(io->i_unit, ubastd[0]); 639974Ssam while ((upaddr->upcs1 & UP_DVA) == 0) /* infinite wait */ 649974Ssam ; 659974Ssam st = &upst[up_type[io->i_unit]]; 669974Ssam if (up_gottype[io->i_unit] == 0) { 67*10023Ssam register int i; 68*10023Ssam struct iob tio; 69*10023Ssam 709974Ssam upaddr->uphr = UPHR_MAXTRAK; 71*10023Ssam for (st = upst; st < &upst[NUPTYPES]; st++) 72*10023Ssam if (upaddr->uphr == st->ntrak - 1) { 73*10023Ssam up_type[io->i_unit] = st - upst; 74*10023Ssam break; 75*10023Ssam } 76*10023Ssam if (st == &upst[NUPTYPES]) { 77*10023Ssam printf("up%d: uphr=%x\n", upaddr->uphr); 78*10023Ssam _stop("unknown drive type"); 79*10023Ssam } 809974Ssam upaddr->upcs2 = UPCS2_CLR; 819974Ssam #ifdef DEBUG 829974Ssam printf("Unittype=%d\n",up_type[io->i_unit]); 839974Ssam #endif 849974Ssam 85*10023Ssam /* 86*10023Ssam * Read in the bad sector table: 87*10023Ssam * copy the contents of the io structure 88*10023Ssam * to tio for use during the bb pointer 89*10023Ssam * read operation. 90*10023Ssam */ 91*10023Ssam tio = *io; 92*10023Ssam tio.i_bn = st->nspc * st->ncyl - st->nsect; 939974Ssam tio.i_ma = (char *)&upbad[tio.i_unit]; 94*10023Ssam tio.i_cc = sizeof (upbad); 95*10023Ssam tio.i_flgs |= F_RDDATA; 96*10023Ssam for (i = 0; i < 5; i++) { 97*10023Ssam if (upstrategy(&tio, READ) == sizeof (upbad)) 98*10023Ssam break; 999974Ssam tio.i_bn += 2; 1009974Ssam } 1019974Ssam if (i == 5) { 102*10023Ssam printf("Unable to read bad sector table\n"); 103*10023Ssam for (i = 0; i < 126; i++) { 1049974Ssam upbad[io->i_unit].bt_bad[i].bt_cyl = -1; 1059974Ssam upbad[io->i_unit].bt_bad[i].bt_trksec = -1; 1069974Ssam } 1079974Ssam } 1089974Ssam up_gottype[io->i_unit] = 1; 1099974Ssam } 1109974Ssam io->i_boff = st->off[io->i_boff] * st->nspc; 111*10023Ssam io->i_flgs &= ~F_TYPEMASK; 1129974Ssam } 1139974Ssam 1149974Ssam upstrategy(io, func) 1159974Ssam register struct iob *io; 1169974Ssam { 1179974Ssam int unit, cn, tn, sn; 1189974Ssam daddr_t bn; 1199974Ssam int recal, info, waitdry; 1209974Ssam register struct updevice *upaddr = 1219974Ssam (struct updevice *)ubamem(io->i_unit, ubastd[0]); 1229974Ssam register struct upst *st = &upst[up_type[io->i_unit]]; 1239974Ssam 124*10023Ssam if (func != READ && func != WRITE) { 125*10023Ssam io->i_error = ECMD; 126*10023Ssam return (-1); 127*10023Ssam } 1289974Ssam unit = io->i_unit; 1299974Ssam io->i_errcnt = 0; 1309974Ssam recal = 3; 1319974Ssam upaddr->upcs2 = unit; 1329974Ssam if ((upaddr->upds & UPDS_VV) == 0) { 1339974Ssam upaddr->upcs1 = UP_DCLR|UP_GO; 1349974Ssam upaddr->upcs1 = UP_PRESET|UP_GO; 1359974Ssam upaddr->upof = UPOF_FMT22; 1369974Ssam } 1379974Ssam if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) 1389974Ssam _stop("up not ready"); 1399974Ssam info = ubasetup(io, 1); 1409974Ssam upaddr->upwc = -io->i_cc / sizeof (short); 1419974Ssam upaddr->upba = info; 1429974Ssam readmore: 1439974Ssam bn = io->i_bn + btop(io->i_cc + upaddr->upwc*sizeof(short)); 144*10023Ssam while((upaddr->upds & UPDS_DRY) == 0) 145*10023Ssam ; 1469974Ssam if (upstart(io, bn) != 0) 1479974Ssam return (-1); 1489974Ssam do { 1499974Ssam DELAY(25); 1509974Ssam } while ((upaddr->upcs1 & UP_RDY) == 0); 1519974Ssam 1529974Ssam if (((upaddr->upds&UPDS_ERR) | (upaddr->upcs1&UP_TRE)) == 0 ) 1539974Ssam return(io->i_cc); 1549974Ssam 1559974Ssam #ifdef LOGALLERRS 1569974Ssam printf("uper: (c,t,s)=(%d,%d,%d) cs2=%b er1=%b er2=%b wc=%x\n", 1579974Ssam upaddr->updc, upaddr->upda>>8, (upaddr->upda&0x1f-1), 1589974Ssam upaddr->upcs2, UPCS2_BITS, upaddr->uper1, 1599974Ssam UPER1_BITS, upaddr->uper2, UPER2_BITS,-upaddr->upwc); 1609974Ssam #endif 1619974Ssam waitdry = 0; 162*10023Ssam while ((upaddr->upds & UPDS_DRY) == 0 && ++waitdry < 512) 163*10023Ssam DELAY(5); 1649974Ssam if (++io->i_errcnt > 27) { 1659974Ssam /* 1669974Ssam * After 28 retries (16 without offset, and 1679974Ssam * 12 with offset positioning) give up. 1689974Ssam */ 169*10023Ssam io->i_error = EHER; 1709974Ssam hard: 171*10023Ssam if (upaddr->upcs2 & UPCS2_WCE) 172*10023Ssam io->i_error = EWCK; 1739974Ssam bn = io->i_bn + btop(io->i_cc + upaddr->upwc*sizeof(short)); 1749974Ssam cn = bn/st->nspc; 1759974Ssam sn = bn%st->nspc; 1769974Ssam tn = sn/st->nsect; 1779974Ssam sn = sn%st->nsect; 1789974Ssam printf("up error: (cyl,trk,sec)=(%d,%d,%d) cs2=%b er1=%b er2=%b\n", 1799974Ssam cn, tn, sn, 1809974Ssam upaddr->upcs2, UPCS2_BITS, upaddr->uper1, 1819974Ssam UPER1_BITS, upaddr->uper2, UPER2_BITS); 1829974Ssam upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO; 1839974Ssam return (io->i_cc + upaddr->upwc*sizeof(short)); 1849974Ssam } else 1859974Ssam if (upaddr->uper1&UPER1_WLE) { 186*10023Ssam /* 187*10023Ssam * Give up on write locked devices 188*10023Ssam * immediately. 189*10023Ssam */ 190*10023Ssam printf("up%d: write locked\n", unit); 191*10023Ssam return(-1); 1929974Ssam } 1939974Ssam #ifndef NOBADSECT 1949974Ssam else if (upaddr->uper2 & UPER2_BSE) { 1959974Ssam if (upecc( io, BSE)) 1969974Ssam goto success; 1979974Ssam else { 198*10023Ssam io->i_error = EBSE; 1999974Ssam goto hard; 2009974Ssam } 2019974Ssam } 2029974Ssam #endif 2039974Ssam else { 2049974Ssam /* 2059974Ssam * Retriable error. 2069974Ssam * If a soft ecc, correct it 2079974Ssam * Otherwise fall through and retry the transfer 2089974Ssam */ 2099974Ssam if ((upaddr->uper1&(UPER1_DCK|UPER1_ECH))==UPER1_DCK) { 210*10023Ssam upecc(io, ECC); 2119974Ssam goto success; 212*10023Ssam } else 2139974Ssam io->i_active = 0; /* force retry */ 2149974Ssam } 2159974Ssam /* 2169974Ssam * Clear drive error and, every eight attempts, 2179974Ssam * (starting with the fourth) 2189974Ssam * recalibrate to clear the slate. 2199974Ssam */ 2209974Ssam upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO; 2219974Ssam if ((io->i_errcnt&07) == 4 && io->i_active == 0) { 2229974Ssam upaddr->upcs1 = UP_RECAL|UP_GO; 2239974Ssam recal = 0; 2249974Ssam goto nextrecal; 2259974Ssam } 2269974Ssam /* 2279974Ssam * Advance recalibration finite state machine 2289974Ssam * if recalibrate in progress, through 2299974Ssam * RECAL 2309974Ssam * SEEK 2319974Ssam * OFFSET (optional) 2329974Ssam * RETRY 2339974Ssam */ 2349974Ssam switch (recal) { 2359974Ssam 2369974Ssam case 1: 2379974Ssam upaddr->updc = cn; 2389974Ssam upaddr->upcs1 = UP_SEEK|UP_GO; 2399974Ssam goto nextrecal; 240*10023Ssam 2419974Ssam case 2: 2429974Ssam if (io->i_errcnt < 16 || (func & READ) == 0) 2439974Ssam goto donerecal; 2449974Ssam upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22; 2459974Ssam upaddr->upcs1 = UP_OFFSET|UP_GO; 2469974Ssam nextrecal: 2479974Ssam recal++; 2489974Ssam io->i_active = 1; 2499974Ssam goto readmore; 250*10023Ssam 2519974Ssam donerecal: 2529974Ssam case 3: 2539974Ssam recal = 0; 2549974Ssam io->i_active = 0; 2559974Ssam break; 2569974Ssam } 2579974Ssam /* 2589974Ssam * If still ``active'', then don't need any more retries. 2599974Ssam */ 2609974Ssam if (io->i_active) { 2619974Ssam /* 2629974Ssam * If we were offset positioning, 2639974Ssam * return to centerline. 2649974Ssam */ 2659974Ssam if (io->i_errcnt >= 16) { 2669974Ssam upaddr->upof = UPOF_FMT22; 2679974Ssam upaddr->upcs1 = UP_RTC|UP_GO; 268*10023Ssam while ((upaddr->upds&UPDS_DRY) == 0) 2699974Ssam DELAY(25); 2709974Ssam } 2719974Ssam goto readmore; 2729974Ssam } 2739974Ssam success: 2749974Ssam io->i_active = 1; 275*10023Ssam if (upaddr->upwc != 0) 2769974Ssam goto readmore; 2779974Ssam /* 2789974Ssam * Release unibus 2799974Ssam */ 2809974Ssam ubafree(io, info); 2819974Ssam return (io->i_cc); 2829974Ssam } 2839974Ssam 2849974Ssam /* 2859974Ssam * Correct an ECC error, and restart the i/o to complete 2869974Ssam * the transfer if necessary. This is quite complicated because 2879974Ssam * the transfer may be going to an odd memory address base and/or 2889974Ssam * across a page boundary. 2899974Ssam */ 290*10023Ssam upecc(io, flag) 2919974Ssam register struct iob *io; 2929974Ssam int flag; 2939974Ssam { 2949974Ssam register struct updevice *up = 2959974Ssam (struct updevice *)ubamem(io->i_unit, ubastd[0]); 2969974Ssam register struct upst *st; 2979974Ssam register int i; 2989974Ssam caddr_t addr; 2999974Ssam int bit, byte, npf, mask; 3009974Ssam int bn, twc, bbn; 3019974Ssam 3029974Ssam /* 3039974Ssam * Npf is the number of sectors transferred before the sector 3049974Ssam * containing the ECC error, bn is the current block number 3059974Ssam */ 3069974Ssam npf = btop((up->upwc * sizeof(short)) + io->i_cc); 3079974Ssam mask = up->upec2; 3089974Ssam #ifdef UPECCDEBUG 3099974Ssam printf("npf %d mask 0x%x pos %d wc 0x%x\n",npf,mask,up->upec1,-up->upwc); 3109974Ssam #endif 3119974Ssam bn = io->i_bn + npf + 1 ; 3129974Ssam st = &upst[up_type[io->i_unit]]; 3139974Ssam twc = up->upwc; 3149974Ssam io->i_active = 2; 3159974Ssam /* 3169974Ssam * action taken depends on the flag 3179974Ssam */ 3189974Ssam if (flag == ECC) { 3199974Ssam mask = up->upec2; 3209974Ssam printf("up%d: soft ecc sn%d\n", io->i_unit, io->i_bn + npf +1); 3219974Ssam /* 3229974Ssam * Compute the 3239974Ssam * byte and bit position of the error. The variable i 3249974Ssam * is the byte offset in the transfer. 3259974Ssam */ 3269974Ssam i = up->upec1 - 1; /* -1 makes 0 origin */ 3279974Ssam bit = i&07; 3289974Ssam i = (i&~07)>>3; 3299974Ssam byte = i; 3309974Ssam up->upcs1 = UP_TRE|UP_DCLR|UP_GO; 3319974Ssam /* 3329974Ssam * Correct while possible bits remain of mask. Since mask 3339974Ssam * contains 11 bits, we continue while the bit offset is > -11. 3349974Ssam * Also watch out for end of this block and the end of the whole 3359974Ssam * transfer. 3369974Ssam */ 3379974Ssam while (i < 512 && (int)ptob(npf)+i < io->i_cc && bit > -11) { 3389974Ssam /* 339*10023Ssam * addr = vax base addr + (number of sectors transferred 3409974Ssam * before the error sector times the sector size) 3419974Ssam * + byte number 3429974Ssam */ 3439974Ssam addr = io->i_ma + (npf*512) + byte; 3449974Ssam #ifdef UPECCDEBUG 3459974Ssam printf("addr %x old: %x ",addr, *addr); 3469974Ssam #endif 3479974Ssam *addr ^= (mask << bit); 3489974Ssam #ifdef UPECCDEBUG 3499974Ssam printf("new: %x\n", *addr); 3509974Ssam #endif 3519974Ssam byte++; 3529974Ssam i++; 3539974Ssam bit -= 8; 3549974Ssam } 3559974Ssam #ifndef NOBADSECT 3569974Ssam } else if (flag == BSE) { 3579974Ssam /* 3589974Ssam * if not in bad sector table, return 0 3599974Ssam */ 3609974Ssam if ((bbn = isbad(&upbad[io->i_unit], st, bn)) < 0) 3619974Ssam return(0); 3629974Ssam up->upcs1 = UP_TRE|UP_DCLR|UP_GO; 3639974Ssam bbn = st->ncyl * st->nspc -st->nsect - 1 - bbn; 3649974Ssam twc = up->upwc + 512; 3659974Ssam up->upwc = -(512 / sizeof (short)); 3669974Ssam #ifdef UPECCDEBUG 3679974Ssam printf("revector to block %d\n", bbn); 3689974Ssam #endif 3699974Ssam /* 3709974Ssam * Clear the drive & read the replacement sector. 3719974Ssam * If this is in the middle of a transfer, then set up the 3729974Ssam * controller registers in a normal fashion. 3739974Ssam * The ub-address need not be changed. 3749974Ssam */ 375*10023Ssam while (up->upcs1 & UP_RDY == 0) 3769974Ssam ; 3779974Ssam up->upcs1 = UP_TRE|UP_DCLR|UP_GO; 378*10023Ssam if (upstart(io, bbn) != 0) 379*10023Ssam return (0); 3809974Ssam io->i_errcnt = 0; 3819974Ssam do { 3829974Ssam DELAY(25); 3839974Ssam } while ( up->upcs1 & UP_RDY == 0) ; 3849974Ssam if (up->upds & UPDS_ERR || up->upcs1 & UP_TRE) { 3859974Ssam up->upwc = twc -512; 3869974Ssam return (0); 3879974Ssam } 3889974Ssam } 389*10023Ssam if (twc != 0) 3909974Ssam up->upwc = twc; 3919974Ssam return (1); 3929974Ssam } 3939974Ssam 3949974Ssam #ifndef NOBADSECT 3959974Ssam /* 3969974Ssam * Search the bad sector table looking for 3979974Ssam * the specified sector. Return index if found. 3989974Ssam * Return -1 if not found. 3999974Ssam */ 4009974Ssam isbad(bt, st, blno) 4019974Ssam register struct dkbad *bt; 4029974Ssam register struct upst *st; 4039974Ssam { 4049974Ssam register int i; 4059974Ssam register long blk, bblk; 4069974Ssam int trk, sec; 4079974Ssam 4089974Ssam sec = blno % st->nspc; 4099974Ssam trk = sec / st->nsect; 4109974Ssam sec %= st->nsect; 4119974Ssam blk = ((long)(blno/st->nspc) << 16) + (trk << 8) + sec; 4129974Ssam for (i = 0; i < 126; i++) { 413*10023Ssam bblk = ((long)bt->bt_bad[i].bt_cyl << 16) + 414*10023Ssam bt->bt_bad[i].bt_trksec; 4159974Ssam if (blk == bblk) 4169974Ssam return (i); 4179974Ssam if (blk < bblk || bblk < 0) 4189974Ssam break; 4199974Ssam } 4209974Ssam return (-1); 4219974Ssam } 4229974Ssam #endif 4239974Ssam 4249974Ssam upstart(io, bn) 425*10023Ssam register struct iob *io; 426*10023Ssam daddr_t bn; 4279974Ssam { 4289974Ssam register struct updevice *upaddr = 429*10023Ssam (struct updevice *)ubamem(io->i_unit, ubastd[0]); 4309974Ssam register struct upst *st = &upst[up_type[io->i_unit]]; 4319974Ssam int sn, tn; 4329974Ssam 4339974Ssam sn = bn%st->nspc; 4349974Ssam tn = sn/st->nsect; 4359974Ssam sn %= st->nsect; 4369974Ssam upaddr->updc = bn/st->nspc; 4379974Ssam upaddr->upda = (tn << 8) + sn; 438*10023Ssam switch (io->i_flgs&F_TYPEMASK) { 439*10023Ssam 440*10023Ssam case F_RDDATA: 441*10023Ssam upaddr->upcs1 = UP_RCOM|UP_GO; 4429974Ssam break; 443*10023Ssam 444*10023Ssam case F_WRDATA: 445*10023Ssam upaddr->upcs1 = UP_WCOM|UP_GO; 4469974Ssam break; 447*10023Ssam 448*10023Ssam case F_HDR|F_RDDATA: 449*10023Ssam upaddr->upcs1 = UP_RHDR|UP_GO; 450*10023Ssam break; 451*10023Ssam 452*10023Ssam case F_HDR|F_WRDATA: 453*10023Ssam upaddr->upcs1 = UP_WHDR|UP_GO; 454*10023Ssam break; 455*10023Ssam 456*10023Ssam case F_CHECK|F_WRDATA: 457*10023Ssam case F_CHECK|F_RDDATA: 4589974Ssam upaddr->upcs1 = UP_WCDATA|UP_GO; 4599974Ssam break; 460*10023Ssam 461*10023Ssam case F_HCHECK|F_WRDATA: 462*10023Ssam case F_HCHECK|F_RDDATA: 4639974Ssam upaddr->upcs1 = UP_WCHDR|UP_GO; 4649974Ssam break; 465*10023Ssam 4669974Ssam default: 467*10023Ssam io->i_error = ECMD; 468*10023Ssam io->i_flgs &= ~F_TYPEMASK; 469*10023Ssam return (1); 4709974Ssam } 471*10023Ssam return (0); 4729974Ssam } 4739974Ssam 474*10023Ssam /*ARGSUSED*/ 475*10023Ssam upioctl(io, cmd, arg) 476*10023Ssam struct iob *io; 477*10023Ssam int cmd; 478*10023Ssam caddr_t arg; 479*10023Ssam { 480*10023Ssam 481*10023Ssam return (ECMD); 482*10023Ssam } 483