4c9a4bee11777d8fb4d86891a216f2973f6fd0c5
[unix-history] / usr / src / sys / vax / uba / idc.c
/*
* Copyright (c) 1982 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)idc.c 6.12 (Berkeley) %G%
*/
#include "rb.h"
#if NIDC > 0
int idcdebug = 0;
#define printd if(idcdebug)printf
int idctrb[1000];
int *trp = idctrb;
#define trace(a,b) {*trp++ = *(int*)a; *trp++ = (int)b; if(trp>&idctrb[998])trp=idctrb;}
/*
* IDC (RB730) disk driver
*
* There can only ever be one IDC on a machine,
* and only on a VAX-11/730. We take advantage
* of that to simplify the driver.
*
* TODO:
* ecc
*/
#include "../machine/pte.h"
#include "param.h"
#include "systm.h"
#include "buf.h"
#include "conf.h"
#include "dir.h"
#include "user.h"
#include "map.h"
#include "vm.h"
#include "dk.h"
#include "cmap.h"
#include "dkbad.h"
#include "uio.h"
#include "kernel.h"
#include "syslog.h"
#include "../vax/cpu.h"
#include "ubareg.h"
#include "ubavar.h"
#include "idcreg.h"
struct idc_softc {
int sc_bcnt; /* number of bytes to transfer */
int sc_resid; /* total number of bytes to transfer */
int sc_ubaddr; /* Unibus address of data */
short sc_unit; /* unit doing transfer */
short sc_softas; /* software attention summary bits */
union idc_dar {
long dar_l;
u_short dar_w[2];
u_char dar_b[4];
} sc_un; /* prototype disk address register */
} idc_softc;
#define dar_dar dar_l /* the whole disk address */
#define dar_cyl dar_w[1] /* cylinder address */
#define dar_trk dar_b[1] /* track */
#define dar_sect dar_b[0] /* sector */
#define sc_dar sc_un.dar_dar
#define sc_cyl sc_un.dar_cyl
#define sc_trk sc_un.dar_trk
#define sc_sect sc_un.dar_sect
#define idcunit(dev) (minor(dev) >> 3)
/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
struct size {
daddr_t nblocks;
int cyloff;
} rb02_sizes[8] ={
15884, 0, /* A=cyl 0 thru 399 */
4480, 400, /* B=cyl 400 thru 510 */
20480, 0, /* C=cyl 0 thru 511 */
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
}, rb80_sizes[8] ={
15884, 0, /* A=cyl 0 thru 36 */
33440, 37, /* B=cyl 37 thru 114 */
242606, 0, /* C=cyl 0 thru 558 */
0, 0,
0, 0,
0, 0,
82080, 115, /* G=cyl 115 thru 304 */
110143, 305, /* H=cyl 305 thru 558 */
};
/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */
int idcprobe(), idcslave(), idcattach(), idcdgo(), idcintr();
struct uba_ctlr *idcminfo[NIDC];
struct uba_device *idcdinfo[NRB];
u_short idcstd[] = { 0174400, 0};
struct uba_driver idcdriver =
{ idcprobe, idcslave, idcattach, idcdgo, idcstd, "rb", idcdinfo, "idc", idcminfo, 0 };
struct buf idcutab[NRB];
union idc_dar idccyl[NRB];
struct idcst {
short nbps;
short nsect;
short ntrak;
short nspc;
short ncyl;
struct size *sizes;
} idcst[] = {
256, NRB02SECT, NRB02TRK, NRB02SECT*NRB02TRK, NRB02CYL, rb02_sizes,
512, NRB80SECT, NRB80TRK, NRB80SECT*NRB80TRK, NRB80CYL, rb80_sizes,
};
struct buf ridcbuf[NRB];
#define b_cylin b_resid
int idcwstart, idcwticks, idcwatch();
/*ARGSUSED*/
idcprobe(reg)
caddr_t reg;
{
register int br, cvec;
register struct idcdevice *idcaddr;
#ifdef lint
br = 0; cvec = br; br = cvec;
#endif
idcaddr = (struct idcdevice *)((caddr_t)uba_hd[0].uh_uba + 0x200);
idcaddr->idccsr = IDC_ATTN|IDC_IE;
while ((idcaddr->idccsr & IDC_CRDY) == 0)
;
idcaddr->idccsr = IDC_ATTN|IDC_CRDY;
return (sizeof (struct idcdevice));
}
/*ARGSUSED*/
idcslave(ui, reg)
struct uba_device *ui;
caddr_t reg;
{
register struct idcdevice *idcaddr;
register int i;
idcaddr = (struct idcdevice *)((caddr_t)uba_hd[0].uh_uba + 0x200);
ui->ui_type = 0;
idcaddr->idcmpr = IDCGS_GETSTAT;
idcaddr->idccsr = IDC_GETSTAT|(ui->ui_slave<<8);
(void) idcwait(idcaddr, 0);
i = idcaddr->idcmpr;
idcaddr->idccsr = IDC_CRDY|(1<<(ui->ui_slave+16));
(void) idcwait(idcaddr, 0);
/* read header to synchronize microcode */
idcaddr->idccsr = (ui->ui_slave<<8)|IDC_RHDR;
(void) idcwait(idcaddr, 0);
i = idcaddr->idcmpr; /* read header word 1 */
i = idcaddr->idcmpr; /* read header word 2 */
#ifdef lint
i = i;
#endif
if ((idcaddr->idccsr & (IDC_ERR|IDC_R80)) == IDC_R80)
ui->ui_type = 1;
else if ((idcaddr->idccsr & (IDC_DE|IDC_R80)) == 0)
/*
* RB02 may not have pack spun up, just look for drive error.
*/
ui->ui_type = 0;
else
return (0);
return (1);
}
idcattach(ui)
register struct uba_device *ui;
{
/*
* Fix all addresses to correspond
* to the "real" IDC address.
*/
ui->ui_mi->um_addr = ui->ui_addr = (caddr_t)uba_hd[0].uh_uba + 0x200;
ui->ui_physaddr = (caddr_t)uba_hd[0].uh_physuba + 0x200;
if (idcwstart == 0) {
timeout(idcwatch, (caddr_t)0, hz);
idcwstart++;
}
if (ui->ui_dk >= 0)
if (ui->ui_type)
dk_mspw[ui->ui_dk] = 1.0 / (60 * NRB80SECT * 256);
else
dk_mspw[ui->ui_dk] = 1.0 / (60 * NRB02SECT * 128);
idccyl[ui->ui_unit].dar_dar = -1;
ui->ui_flags = 0;
}
idcopen(dev)
dev_t dev;
{
register int unit = idcunit(dev);
register struct uba_device *ui;
if (unit >= NRB || (ui = idcdinfo[unit]) == 0 || ui->ui_alive == 0)
return (ENXIO);
return (0);
}
idcstrategy(bp)
register struct buf *bp;
{
register struct uba_device *ui;
register struct idcst *st;
register int unit;
register struct buf *dp;
int xunit = minor(bp->b_dev) & 07;
long bn, sz;
sz = (bp->b_bcount+511) >> 9;
unit = idcunit(bp->b_dev);
if (unit >= NRB) {
bp->b_error = ENXIO;
goto bad;
}
ui = idcdinfo[unit];
if (ui == 0 || ui->ui_alive == 0) {
bp->b_error = ENXIO;
goto bad;
}
st = &idcst[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;
}
if (ui->ui_type == 0)
bn *= 2;
bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff;
(void) spl5();
trace("strt",bp);
dp = &idcutab[ui->ui_unit];
disksort(dp, bp);
if (dp->b_active == 0) {
trace("!act",dp);
(void) idcustart(ui);
bp = &ui->ui_mi->um_tab;
if (bp->b_actf && bp->b_active == 0)
(void) idcstart(ui->ui_mi);
}
(void) spl0();
return;
bad:
bp->b_flags |= B_ERROR;
done:
iodone(bp);
return;
}
idcustart(ui)
register struct uba_device *ui;
{
register struct buf *bp, *dp;
register struct uba_ctlr *um;
register struct idcdevice *idcaddr;
register struct idcst *st;
union idc_dar cyltrk;
daddr_t bn;
int unit;
if (ui == 0)
return (0);
dk_busy &= ~(1<<ui->ui_dk);
dp = &idcutab[ui->ui_unit];
um = ui->ui_mi;
unit = ui->ui_slave;
trace("ust", dp);
idcaddr = (struct idcdevice *)um->um_addr;
if (um->um_tab.b_active) {
idc_softc.sc_softas |= 1<<unit;
trace("umac",idc_softc.sc_softas);
return (0);
}
if ((bp = dp->b_actf) == NULL) {
trace("!bp",0);
return (0);
}
if (dp->b_active) {
trace("dpac",dp->b_active);
goto done;
}
dp->b_active = 1;
/* CHECK DRIVE READY? */
bn = bp->b_blkno;
trace("seek", bn);
if (ui->ui_type == 0)
bn *= 2;
st = &idcst[ui->ui_type];
cyltrk.dar_cyl = bp->b_cylin;
cyltrk.dar_trk = (bn / st->nsect) % st->ntrak;
cyltrk.dar_sect = 0;
printd("idcustart, unit %d, cyltrk 0x%x\n", unit, cyltrk.dar_dar);
/*
* If on cylinder, no need to seek.
*/
if (cyltrk.dar_dar == idccyl[ui->ui_unit].dar_dar)
goto done;
/*
* RB80 can change heads (tracks) just by loading
* the disk address register, perform optimization
* here instead of doing a full seek.
*/
if (ui->ui_type && cyltrk.dar_cyl == idccyl[ui->ui_unit].dar_cyl) {
idcaddr->idccsr = IDC_CRDY|IDC_IE|IDC_SEEK|(unit<<8);
idcaddr->idcdar = cyltrk.dar_dar;
idccyl[ui->ui_unit].dar_dar = cyltrk.dar_dar;
goto done;
}
/*
* Need to do a full seek. Select the unit, clear
* its attention bit, set the command, load the
* disk address register, and then go.
*/
idcaddr->idccsr =
IDC_CRDY|IDC_IE|IDC_SEEK|(unit<<8)|(1<<(unit+16));
idcaddr->idcdar = cyltrk.dar_dar;
idccyl[ui->ui_unit].dar_dar = cyltrk.dar_dar;
printd(" seek");
idcaddr->idccsr = IDC_IE|IDC_SEEK|(unit<<8);
if (ui->ui_dk >= 0) {
dk_busy |= 1<<ui->ui_dk;
dk_seek[ui->ui_dk]++;
}
/*
* RB80's initiate seeks very quickly. Wait for it
* to come ready rather than taking the interrupt.
*/
if (ui->ui_type) {
if (idcwait(idcaddr, 10) == 0)
return (1);
idcaddr->idccsr &= ~IDC_ATTN;
/* has the seek completed? */
if (idcaddr->idccsr & IDC_DRDY) {
printd(", drdy");
idcaddr->idccsr =
IDC_CRDY|IDC_IE|IDC_SEEK|(unit<<8)|(1<<(unit+16));
goto done;
}
}
printd(", idccsr = 0x%x\n", idcaddr->idccsr);
return (1);
done:
if (dp->b_active != 2) {
trace("!=2",dp->b_active);
dp->b_forw = NULL;
if (um->um_tab.b_actf == NULL)
um->um_tab.b_actf = dp;
else {
trace("!NUL",um->um_tab.b_actl);
um->um_tab.b_actl->b_forw = dp;
}
um->um_tab.b_actl = dp;
dp->b_active = 2;
}
return (0);
}
idcstart(um)
register struct uba_ctlr *um;
{
register struct buf *bp, *dp;
register struct uba_device *ui;
register struct idcdevice *idcaddr;
register struct idc_softc *sc;
struct idcst *st;
daddr_t bn;
int sn, tn, cmd;
loop:
if ((dp = um->um_tab.b_actf) == NULL) {
trace("nodp",um);
return (0);
}
if ((bp = dp->b_actf) == NULL) {
trace("nobp", dp);
um->um_tab.b_actf = dp->b_forw;
goto loop;
}
um->um_tab.b_active = 1;
ui = idcdinfo[idcunit(bp->b_dev)];
bn = bp->b_blkno;
trace("star",bp);
if (ui->ui_type == 0)
bn *= 2;
sc = &idc_softc;
st = &idcst[ui->ui_type];
sn = bn%st->nspc;
tn = sn/st->nsect;
sn %= st->nsect;
sc->sc_sect = sn;
sc->sc_trk = tn;
sc->sc_cyl = bp->b_cylin;
idcaddr = (struct idcdevice *)ui->ui_addr;
printd("idcstart, unit %d, dar 0x%x", ui->ui_slave, sc->sc_dar);
if (bp->b_flags & B_READ)
cmd = IDC_IE|IDC_READ|(ui->ui_slave<<8);
else
cmd = IDC_IE|IDC_WRITE|(ui->ui_slave<<8);
idcaddr->idccsr = IDC_CRDY|cmd;
if ((idcaddr->idccsr&IDC_DRDY) == 0) {
printf("rb%d: not ready\n", idcunit(bp->b_dev));
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;
}
idccyl[ui->ui_unit].dar_dar = sc->sc_dar;
idccyl[ui->ui_unit].dar_sect = 0;
sn = (st->nsect - sn) * st->nbps;
if (sn > bp->b_bcount)
sn = bp->b_bcount;
sc->sc_bcnt = sn;
sc->sc_resid = bp->b_bcount;
sc->sc_unit = ui->ui_slave;
printd(", bcr 0x%x, cmd 0x%x\n", sn, cmd);
um->um_cmd = cmd;
(void) ubago(ui);
return (1);
}
idcdgo(um)
register struct uba_ctlr *um;
{
register struct idcdevice *idcaddr = (struct idcdevice *)um->um_addr;
register struct idc_softc *sc = &idc_softc;
/*
* VERY IMPORTANT: must load registers in this order.
*/
idcaddr->idcbar = sc->sc_ubaddr = um->um_ubinfo&0x3ffff;
idcaddr->idcbcr = -sc->sc_bcnt;
idcaddr->idcdar = sc->sc_dar;
printd("idcdgo, ubinfo 0x%x, cmd 0x%x\n", um->um_ubinfo, um->um_cmd);
idcaddr->idccsr = um->um_cmd;
trace("go", um);
um->um_tab.b_active = 2;
/*** CLEAR SPURIOUS ATTN ON R80? ***/
}
idcintr(idc)
int idc;
{
register struct uba_ctlr *um = idcminfo[idc];
register struct uba_device *ui;
register struct idcdevice *idcaddr = (struct idcdevice *)um->um_addr;
register struct idc_softc *sc = &idc_softc;
register struct buf *bp, *dp;
struct idcst *st;
int unit, as, er, cmd, ds = 0;
printd("idcintr, idccsr 0x%x", idcaddr->idccsr);
top:
idcwticks = 0;
trace("intr", um->um_tab.b_active);
if (um->um_tab.b_active == 2) {
/*
* Process a data transfer complete interrupt.
*/
um->um_tab.b_active = 1;
dp = um->um_tab.b_actf;
bp = dp->b_actf;
ui = idcdinfo[idcunit(bp->b_dev)];
unit = ui->ui_slave;
st = &idcst[ui->ui_type];
idcaddr->idccsr = IDC_IE|IDC_CRDY|(unit<<8);
if ((er = idcaddr->idccsr) & IDC_ERR) {
if (er & IDC_DE) {
idcaddr->idcmpr = IDCGS_GETSTAT;
idcaddr->idccsr = IDC_GETSTAT|(unit<<8);
(void) idcwait(idcaddr, 0);
ds = idcaddr->idcmpr;
idcaddr->idccsr =
IDC_IE|IDC_CRDY|(1<<(unit+16));
}
printd(", er 0x%x, ds 0x%x", er, ds);
if (ds & IDCDS_WL) {
printf("rb%d: write locked\n",
idcunit(bp->b_dev));
bp->b_flags |= B_ERROR;
} else if (++um->um_tab.b_errcnt > 28 || er&IDC_HARD) {
hard:
harderr(bp, "rb");
printf("csr=%b ds=%b\n", er, IDCCSR_BITS, ds,
ui->ui_type?IDCRB80DS_BITS:IDCRB02DS_BITS);
bp->b_flags |= B_ERROR;
} else if (er & IDC_DCK) {
switch ((int)(er & IDC_ECS)) {
case IDC_ECS_NONE:
break;
case IDC_ECS_SOFT:
idcecc(ui);
break;
case IDC_ECS_HARD:
default:
goto hard;
}
} else
/* recoverable error, set up for retry */
goto seek;
}
if ((sc->sc_resid -= sc->sc_bcnt) != 0) {
sc->sc_ubaddr += sc->sc_bcnt;
/*
* Current transfer is complete, have
* we overflowed to the next track?
*/
if ((sc->sc_sect += sc->sc_bcnt/st->nbps) == st->nsect) {
sc->sc_sect = 0;
if (++sc->sc_trk == st->ntrak) {
sc->sc_trk = 0;
sc->sc_cyl++;
} else if (ui->ui_type) {
/*
* RB80 can change heads just by
* loading the disk address register.
*/
idcaddr->idccsr = IDC_SEEK|IDC_CRDY|
IDC_IE|(unit<<8);
printd(", change to track 0x%x", sc->sc_dar);
idcaddr->idcdar = sc->sc_dar;
idccyl[ui->ui_unit].dar_dar = sc->sc_dar;
idccyl[ui->ui_unit].dar_sect = 0;
goto cont;
}
/*
* Changing tracks on RB02 or cylinders
* on RB80, start a seek.
*/
seek:
cmd = IDC_IE|IDC_SEEK|(unit<<8);
idcaddr->idccsr = cmd|IDC_CRDY;
idcaddr->idcdar = sc->sc_dar;
printd(", seek to 0x%x\n", sc->sc_dar);
idccyl[ui->ui_unit].dar_dar = sc->sc_dar;
idccyl[ui->ui_unit].dar_sect = 0;
sc->sc_bcnt = 0;
idcaddr->idccsr = cmd;
if (ui->ui_type) {
if (idcwait(idcaddr, 10) == 0)
return;
idcaddr->idccsr &= ~IDC_ATTN;
if (idcaddr->idccsr & IDC_DRDY)
goto top;
}
} else {
/*
* Continue transfer on current track.
*/
cont:
sc->sc_bcnt = (st->nsect-sc->sc_sect)*st->nbps;
if (sc->sc_bcnt > sc->sc_resid)
sc->sc_bcnt = sc->sc_resid;
if (bp->b_flags & B_READ)
cmd = IDC_IE|IDC_READ|(unit<<8);
else
cmd = IDC_IE|IDC_WRITE|(unit<<8);
idcaddr->idccsr = cmd|IDC_CRDY;
idcaddr->idcbar = sc->sc_ubaddr;
idcaddr->idcbcr = -sc->sc_bcnt;
idcaddr->idcdar = sc->sc_dar;
printd(", continue I/O 0x%x, 0x%x\n", sc->sc_dar, sc->sc_bcnt);
idcaddr->idccsr = cmd;
um->um_tab.b_active = 2;
}
return;
}
/*
* Entire transfer is done, clean up.
*/
ubadone(um);
dk_busy &= ~(1 << ui->ui_dk);
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;
trace("done", dp); trace(&um->um_tab.b_actf, dp->b_actf);
bp->b_resid = sc->sc_resid;
printd(", iodone, resid 0x%x\n", bp->b_resid);
iodone(bp);
if (dp->b_actf)
if (idcustart(ui))
return;
} else if (um->um_tab.b_active == 1) {
/*
* Got an interrupt while setting up for a command
* or doing a mid-transfer seek. Save any attentions
* for later and process a mid-transfer seek complete.
*/
as = idcaddr->idccsr;
idcaddr->idccsr = IDC_IE|IDC_CRDY|(as&IDC_ATTN);
as = (as >> 16) & 0xf;
unit = sc->sc_unit;
sc->sc_softas |= as & ~(1<<unit);
if (as & (1<<unit)) {
printd(", seek1 complete");
um->um_tab.b_active = 2;
goto top;
}
printd(", as1 %o\n", as);
return;
}
/*
* Process any seek initiated or complete interrupts.
*/
as = idcaddr->idccsr;
idcaddr->idccsr = IDC_IE|IDC_CRDY|(as&IDC_ATTN);
as = ((as >> 16) & 0xf) | sc->sc_softas;
sc->sc_softas = 0;
trace("as", as);
printd(", as %o", as);
for (unit = 0; unit < NRB; unit++)
if (as & (1<<unit)) {
as &= ~(1<<unit);
idcaddr->idccsr = IDC_IE|IDC_CRDY|(unit<<8);
ui = idcdinfo[unit];
if (ui) {
printd(", attn unit %d", unit);
if (idcaddr->idccsr & IDC_DRDY)
if (idcustart(ui)) {
sc->sc_softas = as;
return;
}
} else {
printd(", unsol. intr. unit %d", unit);
}
}
printd("\n");
if (um->um_tab.b_actf && um->um_tab.b_active == 0) {
trace("stum",um->um_tab.b_actf);
(void) idcstart(um);
}
}
idcwait(addr, n)
register struct idcdevice *addr;
register int n;
{
register int i;
while (--n && (addr->idccsr & IDC_CRDY) == 0)
for (i = 10; i; i--)
;
return (n);
}
idcread(dev, uio)
dev_t dev;
struct uio *uio;
{
register int unit = idcunit(dev);
if (unit >= NRB)
return (ENXIO);
return (physio(idcstrategy, &ridcbuf[unit], dev, B_READ, minphys, uio));
}
idcwrite(dev, uio)
dev_t dev;
struct uio *uio;
{
register int unit = idcunit(dev);
if (unit >= NRB)
return (ENXIO);
return (physio(idcstrategy, &ridcbuf[unit], dev, B_WRITE, minphys, uio));
}
idcecc(ui)
register struct uba_device *ui;
{
register struct idcdevice *idc = (struct idcdevice *)ui->ui_addr;
register struct buf *bp = idcutab[ui->ui_unit].b_actf;
register struct uba_ctlr *um = ui->ui_mi;
register struct idcst *st;
register int i;
struct uba_regs *ubp = ui->ui_hd->uh_uba;
int bit, byte, mask;
caddr_t addr;
int reg, npf, o;
int cn, tn, sn;
npf = btop(idc->idcbcr + idc_softc.sc_bcnt) - 1;;
reg = btop(idc_softc.sc_ubaddr) + npf;
o = (int)bp->b_un.b_addr & PGOFSET;
st = &idcst[ui->ui_type];
cn = idc_softc.sc_cyl;
tn = idc_softc.sc_trk;
sn = idc_softc.sc_sect;
um->um_tab.b_active = 1; /* Either complete or continuing... */
log(LOG_WARNING, "rb%d%c: soft ecc sn%d\n", idcunit(bp->b_dev),
'a'+(minor(bp->b_dev)&07),
(cn*st->ntrak + tn) * st->nsect + sn + npf);
mask = idc->idceccpat;
i = idc->idceccpos - 1; /* -1 makes 0 origin */
bit = i&07;
i = (i&~07)>>3;
byte = i + o;
while (i < 512 && (int)ptob(npf)+i < idc_softc.sc_bcnt && bit > -11) {
/*
* should be:
* addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
* (byte & PGOFSET);
* but this generates an extzv which hangs the UNIBUS.
*/
addr = ptob(*(int *)&ubp->uba_map[reg+btop(byte)]&0x1fffff)+
(byte & PGOFSET);
putmemc(addr, getmemc(addr)^(mask<<bit));
byte++;
i++;
bit -= 8;
}
idc_softc.sc_bcnt += idc->idcbcr;
um->um_tab.b_errcnt = 0; /* error has been corrected */
return;
}
idcreset(uban)
int uban;
{
register struct uba_ctlr *um;
register struct uba_device *ui;
register unit;
if ((um = idcminfo[0]) == 0 || um->um_ubanum != uban ||
um->um_alive == 0)
return;
printf(" idc0");
um->um_tab.b_active = 0;
um->um_tab.b_actf = um->um_tab.b_actl = 0;
if (um->um_ubinfo) {
printf("<%d>", (um->um_ubinfo>>28)&0xf);
um->um_ubinfo = 0;
}
for (unit = 0; unit < NRB; unit++) {
if ((ui = idcdinfo[unit]) == 0 || ui->ui_alive == 0)
continue;
idcutab[unit].b_active = 0;
(void) idcustart(ui);
}
(void) idcstart(um);
}
idcwatch()
{
register struct uba_ctlr *um;
register unit;
timeout(idcwatch, (caddr_t)0, hz);
um = idcminfo[0];
if (um == 0 || um->um_alive == 0)
return;
if (um->um_tab.b_active == 0) {
for (unit = 0; unit < NRB; unit++)
if (idcutab[unit].b_active)
goto active;
idcwticks = 0;
return;
}
active:
idcwticks++;
if (idcwticks >= 20) {
idcwticks = 0;
printf("idc0: lost interrupt\n");
idcintr(0);
}
}
/*ARGSUSED*/
idcdump(dev)
dev_t dev;
{
struct idcdevice *idcaddr;
char *start;
int num, blk, unit;
struct size *sizes;
register struct uba_regs *uba;
register struct uba_device *ui;
struct idcst *st;
union idc_dar dar;
int nspg;
unit = idcunit(dev);
if (unit >= NRB)
return (ENXIO);
#define phys(cast, addr) ((cast)((int)addr & 0x7fffffff))
ui = phys(struct uba_device *, idcdinfo[unit]);
if (ui->ui_alive == 0)
return (ENXIO);
uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba;
ubainit(uba);
idcaddr = (struct idcdevice *)ui->ui_physaddr;
if (idcwait(idcaddr, 100) == 0)
return (EFAULT);
/*
* Since we can only transfer one track at a time, and
* the rl02 has 256 byte sectors, all the calculations
* are done in terms of physical sectors (i.e. num and blk
* are in sectors not NBPG blocks.
*/
st = phys(struct idcst *, &idcst[ui->ui_type]);
sizes = phys(struct size *, st->sizes);
if (dumplo < 0)
return (EINVAL);
if (dumplo + maxfree >= sizes[minor(dev)&07].nblocks)
num = sizes[minor(dev)&07].nblocks - dumplo;
nspg = NBPG / st->nbps;
num = num * nspg;
start = 0;
while (num > 0) {
register struct pte *io;
register int i;
daddr_t bn;
bn = (dumplo + btop(start)) * nspg;
dar.dar_cyl = bn / st->nspc + sizes[minor(dev)&07].cyloff;
bn %= st->nspc;
dar.dar_trk = bn / st->nsect;
dar.dar_sect = bn % st->nsect;
blk = st->nsect - dar.dar_sect;
if (num < blk)
blk = num;
io = uba->uba_map;
for (i = 0; i < (blk + nspg - 1) / nspg; i++)
*(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV;
*(int *)io = 0;
idcaddr->idccsr = IDC_CRDY | IDC_SEEK | unit<<8;
if ((idcaddr->idccsr&IDC_DRDY) == 0)
return (EFAULT);
idcaddr->idcdar = dar.dar_dar;
idcaddr->idccsr = IDC_SEEK | unit << 8;
while ((idcaddr->idccsr & (IDC_CRDY|IDC_DRDY))
!= (IDC_CRDY|IDC_DRDY))
;
if (idcaddr->idccsr & IDC_ERR) {
printf("rb%d: seek, csr=%b\n",
unit, idcaddr->idccsr, IDCCSR_BITS);
return (EIO);
}
idcaddr->idccsr = IDC_CRDY | IDC_WRITE | unit<<8;
if ((idcaddr->idccsr&IDC_DRDY) == 0)
return (EFAULT);
idcaddr->idcbar = 0; /* start addr 0 */
idcaddr->idcbcr = - (blk * st->nbps);
idcaddr->idcdar = dar.dar_dar;
idcaddr->idccsr = IDC_WRITE | unit << 8;
while ((idcaddr->idccsr & (IDC_CRDY|IDC_DRDY))
!= (IDC_CRDY|IDC_DRDY))
;
if (idcaddr->idccsr & IDC_ERR) {
printf("rb%d: write, csr=%b\n",
unit, idcaddr->idccsr, IDCCSR_BITS);
return (EIO);
}
start += blk * st->nbps;
num -= blk;
}
return (0);
}
idcsize(dev)
dev_t dev;
{
int unit = idcunit(dev);
struct uba_device *ui;
struct idcst *st;
if (unit >= NRB || (ui = idcdinfo[unit]) == 0 || ui->ui_alive == 0)
return (-1);
st = &idcst[ui->ui_type];
return (st->sizes[minor(dev) & 07].nblocks);
}
#endif