1*10904Shelge /* up.c 4.68 83/02/10 */ 2264Sbill 31937Swnj #include "up.h" 42646Swnj #if NSC > 0 5264Sbill /* 69548Ssam * UNIBUS disk driver with: 79548Ssam * overlapped seeks, 89548Ssam * ECC recovery, and 99548Ssam * bad sector forwarding. 102889Swnj * 112889Swnj * TODO: 123445Sroot * Check that offset recovery code works 13264Sbill */ 149782Ssam #include "../machine/pte.h" 15264Sbill 16264Sbill #include "../h/param.h" 17264Sbill #include "../h/systm.h" 18308Sbill #include "../h/dk.h" 199548Ssam #include "../h/dkbad.h" 20264Sbill #include "../h/buf.h" 21264Sbill #include "../h/conf.h" 22264Sbill #include "../h/dir.h" 23264Sbill #include "../h/user.h" 24264Sbill #include "../h/map.h" 252571Swnj #include "../h/vm.h" 262379Swnj #include "../h/cmap.h" 279357Ssam #include "../h/uio.h" 2810871Ssam #include "../h/kernel.h" 29264Sbill 309357Ssam #include "../vax/cpu.h" 319357Ssam #include "../vax/nexus.h" 329357Ssam #include "../vaxuba/ubavar.h" 339357Ssam #include "../vaxuba/ubareg.h" 349357Ssam #include "../vaxuba/upreg.h" 359357Ssam 362395Swnj struct up_softc { 372395Swnj int sc_softas; 382607Swnj int sc_ndrive; 392395Swnj int sc_wticks; 402674Swnj int sc_recal; 412646Swnj } up_softc[NSC]; 42275Sbill 432395Swnj /* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */ 4410858Ssam struct size { 45264Sbill daddr_t nblocks; 46264Sbill int cyloff; 47264Sbill } up_sizes[8] = { 48264Sbill 15884, 0, /* A=cyl 0 thru 26 */ 49264Sbill 33440, 27, /* B=cyl 27 thru 81 */ 50341Sbill 495520, 0, /* C=cyl 0 thru 814 */ 51264Sbill 15884, 562, /* D=cyl 562 thru 588 */ 52264Sbill 55936, 589, /* E=cyl 589 thru 680 */ 533730Sroot 81376, 681, /* F=cyl 681 thru 814 */ 543730Sroot 153728, 562, /* G=cyl 562 thru 814 */ 55264Sbill 291346, 82, /* H=cyl 82 thru 561 */ 562395Swnj }, fj_sizes[8] = { 572395Swnj 15884, 0, /* A=cyl 0 thru 49 */ 582395Swnj 33440, 50, /* B=cyl 50 thru 154 */ 592395Swnj 263360, 0, /* C=cyl 0 thru 822 */ 602395Swnj 0, 0, 612395Swnj 0, 0, 622395Swnj 0, 0, 632395Swnj 0, 0, 643730Sroot 213664, 155, /* H=cyl 155 thru 822 */ 656851Ssam }, upam_sizes[8] = { 666305Sroot 15884, 0, /* A=cyl 0 thru 31 */ 676305Sroot 33440, 32, /* B=cyl 32 thru 97 */ 686305Sroot 524288, 0, /* C=cyl 0 thru 1023 */ 696602Ssam 27786, 668, 706602Ssam 27786, 723, 716602Ssam 125440, 778, 726305Sroot 181760, 668, /* G=cyl 668 thru 1022 */ 736305Sroot 291346, 98, /* H=cyl 98 thru 667 */ 74264Sbill }; 752395Swnj /* END OF STUFF WHICH SHOULD BE READ IN PER DISK */ 76264Sbill 776346Swnj /* 786346Swnj * On a 780 upSDIST could be 2, but 796346Swnj * in the interest of 750's... 806346Swnj */ 816346Swnj #define _upSDIST 3 /* 1.5 msec */ 822395Swnj #define _upRDIST 4 /* 2.0 msec */ 83264Sbill 842395Swnj int upSDIST = _upSDIST; 852395Swnj int upRDIST = _upRDIST; 862395Swnj 872607Swnj int upprobe(), upslave(), upattach(), updgo(), upintr(); 882983Swnj struct uba_ctlr *upminfo[NSC]; 892983Swnj struct uba_device *updinfo[NUP]; 906383Swnj #define UPIPUNITS 8 916383Swnj struct uba_device *upip[NSC][UPIPUNITS]; /* fuji w/fixed head gives n,n+4 */ 922395Swnj 932607Swnj u_short upstd[] = { 0776700, 0774400, 0776300, 0 }; 942616Swnj struct uba_driver scdriver = 952607Swnj { upprobe, upslave, upattach, updgo, upstd, "up", updinfo, "sc", upminfo }; 962395Swnj struct buf uputab[NUP]; 979548Ssam char upinit[NUP]; 982395Swnj 992395Swnj struct upst { 1002395Swnj short nsect; 1012395Swnj short ntrak; 1022395Swnj short nspc; 1032395Swnj short ncyl; 1042395Swnj struct size *sizes; 1052395Swnj } upst[] = { 1062607Swnj 32, 19, 32*19, 823, up_sizes, /* 9300/cdc */ 1072607Swnj /* 9300 actually has 815 cylinders... */ 1082395Swnj 32, 10, 32*10, 823, fj_sizes, /* fujitsu 160m */ 1096851Ssam 32, 16, 32*16, 1024, upam_sizes, /* ampex capricorn */ 1102395Swnj }; 1112395Swnj 1122629Swnj u_char up_offset[16] = { 1139548Ssam UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400, 1149548Ssam UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800, 1159548Ssam UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200, 1169548Ssam 0, 0, 0, 0 1172629Swnj }; 118264Sbill 1192616Swnj struct buf rupbuf[NUP]; 1209548Ssam struct buf bupbuf[NUP]; 1219548Ssam struct dkbad upbad[NUP]; 122264Sbill 123264Sbill #define b_cylin b_resid 124264Sbill 125264Sbill #ifdef INTRLVE 126264Sbill daddr_t dkblock(); 127264Sbill #endif 1282395Swnj 1292395Swnj int upwstart, upwatch(); /* Have started guardian */ 1302470Swnj int upseek; 1312681Swnj int upwaitdry; 1322395Swnj 1332395Swnj /*ARGSUSED*/ 1342607Swnj upprobe(reg) 1352395Swnj caddr_t reg; 1362395Swnj { 1372459Swnj register int br, cvec; 1382459Swnj 1392607Swnj #ifdef lint 1402607Swnj br = 0; cvec = br; br = cvec; 1412607Swnj #endif 1422629Swnj ((struct updevice *)reg)->upcs1 = UP_IE|UP_RDY; 1432607Swnj DELAY(10); 1442629Swnj ((struct updevice *)reg)->upcs1 = 0; 1459357Ssam return (sizeof (struct updevice)); 1462395Swnj } 1472395Swnj 1482607Swnj upslave(ui, reg) 1492983Swnj struct uba_device *ui; 1502395Swnj caddr_t reg; 1512395Swnj { 1522629Swnj register struct updevice *upaddr = (struct updevice *)reg; 1532395Swnj 1542395Swnj upaddr->upcs1 = 0; /* conservative */ 1552607Swnj upaddr->upcs2 = ui->ui_slave; 1566843Swnj upaddr->upcs1 = UP_NOP|UP_GO; 1573445Sroot if (upaddr->upcs2&UPCS2_NED) { 1582629Swnj upaddr->upcs1 = UP_DCLR|UP_GO; 1592395Swnj return (0); 1602395Swnj } 1612607Swnj return (1); 1622607Swnj } 1632607Swnj 1642607Swnj upattach(ui) 1652983Swnj register struct uba_device *ui; 1662607Swnj { 1672629Swnj register struct updevice *upaddr; 1682607Swnj 1692395Swnj if (upwstart == 0) { 1702759Swnj timeout(upwatch, (caddr_t)0, hz); 1712395Swnj upwstart++; 1722395Swnj } 1732571Swnj if (ui->ui_dk >= 0) 1742571Swnj dk_mspw[ui->ui_dk] = .0000020345; 1752607Swnj upip[ui->ui_ctlr][ui->ui_slave] = ui; 1762607Swnj up_softc[ui->ui_ctlr].sc_ndrive++; 1772629Swnj upaddr = (struct updevice *)ui->ui_addr; 1782629Swnj upaddr->upcs1 = 0; 1792629Swnj upaddr->upcs2 = ui->ui_slave; 1803496Sroot upaddr->uphr = UPHR_MAXTRAK; 1813553Swnj if (upaddr->uphr == 9) 1823496Sroot ui->ui_type = 1; /* fujitsu hack */ 1836305Sroot else if (upaddr->uphr == 15) 1846305Sroot ui->ui_type = 2; /* ampex hack */ 1853496Sroot upaddr->upcs2 = UPCS2_CLR; 1862395Swnj } 187264Sbill 1889548Ssam upopen(dev) 1899548Ssam dev_t dev; 1909548Ssam { 1919548Ssam register int unit = minor(dev) >> 3; 1929548Ssam register struct uba_device *ui; 1939548Ssam 1949548Ssam if (unit >= NUP || (ui = updinfo[unit]) == 0 || ui->ui_alive == 0) 1959548Ssam return (ENXIO); 1969548Ssam return (0); 1979548Ssam } 1989548Ssam 199264Sbill upstrategy(bp) 2002395Swnj register struct buf *bp; 201264Sbill { 2022983Swnj register struct uba_device *ui; 2032395Swnj register struct upst *st; 2042395Swnj register int unit; 2052470Swnj register struct buf *dp; 2062395Swnj int xunit = minor(bp->b_dev) & 07; 2072470Swnj long bn, sz; 208264Sbill 2092470Swnj sz = (bp->b_bcount+511) >> 9; 210264Sbill unit = dkunit(bp); 2112395Swnj if (unit >= NUP) 2122395Swnj goto bad; 2132395Swnj ui = updinfo[unit]; 2142395Swnj if (ui == 0 || ui->ui_alive == 0) 2152395Swnj goto bad; 2162395Swnj st = &upst[ui->ui_type]; 2172395Swnj if (bp->b_blkno < 0 || 2182395Swnj (bn = dkblock(bp))+sz > st->sizes[xunit].nblocks) 2192395Swnj goto bad; 2202395Swnj bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff; 2216305Sroot (void) spl5(); 2222470Swnj dp = &uputab[ui->ui_unit]; 2232470Swnj disksort(dp, bp); 2242470Swnj if (dp->b_active == 0) { 2252395Swnj (void) upustart(ui); 2262395Swnj bp = &ui->ui_mi->um_tab; 2272395Swnj if (bp->b_actf && bp->b_active == 0) 2282395Swnj (void) upstart(ui->ui_mi); 229264Sbill } 2306305Sroot (void) spl0(); 2312395Swnj return; 2322395Swnj 2332395Swnj bad: 2342395Swnj bp->b_flags |= B_ERROR; 2352395Swnj iodone(bp); 2362395Swnj return; 237264Sbill } 238264Sbill 2392674Swnj /* 2402674Swnj * Unit start routine. 2412674Swnj * Seek the drive to be where the data is 2422674Swnj * and then generate another interrupt 2432674Swnj * to actually start the transfer. 2442674Swnj * If there is only one drive on the controller, 2452674Swnj * or we are very close to the data, don't 2462674Swnj * bother with the search. If called after 2472674Swnj * searching once, don't bother to look where 2482674Swnj * we are, just queue for transfer (to avoid 2492674Swnj * positioning forever without transferrring.) 2502674Swnj */ 2512395Swnj upustart(ui) 2522983Swnj register struct uba_device *ui; 253264Sbill { 254264Sbill register struct buf *bp, *dp; 2552983Swnj register struct uba_ctlr *um; 2562629Swnj register struct updevice *upaddr; 2572395Swnj register struct upst *st; 258264Sbill daddr_t bn; 2592674Swnj int sn, csn; 2602607Swnj /* 2612607Swnj * The SC21 cancels commands if you just say 2622629Swnj * cs1 = UP_IE 2632607Swnj * so we are cautious about handling of cs1. 2642607Swnj * Also don't bother to clear as bits other than in upintr(). 2652607Swnj */ 2662674Swnj int didie = 0; 2672674Swnj 2682674Swnj if (ui == 0) 2692674Swnj return (0); 2702983Swnj um = ui->ui_mi; 2712395Swnj dk_busy &= ~(1<<ui->ui_dk); 2722395Swnj dp = &uputab[ui->ui_unit]; 273266Sbill if ((bp = dp->b_actf) == NULL) 274268Sbill goto out; 2752674Swnj /* 2762674Swnj * If the controller is active, just remember 2772674Swnj * that this device would like to be positioned... 2782674Swnj * if we tried to position now we would confuse the SC21. 2792674Swnj */ 2802395Swnj if (um->um_tab.b_active) { 2812459Swnj up_softc[um->um_ctlr].sc_softas |= 1<<ui->ui_slave; 282275Sbill return (0); 283275Sbill } 2842674Swnj /* 2852674Swnj * If we have already positioned this drive, 2862674Swnj * then just put it on the ready queue. 2872674Swnj */ 288276Sbill if (dp->b_active) 289276Sbill goto done; 290276Sbill dp->b_active = 1; 2912629Swnj upaddr = (struct updevice *)um->um_addr; 2922395Swnj upaddr->upcs2 = ui->ui_slave; 2932674Swnj /* 2942674Swnj * If drive has just come up, 2952674Swnj * setup the pack. 2962674Swnj */ 2979548Ssam if ((upaddr->upds & UPDS_VV) == 0 || upinit[ui->ui_unit] == 0) { 2989548Ssam struct buf *bbp = &bupbuf[ui->ui_unit]; 29910858Ssam 3002607Swnj /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ 3019548Ssam upinit[ui->ui_unit] = 1; 3022629Swnj upaddr->upcs1 = UP_IE|UP_DCLR|UP_GO; 3032629Swnj upaddr->upcs1 = UP_IE|UP_PRESET|UP_GO; 3043445Sroot upaddr->upof = UPOF_FMT22; 305268Sbill didie = 1; 3069548Ssam st = &upst[ui->ui_type]; 3079548Ssam bbp->b_flags = B_READ|B_BUSY; 3089548Ssam bbp->b_dev = bp->b_dev; 3099548Ssam bbp->b_bcount = 512; 3109548Ssam bbp->b_un.b_addr = (caddr_t)&upbad[ui->ui_unit]; 3119548Ssam bbp->b_blkno = st->ncyl * st->nspc - st->nsect; 3129548Ssam bbp->b_cylin = st->ncyl - 1; 3139548Ssam dp->b_actf = bbp; 3149548Ssam bbp->av_forw = bp; 3159548Ssam bp = bbp; 316264Sbill } 3172674Swnj /* 3182674Swnj * If drive is offline, forget about positioning. 3192674Swnj */ 3203445Sroot if ((upaddr->upds & (UPDS_DPR|UPDS_MOL)) != (UPDS_DPR|UPDS_MOL)) 321275Sbill goto done; 3222674Swnj /* 3232674Swnj * If there is only one drive, 3242674Swnj * dont bother searching. 3252674Swnj */ 3262607Swnj if (up_softc[um->um_ctlr].sc_ndrive == 1) 3272607Swnj goto done; 3282674Swnj /* 3292674Swnj * Figure out where this transfer is going to 3302674Swnj * and see if we are close enough to justify not searching. 3312674Swnj */ 3322395Swnj st = &upst[ui->ui_type]; 333264Sbill bn = dkblock(bp); 3342395Swnj sn = bn%st->nspc; 3352395Swnj sn = (sn + st->nsect - upSDIST) % st->nsect; 3362674Swnj if (bp->b_cylin - upaddr->updc) 337266Sbill goto search; /* Not on-cylinder */ 338275Sbill else if (upseek) 339275Sbill goto done; /* Ok just to be on-cylinder */ 340264Sbill csn = (upaddr->upla>>6) - sn - 1; 341266Sbill if (csn < 0) 3422395Swnj csn += st->nsect; 3432395Swnj if (csn > st->nsect - upRDIST) 344264Sbill goto done; 345264Sbill search: 3462674Swnj upaddr->updc = bp->b_cylin; 3472674Swnj /* 3482674Swnj * Not on cylinder at correct position, 3492674Swnj * seek/search. 3502674Swnj */ 351275Sbill if (upseek) 3522629Swnj upaddr->upcs1 = UP_IE|UP_SEEK|UP_GO; 3532470Swnj else { 354275Sbill upaddr->upda = sn; 3552629Swnj upaddr->upcs1 = UP_IE|UP_SEARCH|UP_GO; 356275Sbill } 357268Sbill didie = 1; 3582674Swnj /* 3592674Swnj * Mark unit busy for iostat. 3602674Swnj */ 3612395Swnj if (ui->ui_dk >= 0) { 3622395Swnj dk_busy |= 1<<ui->ui_dk; 3632395Swnj dk_seek[ui->ui_dk]++; 364264Sbill } 365268Sbill goto out; 366264Sbill done: 3672674Swnj /* 3682674Swnj * Device is ready to go. 3692674Swnj * Put it on the ready queue for the controller 3702674Swnj * (unless its already there.) 3712674Swnj */ 3722629Swnj if (dp->b_active != 2) { 3732629Swnj dp->b_forw = NULL; 3742629Swnj if (um->um_tab.b_actf == NULL) 3752629Swnj um->um_tab.b_actf = dp; 3762629Swnj else 3772629Swnj um->um_tab.b_actl->b_forw = dp; 3782629Swnj um->um_tab.b_actl = dp; 3792629Swnj dp->b_active = 2; 3802629Swnj } 381268Sbill out: 382268Sbill return (didie); 383264Sbill } 384264Sbill 3852674Swnj /* 3862674Swnj * Start up a transfer on a drive. 3872674Swnj */ 3882395Swnj upstart(um) 3892983Swnj register struct uba_ctlr *um; 390264Sbill { 391264Sbill register struct buf *bp, *dp; 3922983Swnj register struct uba_device *ui; 3932629Swnj register struct updevice *upaddr; 3942470Swnj struct upst *st; 395264Sbill daddr_t bn; 3962681Swnj int dn, sn, tn, cmd, waitdry; 397264Sbill 398264Sbill loop: 3992674Swnj /* 4002674Swnj * Pull a request off the controller queue 4012674Swnj */ 4022395Swnj if ((dp = um->um_tab.b_actf) == NULL) 403268Sbill return (0); 404264Sbill if ((bp = dp->b_actf) == NULL) { 4052395Swnj um->um_tab.b_actf = dp->b_forw; 406264Sbill goto loop; 407264Sbill } 4082674Swnj /* 4092674Swnj * Mark controller busy, and 4102674Swnj * determine destination of this request. 4112674Swnj */ 4122395Swnj um->um_tab.b_active++; 4132395Swnj ui = updinfo[dkunit(bp)]; 414264Sbill bn = dkblock(bp); 4152395Swnj dn = ui->ui_slave; 4162395Swnj st = &upst[ui->ui_type]; 4172395Swnj sn = bn%st->nspc; 4182395Swnj tn = sn/st->nsect; 4192395Swnj sn %= st->nsect; 4202629Swnj upaddr = (struct updevice *)ui->ui_addr; 4212674Swnj /* 4222674Swnj * Select drive if not selected already. 4232674Swnj */ 4242674Swnj if ((upaddr->upcs2&07) != dn) 4252674Swnj upaddr->upcs2 = dn; 4262674Swnj /* 4272674Swnj * Check that it is ready and online 4282674Swnj */ 4292681Swnj waitdry = 0; 4303445Sroot while ((upaddr->upds&UPDS_DRY) == 0) { 4317036Swnj printf("up%d: ds wait ds=%o\n",dkunit(bp),upaddr->upds); 4322681Swnj if (++waitdry > 512) 4332681Swnj break; 4342681Swnj upwaitdry++; 4352681Swnj } 4363445Sroot if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { 4372931Swnj printf("up%d: not ready", dkunit(bp)); 4383445Sroot if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { 4392607Swnj printf("\n"); 4402395Swnj um->um_tab.b_active = 0; 4412395Swnj um->um_tab.b_errcnt = 0; 442893Sbill dp->b_actf = bp->av_forw; 443893Sbill dp->b_active = 0; 444893Sbill bp->b_flags |= B_ERROR; 445893Sbill iodone(bp); 446893Sbill goto loop; 447893Sbill } 4482674Swnj /* 4492674Swnj * Oh, well, sometimes this 4502674Swnj * happens, for reasons unknown. 4512674Swnj */ 4522629Swnj printf(" (flakey)\n"); 453264Sbill } 4542674Swnj /* 4552674Swnj * Setup for the transfer, and get in the 4562674Swnj * UNIBUS adaptor queue. 4572674Swnj */ 4582424Skre upaddr->updc = bp->b_cylin; 459264Sbill upaddr->upda = (tn << 8) + sn; 460264Sbill upaddr->upwc = -bp->b_bcount / sizeof (short); 461264Sbill if (bp->b_flags & B_READ) 4622629Swnj cmd = UP_IE|UP_RCOM|UP_GO; 463264Sbill else 4642629Swnj cmd = UP_IE|UP_WCOM|UP_GO; 4652571Swnj um->um_cmd = cmd; 4663107Swnj (void) ubago(ui); 467268Sbill return (1); 468264Sbill } 469264Sbill 4702674Swnj /* 4712674Swnj * Now all ready to go, stuff the registers. 4722674Swnj */ 4732571Swnj updgo(um) 4742983Swnj struct uba_ctlr *um; 4752395Swnj { 4762629Swnj register struct updevice *upaddr = (struct updevice *)um->um_addr; 4772470Swnj 4786953Swnj um->um_tab.b_active = 2; /* should now be 2 */ 4792571Swnj upaddr->upba = um->um_ubinfo; 4802571Swnj upaddr->upcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300); 4812395Swnj } 4822395Swnj 4832674Swnj /* 4842674Swnj * Handle a disk interrupt. 4852674Swnj */ 4862707Swnj upintr(sc21) 4872395Swnj register sc21; 488264Sbill { 489264Sbill register struct buf *bp, *dp; 4902983Swnj register struct uba_ctlr *um = upminfo[sc21]; 4912983Swnj register struct uba_device *ui; 4922629Swnj register struct updevice *upaddr = (struct updevice *)um->um_addr; 493264Sbill register unit; 4942470Swnj struct up_softc *sc = &up_softc[um->um_ctlr]; 4952607Swnj int as = (upaddr->upas & 0377) | sc->sc_softas; 4962681Swnj int needie = 1, waitdry; 497264Sbill 4982470Swnj sc->sc_wticks = 0; 4992607Swnj sc->sc_softas = 0; 5002674Swnj /* 5012674Swnj * If controller wasn't transferring, then this is an 5022674Swnj * interrupt for attention status on seeking drives. 5032674Swnj * Just service them. 5042674Swnj */ 5056346Swnj if (um->um_tab.b_active != 2 && !sc->sc_recal) { 5062674Swnj if (upaddr->upcs1 & UP_TRE) 5072674Swnj upaddr->upcs1 = UP_TRE; 5082674Swnj goto doattn; 5092674Swnj } 5106953Swnj um->um_tab.b_active = 1; 5112674Swnj /* 5122674Swnj * Get device and block structures, and a pointer 5132983Swnj * to the uba_device for the drive. Select the drive. 5142674Swnj */ 5152674Swnj dp = um->um_tab.b_actf; 5162674Swnj bp = dp->b_actf; 5172674Swnj ui = updinfo[dkunit(bp)]; 5182674Swnj dk_busy &= ~(1 << ui->ui_dk); 5192674Swnj if ((upaddr->upcs2&07) != ui->ui_slave) 5202395Swnj upaddr->upcs2 = ui->ui_slave; 5219548Ssam if (bp->b_flags&B_BAD) { 5229548Ssam if (upecc(ui, CONT)) 5239548Ssam return; 5249548Ssam } 5252674Swnj /* 5262674Swnj * Check for and process errors on 5272674Swnj * either the drive or the controller. 5282674Swnj */ 5293445Sroot if ((upaddr->upds&UPDS_ERR) || (upaddr->upcs1&UP_TRE)) { 5302681Swnj waitdry = 0; 5313445Sroot while ((upaddr->upds & UPDS_DRY) == 0) { 5322681Swnj if (++waitdry > 512) 5332681Swnj break; 5342681Swnj upwaitdry++; 5352681Swnj } 5363445Sroot if (upaddr->uper1&UPER1_WLE) { 5372674Swnj /* 5382674Swnj * Give up on write locked devices 5392674Swnj * immediately. 5402674Swnj */ 5412931Swnj printf("up%d: write locked\n", dkunit(bp)); 5422674Swnj bp->b_flags |= B_ERROR; 5432674Swnj } else if (++um->um_tab.b_errcnt > 27) { 5442674Swnj /* 5452674Swnj * After 28 retries (16 without offset, and 5462674Swnj * 12 with offset positioning) give up. 547*10904Shelge * If the error was header CRC, the header is 548*10904Shelge * screwed up, and the sector may in fact exist 549*10904Shelge * in the bad sector table, better check... 5502674Swnj */ 551*10904Shelge if (upaddr->uper1&UPER1_HCRC) { 552*10904Shelge if (upecc(ui, BSE)) 553*10904Shelge return; 554*10904Shelge } 5559548Ssam hard: 5562931Swnj harderr(bp, "up"); 5579548Ssam printf("cn=%d tn=%d sn=%d cs2=%b er1=%b er2=%b\n", 5589548Ssam upaddr->updc, ((upaddr->upda)>>8)&077, 5599548Ssam (upaddr->upda)&037, 5609548Ssam upaddr->upcs2, UPCS2_BITS, 5619548Ssam upaddr->uper1, UPER1_BITS, 5629548Ssam upaddr->uper2, UPER2_BITS); 5632674Swnj bp->b_flags |= B_ERROR; 5649548Ssam } else if (upaddr->uper2 & UPER2_BSE) { 5659548Ssam if (upecc(ui, BSE)) 5669548Ssam return; 5679548Ssam else 5689548Ssam goto hard; 5692674Swnj } else { 5702674Swnj /* 5712674Swnj * Retriable error. 5722674Swnj * If a soft ecc, correct it (continuing 5732674Swnj * by returning if necessary. 5742674Swnj * Otherwise fall through and retry the transfer 5752674Swnj */ 5767183Sroot if ((upaddr->uper1&(UPER1_DCK|UPER1_ECH))==UPER1_DCK) { 5779548Ssam if (upecc(ui, ECC)) 5782629Swnj return; 5797183Sroot } else 5807183Sroot um->um_tab.b_active = 0; /* force retry */ 5812674Swnj } 5822674Swnj /* 5832674Swnj * Clear drive error and, every eight attempts, 5842674Swnj * (starting with the fourth) 5852674Swnj * recalibrate to clear the slate. 5862674Swnj */ 5872674Swnj upaddr->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO; 5882674Swnj needie = 0; 5893182Swnj if ((um->um_tab.b_errcnt&07) == 4 && um->um_tab.b_active == 0) { 5902674Swnj upaddr->upcs1 = UP_RECAL|UP_IE|UP_GO; 5913160Swnj sc->sc_recal = 0; 5923160Swnj goto nextrecal; 5932674Swnj } 5942674Swnj } 5952674Swnj /* 5963160Swnj * Advance recalibration finite state machine 5973160Swnj * if recalibrate in progress, through 5983160Swnj * RECAL 5993160Swnj * SEEK 6003160Swnj * OFFSET (optional) 6013160Swnj * RETRY 6022674Swnj */ 6033160Swnj switch (sc->sc_recal) { 6043160Swnj 6053160Swnj case 1: 6063160Swnj upaddr->updc = bp->b_cylin; 6073160Swnj upaddr->upcs1 = UP_SEEK|UP_IE|UP_GO; 6083160Swnj goto nextrecal; 6093160Swnj case 2: 6103160Swnj if (um->um_tab.b_errcnt < 16 || (bp->b_flags&B_READ) == 0) 6113160Swnj goto donerecal; 6123445Sroot upaddr->upof = up_offset[um->um_tab.b_errcnt & 017] | UPOF_FMT22; 6133160Swnj upaddr->upcs1 = UP_IE|UP_OFFSET|UP_GO; 6143160Swnj goto nextrecal; 6153160Swnj nextrecal: 6163160Swnj sc->sc_recal++; 6173160Swnj um->um_tab.b_active = 1; 6183160Swnj return; 6193160Swnj donerecal: 6203160Swnj case 3: 6212674Swnj sc->sc_recal = 0; 6223160Swnj um->um_tab.b_active = 0; 6233160Swnj break; 6242674Swnj } 6252674Swnj /* 6262674Swnj * If still ``active'', then don't need any more retries. 6272674Swnj */ 6282674Swnj if (um->um_tab.b_active) { 6292674Swnj /* 6302674Swnj * If we were offset positioning, 6312674Swnj * return to centerline. 6322674Swnj */ 6332674Swnj if (um->um_tab.b_errcnt >= 16) { 6343445Sroot upaddr->upof = UPOF_FMT22; 6352674Swnj upaddr->upcs1 = UP_RTC|UP_GO|UP_IE; 6363445Sroot while (upaddr->upds & UPDS_PIP) 6372674Swnj DELAY(25); 638268Sbill needie = 0; 639264Sbill } 6402674Swnj um->um_tab.b_active = 0; 6412674Swnj um->um_tab.b_errcnt = 0; 6422674Swnj um->um_tab.b_actf = dp->b_forw; 6432674Swnj dp->b_active = 0; 6442674Swnj dp->b_errcnt = 0; 6452674Swnj dp->b_actf = bp->av_forw; 6462674Swnj bp->b_resid = (-upaddr->upwc * sizeof(short)); 6472674Swnj iodone(bp); 6482674Swnj /* 6492674Swnj * If this unit has more work to do, 6502674Swnj * then start it up right away. 6512674Swnj */ 6522674Swnj if (dp->b_actf) 6532674Swnj if (upustart(ui)) 654268Sbill needie = 0; 655264Sbill } 6562674Swnj as &= ~(1<<ui->ui_slave); 6573403Swnj /* 6583403Swnj * Release unibus resources and flush data paths. 6593403Swnj */ 6603403Swnj ubadone(um); 6612674Swnj doattn: 6622674Swnj /* 6632674Swnj * Process other units which need attention. 6642674Swnj * For each unit which needs attention, call 6652674Swnj * the unit start routine to place the slave 6662674Swnj * on the controller device queue. 6672674Swnj */ 6683160Swnj while (unit = ffs(as)) { 6693160Swnj unit--; /* was 1 origin */ 6703160Swnj as &= ~(1<<unit); 6713160Swnj upaddr->upas = 1<<unit; 6726383Swnj if (unit < UPIPUNITS && upustart(upip[sc21][unit])) 6733160Swnj needie = 0; 6743160Swnj } 6752674Swnj /* 6762674Swnj * If the controller is not transferring, but 6772674Swnj * there are devices ready to transfer, start 6782674Swnj * the controller. 6792674Swnj */ 6802395Swnj if (um->um_tab.b_actf && um->um_tab.b_active == 0) 6812395Swnj if (upstart(um)) 682268Sbill needie = 0; 683275Sbill if (needie) 6842629Swnj upaddr->upcs1 = UP_IE; 685264Sbill } 686264Sbill 6879357Ssam upread(dev, uio) 6882616Swnj dev_t dev; 6899357Ssam struct uio *uio; 690264Sbill { 6912616Swnj register int unit = minor(dev) >> 3; 6922470Swnj 6932616Swnj if (unit >= NUP) 6949357Ssam return (ENXIO); 6959357Ssam return (physio(upstrategy, &rupbuf[unit], dev, B_READ, minphys, uio)); 696264Sbill } 697264Sbill 6989357Ssam upwrite(dev, uio) 6992616Swnj dev_t dev; 7009357Ssam struct uio *uio; 701264Sbill { 7022616Swnj register int unit = minor(dev) >> 3; 7032470Swnj 7042616Swnj if (unit >= NUP) 7059357Ssam return (ENXIO); 7069357Ssam return (physio(upstrategy, &rupbuf[unit], dev, B_WRITE, minphys, uio)); 707264Sbill } 708264Sbill 709266Sbill /* 710266Sbill * Correct an ECC error, and restart the i/o to complete 711266Sbill * the transfer if necessary. This is quite complicated because 712266Sbill * the transfer may be going to an odd memory address base and/or 713266Sbill * across a page boundary. 714266Sbill */ 7159548Ssam upecc(ui, flag) 7162983Swnj register struct uba_device *ui; 7179548Ssam int flag; 718264Sbill { 7192629Swnj register struct updevice *up = (struct updevice *)ui->ui_addr; 7202395Swnj register struct buf *bp = uputab[ui->ui_unit].b_actf; 7212983Swnj register struct uba_ctlr *um = ui->ui_mi; 7222395Swnj register struct upst *st; 7232395Swnj struct uba_regs *ubp = ui->ui_hd->uh_uba; 724266Sbill register int i; 725264Sbill caddr_t addr; 726266Sbill int reg, bit, byte, npf, mask, o, cmd, ubaddr; 727264Sbill int bn, cn, tn, sn; 728264Sbill 729264Sbill /* 730266Sbill * Npf is the number of sectors transferred before the sector 731266Sbill * containing the ECC error, and reg is the UBA register 732266Sbill * mapping (the first part of) the transfer. 733266Sbill * O is offset within a memory page of the first byte transferred. 734264Sbill */ 7359548Ssam if (flag == CONT) 7369548Ssam npf = bp->b_error; 7379548Ssam else 73810858Ssam npf = btop((up->upwc * sizeof(short)) + bp->b_bcount); 7392571Swnj reg = btop(um->um_ubinfo&0x3ffff) + npf; 740264Sbill o = (int)bp->b_un.b_addr & PGOFSET; 741264Sbill mask = up->upec2; 7423445Sroot #ifdef UPECCDEBUG 7433403Swnj printf("npf %d reg %x o %d mask %o pos %d\n", npf, reg, o, mask, 7443403Swnj up->upec1); 7453445Sroot #endif 7469548Ssam bn = dkblock(bp); 7479548Ssam st = &upst[ui->ui_type]; 7489548Ssam cn = bp->b_cylin; 7499548Ssam sn = bn%st->nspc + npf; 7509548Ssam tn = sn/st->nsect; 7519548Ssam sn %= st->nsect; 7529548Ssam cn += tn/st->ntrak; 7539548Ssam tn %= st->ntrak; 7542725Swnj ubapurge(um); 7559548Ssam um->um_tab.b_active=2; 756266Sbill /* 7579548Ssam * action taken depends on the flag 758266Sbill */ 7599548Ssam switch(flag){ 7609548Ssam case ECC: 7619548Ssam npf--; 7629548Ssam reg--; 7639548Ssam mask = up->upec2; 7649548Ssam printf("up%d%c: soft ecc sn%d\n", dkunit(bp), 7659548Ssam 'a'+(minor(bp->b_dev)&07), bp->b_blkno + npf); 7669548Ssam /* 7679548Ssam * Flush the buffered data path, and compute the 7689548Ssam * byte and bit position of the error. The variable i 7699548Ssam * is the byte offset in the transfer, the variable byte 7709548Ssam * is the offset from a page boundary in main memory. 7719548Ssam */ 7729548Ssam i = up->upec1 - 1; /* -1 makes 0 origin */ 7739548Ssam bit = i&07; 7749548Ssam i = (i&~07)>>3; 7759548Ssam byte = i + o; 7769548Ssam /* 7779548Ssam * Correct while possible bits remain of mask. Since mask 7789548Ssam * contains 11 bits, we continue while the bit offset is > -11. 7799548Ssam * Also watch out for end of this block and the end of the whole 7809548Ssam * transfer. 7819548Ssam */ 7829548Ssam while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { 7839548Ssam addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ 7849548Ssam (byte & PGOFSET); 7853445Sroot #ifdef UPECCDEBUG 7869548Ssam printf("addr %x map reg %x\n", 7879548Ssam addr, *(int *)(&ubp->uba_map[reg+btop(byte)])); 7889548Ssam printf("old: %x, ", getmemc(addr)); 7893445Sroot #endif 7909548Ssam putmemc(addr, getmemc(addr)^(mask<<bit)); 7913445Sroot #ifdef UPECCDEBUG 7929548Ssam printf("new: %x\n", getmemc(addr)); 7933445Sroot #endif 7949548Ssam byte++; 7959548Ssam i++; 7969580Shelge bit -= 8; 7979548Ssam } 7989548Ssam if (up->upwc == 0) 7999548Ssam return (0); 8009548Ssam npf++; 8019548Ssam reg++; 8029548Ssam break; 8039548Ssam case BSE: 8049548Ssam /* 8059548Ssam * if not in bad sector table, return 0 8069548Ssam */ 8079548Ssam if ((bn = isbad(&upbad[ui->ui_unit], cn, tn, sn)) < 0) 8089548Ssam return(0); 8099548Ssam /* 8109548Ssam * flag this one as bad 8119548Ssam */ 8129548Ssam bp->b_flags |= B_BAD; 8139548Ssam bp->b_error = npf + 1; 8149548Ssam #ifdef UPECCDEBUG 8159548Ssam printf("BSE: restart at %d\n",npf+1); 8169548Ssam #endif 8179548Ssam bn = st->ncyl * st->nspc -st->nsect - 1 - bn; 8189548Ssam cn = bn / st->nspc; 8199548Ssam sn = bn % st->nspc; 8209548Ssam tn = sn / st->nsect; 8219548Ssam sn %= st->nsect; 8229548Ssam up->upwc = -(512 / sizeof (short)); 8239548Ssam #ifdef UPECCDEBUG 8249548Ssam printf("revector to cn %d tn %d sn %d\n", cn, tn, sn); 8259548Ssam #endif 8269548Ssam break; 8279548Ssam case CONT: 8289548Ssam #ifdef UPECCDEBUG 8299548Ssam printf("upecc, CONT: bn %d cn %d tn %d sn %d\n", bn, cn, tn, sn); 8309548Ssam #endif 8319548Ssam bp->b_flags &= ~B_BAD; 8329548Ssam up->upwc = -((bp->b_bcount - (int)ptob(npf)) / sizeof(short)); 8339548Ssam if (up->upwc == 0) 8349548Ssam return(0); 8359548Ssam break; 836264Sbill } 8377183Sroot if (up->upwc == 0) { 8387183Sroot um->um_tab.b_active = 0; 839264Sbill return (0); 8407183Sroot } 841266Sbill /* 842266Sbill * Have to continue the transfer... clear the drive, 843266Sbill * and compute the position where the transfer is to continue. 844266Sbill * We have completed npf+1 sectors of the transfer already; 845266Sbill * restart at offset o of next sector (i.e. in UBA register reg+1). 846266Sbill */ 8472629Swnj #ifdef notdef 8482629Swnj up->uper1 = 0; 8492629Swnj up->upcs1 |= UP_GO; 8502629Swnj #else 8512629Swnj up->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO; 852264Sbill up->updc = cn; 853266Sbill up->upda = (tn << 8) | sn; 8549548Ssam ubaddr = (int)ptob(reg) + o; 855266Sbill up->upba = ubaddr; 856266Sbill cmd = (ubaddr >> 8) & 0x300; 8579548Ssam cmd |= ((bp->b_flags&B_READ)?UP_RCOM:UP_WCOM)|UP_IE|UP_GO; 8589548Ssam um->um_tab.b_errcnt = 0; 859266Sbill up->upcs1 = cmd; 8602629Swnj #endif 861264Sbill return (1); 862264Sbill } 863286Sbill 864286Sbill /* 865286Sbill * Reset driver after UBA init. 866286Sbill * Cancel software state of all pending transfers 867286Sbill * and restart all units and the controller. 868286Sbill */ 8692395Swnj upreset(uban) 8702931Swnj int uban; 871286Sbill { 8722983Swnj register struct uba_ctlr *um; 8732983Swnj register struct uba_device *ui; 8742395Swnj register sc21, unit; 875286Sbill 8762646Swnj for (sc21 = 0; sc21 < NSC; sc21++) { 8772470Swnj if ((um = upminfo[sc21]) == 0 || um->um_ubanum != uban || 8782470Swnj um->um_alive == 0) 8792395Swnj continue; 8802931Swnj printf(" sc%d", sc21); 8812395Swnj um->um_tab.b_active = 0; 8822395Swnj um->um_tab.b_actf = um->um_tab.b_actl = 0; 8832931Swnj up_softc[sc21].sc_recal = 0; 8846346Swnj up_softc[sc21].sc_wticks = 0; 8852571Swnj if (um->um_ubinfo) { 8862571Swnj printf("<%d>", (um->um_ubinfo>>28)&0xf); 8879357Ssam um->um_ubinfo = 0; 8882395Swnj } 8893445Sroot ((struct updevice *)(um->um_addr))->upcs2 = UPCS2_CLR; 8902395Swnj for (unit = 0; unit < NUP; unit++) { 8912395Swnj if ((ui = updinfo[unit]) == 0) 8922395Swnj continue; 8932931Swnj if (ui->ui_alive == 0 || ui->ui_mi != um) 8942395Swnj continue; 8952395Swnj uputab[unit].b_active = 0; 8962395Swnj (void) upustart(ui); 8972395Swnj } 8982395Swnj (void) upstart(um); 899286Sbill } 900286Sbill } 901313Sbill 902313Sbill /* 903313Sbill * Wake up every second and if an interrupt is pending 904313Sbill * but nothing has happened increment a counter. 9052931Swnj * If nothing happens for 20 seconds, reset the UNIBUS 906313Sbill * and begin anew. 907313Sbill */ 908313Sbill upwatch() 909313Sbill { 9102983Swnj register struct uba_ctlr *um; 9112395Swnj register sc21, unit; 9122470Swnj register struct up_softc *sc; 913313Sbill 9142759Swnj timeout(upwatch, (caddr_t)0, hz); 9152646Swnj for (sc21 = 0; sc21 < NSC; sc21++) { 9162395Swnj um = upminfo[sc21]; 9172470Swnj if (um == 0 || um->um_alive == 0) 9182470Swnj continue; 9192470Swnj sc = &up_softc[sc21]; 9202395Swnj if (um->um_tab.b_active == 0) { 9212395Swnj for (unit = 0; unit < NUP; unit++) 9222629Swnj if (uputab[unit].b_active && 9232629Swnj updinfo[unit]->ui_mi == um) 9242395Swnj goto active; 9252470Swnj sc->sc_wticks = 0; 9262395Swnj continue; 9272395Swnj } 9282931Swnj active: 9292470Swnj sc->sc_wticks++; 9302470Swnj if (sc->sc_wticks >= 20) { 9312470Swnj sc->sc_wticks = 0; 9322931Swnj printf("sc%d: lost interrupt\n", sc21); 9332646Swnj ubareset(um->um_ubanum); 9342395Swnj } 935313Sbill } 936313Sbill } 9372379Swnj 9382379Swnj #define DBSIZE 20 9392379Swnj 9402379Swnj updump(dev) 9412379Swnj dev_t dev; 9422379Swnj { 9432629Swnj struct updevice *upaddr; 9442379Swnj char *start; 9453107Swnj int num, blk, unit; 9462379Swnj struct size *sizes; 9472395Swnj register struct uba_regs *uba; 9482983Swnj register struct uba_device *ui; 9492379Swnj register short *rp; 9502395Swnj struct upst *st; 9516848Ssam register int retry; 9522379Swnj 9532395Swnj unit = minor(dev) >> 3; 9542889Swnj if (unit >= NUP) 9552889Swnj return (ENXIO); 9562470Swnj #define phys(cast, addr) ((cast)((int)addr & 0x7fffffff)) 9572983Swnj ui = phys(struct uba_device *, updinfo[unit]); 9582889Swnj if (ui->ui_alive == 0) 9592889Swnj return (ENXIO); 9602395Swnj uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba; 9612983Swnj ubainit(uba); 9622629Swnj upaddr = (struct updevice *)ui->ui_physaddr; 9636848Ssam DELAY(5000000); 9642379Swnj num = maxfree; 9652379Swnj upaddr->upcs2 = unit; 9662983Swnj DELAY(100); 9676848Ssam upaddr->upcs1 = UP_DCLR|UP_GO; 9686848Ssam upaddr->upcs1 = UP_PRESET|UP_GO; 9696848Ssam upaddr->upof = UPOF_FMT22; 9706848Ssam retry = 0; 9716848Ssam do { 9726848Ssam DELAY(25); 9736848Ssam if (++retry > 527) 9746848Ssam break; 9756861Ssam } while ((upaddr->upds & UP_RDY) == 0); 9763445Sroot if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) 9772889Swnj return (EFAULT); 9789357Ssam start = 0; 9798489Sroot st = &upst[ui->ui_type]; 9802395Swnj sizes = phys(struct size *, st->sizes); 9812889Swnj if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks) 9822889Swnj return (EINVAL); 9832379Swnj while (num > 0) { 9842379Swnj register struct pte *io; 9852379Swnj register int i; 9862379Swnj int cn, sn, tn; 9872379Swnj daddr_t bn; 9882379Swnj 9892379Swnj blk = num > DBSIZE ? DBSIZE : num; 9902395Swnj io = uba->uba_map; 9912379Swnj for (i = 0; i < blk; i++) 9922983Swnj *(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV; 9932379Swnj *(int *)io = 0; 9942379Swnj bn = dumplo + btop(start); 9952607Swnj cn = bn/st->nspc + sizes[minor(dev)&07].cyloff; 9962607Swnj sn = bn%st->nspc; 9972607Swnj tn = sn/st->nsect; 9982607Swnj sn = sn%st->nsect; 9992379Swnj upaddr->updc = cn; 10002379Swnj rp = (short *) &upaddr->upda; 10012379Swnj *rp = (tn << 8) + sn; 10022379Swnj *--rp = 0; 10032379Swnj *--rp = -blk*NBPG / sizeof (short); 10042629Swnj *--rp = UP_GO|UP_WCOM; 10056848Ssam retry = 0; 10062379Swnj do { 10072379Swnj DELAY(25); 10086848Ssam if (++retry > 527) 10096848Ssam break; 10102629Swnj } while ((upaddr->upcs1 & UP_RDY) == 0); 10116848Ssam if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { 10126861Ssam printf("up%d: not ready", unit); 10136848Ssam if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { 10146848Ssam printf("\n"); 10156848Ssam return (EIO); 10166848Ssam } 10176848Ssam printf(" (flakey)\n"); 10186848Ssam } 10193445Sroot if (upaddr->upds&UPDS_ERR) 10202889Swnj return (EIO); 10212379Swnj start += blk*NBPG; 10222379Swnj num -= blk; 10232379Swnj } 10242379Swnj return (0); 10252379Swnj } 10261902Swnj #endif 1027