/* * Copyright (c) 1982, 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)rk.c 7.9 (Berkeley) 12/16/90 */ #include "rk.h" #if NHK > 0 int rkpip; /* DEBUG */ int rknosval; /* DEBUG */ #ifdef RKDEBUG int rkdebug; #endif #ifdef RKBDEBUG int rkbdebug; #endif /* * RK611/RK0[67] disk driver * * This driver mimics up.c; see it for an explanation of common code. * * TODO: * Learn why we lose an interrupt sometime when spinning drives down */ #include "../include/pte.h" #include "sys/param.h" #include "sys/systm.h" #include "sys/buf.h" #include "sys/conf.h" #include "sys/user.h" #include "sys/map.h" #include "sys/vm.h" #include "sys/dkstat.h" #include "sys/cmap.h" #include "sys/dkbad.h" #include "sys/ioctl.h" #include "sys/disklabel.h" #include "sys/uio.h" #include "sys/kernel.h" #include "sys/syslog.h" #include "../include/cpu.h" #include "ubareg.h" #include "ubavar.h" #include "rkreg.h" struct rk_softc { int sc_softas; int sc_ndrive; int sc_wticks; int sc_recal; } rk_softc[NHK]; #define rkunit(dev) (minor(dev) >> 3) /* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */ struct size { daddr_t nblocks; int cyloff; } rk7_sizes[8] = { 15884, 0, /* A=cyl 0 thru 240 */ 10032, 241, /* B=cyl 241 thru 392 */ 53790, 0, /* C=cyl 0 thru 814 */ 15884, 393, /* D=cyl 393 thru 633 */ 0, 0, 11792, 634, /* F=cyl 634 thru 814 */ 27786, 393, /* G=cyl 393 thru 814, should be 27698 */ 0, 0, }, rk6_sizes[8] ={ 15884, 0, /* A=cyl 0 thru 240 */ 11154, 241, /* B=cyl 241 thru 409 */ 27126, 0, /* C=cyl 0 thru 410 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; /* END OF STUFF WHICH SHOULD BE READ IN PER DISK */ short rktypes[] = { RK_CDT, 0 }; int rkprobe(), rkslave(), rkattach(), rkdgo(), rkintr(); struct uba_ctlr *rkminfo[NHK]; struct uba_device *rkdinfo[NRK]; struct uba_device *rkip[NHK][4]; u_short rkstd[] = { 0777440, 0 }; struct uba_driver hkdriver = { rkprobe, rkslave, rkattach, rkdgo, rkstd, "rk", rkdinfo, "hk", rkminfo, 1 }; struct buf rkutab[NRK]; short rkcyl[NRK]; struct dkbad rkbad[NRK]; struct buf brkbuf[NRK]; struct rkst { short nsect; short ntrak; short nspc; short ncyl; struct size *sizes; } rkst[] = { NRKSECT, NRKTRK, NRKSECT*NRKTRK, NRK7CYL, rk7_sizes, NRKSECT, NRKTRK, NRKSECT*NRKTRK, NRK6CYL, rk6_sizes, }; u_char rk_offset[16] = { RKAS_P400,RKAS_M400,RKAS_P400,RKAS_M400,RKAS_P800,RKAS_M800,RKAS_P800, RKAS_M800,RKAS_P1200,RKAS_M1200,RKAS_P1200,RKAS_M1200,0,0,0,0 }; #define b_cylin b_resid int rkwstart, rkwatch(); rkprobe(reg) caddr_t reg; { register int br, cvec; #ifdef lint br = 0; cvec = br; br = cvec; rkintr(0); #endif ((struct rkdevice *)reg)->rkcs1 = RK_CDT|RK_IE|RK_CRDY; DELAY(10); ((struct rkdevice *)reg)->rkcs1 = RK_CDT; return (sizeof (struct rkdevice)); } rkslave(ui, reg) struct uba_device *ui; caddr_t reg; { register struct rkdevice *rkaddr = (struct rkdevice *)reg; ui->ui_type = 0; rkaddr->rkcs1 = RK_CCLR; rkaddr->rkcs2 = ui->ui_slave; rkaddr->rkcs1 = RK_CDT|RK_DCLR|RK_GO; rkwait(rkaddr); DELAY(50); if (rkaddr->rkcs2&RKCS2_NED || (rkaddr->rkds&RKDS_SVAL) == 0) { rkaddr->rkcs1 = RK_CCLR; return (0); } if (rkaddr->rkcs1&RK_CERR && rkaddr->rker&RKER_DTYE) { ui->ui_type = 1; rkaddr->rkcs1 = RK_CCLR; } return (1); } rkattach(ui) register struct uba_device *ui; { if (rkwstart == 0) { timeout(rkwatch, (caddr_t)0, hz); rkwstart++; } if (ui->ui_dk >= 0) dk_wpms[ui->ui_dk] = (60 * NRKSECT * 256); rkip[ui->ui_ctlr][ui->ui_slave] = ui; rk_softc[ui->ui_ctlr].sc_ndrive++; rkcyl[ui->ui_unit] = -1; ui->ui_flags = 0; } rkopen(dev) dev_t dev; { register int unit = rkunit(dev); register struct uba_device *ui; if (unit >= NRK || (ui = rkdinfo[unit]) == 0 || ui->ui_alive == 0) return (ENXIO); return (0); } rkstrategy(bp) register struct buf *bp; { register struct uba_device *ui; register struct rkst *st; register int unit; register struct buf *dp; int xunit = minor(bp->b_dev) & 07; long bn, sz; int s; sz = (bp->b_bcount+511) >> 9; unit = rkunit(bp->b_dev); if (unit >= NRK) { bp->b_error = ENXIO; goto bad; } ui = rkdinfo[unit]; if (ui == 0 || ui->ui_alive == 0) { bp->b_error = ENXIO; goto bad; } st = &rkst[ui->ui_type]; if (bp->b_blkno < 0 || (bn = bp->b_blkno)+sz > st->sizes[xunit].nblocks) { if (bp->b_blkno == st->sizes[xunit].nblocks) { bp->b_resid = bp->b_bcount; goto done; } bp->b_error = EINVAL; goto bad; } bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff; s = spl5(); dp = &rkutab[ui->ui_unit]; disksort(dp, bp); if (dp->b_active == 0) { (void) rkustart(ui); bp = &ui->ui_mi->um_tab; if (bp->b_actf && bp->b_active == 0) (void) rkstart(ui->ui_mi); } splx(s); return; bad: bp->b_flags |= B_ERROR; done: iodone(bp); return; } rkustart(ui) register struct uba_device *ui; { register struct buf *bp, *dp; register struct uba_ctlr *um; register struct rkdevice *rkaddr; if (ui == 0) return; dk_busy &= ~(1<ui_dk); dp = &rkutab[ui->ui_unit]; um = ui->ui_mi; rkaddr = (struct rkdevice *)um->um_addr; if (um->um_tab.b_active) { rk_softc[um->um_ctlr].sc_softas |= 1<ui_slave; return; } if ((bp = dp->b_actf) == NULL) return; rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_CERR; rkaddr->rkcs2 = ui->ui_slave; rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; rkwait(rkaddr); if ((rkaddr->rkds & RKDS_VV) == 0 || ui->ui_flags == 0) { /* SHOULD WARN SYSTEM THAT THIS HAPPENED */ struct rkst *st = &rkst[ui->ui_type]; struct buf *bbp = &brkbuf[ui->ui_unit]; rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_PACK|RK_GO; ui->ui_flags = 1; bbp->b_flags = B_READ|B_BUSY; bbp->b_dev = bp->b_dev; bbp->b_bcount = 512; bbp->b_un.b_addr = (caddr_t)&rkbad[ui->ui_unit]; bbp->b_blkno = st->ncyl*st->nspc - st->nsect; bbp->b_cylin = st->ncyl - 1; dp->b_actf = bbp; bbp->av_forw = bp; bp = bbp; rkwait(rkaddr); } if (dp->b_active) goto done; dp->b_active = 1; if ((rkaddr->rkds & RKDS_DREADY) != RKDS_DREADY) goto done; if (rk_softc[um->um_ctlr].sc_ndrive == 1) goto done; if (bp->b_cylin == rkcyl[ui->ui_unit]) goto done; rkaddr->rkcyl = bp->b_cylin; rkcyl[ui->ui_unit] = bp->b_cylin; rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_SEEK|RK_GO; if (ui->ui_dk >= 0) { dk_busy |= 1<ui_dk; dk_seek[ui->ui_dk]++; } goto out; done: 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; } rkstart(um) register struct uba_ctlr *um; { register struct buf *bp, *dp; register struct uba_device *ui; register struct rkdevice *rkaddr; struct rkst *st; daddr_t bn; int sn, tn, cmd; loop: if ((dp = um->um_tab.b_actf) == NULL) return; if ((bp = dp->b_actf) == NULL) { um->um_tab.b_actf = dp->b_forw; goto loop; } um->um_tab.b_active++; ui = rkdinfo[rkunit(bp->b_dev)]; bn = bp->b_blkno; st = &rkst[ui->ui_type]; sn = bn%st->nspc; tn = sn/st->nsect; sn %= st->nsect; rkaddr = (struct rkdevice *)ui->ui_addr; retry: rkaddr->rkcs1 = RK_CCLR; rkaddr->rkcs2 = ui->ui_slave; rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; rkwait(rkaddr); if ((rkaddr->rkds&RKDS_SVAL) == 0) { rknosval++; goto nosval; } if (rkaddr->rkds&RKDS_PIP) { rkpip++; goto retry; } if ((rkaddr->rkds&RKDS_DREADY) != RKDS_DREADY) { printf("rk%d: not ready", rkunit(bp->b_dev)); if ((rkaddr->rkds&RKDS_DREADY) != RKDS_DREADY) { printf("\n"); rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; rkwait(rkaddr); rkaddr->rkcs1 = RK_CCLR; rkwait(rkaddr); 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); goto loop; } printf(" (came back!)\n"); } nosval: rkaddr->rkcyl = bp->b_cylin; rkcyl[ui->ui_unit] = bp->b_cylin; rkaddr->rkda = (tn << 8) + sn; rkaddr->rkwc = -bp->b_bcount / sizeof (short); if (bp->b_flags & B_READ) cmd = rktypes[ui->ui_type]|RK_IE|RK_READ|RK_GO; else cmd = rktypes[ui->ui_type]|RK_IE|RK_WRITE|RK_GO; um->um_cmd = cmd; (void) ubago(ui); } rkdgo(um) register struct uba_ctlr *um; { register struct rkdevice *rkaddr = (struct rkdevice *)um->um_addr; um->um_tab.b_active = 2; /* should now be 2 */ rkaddr->rkba = um->um_ubinfo; rkaddr->rkcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300); } rkintr(rk11) int rk11; { register struct uba_ctlr *um = rkminfo[rk11]; register struct uba_device *ui; register struct rkdevice *rkaddr = (struct rkdevice *)um->um_addr; register struct buf *bp, *dp; int unit; struct rk_softc *sc = &rk_softc[um->um_ctlr]; int as = (rkaddr->rkatt >> 8) | sc->sc_softas; sc->sc_wticks = 0; sc->sc_softas = 0; if (um->um_tab.b_active == 2 || sc->sc_recal) { um->um_tab.b_active = 1; dp = um->um_tab.b_actf; bp = dp->b_actf; ui = rkdinfo[rkunit(bp->b_dev)]; dk_busy &= ~(1 << ui->ui_dk); if (bp->b_flags&B_BAD) if (rkecc(ui, CONT)) return; if (rkaddr->rkcs1 & RK_CERR) { int recal; u_short ds = rkaddr->rkds; u_short cs2 = rkaddr->rkcs2; u_short er = rkaddr->rker; #ifdef RKDEBUG if (rkdebug) { printf("cs2=%b ds=%b er=%b\n", cs2, RKCS2_BITS, ds, RKDS_BITS, er, RKER_BITS); } #endif if (er & RKER_WLE) { printf("rk%d: write locked\n", rkunit(bp->b_dev)); bp->b_flags |= B_ERROR; } else if (++um->um_tab.b_errcnt > 28 || ds&RKDS_HARD || er&RKER_HARD || cs2&RKCS2_HARD) { hard: diskerr(bp, "rk", "hard error", LOG_PRINTF, -1, (struct disklabel *)0); printf(" cs2=%b ds=%b er=%b\n", cs2, RKCS2_BITS, ds, RKDS_BITS, er, RKER_BITS); bp->b_flags |= B_ERROR; sc->sc_recal = 0; } else if (er & RKER_BSE) { if (rkecc(ui, BSE)) return; else goto hard; } else { if ((er & (RKER_DCK|RKER_ECH)) == RKER_DCK) { if (rkecc(ui, ECC)) return; } else um->um_tab.b_active = 0; } if (cs2&RKCS2_MDS) { rkaddr->rkcs2 = RKCS2_SCLR; goto retry; } recal = 0; if (ds&RKDS_DROT || er&(RKER_OPI|RKER_SKI|RKER_UNS) || (um->um_tab.b_errcnt&07) == 4) recal = 1; rkaddr->rkcs1 = RK_CCLR; rkaddr->rkcs2 = ui->ui_slave; rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; rkwait(rkaddr); if (recal && um->um_tab.b_active == 0) { rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_RECAL|RK_GO; rkcyl[ui->ui_unit] = -1; sc->sc_recal = 0; goto nextrecal; } } retry: switch (sc->sc_recal) { case 1: rkaddr->rkcyl = bp->b_cylin; rkcyl[ui->ui_unit] = bp->b_cylin; rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_SEEK|RK_GO; goto nextrecal; case 2: if (um->um_tab.b_errcnt < 16 || (bp->b_flags&B_READ) == 0) goto donerecal; rkaddr->rkatt = rk_offset[um->um_tab.b_errcnt & 017]; rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_OFFSET|RK_GO; /* fall into ... */ nextrecal: sc->sc_recal++; rkwait(rkaddr); um->um_tab.b_active = 1; return; donerecal: case 3: sc->sc_recal = 0; um->um_tab.b_active = 0; break; } ubadone(um); if (um->um_tab.b_active) { 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 = -rkaddr->rkwc * sizeof(short); iodone(bp); if (dp->b_actf) rkustart(ui); } as &= ~(1<ui_slave); } for (unit = 0; as; as >>= 1, unit++) if (as & 1) { ui = rkip[rk11][unit]; if (ui) { rkustart(rkip[rk11][unit]); } else { rkaddr->rkcs1 = RK_CCLR; rkaddr->rkcs2 = unit; rkaddr->rkcs1 = RK_DCLR|RK_GO; rkwait(rkaddr); rkaddr->rkcs1 = RK_CCLR; } } if (um->um_tab.b_actf && um->um_tab.b_active == 0) rkstart(um); if (((rkaddr->rkcs1) & RK_IE) == 0) rkaddr->rkcs1 = RK_IE; } rkwait(addr) register struct rkdevice *addr; { while ((addr->rkcs1 & RK_CRDY) == 0) ; } rkecc(ui, flag) register struct uba_device *ui; { register struct rkdevice *rk = (struct rkdevice *)ui->ui_addr; register struct buf *bp = rkutab[ui->ui_unit].b_actf; register struct uba_ctlr *um = ui->ui_mi; register struct rkst *st; struct uba_regs *ubp = ui->ui_hd->uh_uba; caddr_t addr; int reg, npf, o, cmd, ubaddr; int bn, cn, tn, sn; if (flag == CONT) npf = bp->b_error; else npf = btodb(bp->b_bcount + (rk->rkwc * sizeof(short)) + 511); reg = btop(UBAI_ADDR(um->um_ubinfo)) + npf; o = (int)bp->b_un.b_addr & PGOFSET; bn = bp->b_blkno; st = &rkst[ui->ui_type]; cn = bp->b_cylin; sn = bn%st->nspc + npf; tn = sn/st->nsect; sn %= st->nsect; cn += tn/st->ntrak; tn %= st->ntrak; ubapurge(um); switch (flag) { case ECC: { register int i; int bit, byte, mask; npf--; reg--; diskerr(bp, "rk", "soft ecc", LOG_WARNING, npf, (struct disklabel *)0); addlog("\n"); mask = rk->rkec2; i = rk->rkec1 - 1; /* -1 makes 0 origin */ bit = i&07; i = (i&~07)>>3; byte = i + o; while (i < 512 && (int)dbtob(npf)+i < bp->b_bcount && bit > -11) { addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+ (byte & PGOFSET); putmemc(addr, getmemc(addr)^(mask<rkwc == 0) { um->um_tab.b_active = 0; return (0); } npf++; reg++; break; } case BSE: #ifdef RKBDEBUG if (rkbdebug) printf("rkecc, BSE: bn %d cn %d tn %d sn %d\n", bn, cn, tn, sn); #endif if ((bn = isbad(&rkbad[ui->ui_unit], cn, tn, sn)) < 0) return(0); bp->b_flags |= B_BAD; bp->b_error = npf + 1; bn = st->ncyl*st->nspc - st->nsect - 1 - bn; cn = bn/st->nspc; sn = bn%st->nspc; tn = sn/st->nsect; sn %= st->nsect; #ifdef RKBDEBUG if (rkbdebug) printf("revector to cn %d tn %d sn %d\n", cn, tn, sn); #endif rk->rkwc = -(512 / sizeof (short)); break; case CONT: #ifdef RKBDEBUG if (rkbdebug) printf("rkecc, CONT: bn %d cn %d tn %d sn %d\n", bn,cn,tn,sn); #endif bp->b_flags &= ~B_BAD; if ((int)dbtob(npf) >= bp->b_bcount) return (0); rk->rkwc = -((bp->b_bcount - (int)dbtob(npf)) / sizeof (short)); break; } rk->rkcs1 = RK_CCLR; rk->rkcs2 = ui->ui_slave; rk->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; rkwait(rk); rk->rkcyl = cn; rk->rkda = (tn << 8) | sn; ubaddr = (int)ptob(reg) + o; rk->rkba = ubaddr; cmd = (bp->b_flags&B_READ ? RK_READ : RK_WRITE)|RK_IE|RK_GO; cmd |= (ubaddr >> 8) & 0x300; cmd |= rktypes[ui->ui_type]; rk->rkcs1 = cmd; um->um_tab.b_active = 2; /* continuing */ um->um_tab.b_errcnt = 0; /* error has been corrected */ return (1); } rkreset(uban) int uban; { register struct uba_ctlr *um; register struct uba_device *ui; register rk11, unit; for (rk11 = 0; rk11 < NHK; rk11++) { if ((um = rkminfo[rk11]) == 0 || um->um_ubanum != uban || um->um_alive == 0) continue; printf(" hk%d", rk11); um->um_tab.b_active = 0; um->um_tab.b_actf = um->um_tab.b_actl = 0; rk_softc[um->um_ctlr].sc_recal = 0; rk_softc[um->um_ctlr].sc_wticks = 0; if (um->um_ubinfo) { printf("<%d>", (um->um_ubinfo>>28)&0xf); um->um_ubinfo = 0; } for (unit = 0; unit < NRK; unit++) { if ((ui = rkdinfo[unit]) == 0) continue; if (ui->ui_alive == 0 || ui->ui_mi != um) continue; rkutab[unit].b_active = 0; (void) rkustart(ui); } (void) rkstart(um); } } rkwatch() { register struct uba_ctlr *um; register rk11, unit; register struct rk_softc *sc; timeout(rkwatch, (caddr_t)0, hz); for (rk11 = 0; rk11 < NHK; rk11++) { um = rkminfo[rk11]; if (um == 0 || um->um_alive == 0) continue; sc = &rk_softc[rk11]; if (um->um_tab.b_active == 0) { for (unit = 0; unit < NRK; unit++) if (rkutab[unit].b_active && rkdinfo[unit]->ui_mi == um) goto active; sc->sc_wticks = 0; continue; } active: sc->sc_wticks++; if (sc->sc_wticks >= 20) { sc->sc_wticks = 0; printf("hk%d: lost interrupt\n", rk11); ubareset(um->um_ubanum); } } } #define DBSIZE 20 rkdump(dev) dev_t dev; { struct rkdevice *rkaddr; char *start; int num, blk, unit; struct size *sizes; register struct uba_regs *uba; register struct uba_device *ui; register short *rp; struct rkst *st; unit = rkunit(dev); if (unit >= NRK) return (ENXIO); #define phys(cast, addr) ((cast)((int)addr & 0x7fffffff)) ui = phys(struct uba_device *, rkdinfo[unit]); if (ui->ui_alive == 0) return (ENXIO); uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba; ubainit(uba); rkaddr = (struct rkdevice *)ui->ui_physaddr; num = maxfree; start = 0; rkaddr->rkcs1 = RK_CCLR; rkaddr->rkcs2 = unit; rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_DCLR|RK_GO; rkwait(rkaddr); if ((rkaddr->rkds & RKDS_VV) == 0) { rkaddr->rkcs1 = rktypes[ui->ui_type]|RK_IE|RK_PACK|RK_GO; rkwait(rkaddr); } st = &rkst[ui->ui_type]; sizes = phys(struct size *, st->sizes); if (dumplo < 0) return (EINVAL); if (dumplo + num >= sizes[minor(dev)&07].nblocks) num = sizes[minor(dev)&07].nblocks - dumplo; while (num > 0) { register struct pte *io; register int i; int cn, sn, tn; daddr_t bn; blk = num > DBSIZE ? DBSIZE : num; io = uba->uba_map; for (i = 0; i < blk; i++) *(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV; *(int *)io = 0; bn = dumplo + btop(start); cn = bn/st->nspc + sizes[minor(dev)&07].cyloff; sn = bn%st->nspc; tn = sn/st->nsect; sn = sn%st->nsect; rkaddr->rkcyl = cn; rp = (short *) &rkaddr->rkda; *rp = (tn << 8) + sn; *--rp = 0; *--rp = -blk*NBPG / sizeof (short); *--rp = rktypes[ui->ui_type]|RK_GO|RK_WRITE; rkwait(rkaddr); if (rkaddr->rkcs1 & RK_CERR) return (EIO); start += blk*NBPG; num -= blk; } return (0); } rksize(dev) dev_t dev; { int unit = rkunit(dev); struct uba_device *ui; struct rkst *st; if (unit >= NRK || (ui = rkdinfo[unit]) == 0 || ui->ui_alive == 0) return (-1); st = &rkst[ui->ui_type]; return (st->sizes[minor(dev) & 07].nblocks); } #endif