1*11118Ssam /* up.c 4.7 83/02/17 */ 29974Ssam 310023Ssam /* 410023Ssam * UNIBUS peripheral standalone driver 510023Ssam * with ECC correction and bad block forwarding. 610023Ssam * Also supports header operation and write 710023Ssam * check for data and/or header. 810023Ssam */ 910352Shelge 109974Ssam #include "../h/param.h" 119974Ssam #include "../h/inode.h" 129974Ssam #include "../h/fs.h" 139974Ssam #include "../h/dkbad.h" 149974Ssam #include "../h/vmmac.h" 159974Ssam 169974Ssam #include "../vax/pte.h" 179974Ssam #include "../vaxuba/upreg.h" 189974Ssam #include "../vaxuba/ubareg.h" 199974Ssam 2010023Ssam #include "saio.h" 219974Ssam #include "savax.h" 229974Ssam 2310352Shelge #define MAXBADDESC 126 /* max number of bad sectors recorded */ 2410352Shelge #define SECTSIZ 512 /* sector size in bytes */ 2510352Shelge #define HDRSIZ 4 /* number of bytes in sector header */ 26*11118Ssam #define MAXECC 5 /* max # bad bits allowed on ecc w/ F_ECCLM */ 2710352Shelge 2810023Ssam u_short ubastd[] = { 0776700 }; 299974Ssam 309974Ssam char up_gottype[MAXNUBA*8] = { 0 }; 319974Ssam char up_type[MAXNUBA*8] = { 0 }; 32*11118Ssam short up9300_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 }; 33*11118Ssam short up9766_off[] = { 0, 27, 68, -1, -1, -1, -1, 82 }; 349974Ssam short fj_off[] = { 0, 50, 0, -1, -1, -1, -1, 155 }; 359974Ssam /* this is called upam instead of am because hp.c has a similar array */ 369974Ssam short upam_off[] = { 0, 32, 0, 668, 723, 778, 668, 98 }; 3710023Ssam 38*11118Ssam #define NUPTYPES 4 3910352Shelge struct st upst[NUPTYPES] = { 40*11118Ssam 32, 19, 32*19, 815, up9300_off, /* 9300 */ 41*11118Ssam 32, 19, 32*19, 823, up9766_off, /* 9766 */ 4210023Ssam 32, 10, 32*10, 823, fj_off, /* Fuji 160 */ 4310023Ssam 32, 16, 32*16, 1024, upam_off, /* Capricorn */ 449974Ssam }; 4510023Ssam 469974Ssam u_char up_offset[16] = { 479974Ssam UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400, 489974Ssam UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800, 499974Ssam UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200, 509974Ssam 0, 0, 0, 0 519974Ssam }; 529974Ssam 5310023Ssam struct dkbad upbad[MAXNUBA*8]; /* bad sector table */ 5410352Shelge int sectsiz; /* real sector size */ 5510023Ssam 569974Ssam upopen(io) 579974Ssam register struct iob *io; 589974Ssam { 5910352Shelge register unit = io->i_unit; 6010023Ssam register struct updevice *upaddr; 61*11118Ssam register struct st *st; 629974Ssam 6310023Ssam if (io->i_boff < 0 || io->i_boff > 7 || st->off[io->i_boff] == -1) 6410023Ssam _stop("up bad unit"); 6510352Shelge upaddr = (struct updevice *)ubamem(unit, ubastd[0]); 6611085Ssam while ((upaddr->upcs1 & UP_DVA) == 0) 679974Ssam ; 6810352Shelge if (up_gottype[unit] == 0) { 6910023Ssam register int i; 7010023Ssam struct iob tio; 7110023Ssam 72*11118Ssam up_type[unit] = upmaptype(unit, upaddr); 73*11118Ssam if (up_type[unit] < 0) 7410023Ssam _stop("unknown drive type"); 75*11118Ssam st = &upst[up_type[unit]]; 7610023Ssam /* 7710023Ssam * Read in the bad sector table: 7810023Ssam * copy the contents of the io structure 7910023Ssam * to tio for use during the bb pointer 8010023Ssam * read operation. 8110023Ssam */ 8210023Ssam tio = *io; 8310023Ssam tio.i_bn = st->nspc * st->ncyl - st->nsect; 849974Ssam tio.i_ma = (char *)&upbad[tio.i_unit]; 8510638Shelge tio.i_cc = sizeof (struct dkbad); 8610023Ssam tio.i_flgs |= F_RDDATA; 8710023Ssam for (i = 0; i < 5; i++) { 8810638Shelge if (upstrategy(&tio, READ) == sizeof (struct dkbad)) 8910023Ssam break; 909974Ssam tio.i_bn += 2; 919974Ssam } 929974Ssam if (i == 5) { 9310023Ssam printf("Unable to read bad sector table\n"); 9410352Shelge for (i = 0; i < MAXBADDESC; i++) { 9510352Shelge upbad[unit].bt_bad[i].bt_cyl = -1; 9610352Shelge upbad[unit].bt_bad[i].bt_trksec = -1; 979974Ssam } 989974Ssam } 9910352Shelge up_gottype[unit] = 1; 1009974Ssam } 1019974Ssam io->i_boff = st->off[io->i_boff] * st->nspc; 10210023Ssam io->i_flgs &= ~F_TYPEMASK; 1039974Ssam } 1049974Ssam 105*11118Ssam upmaptype(unit, upaddr) 106*11118Ssam int unit; 107*11118Ssam register struct updevice *upaddr; 108*11118Ssam { 109*11118Ssam register struct st *st; 110*11118Ssam int type = -1; 111*11118Ssam 112*11118Ssam upaddr->upcs1 = 0; 113*11118Ssam upaddr->upcs2 = unit % 8; 114*11118Ssam upaddr->uphr = UPHR_MAXTRAK; 115*11118Ssam for (st = upst; st < &upst[NUPTYPES]; st++) 116*11118Ssam if (upaddr->uphr == st->ntrak - 1) { 117*11118Ssam type = st - upst; 118*11118Ssam break; 119*11118Ssam } 120*11118Ssam if (type < 0) 121*11118Ssam printf("up%d: uphr=%x\n", unit, upaddr->uphr); 122*11118Ssam if (type == 0) { 123*11118Ssam upaddr->uphr = UPHR_MAXCYL; 124*11118Ssam if (upaddr->uphr == 822) /* CDC 9766 */ 125*11118Ssam type++; 126*11118Ssam } 127*11118Ssam upaddr->upcs2 = UPCS2_CLR; 128*11118Ssam return (type); 129*11118Ssam } 130*11118Ssam 1319974Ssam upstrategy(io, func) 1329974Ssam register struct iob *io; 1339974Ssam { 13410352Shelge int cn, tn, sn; 13510352Shelge register unit = io->i_unit; 1369974Ssam daddr_t bn; 1379974Ssam int recal, info, waitdry; 1389974Ssam register struct updevice *upaddr = 13910352Shelge (struct updevice *)ubamem(unit, ubastd[0]); 14010352Shelge register struct st *st = &upst[up_type[unit]]; 1419974Ssam 14210352Shelge sectsiz = SECTSIZ; 14311085Ssam if (io->i_flgs & (F_HDR|F_HCHECK)) 14410352Shelge sectsiz += HDRSIZ; 1459974Ssam upaddr->upcs2 = unit; 1469974Ssam if ((upaddr->upds & UPDS_VV) == 0) { 1479974Ssam upaddr->upcs1 = UP_DCLR|UP_GO; 1489974Ssam upaddr->upcs1 = UP_PRESET|UP_GO; 1499974Ssam upaddr->upof = UPOF_FMT22; 1509974Ssam } 15111085Ssam if ((upaddr->upds & UPDS_DREADY) == 0) 1529974Ssam _stop("up not ready"); 1539974Ssam info = ubasetup(io, 1); 1549974Ssam upaddr->upwc = -io->i_cc / sizeof (short); 1559974Ssam upaddr->upba = info; 15611085Ssam recal = 0; 15711085Ssam io->i_errcnt = 0; 15811085Ssam 15910638Shelge restart: 16011085Ssam bn = io->i_bn + (io->i_cc + upaddr->upwc * sizeof(short)) / sectsiz; 16110023Ssam while((upaddr->upds & UPDS_DRY) == 0) 16210023Ssam ; 16310352Shelge if (upstart(io, bn) != 0) { 16410352Shelge ubafree(io, info); 1659974Ssam return (-1); 16610352Shelge } 1679974Ssam do { 1689974Ssam DELAY(25); 1699974Ssam } while ((upaddr->upcs1 & UP_RDY) == 0); 17011085Ssam /* 17111085Ssam * If transfer has completed, free UNIBUS 17211085Ssam * resources and return transfer size. 17311085Ssam */ 17411085Ssam if ((upaddr->upds&UPDS_ERR) == 0 && (upaddr->upcs1&UP_TRE) == 0) { 17510352Shelge ubafree(io, info); 17611085Ssam return (io->i_cc); 17710352Shelge } 1789974Ssam #ifdef LOGALLERRS 1799974Ssam printf("uper: (c,t,s)=(%d,%d,%d) cs2=%b er1=%b er2=%b wc=%x\n", 18011085Ssam upaddr->updc, upaddr->upda>>8, (upaddr->upda&0x1f-1), 18111085Ssam upaddr->upcs2, UPCS2_BITS, upaddr->uper1, 18211085Ssam UPER1_BITS, upaddr->uper2, UPER2_BITS,-upaddr->upwc); 1839974Ssam #endif 1849974Ssam waitdry = 0; 18510352Shelge while ((upaddr->upds & UPDS_DRY) == 0 && ++waitdry < sectsiz) 18610023Ssam DELAY(5); 18711085Ssam if (upaddr->uper1&UPER1_WLE) { 18811085Ssam /* 18911085Ssam * Give up on write locked devices immediately. 19011085Ssam */ 19111085Ssam printf("up%d: write locked\n", unit); 19211085Ssam return (-1); 19311085Ssam } 1949974Ssam if (++io->i_errcnt > 27) { 1959974Ssam /* 1969974Ssam * After 28 retries (16 without offset, and 1979974Ssam * 12 with offset positioning) give up. 1989974Ssam */ 19910023Ssam io->i_error = EHER; 20010023Ssam if (upaddr->upcs2 & UPCS2_WCE) 20110023Ssam io->i_error = EWCK; 20210352Shelge hard: 20311085Ssam bn = io->i_bn + 20411085Ssam (io->i_cc + upaddr->upwc * sizeof (short)) / sectsiz; 2059974Ssam cn = bn/st->nspc; 2069974Ssam sn = bn%st->nspc; 2079974Ssam tn = sn/st->nsect; 2089974Ssam sn = sn%st->nsect; 20911085Ssam printf( 21011085Ssam "up error: (cyl,trk,sec)=(%d,%d,%d) cs2=%b er1=%b er2=%b\n", 21111085Ssam cn, tn, sn, 21211085Ssam upaddr->upcs2, UPCS2_BITS, upaddr->uper1, 21311085Ssam UPER1_BITS, upaddr->uper2, UPER2_BITS); 2149974Ssam upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO; 21510352Shelge io->i_errblk = bn; 21611085Ssam return (io->i_cc + upaddr->upwc * sizeof(short)); 21711085Ssam } 21811085Ssam if (upaddr->uper2 & UPER2_BSE) { 21911085Ssam short wc = upaddr->upwc; 22011085Ssam if ((io->i_flgs&F_NBSF) == 0 && upecc(io, BSE) == 0) { 22111085Ssam if (wc != upaddr->upwc) 22211085Ssam printf("wc %x upwc %x\n", wc, upaddr->upwc); 2239974Ssam goto success; 2249974Ssam } 22511085Ssam io->i_error = EBSE; 22611085Ssam goto hard; 2279974Ssam } 22811085Ssam /* 22911085Ssam * Retriable error. 23011085Ssam * If a soft ecc, correct it 23111085Ssam * Otherwise fall through and retry the transfer 23211085Ssam */ 23311085Ssam if (upaddr->uper1 & UPER1_DCK) { 2349974Ssam /* 23511085Ssam * If a write check command is active, all 23611085Ssam * ecc errors give UPER1_ECH. 2379974Ssam */ 23811085Ssam if ((upaddr->uper1 & UPER1_ECH) == 0 || 23911085Ssam (upaddr->upcs2 & UPCS2_WCE)) { 24011085Ssam if (upecc(io, ECC) == 0) 24111085Ssam goto success; 24211085Ssam io->i_error = EECC; 24311085Ssam goto hard; 24411085Ssam } 24511085Ssam } 2469974Ssam /* 2479974Ssam * Clear drive error and, every eight attempts, 2489974Ssam * (starting with the fourth) 2499974Ssam * recalibrate to clear the slate. 2509974Ssam */ 2519974Ssam upaddr->upcs1 = UP_TRE|UP_DCLR|UP_GO; 25210638Shelge if ((io->i_errcnt&07) == 4 ) { 2539974Ssam upaddr->upcs1 = UP_RECAL|UP_GO; 25410638Shelge recal = 1; 25510638Shelge goto restart; 2569974Ssam } 2579974Ssam /* 2589974Ssam * Advance recalibration finite state machine 2599974Ssam * if recalibrate in progress, through 2609974Ssam * RECAL 2619974Ssam * SEEK 2629974Ssam * OFFSET (optional) 2639974Ssam * RETRY 2649974Ssam */ 2659974Ssam switch (recal) { 2669974Ssam 2679974Ssam case 1: 2689974Ssam upaddr->updc = cn; 2699974Ssam upaddr->upcs1 = UP_SEEK|UP_GO; 27011085Ssam recal = 2; 27110638Shelge goto restart; 27210023Ssam 2739974Ssam case 2: 2749974Ssam if (io->i_errcnt < 16 || (func & READ) == 0) 2759974Ssam goto donerecal; 2769974Ssam upaddr->upof = up_offset[io->i_errcnt & 017] | UPOF_FMT22; 2779974Ssam upaddr->upcs1 = UP_OFFSET|UP_GO; 27811085Ssam recal = 3; 27910638Shelge goto restart; 28010023Ssam 2819974Ssam donerecal: 2829974Ssam case 3: 2839974Ssam recal = 0; 2849974Ssam break; 2859974Ssam } 2869974Ssam /* 28710638Shelge * If we were offset positioning, 28810638Shelge * return to centerline. 2899974Ssam */ 29010638Shelge if (io->i_errcnt >= 16) { 29110638Shelge upaddr->upof = UPOF_FMT22; 29210638Shelge upaddr->upcs1 = UP_RTC|UP_GO; 29310638Shelge while ((upaddr->upds&UPDS_DRY) == 0) 29410638Shelge DELAY(25); 2959974Ssam } 29610638Shelge goto restart; 29711085Ssam 2989974Ssam success: 29910023Ssam if (upaddr->upwc != 0) 30010638Shelge goto restart; 3019974Ssam /* 3029974Ssam * Release unibus 3039974Ssam */ 3049974Ssam ubafree(io, info); 3059974Ssam return (io->i_cc); 3069974Ssam } 3079974Ssam 3089974Ssam /* 3099974Ssam * Correct an ECC error, and restart the i/o to complete 3109974Ssam * the transfer if necessary. This is quite complicated because 3119974Ssam * the transfer may be going to an odd memory address base and/or 3129974Ssam * across a page boundary. 3139974Ssam */ 31410023Ssam upecc(io, flag) 3159974Ssam register struct iob *io; 3169974Ssam int flag; 3179974Ssam { 3189974Ssam register struct updevice *up = 3199974Ssam (struct updevice *)ubamem(io->i_unit, ubastd[0]); 32010352Shelge register struct st *st; 3219974Ssam register int i; 3229974Ssam caddr_t addr; 32310410Shelge int bn, twc, npf, mask, cn, tn, sn; 32410352Shelge daddr_t bbn; 3259974Ssam 3269974Ssam /* 3279974Ssam * Npf is the number of sectors transferred before the sector 3289974Ssam * containing the ECC error, bn is the current block number 3299974Ssam */ 33010352Shelge twc = up->upwc; 33110352Shelge npf = ((twc * sizeof(short)) + io->i_cc)/sectsiz; 3329974Ssam #ifdef UPECCDEBUG 3339974Ssam printf("npf %d mask 0x%x pos %d wc 0x%x\n",npf,mask,up->upec1,-up->upwc); 3349974Ssam #endif 33510352Shelge bn = io->i_bn + npf ; 3369974Ssam st = &upst[up_type[io->i_unit]]; 33710410Shelge cn = bn/st->nspc; 33810410Shelge sn = bn%st->nspc; 33910410Shelge tn = sn/st->nsect; 34010410Shelge sn = sn%st->nsect; 3419974Ssam /* 3429974Ssam * action taken depends on the flag 3439974Ssam */ 3449974Ssam if (flag == ECC) { 34510352Shelge int bit, byte, ecccnt; 34611085Ssam 34710352Shelge ecccnt = 0; 3489974Ssam mask = up->upec2; 34910638Shelge printf("up%d: soft ecc sn%d\n", io->i_unit, bn); 3509974Ssam /* 3519974Ssam * Compute the 3529974Ssam * byte and bit position of the error. The variable i 3539974Ssam * is the byte offset in the transfer. 3549974Ssam */ 3559974Ssam i = up->upec1 - 1; /* -1 makes 0 origin */ 3569974Ssam bit = i&07; 3579974Ssam i = (i&~07)>>3; 3589974Ssam byte = i; 3599974Ssam up->upcs1 = UP_TRE|UP_DCLR|UP_GO; 3609974Ssam /* 3619974Ssam * Correct while possible bits remain of mask. Since mask 3629974Ssam * contains 11 bits, we continue while the bit offset is > -11. 3639974Ssam * Also watch out for end of this block and the end of the whole 3649974Ssam * transfer. 3659974Ssam */ 36610352Shelge while (i < sectsiz && (npf*sectsiz)+i < io->i_cc && bit > -11) { 3679974Ssam /* 36810023Ssam * addr = vax base addr + (number of sectors transferred 3699974Ssam * before the error sector times the sector size) 3709974Ssam * + byte number 3719974Ssam */ 37211085Ssam addr = io->i_ma + (npf * sectsiz) + byte; 3739974Ssam #ifdef UPECCDEBUG 37410352Shelge printf("addr %x old: %x ",addr, (*addr&0xff)); 3759974Ssam #endif 37610352Shelge if ((io->i_flgs & (F_CHECK|F_HCHECK)) == 0) 37710352Shelge *addr ^= (mask << bit); 3789974Ssam #ifdef UPECCDEBUG 37910352Shelge printf("new: %x\n", (*addr&0xff)); 3809974Ssam #endif 3819974Ssam byte++; 3829974Ssam i++; 3839974Ssam bit -= 8; 38411085Ssam if ((io->i_flgs&F_ECCLM) && ++ecccnt > MAXECC) 38511085Ssam return (1); 3869974Ssam } 38711085Ssam return (0); 38811085Ssam } 38911085Ssam if (flag == BSE) { 3909974Ssam /* 39110352Shelge * if not in bad sector table, return 1 (= hard error) 3929974Ssam */ 39310352Shelge up->upcs1 = UP_TRE|UP_DCLR|UP_GO; 39410410Shelge if ((bbn = isbad(&upbad[io->i_unit], cn, tn, sn)) < 0) 39511085Ssam return (1); 3969974Ssam bbn = st->ncyl * st->nspc -st->nsect - 1 - bbn; 39710352Shelge twc = up->upwc + sectsiz; 39811085Ssam up->upwc = - (sectsiz / sizeof (short)); 3999974Ssam #ifdef UPECCDEBUG 4009974Ssam printf("revector to block %d\n", bbn); 4019974Ssam #endif 4029974Ssam /* 4039974Ssam * Clear the drive & read the replacement sector. 4049974Ssam * If this is in the middle of a transfer, then set up the 4059974Ssam * controller registers in a normal fashion. 4069974Ssam * The ub-address need not be changed. 4079974Ssam */ 40810023Ssam while (up->upcs1 & UP_RDY == 0) 4099974Ssam ; 41010023Ssam if (upstart(io, bbn) != 0) 41110352Shelge return (1); /* error */ 41210352Shelge io->i_errcnt = 0; /* success */ 4139974Ssam do { 4149974Ssam DELAY(25); 4159974Ssam } while ( up->upcs1 & UP_RDY == 0) ; 4169974Ssam if (up->upds & UPDS_ERR || up->upcs1 & UP_TRE) { 41710352Shelge up->upwc = twc -sectsiz; 41810352Shelge return (1); 4199974Ssam } 4209974Ssam } 42110638Shelge if (twc) 4229974Ssam up->upwc = twc; 42310352Shelge return (0); 4249974Ssam } 4259974Ssam 4269974Ssam upstart(io, bn) 42710023Ssam register struct iob *io; 42810023Ssam daddr_t bn; 4299974Ssam { 4309974Ssam register struct updevice *upaddr = 43110023Ssam (struct updevice *)ubamem(io->i_unit, ubastd[0]); 43210352Shelge register struct st *st = &upst[up_type[io->i_unit]]; 4339974Ssam int sn, tn; 4349974Ssam 4359974Ssam sn = bn%st->nspc; 4369974Ssam tn = sn/st->nsect; 4379974Ssam sn %= st->nsect; 4389974Ssam upaddr->updc = bn/st->nspc; 4399974Ssam upaddr->upda = (tn << 8) + sn; 44010352Shelge switch (io->i_flgs & F_TYPEMASK) { 44110023Ssam 44210023Ssam case F_RDDATA: 44310023Ssam upaddr->upcs1 = UP_RCOM|UP_GO; 4449974Ssam break; 44510023Ssam 44610023Ssam case F_WRDATA: 44710023Ssam upaddr->upcs1 = UP_WCOM|UP_GO; 4489974Ssam break; 44910023Ssam 45010023Ssam case F_HDR|F_RDDATA: 45110023Ssam upaddr->upcs1 = UP_RHDR|UP_GO; 45210023Ssam break; 45310023Ssam 45410023Ssam case F_HDR|F_WRDATA: 45510023Ssam upaddr->upcs1 = UP_WHDR|UP_GO; 45610023Ssam break; 45710023Ssam 45810023Ssam case F_CHECK|F_WRDATA: 45910023Ssam case F_CHECK|F_RDDATA: 4609974Ssam upaddr->upcs1 = UP_WCDATA|UP_GO; 4619974Ssam break; 46210023Ssam 46310023Ssam case F_HCHECK|F_WRDATA: 46410023Ssam case F_HCHECK|F_RDDATA: 4659974Ssam upaddr->upcs1 = UP_WCHDR|UP_GO; 4669974Ssam break; 46710023Ssam 4689974Ssam default: 46910023Ssam io->i_error = ECMD; 47010023Ssam io->i_flgs &= ~F_TYPEMASK; 47110023Ssam return (1); 4729974Ssam } 47310023Ssam return (0); 4749974Ssam } 4759974Ssam 47610023Ssam /*ARGSUSED*/ 47710023Ssam upioctl(io, cmd, arg) 47810023Ssam struct iob *io; 47910023Ssam int cmd; 48010023Ssam caddr_t arg; 48110023Ssam { 48210352Shelge struct st *st = &upst[up_type[io->i_unit]], *tmp; 48310352Shelge 48410352Shelge switch(cmd) { 48510352Shelge 48610352Shelge case SAIODEVDATA: 48710352Shelge tmp = (struct st *)arg; 48810352Shelge *tmp = *st; 48911085Ssam return (0); 49010352Shelge } 49111085Ssam return (ECMD); 49210023Ssam } 493