X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/5aa9d5ea715be4a2324f99215211875eb3a086e2..de59ccc2375aad4c9f0ff41f3291584ead755f15:/usr/src/sys/vax/uba/up.c diff --git a/usr/src/sys/vax/uba/up.c b/usr/src/sys/vax/uba/up.c index 1a9ee909d7..44e1bdbeb2 100644 --- a/usr/src/sys/vax/uba/up.c +++ b/usr/src/sys/vax/uba/up.c @@ -1,11 +1,14 @@ -/* up.c 4.14 81/02/15 */ +/* up.c 4.53 82/05/27 */ #include "up.h" -#if NSC21 > 0 +#if NSC > 0 /* * UNIBUS disk driver with overlapped seeks and ECC recovery. + * + * TODO: + * Add bad sector forwarding code + * Check that offset recovery code works */ -#define DELAY(N) { register int d; d = N; while (--d > 0); } #include "../h/param.h" #include "../h/systm.h" @@ -18,21 +21,20 @@ #include "../h/user.h" #include "../h/map.h" #include "../h/pte.h" -#include "../h/mba.h" #include "../h/mtpr.h" -#include "../h/uba.h" #include "../h/vm.h" +#include "../h/ubavar.h" +#include "../h/ubareg.h" #include "../h/cmap.h" #include "../h/upreg.h" struct up_softc { int sc_softas; - int sc_seek; - int sc_info; + int sc_ndrive; int sc_wticks; - /* struct uba_minfo sc_minfo; */ -} up_softc[NSC21]; + int sc_recal; +} up_softc[NSC]; /* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */ struct size @@ -40,13 +42,22 @@ struct size daddr_t nblocks; int cyloff; } up_sizes[8] = { +#ifdef ERNIE + 49324, 0, /* A=cyl 0 thru 26 */ +#else 15884, 0, /* A=cyl 0 thru 26 */ +#endif 33440, 27, /* B=cyl 27 thru 81 */ 495520, 0, /* C=cyl 0 thru 814 */ 15884, 562, /* D=cyl 562 thru 588 */ 55936, 589, /* E=cyl 589 thru 680 */ - 81472, 681, /* F=cyl 681 thru 814 */ - 153824, 562, /* G=cyl 562 thru 814 */ +#ifndef NOBADSECT + 81376, 681, /* F=cyl 681 thru 814 */ + 153728, 562, /* G=cyl 562 thru 814 */ +#else + 81472, 681, + 153824, 562, +#endif 291346, 82, /* H=cyl 82 thru 561 */ }, fj_sizes[8] = { 15884, 0, /* A=cyl 0 thru 49 */ @@ -56,30 +67,42 @@ struct size 0, 0, 0, 0, 0, 0, - 213760, 155, /* H=cyl 155 thru 822 */ +#ifndef NOBADSECT + 213664, 155, /* H=cyl 155 thru 822 */ +#else + 213760, 155, +#endif +}, upam_sizes[8] = { + 15884, 0, /* A=cyl 0 thru 31 */ + 33440, 32, /* B=cyl 32 thru 97 */ + 524288, 0, /* C=cyl 0 thru 1023 */ + 27786, 668, + 27786, 723, + 125440, 778, + 181760, 668, /* G=cyl 668 thru 1022 */ + 291346, 98, /* H=cyl 98 thru 667 */ }; /* END OF STUFF WHICH SHOULD BE READ IN PER DISK */ -#define _upSDIST 2 /* 1.0 msec */ +/* + * On a 780 upSDIST could be 2, but + * in the interest of 750's... + */ +#define _upSDIST 3 /* 1.5 msec */ #define _upRDIST 4 /* 2.0 msec */ int upSDIST = _upSDIST; int upRDIST = _upRDIST; -int upcntrlr(), upslave(), updgo(), upintr(); -struct uba_minfo *upminfo[NSC21]; -struct uba_dinfo *updinfo[NUP]; -struct uba_minfo up_minfo[NSC21]; - /* there is no reason for this to be a global structure, it - is only known/used locally, it would be better combined - with up_softc - but that would mean I would have to alter - more than I want to just now. Similarly, there is no longer - any real need for upminfo, but the code still uses it so ... - */ - -extern u_short upstd[]; -struct uba_driver updriver = - { upcntrlr, upslave, updgo, 4, 0, upstd, "up", updinfo }; +int upprobe(), upslave(), upattach(), updgo(), upintr(); +struct uba_ctlr *upminfo[NSC]; +struct uba_device *updinfo[NUP]; +#define UPIPUNITS 8 +struct uba_device *upip[NSC][UPIPUNITS]; /* fuji w/fixed head gives n,n+4 */ + +u_short upstd[] = { 0776700, 0774400, 0776300, 0 }; +struct uba_driver scdriver = + { upprobe, upslave, upattach, updgo, upstd, "up", updinfo, "sc", upminfo }; struct buf uputab[NUP]; struct upst { @@ -89,20 +112,20 @@ struct upst { short ncyl; struct size *sizes; } upst[] = { - 32, 19, 32*19, 815, up_sizes, /* 9300 */ - 32, 19, 32*19, 823, up_sizes, /* so cdc will boot */ + 32, 19, 32*19, 823, up_sizes, /* 9300/cdc */ +/* 9300 actually has 815 cylinders... */ 32, 10, 32*10, 823, fj_sizes, /* fujitsu 160m */ + 32, 16, 32*16, 1024, upam_sizes, /* ampex capricorn */ }; -int up_offset[16] = -{ - P400, M400, P400, M400, - P800, M800, P800, M800, - P1200, M1200, P1200, M1200, - 0, 0, 0, 0, +u_char up_offset[16] = { + UPOF_P400, UPOF_M400, UPOF_P400, UPOF_M400, + UPOF_P800, UPOF_M800, UPOF_P800, UPOF_M800, + UPOF_P1200, UPOF_M1200, UPOF_P1200, UPOF_M1200, + 0, 0, 0, 0 }; -struct buf rupbuf; /* GROT */ +struct buf rupbuf[NUP]; #define b_cylin b_resid @@ -111,78 +134,83 @@ daddr_t dkblock(); #endif int upwstart, upwatch(); /* Have started guardian */ +int upseek; +int upwaitdry; /*ARGSUSED*/ -upcntrlr(um, reg) - struct uba_minfo *um; +upprobe(reg) caddr_t reg; { - ((struct device *)reg)->upcs1 |= (IE|RDY); - return(1); /* just assume it is us (for now) */ + register int br, cvec; + +#ifdef lint + br = 0; cvec = br; br = cvec; +#endif + ((struct updevice *)reg)->upcs1 = UP_IE|UP_RDY; + DELAY(10); + ((struct updevice *)reg)->upcs1 = 0; + return (1); } -upslave(ui, reg, slaveno, uban) - struct uba_dinfo *ui; +upslave(ui, reg) + struct uba_device *ui; caddr_t reg; { - register struct device *upaddr = (struct device *)reg; - register struct uba_minfo *um; - register int sc21; + register struct updevice *upaddr = (struct updevice *)reg; upaddr->upcs1 = 0; /* conservative */ - upaddr->upcs2 = slaveno; - if (upaddr->upcs2&NED) { - upaddr->upcs1 = DCLR|GO; + upaddr->upcs2 = ui->ui_slave; + upaddr->upcs1 = UP_NOP|UP_GO; + if (upaddr->upcs2&UPCS2_NED) { + upaddr->upcs1 = UP_DCLR|UP_GO; return (0); } - /*** we should check device type (return 0 if we don't like it) ***/ - /*** and set type index in ui->ui_type, but we KNOW all we are ***/ - /*** going to see at the minute is a 9300, and the index for a ***/ - /*** 9300 is 0, which is the value already in ui->ui_type, so ..***/ - - um = &up_minfo[0]; - for (sc21 = 0; sc21 < NSC21; sc21++) { - if (um->um_alive == 0) { /* this is a new ctrlr */ - um->um_addr = reg; - um->um_ubanum = uban; - um->um_num = sc21; /* not needed after up_softc - combined with um ??? */ - um->um_alive = 1; - upminfo[sc21] = um; /* just till upminfo vanishes */ - goto found; - } - if (um->um_addr == reg && um->um_ubanum == uban) - goto found; - um++; - } - return(0); /* too many sc21's */ + return (1); +} - found: - ui->ui_mi = um; +upattach(ui) + register struct uba_device *ui; +{ + register struct updevice *upaddr; if (upwstart == 0) { - timeout(upwatch, (caddr_t)0, HZ); + timeout(upwatch, (caddr_t)0, hz); upwstart++; } - return (1); -} - + if (ui->ui_dk >= 0) + dk_mspw[ui->ui_dk] = .0000020345; + upip[ui->ui_ctlr][ui->ui_slave] = ui; + up_softc[ui->ui_ctlr].sc_ndrive++; + upaddr = (struct updevice *)ui->ui_addr; + upaddr->upcs1 = 0; + upaddr->upcs2 = ui->ui_slave; + upaddr->uphr = UPHR_MAXTRAK; + if (upaddr->uphr == 9) + ui->ui_type = 1; /* fujitsu hack */ + else if (upaddr->uphr == 15) + ui->ui_type = 2; /* ampex hack */ + upaddr->upcs2 = UPCS2_CLR; /* - dk_mspw[UPDK_N+unit] = .0000020345; + upaddr->uphr = UPHR_MAXCYL; + printf("maxcyl %d\n", upaddr->uphr); + upaddr->uphr = UPHR_MAXTRAK; + printf("maxtrak %d\n", upaddr->uphr); + upaddr->uphr = UPHR_MAXSECT; + printf("maxsect %d\n", upaddr->uphr); */ - +} + upstrategy(bp) register struct buf *bp; { - register struct uba_dinfo *ui; - register struct uba_minfo *um; + register struct uba_device *ui; register struct upst *st; register int unit; + register struct buf *dp; int xunit = minor(bp->b_dev) & 07; - long sz, bn; + long bn, sz; - sz = bp->b_bcount; - sz = (sz+511) >> 9; /* transfer size in 512 byte sectors */ + sz = (bp->b_bcount+511) >> 9; unit = dkunit(bp); if (unit >= NUP) goto bad; @@ -195,8 +223,9 @@ upstrategy(bp) goto bad; bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff; (void) spl5(); - disksort(&uputab[ui->ui_unit], bp); - if (uputab[ui->ui_unit].b_active == 0) { + dp = &uputab[ui->ui_unit]; + disksort(dp, bp); + if (dp->b_active == 0) { (void) upustart(ui); bp = &ui->ui_mi->um_tab; if (bp->b_actf && bp->b_active == 0) @@ -211,50 +240,92 @@ bad: return; } +/* + * Unit start routine. + * Seek the drive to be where the data is + * and then generate another interrupt + * to actually start the transfer. + * If there is only one drive on the controller, + * or we are very close to the data, don't + * bother with the search. If called after + * searching once, don't bother to look where + * we are, just queue for transfer (to avoid + * positioning forever without transferrring.) + */ upustart(ui) - register struct uba_dinfo *ui; + register struct uba_device *ui; { register struct buf *bp, *dp; - register struct uba_minfo *um; - register struct device *upaddr; + register struct uba_ctlr *um; + register struct updevice *upaddr; register struct upst *st; daddr_t bn; - int sn, cn, csn; + int sn, csn; + /* + * The SC21 cancels commands if you just say + * cs1 = UP_IE + * so we are cautious about handling of cs1. + * Also don't bother to clear as bits other than in upintr(). + */ int didie = 0; - /* SC21 cancels commands if you say cs1 = IE, so dont */ - /* being ultra-cautious, we clear as bits only in upintr() */ + if (ui == 0) + return (0); + um = ui->ui_mi; dk_busy &= ~(1<ui_dk); dp = &uputab[ui->ui_unit]; if ((bp = dp->b_actf) == NULL) goto out; - /* dont confuse controller by giving SEARCH while dt in progress */ - um = ui->ui_mi; + /* + * If the controller is active, just remember + * that this device would like to be positioned... + * if we tried to position now we would confuse the SC21. + */ if (um->um_tab.b_active) { - up_softc[um->um_num].sc_softas |= 1<ui_slave; + up_softc[um->um_ctlr].sc_softas |= 1<ui_slave; return (0); } + /* + * If we have already positioned this drive, + * then just put it on the ready queue. + */ if (dp->b_active) goto done; dp->b_active = 1; - upaddr = (struct device *)um->um_addr; + upaddr = (struct updevice *)um->um_addr; upaddr->upcs2 = ui->ui_slave; - if ((upaddr->upds & VV) == 0) { - upaddr->upcs1 = IE|DCLR|GO; - upaddr->upcs1 = IE|PRESET|GO; - upaddr->upof = FMT22; + /* + * If drive has just come up, + * setup the pack. + */ + if ((upaddr->upds & UPDS_VV) == 0) { + /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ + upaddr->upcs1 = UP_IE|UP_DCLR|UP_GO; + upaddr->upcs1 = UP_IE|UP_PRESET|UP_GO; + upaddr->upof = UPOF_FMT22; didie = 1; } - if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) + /* + * If drive is offline, forget about positioning. + */ + if ((upaddr->upds & (UPDS_DPR|UPDS_MOL)) != (UPDS_DPR|UPDS_MOL)) goto done; + /* + * If there is only one drive, + * dont bother searching. + */ + if (up_softc[um->um_ctlr].sc_ndrive == 1) + goto done; + /* + * Figure out where this transfer is going to + * and see if we are close enough to justify not searching. + */ st = &upst[ui->ui_type]; bn = dkblock(bp); - cn = bp->b_cylin; sn = bn%st->nspc; sn = (sn + st->nsect - upSDIST) % st->nsect; - if (cn - upaddr->updc) + if (bp->b_cylin - upaddr->updc) goto search; /* Not on-cylinder */ -/**** WHAT SHOULD THIS BE NOW ??? else if (upseek) goto done; /* Ok just to be on-cylinder */ csn = (upaddr->upla>>6) - sn - 1; @@ -263,43 +334,62 @@ upustart(ui) if (csn > st->nsect - upRDIST) goto done; search: - upaddr->updc = cn; -/*** ANOTHER OCCURRENCE + upaddr->updc = bp->b_cylin; + /* + * Not on cylinder at correct position, + * seek/search. + */ if (upseek) - upaddr->upcs1 = IE|SEEK|GO; - else ****/ { + upaddr->upcs1 = UP_IE|UP_SEEK|UP_GO; + else { upaddr->upda = sn; - upaddr->upcs1 = IE|SEARCH|GO; + upaddr->upcs1 = UP_IE|UP_SEARCH|UP_GO; } didie = 1; + /* + * Mark unit busy for iostat. + */ if (ui->ui_dk >= 0) { dk_busy |= 1<ui_dk; dk_seek[ui->ui_dk]++; } goto out; done: - dp->b_forw = NULL; - if (um->um_tab.b_actf == NULL) - um->um_tab.b_actf = dp; - else - um->um_tab.b_actl->b_forw = dp; - um->um_tab.b_actl = dp; + /* + * Device is ready to go. + * Put it on the ready queue for the controller + * (unless its already there.) + */ + if (dp->b_active != 2) { + dp->b_forw = NULL; + if (um->um_tab.b_actf == NULL) + um->um_tab.b_actf = dp; + else + um->um_tab.b_actl->b_forw = dp; + um->um_tab.b_actl = dp; + dp->b_active = 2; + } out: return (didie); } +/* + * Start up a transfer on a drive. + */ upstart(um) - register struct uba_minfo *um; + register struct uba_ctlr *um; { register struct buf *bp, *dp; - register struct uba_dinfo *ui; - register unit; - register struct device *upaddr; - register struct upst *st; + register struct uba_device *ui; + register struct updevice *upaddr; + struct upst *st; daddr_t bn; - int dn, sn, tn, cmd; + int dn, sn, tn, cmd, waitdry; loop: + /* + * Pull a request off the controller queue + */ if ((dp = um->um_tab.b_actf) == NULL) return (0); if ((bp = dp->b_actf) == NULL) { @@ -307,8 +397,8 @@ loop: goto loop; } /* - * Mark the controller busy, and multi-part disk address. - * Select the unit on which the i/o is to take place. + * Mark controller busy, and + * determine destination of this request. */ um->um_tab.b_active++; ui = updinfo[dkunit(bp)]; @@ -318,192 +408,272 @@ loop: sn = bn%st->nspc; tn = sn/st->nsect; sn %= st->nsect; - upaddr = (struct device *)ui->ui_addr; - if ((upaddr->upcs2 & 07) != dn) + upaddr = (struct updevice *)ui->ui_addr; + /* + * Select drive if not selected already. + */ + if ((upaddr->upcs2&07) != dn) upaddr->upcs2 = dn; - up_softc[um->um_num].sc_info = - ubasetup(ui->ui_ubanum, bp, UBA_NEEDBDP|UBA_CANTWAIT); /* - * If drive is not present and on-line, then - * get rid of this with an error and loop to get - * rid of the rest of its queued requests. - * (Then on to any other ready drives.) + * Check that it is ready and online */ - if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { - printf("!DPR || !MOL, unit %d, ds %o", dn, upaddr->upds); - if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { - printf("-- hard\n"); + waitdry = 0; + while ((upaddr->upds&UPDS_DRY) == 0) { + if (++waitdry > 512) + break; + upwaitdry++; + } + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { + printf("up%d: not ready", dkunit(bp)); + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { + printf("\n"); um->um_tab.b_active = 0; um->um_tab.b_errcnt = 0; dp->b_actf = bp->av_forw; dp->b_active = 0; bp->b_flags |= B_ERROR; iodone(bp); - /* A funny place to do this ... */ - ubarelse(ui->ui_ubanum, &up_softc[um->um_num].sc_info); goto loop; } - printf("-- came back\n"); + /* + * Oh, well, sometimes this + * happens, for reasons unknown. + */ + printf(" (flakey)\n"); } /* - * If this is a retry, then with the 16'th retry we - * begin to try offsetting the heads to recover the data. - */ - if (um->um_tab.b_errcnt >= 16 && (bp->b_flags&B_READ) != 0) { - upaddr->upof = up_offset[um->um_tab.b_errcnt & 017] | FMT22; - upaddr->upcs1 = IE|OFFSET|GO; - while (upaddr->upds & PIP) - DELAY(25); - } - /* - * Now set up the transfer, retrieving the high - * 2 bits of the UNIBUS address from the information - * returned by ubasetup() for the cs1 register bits 8 and 9. + * Setup for the transfer, and get in the + * UNIBUS adaptor queue. */ upaddr->updc = bp->b_cylin; upaddr->upda = (tn << 8) + sn; - upaddr->upba = up_softc[um->um_num].sc_info; upaddr->upwc = -bp->b_bcount / sizeof (short); - cmd = (up_softc[um->um_num].sc_info >> 8) & 0x300; if (bp->b_flags & B_READ) - cmd |= IE|RCOM|GO; + cmd = UP_IE|UP_RCOM|UP_GO; else - cmd |= IE|WCOM|GO; - upaddr->upcs1 = cmd; - /* - * This is a controller busy situation. - * Record in dk slot NUP+UPDK_N (after last drive) - * unless there aren't that many slots reserved for - * us in which case we record this as a drive busy - * (if there is room for that). - */ - unit = ui->ui_dk; - dk_busy |= 1<b_bcount>>6; + cmd = UP_IE|UP_WCOM|UP_GO; + um->um_cmd = cmd; + (void) ubago(ui); return (1); } -updgo() +/* + * Now all ready to go, stuff the registers. + */ +updgo(um) + struct uba_ctlr *um; { + register struct updevice *upaddr = (struct updevice *)um->um_addr; + + um->um_tab.b_active = 2; /* should now be 2 */ + upaddr->upba = um->um_ubinfo; + upaddr->upcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300); } /* - * Handle a device interrupt. - * - * If the transferring drive needs attention, service it - * retrying on error or beginning next transfer. - * Service all other ready drives, calling ustart to transfer - * their blocks to the ready queue in um->um_tab, and then restart - * the controller if there is anything to do. + * Handle a disk interrupt. */ upintr(sc21) register sc21; { register struct buf *bp, *dp; - register struct uba_minfo *um = upminfo[sc21]; - register struct uba_dinfo *ui; - register struct device *upaddr = (struct device *)um->um_addr; + register struct uba_ctlr *um = upminfo[sc21]; + register struct uba_device *ui; + register struct updevice *upaddr = (struct updevice *)um->um_addr; register unit; - int as = upaddr->upas & 0377; - int needie = 1; + struct up_softc *sc = &up_softc[um->um_ctlr]; + int as = (upaddr->upas & 0377) | sc->sc_softas; + int needie = 1, waitdry; - (void) spl6(); - up_softc[um->um_num].sc_wticks = 0; - if (um->um_tab.b_active) { - if ((upaddr->upcs1 & RDY) == 0) { - printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr->upcs1, - upaddr->upds, upaddr->upwc); - printf("as=%d act %d %d %d\n", as, um->um_tab.b_active, - uputab[0].b_active, uputab[1].b_active); - } - dp = um->um_tab.b_actf; - bp = dp->b_actf; - ui = updinfo[dkunit(bp)]; - dk_busy &= ~(1 << ui->ui_dk); + sc->sc_wticks = 0; + sc->sc_softas = 0; + /* + * If controller wasn't transferring, then this is an + * interrupt for attention status on seeking drives. + * Just service them. + */ + if (um->um_tab.b_active != 2 && !sc->sc_recal) { + if (upaddr->upcs1 & UP_TRE) + upaddr->upcs1 = UP_TRE; + goto doattn; + } + um->um_tab.b_active = 1; + /* + * Get device and block structures, and a pointer + * to the uba_device for the drive. Select the drive. + */ + dp = um->um_tab.b_actf; + bp = dp->b_actf; + ui = updinfo[dkunit(bp)]; + dk_busy &= ~(1 << ui->ui_dk); + if ((upaddr->upcs2&07) != ui->ui_slave) upaddr->upcs2 = ui->ui_slave; - if ((upaddr->upds&ERR) || (upaddr->upcs1&TRE)) { - int cs2; - while ((upaddr->upds & DRY) == 0) - DELAY(25); - if (++um->um_tab.b_errcnt > 28 || upaddr->uper1&WLE) - bp->b_flags |= B_ERROR; - else - um->um_tab.b_active = 0; /* force retry */ - if (um->um_tab.b_errcnt > 27) { - cs2 = (int)upaddr->upcs2; - deverror(bp, cs2, (int)upaddr->uper1); - } - if ((upaddr->uper1&(DCK|ECH))==DCK && upecc(ui)) - return; - upaddr->upcs1 = TRE|IE|DCLR|GO; - needie = 0; - if ((um->um_tab.b_errcnt&07) == 4) { - upaddr->upcs1 = RECAL|GO|IE; - while(upaddr->upds & PIP) - DELAY(25); - } - if (um->um_tab.b_errcnt == 28 && cs2&(NEM|MXF)) { - printf("FLAKEY UP "); - ubareset(um->um_ubanum); - return; - } + /* + * Check for and process errors on + * either the drive or the controller. + */ + if ((upaddr->upds&UPDS_ERR) || (upaddr->upcs1&UP_TRE)) { + waitdry = 0; + while ((upaddr->upds & UPDS_DRY) == 0) { + if (++waitdry > 512) + break; + upwaitdry++; } - if (um->um_tab.b_active) { - if (um->um_tab.b_errcnt >= 16) { - upaddr->upcs1 = RTC|GO|IE; - while (upaddr->upds & PIP) - DELAY(25); - needie = 0; - } - um->um_tab.b_active = 0; - um->um_tab.b_errcnt = 0; - um->um_tab.b_actf = dp->b_forw; - dp->b_active = 0; - dp->b_errcnt = 0; - dp->b_actf = bp->av_forw; - bp->b_resid = (-upaddr->upwc * sizeof(short)); - if (bp->b_resid) - printf("resid %d ds %o er? %o %o %o\n", - bp->b_resid, upaddr->upds, - upaddr->uper1, upaddr->uper2, upaddr->uper3); - iodone(bp); - if (dp->b_actf) - if (upustart(ui)) - needie = 0; + if (upaddr->uper1&UPER1_WLE) { + /* + * Give up on write locked devices + * immediately. + */ + printf("up%d: write locked\n", dkunit(bp)); + bp->b_flags |= B_ERROR; + } else if (++um->um_tab.b_errcnt > 27) { + /* + * After 28 retries (16 without offset, and + * 12 with offset positioning) give up. + */ + harderr(bp, "up"); + printf("cs2=%b er1=%b er2=%b\n", + upaddr->upcs2, UPCS2_BITS, + upaddr->uper1, UPER1_BITS, + upaddr->uper2, UPER2_BITS); + bp->b_flags |= B_ERROR; + } else { + /* + * Retriable error. + * If a soft ecc, correct it (continuing + * by returning if necessary. + * Otherwise fall through and retry the transfer + */ + um->um_tab.b_active = 0; /* force retry */ + if ((upaddr->uper1&(UPER1_DCK|UPER1_ECH))==UPER1_DCK) + if (upecc(ui)) + return; + } + /* + * Clear drive error and, every eight attempts, + * (starting with the fourth) + * recalibrate to clear the slate. + */ + upaddr->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO; + needie = 0; + if ((um->um_tab.b_errcnt&07) == 4 && um->um_tab.b_active == 0) { + upaddr->upcs1 = UP_RECAL|UP_IE|UP_GO; + sc->sc_recal = 0; + goto nextrecal; } - up_softc[um->um_num].sc_softas &= ~(1<ui_slave); - ubarelse(ui->ui_ubanum, &up_softc[um->um_num].sc_info); - } else { - if (upaddr->upcs1 & TRE) - upaddr->upcs1 = TRE; } - as |= up_softc[um->um_num].sc_softas; - for (unit = 0; unit < NUP; unit++) { - if ((ui = updinfo[unit]) == 0 || ui->ui_mi != um) - continue; - if (as & (1<upas = 1<sc_recal) { + + case 1: + upaddr->updc = bp->b_cylin; + upaddr->upcs1 = UP_SEEK|UP_IE|UP_GO; + goto nextrecal; + case 2: + if (um->um_tab.b_errcnt < 16 || (bp->b_flags&B_READ) == 0) + goto donerecal; + upaddr->upof = up_offset[um->um_tab.b_errcnt & 017] | UPOF_FMT22; + upaddr->upcs1 = UP_IE|UP_OFFSET|UP_GO; + goto nextrecal; + nextrecal: + sc->sc_recal++; + um->um_tab.b_active = 1; + return; + donerecal: + case 3: + sc->sc_recal = 0; + um->um_tab.b_active = 0; + break; + } + /* + * If still ``active'', then don't need any more retries. + */ + if (um->um_tab.b_active) { + /* + * If we were offset positioning, + * return to centerline. + */ + if (um->um_tab.b_errcnt >= 16) { + upaddr->upof = UPOF_FMT22; + upaddr->upcs1 = UP_RTC|UP_GO|UP_IE; + while (upaddr->upds & UPDS_PIP) + DELAY(25); + needie = 0; + } + um->um_tab.b_active = 0; + um->um_tab.b_errcnt = 0; + um->um_tab.b_actf = dp->b_forw; + dp->b_active = 0; + dp->b_errcnt = 0; + dp->b_actf = bp->av_forw; + bp->b_resid = (-upaddr->upwc * sizeof(short)); + iodone(bp); + /* + * If this unit has more work to do, + * then start it up right away. + */ + if (dp->b_actf) if (upustart(ui)) needie = 0; - } } + as &= ~(1<ui_slave); + /* + * Release unibus resources and flush data paths. + */ + ubadone(um); +doattn: + /* + * Process other units which need attention. + * For each unit which needs attention, call + * the unit start routine to place the slave + * on the controller device queue. + */ + while (unit = ffs(as)) { + unit--; /* was 1 origin */ + as &= ~(1<upas = 1<um_tab.b_actf && um->um_tab.b_active == 0) if (upstart(um)) needie = 0; if (needie) - upaddr->upcs1 = IE; + upaddr->upcs1 = UP_IE; } upread(dev) + dev_t dev; { - physio(upstrategy, &rupbuf, dev, B_READ, minphys); + register int unit = minor(dev) >> 3; + + if (unit >= NUP) + u.u_error = ENXIO; + else + physio(upstrategy, &rupbuf[unit], dev, B_READ, minphys); } upwrite(dev) + dev_t dev; { - physio(upstrategy, &rupbuf, dev, B_WRITE, minphys); + register int unit = minor(dev) >> 3; + + if (unit >= NUP) + u.u_error = ENXIO; + else + physio(upstrategy, &rupbuf[unit], dev, B_WRITE, minphys); } /* @@ -513,11 +683,11 @@ upwrite(dev) * across a page boundary. */ upecc(ui) - register struct uba_dinfo *ui; + register struct uba_device *ui; { - register struct device *up = (struct device *)ui->ui_addr; + register struct updevice *up = (struct updevice *)ui->ui_addr; register struct buf *bp = uputab[ui->ui_unit].b_actf; - register struct uba_minfo *um = ui->ui_mi; + register struct uba_ctlr *um = ui->ui_mi; register struct upst *st; struct uba_regs *ubp = ui->ui_hd->uh_uba; register int i; @@ -532,22 +702,22 @@ upecc(ui) * O is offset within a memory page of the first byte transferred. */ npf = btop((up->upwc * sizeof(short)) + bp->b_bcount) - 1; - reg = btop(up_softc[um->um_num].sc_info&0x3ffff) + npf; + reg = btop(um->um_ubinfo&0x3ffff) + npf; o = (int)bp->b_un.b_addr & PGOFSET; - printf("%D ", bp->b_blkno+npf); - prdev("ECC", bp->b_dev); + printf("up%d%c: soft ecc sn%d\n", dkunit(bp), + 'a'+(minor(bp->b_dev)&07), bp->b_blkno + npf); mask = up->upec2; - if (mask == 0) { - up->upof = FMT22; /* == RTC ???? */ - return (0); - } +#ifdef UPECCDEBUG + printf("npf %d reg %x o %d mask %o pos %d\n", npf, reg, o, mask, + up->upec1); +#endif /* * Flush the buffered data path, and compute the * byte and bit position of the error. The variable i * is the byte offset in the transfer, the variable byte * is the offset from a page boundary in main memory. */ - ubp->uba_dpr[(up_softc[um->um_num].sc_info>>28)&0x0f] |= UBA_BNE; + ubapurge(um); i = up->upec1 - 1; /* -1 makes 0 origin */ bit = i&07; i = (i&~07)>>3; @@ -561,12 +731,19 @@ upecc(ui) while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ (byte & PGOFSET); +#ifdef UPECCDEBUG + printf("addr %x map reg %x\n", + addr, *(int *)(&ubp->uba_map[reg+btop(byte)])); + printf("old: %x, ", getmemc(addr)); +#endif putmemc(addr, getmemc(addr)^(mask<um_tab.b_active++; /* Either complete or continuing... */ if (up->upwc == 0) return (0); /* @@ -575,7 +752,11 @@ upecc(ui) * We have completed npf+1 sectors of the transfer already; * restart at offset o of next sector (i.e. in UBA register reg+1). */ - up->upcs1 = TRE|IE|DCLR|GO; +#ifdef notdef + up->uper1 = 0; + up->upcs1 |= UP_GO; +#else + up->upcs1 = UP_TRE|UP_IE|UP_DCLR|UP_GO; bn = dkblock(bp); st = &upst[ui->ui_type]; cn = bp->b_cylin; @@ -589,8 +770,10 @@ upecc(ui) ubaddr = (int)ptob(reg+1) + o; up->upba = ubaddr; cmd = (ubaddr >> 8) & 0x300; - cmd |= IE|GO|RCOM; + cmd |= UP_IE|UP_GO|UP_RCOM; + um->um_tab.b_active = 2; /* continuing transfer ... */ up->upcs1 = cmd; +#endif return (1); } @@ -600,35 +783,30 @@ upecc(ui) * and restart all units and the controller. */ upreset(uban) + int uban; { - register struct uba_minfo *um; - register struct uba_dinfo *ui; + register struct uba_ctlr *um; + register struct uba_device *ui; register sc21, unit; - int any = 0; - for (sc21 = 0; sc21 < NSC21; sc21++) { - if ((um = upminfo[sc21]) == 0) - continue; - if (um->um_ubanum != uban) + for (sc21 = 0; sc21 < NSC; sc21++) { + if ((um = upminfo[sc21]) == 0 || um->um_ubanum != uban || + um->um_alive == 0) continue; - if (!um->um_alive) - continue; - if (any == 0) { - printf(" up"); - DELAY(15000000); /* give it time to self-test */ - any++; - } + printf(" sc%d", sc21); um->um_tab.b_active = 0; um->um_tab.b_actf = um->um_tab.b_actl = 0; - if (up_softc[um->um_num].sc_info) { - printf("<%d>", (up_softc[um->um_num].sc_info>>28)&0xf); - ubarelse(um->um_ubanum, &up_softc[um->um_num].sc_info); + up_softc[sc21].sc_recal = 0; + up_softc[sc21].sc_wticks = 0; + if (um->um_ubinfo) { + printf("<%d>", (um->um_ubinfo>>28)&0xf); + ubadone(um); } - ((struct device *)(um->um_addr))->upcs2 = CLR; + ((struct updevice *)(um->um_addr))->upcs2 = UPCS2_CLR; for (unit = 0; unit < NUP; unit++) { if ((ui = updinfo[unit]) == 0) continue; - if (ui->ui_alive == 0) + if (ui->ui_alive == 0 || ui->ui_mi != um) continue; uputab[unit].b_active = 0; (void) upustart(ui); @@ -640,32 +818,35 @@ upreset(uban) /* * Wake up every second and if an interrupt is pending * but nothing has happened increment a counter. - * If nothing happens for 20 seconds, reset the controller + * If nothing happens for 20 seconds, reset the UNIBUS * and begin anew. */ upwatch() { - register struct uba_minfo *um; + register struct uba_ctlr *um; register sc21, unit; + register struct up_softc *sc; - timeout(upwatch, (caddr_t)0, HZ); - for (sc21 = 0; sc21 < NSC21; sc21++) { + timeout(upwatch, (caddr_t)0, hz); + for (sc21 = 0; sc21 < NSC; sc21++) { um = upminfo[sc21]; + if (um == 0 || um->um_alive == 0) + continue; + sc = &up_softc[sc21]; if (um->um_tab.b_active == 0) { for (unit = 0; unit < NUP; unit++) - if (updinfo[unit]->ui_mi == um && - uputab[unit].b_active) + if (uputab[unit].b_active && + updinfo[unit]->ui_mi == um) goto active; - up_softc[sc21].sc_wticks = 0; + sc->sc_wticks = 0; continue; } - active: - up_softc[sc21].sc_wticks++; - if (up_softc[sc21].sc_wticks >= 20) { - up_softc[sc21].sc_wticks = 0; - printf("LOST INTERRUPT RESET"); - upreset(um->um_ubanum); - printf("\n"); +active: + sc->sc_wticks++; + if (sc->sc_wticks >= 20) { + sc->sc_wticks = 0; + printf("sc%d: lost interrupt\n", sc21); + ubareset(um->um_ubanum); } } } @@ -675,61 +856,46 @@ upwatch() updump(dev) dev_t dev; { - struct device *upaddr; + struct updevice *upaddr; char *start; - int num, blk, unit, nsect, ntrak, nspc; + int num, blk, unit; struct size *sizes; register struct uba_regs *uba; - register struct uba_dinfo *ui; + register struct uba_device *ui; register short *rp; struct upst *st; + register int retry; unit = minor(dev) >> 3; - if (unit >= NUP) { - printf("bad unit\n"); - return (-1); - } -#define phys1(cast, addr) ((cast)((int)addr & 0x7fffffff)) -#define phys(cast, addr) phys1(cast, phys1(cast *, &addr)) - ui = phys(struct uba_dinfo *, updinfo[unit]); - if (ui->ui_alive == 0) { - printf("dna\n"); - return(-1); - } + if (unit >= NUP) + return (ENXIO); +#define phys(cast, addr) ((cast)((int)addr & 0x7fffffff)) + ui = phys(struct uba_device *, updinfo[unit]); + if (ui->ui_alive == 0) + return (ENXIO); uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba; -#if VAX780 - if (cpu == VAX_780) { - uba->uba_cr = UBA_ADINIT; - uba->uba_cr = UBA_IFS|UBA_BRIE|UBA_USEFIE|UBA_SUEFIE; - while ((uba->uba_cnfgr & UBA_UBIC) == 0) - ; - } -#endif - DELAY(1000000); - upaddr = (struct device *)ui->ui_physaddr; - while ((upaddr->upcs1&DVA) == 0) - ; + ubainit(uba); + upaddr = (struct updevice *)ui->ui_physaddr; + DELAY(5000000); num = maxfree; - start = 0; upaddr->upcs2 = unit; - if ((upaddr->upds & VV) == 0) { - upaddr->upcs1 = DCLR|GO; - upaddr->upcs1 = PRESET|GO; - upaddr->upof = FMT22; - } - if ((upaddr->upds & (DPR|MOL)) != (DPR|MOL)) { - printf("up !DPR || !MOL\n"); - return (-1); - } - st = phys1(struct upst *, &upst[ui->ui_type]); - nsect = st->nsect; - ntrak = st->ntrak; + DELAY(100); + upaddr->upcs1 = UP_DCLR|UP_GO; + upaddr->upcs1 = UP_PRESET|UP_GO; + upaddr->upof = UPOF_FMT22; + retry = 0; + do { + DELAY(25); + if (++retry > 527) + break; + } while ((upaddr->upds & UP_RDY) == 0); + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) + return (EFAULT); + start = 0; + st = &upst[ui->ui_type]; sizes = phys(struct size *, st->sizes); - if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks) { - printf("oor\n"); - return (-1); - } - nspc = st->nspc; + if (dumplo < 0 || dumplo + num >= sizes[minor(dev)&07].nblocks) + return (EINVAL); while (num > 0) { register struct pte *io; register int i; @@ -739,27 +905,35 @@ updump(dev) blk = num > DBSIZE ? DBSIZE : num; io = uba->uba_map; for (i = 0; i < blk; i++) - *(int *)io++ = (btop(start)+i) | (1<<21) | UBA_MRV; + *(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV; *(int *)io = 0; bn = dumplo + btop(start); - cn = bn/nspc + sizes[minor(dev)&07].cyloff; - sn = bn%nspc; - tn = sn/nsect; - sn = sn%nsect; + cn = bn/st->nspc + sizes[minor(dev)&07].cyloff; + sn = bn%st->nspc; + tn = sn/st->nsect; + sn = sn%st->nsect; upaddr->updc = cn; rp = (short *) &upaddr->upda; *rp = (tn << 8) + sn; *--rp = 0; *--rp = -blk*NBPG / sizeof (short); - *--rp = GO|WCOM; + *--rp = UP_GO|UP_WCOM; + retry = 0; do { DELAY(25); - } while ((upaddr->upcs1 & RDY) == 0); - if (upaddr->upcs1&ERR) { - printf("up dump dsk err: (%d,%d,%d) cs1=%x, er1=%x\n", - cn, tn, sn, upaddr->upcs1, upaddr->uper1); - return (-1); + if (++retry > 527) + break; + } while ((upaddr->upcs1 & UP_RDY) == 0); + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { + printf("up%d: not ready", unit); + if ((upaddr->upds & UPDS_DREADY) != UPDS_DREADY) { + printf("\n"); + return (EIO); + } + printf(" (flakey)\n"); } + if (upaddr->upds&UPDS_ERR) + return (EIO); start += blk*NBPG; num -= blk; }