xref: /csrg-svn/sys/vax/uba/idc.c (revision 24784)
123328Smckusick /*
223328Smckusick  * Copyright (c) 1982 Regents of the University of California.
323328Smckusick  * All rights reserved.  The Berkeley software License Agreement
423328Smckusick  * specifies the terms and conditions for redistribution.
523328Smckusick  *
6*24784Skarels  *	@(#)idc.c	6.9 (Berkeley) 09/16/85
723328Smckusick  */
86941Ssam 
96941Ssam #include "rb.h"
106941Ssam #if NIDC > 0
118569Sroot int	idcdebug = 0;
128569Sroot #define	printd if(idcdebug)printf
138569Sroot int	idctrb[1000];
148569Sroot int	*trp = idctrb;
158608Sroot #define	trace(a,b) {*trp++ = *(int*)a; *trp++ = (int)b; if(trp>&idctrb[998])trp=idctrb;}
166941Ssam /*
176941Ssam  * IDC (RB730) disk driver
186941Ssam  *
196941Ssam  * There can only ever be one IDC on a machine,
206941Ssam  * and only on a VAX-11/730.  We take advantage
216941Ssam  * of that to simplify the driver.
226941Ssam  *
236941Ssam  * TODO:
246941Ssam  *	ecc
256941Ssam  */
269774Ssam #include "../machine/pte.h"
279774Ssam 
2817073Sbloom #include "param.h"
2917073Sbloom #include "systm.h"
3017073Sbloom #include "buf.h"
3117073Sbloom #include "conf.h"
3217073Sbloom #include "dir.h"
3317073Sbloom #include "user.h"
3417073Sbloom #include "map.h"
3517073Sbloom #include "vm.h"
3617073Sbloom #include "dk.h"
3717073Sbloom #include "cmap.h"
3817073Sbloom #include "dkbad.h"
3917073Sbloom #include "uio.h"
4017073Sbloom #include "kernel.h"
4118316Sralph #include "syslog.h"
426941Ssam 
438475Sroot #include "../vax/cpu.h"
4417073Sbloom #include "ubareg.h"
4517073Sbloom #include "ubavar.h"
4617073Sbloom #include "idcreg.h"
476941Ssam 
486941Ssam struct idc_softc {
496941Ssam 	int	sc_bcnt;	/* number of bytes to transfer */
506941Ssam 	int	sc_resid;	/* total number of bytes to transfer */
516941Ssam 	int	sc_ubaddr;	/* Unibus address of data */
526941Ssam 	short	sc_unit;	/* unit doing transfer */
536941Ssam 	short	sc_softas;	/* software attention summary bits */
546941Ssam 	union idc_dar {
556941Ssam 		long	dar_l;
566941Ssam 		u_short	dar_w[2];
576941Ssam 		u_char	dar_b[4];
586941Ssam 	} sc_un;		/* prototype disk address register */
596941Ssam } idc_softc;
606941Ssam 
616941Ssam #define	dar_dar		dar_l		/* the whole disk address */
626941Ssam #define	dar_cyl		dar_w[1]	/* cylinder address */
636941Ssam #define	dar_trk		dar_b[1]	/* track */
646941Ssam #define	dar_sect	dar_b[0]	/* sector */
656941Ssam #define	sc_dar		sc_un.dar_dar
666941Ssam #define	sc_cyl		sc_un.dar_cyl
676941Ssam #define	sc_trk		sc_un.dar_trk
686941Ssam #define	sc_sect		sc_un.dar_sect
696941Ssam 
7024739Sbloom #define idcunit(dev)	(minor(dev) >> 3)
7124739Sbloom 
726941Ssam /* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
736941Ssam struct size {
746941Ssam 	daddr_t	nblocks;
756941Ssam 	int	cyloff;
766941Ssam } rb02_sizes[8] ={
776941Ssam 	15884,	0,		/* A=cyl 0 thru 399 */
786941Ssam 	4480,	400,		/* B=cyl 400 thru 510 */
796941Ssam 	20480,	0,		/* C=cyl 0 thru 511 */
806941Ssam 	0,	0,
816941Ssam 	0,	0,
826941Ssam 	0,	0,
836941Ssam 	0,	0,
846941Ssam 	0,	0,
856941Ssam }, rb80_sizes[8] ={
866941Ssam 	15884,	0,		/* A=cyl 0 thru 36 */
876941Ssam 	33440,	37,		/* B=cyl 37 thru 114 */
886941Ssam 	242606,	0,		/* C=cyl 0 thru 558 */
896941Ssam 	0,	0,
906941Ssam 	0,	0,
916941Ssam 	0,	0,
926941Ssam 	82080,	115,		/* G=cyl 115 thru 304 */
936941Ssam 	110143,	305,		/* H=cyl 305 thru 558 */
946941Ssam };
956941Ssam /* END OF STUFF WHICH SHOULD BE READ IN PER DISK */
966941Ssam 
976941Ssam int	idcprobe(), idcslave(), idcattach(), idcdgo(), idcintr();
986941Ssam struct	uba_ctlr *idcminfo[NIDC];
996941Ssam struct	uba_device *idcdinfo[NRB];
1006941Ssam 
1016941Ssam u_short	idcstd[] = { 0174400, 0};
1026941Ssam struct	uba_driver idcdriver =
1036941Ssam  { idcprobe, idcslave, idcattach, idcdgo, idcstd, "rb", idcdinfo, "idc", idcminfo, 0 };
1046941Ssam struct	buf idcutab[NRB];
1056941Ssam union	idc_dar idccyl[NRB];
1066941Ssam 
1076941Ssam struct	idcst {
1086941Ssam 	short	nbps;
1096941Ssam 	short	nsect;
1106941Ssam 	short	ntrak;
1116941Ssam 	short	nspc;
1126941Ssam 	short	ncyl;
1136941Ssam 	struct	size *sizes;
1146941Ssam } idcst[] = {
1156941Ssam 	256, NRB02SECT, NRB02TRK, NRB02SECT*NRB02TRK, NRB02CYL,	rb02_sizes,
1166941Ssam 	512, NRB80SECT, NRB80TRK, NRB80SECT*NRB80TRK, NRB80CYL,	rb80_sizes,
1176941Ssam };
1186941Ssam 
1196941Ssam struct	buf ridcbuf[NRB];
1206941Ssam 
1216941Ssam #define	b_cylin	b_resid
1226941Ssam 
1236941Ssam int	idcwstart, idcwticks, idcwatch();
1246941Ssam 
1258608Sroot /*ARGSUSED*/
1266941Ssam idcprobe(reg)
1276941Ssam 	caddr_t reg;
1286941Ssam {
1296941Ssam 	register int br, cvec;
1306941Ssam 	register struct idcdevice *idcaddr;
1316941Ssam 
1326941Ssam #ifdef lint
1336941Ssam 	br = 0; cvec = br; br = cvec;
1346941Ssam #endif
1356941Ssam 	idcaddr = (struct idcdevice *)((caddr_t)uba_hd[0].uh_uba + 0x200);
1366941Ssam 	idcaddr->idccsr = IDC_ATTN|IDC_IE;
1376941Ssam 	while ((idcaddr->idccsr & IDC_CRDY) == 0)
1386941Ssam 		;
1396941Ssam 	idcaddr->idccsr = IDC_ATTN|IDC_CRDY;
1407414Skre 	return (sizeof (struct idcdevice));
1416941Ssam }
1426941Ssam 
1438608Sroot /*ARGSUSED*/
1446941Ssam idcslave(ui, reg)
1456941Ssam 	struct uba_device *ui;
1466941Ssam 	caddr_t reg;
1476941Ssam {
1486941Ssam 	register struct idcdevice *idcaddr;
1496941Ssam 	register int i;
1506941Ssam 
1516941Ssam 	idcaddr = (struct idcdevice *)((caddr_t)uba_hd[0].uh_uba + 0x200);
1526941Ssam 	ui->ui_type = 0;
1536941Ssam 	idcaddr->idcmpr = IDCGS_GETSTAT;
1546941Ssam 	idcaddr->idccsr = IDC_GETSTAT|(ui->ui_slave<<8);
1558721Sroot 	(void) idcwait(idcaddr, 0);
1566941Ssam 	i = idcaddr->idcmpr;
1576941Ssam 	idcaddr->idccsr = IDC_CRDY|(1<<(ui->ui_slave+16));
1588721Sroot 	(void) idcwait(idcaddr, 0);
15919914Sedward 	/* read header to synchronize microcode */
16019914Sedward 	idcaddr->idccsr = (ui->ui_slave<<8)|IDC_RHDR;
16119914Sedward 	(void) idcwait(idcaddr, 0);
16219914Sedward 	i = idcaddr->idcmpr;		/* read header word 1 */
16319914Sedward 	i = idcaddr->idcmpr;		/* read header word 2 */
1648608Sroot #ifdef lint
16519914Sedward 	i = i;
1668608Sroot #endif
16719914Sedward 	if ((idcaddr->idccsr & (IDC_ERR|IDC_R80)) == IDC_R80)
1686941Ssam 		ui->ui_type = 1;
16919914Sedward 	else if ((idcaddr->idccsr & (IDC_DE|IDC_R80)) == 0)
17017558Skarels 		/*
17117558Skarels 		 * RB02 may not have pack spun up, just look for drive error.
17217558Skarels 		 */
17319914Sedward 		ui->ui_type = 0;
17419914Sedward 	else
17519914Sedward 		return (0);
1766941Ssam 	return (1);
1776941Ssam }
1786941Ssam 
1796941Ssam idcattach(ui)
1806941Ssam 	register struct uba_device *ui;
1816941Ssam {
1826941Ssam 
1836941Ssam 	/*
1846941Ssam 	 * Fix all addresses to correspond
1856941Ssam 	 * to the "real" IDC address.
1866941Ssam 	 */
1876941Ssam 	ui->ui_mi->um_addr = ui->ui_addr = (caddr_t)uba_hd[0].uh_uba + 0x200;
1886941Ssam 	ui->ui_physaddr = (caddr_t)uba_hd[0].uh_physuba + 0x200;
1896941Ssam 	if (idcwstart == 0) {
1906941Ssam 		timeout(idcwatch, (caddr_t)0, hz);
1916941Ssam 		idcwstart++;
1926941Ssam 	}
1936941Ssam 	if (ui->ui_dk >= 0)
1946941Ssam 		if (ui->ui_type)
1956941Ssam 			dk_mspw[ui->ui_dk] = 1.0 / (60 * NRB80SECT * 256);
1966941Ssam 		else
1976941Ssam 			dk_mspw[ui->ui_dk] = 1.0 / (60 * NRB02SECT * 128);
1986941Ssam 	idccyl[ui->ui_unit].dar_dar = -1;
1996941Ssam 	ui->ui_flags = 0;
2006941Ssam }
2018569Sroot 
2028569Sroot idcopen(dev)
2038569Sroot 	dev_t dev;
2048569Sroot {
20524739Sbloom 	register int unit = idcunit(dev);
2068569Sroot 	register struct uba_device *ui;
2078569Sroot 
2088569Sroot 	if (unit >= NRB || (ui = idcdinfo[unit]) == 0 || ui->ui_alive == 0)
2098569Sroot 		return (ENXIO);
2108569Sroot 	return (0);
2118569Sroot }
2126941Ssam 
2136941Ssam idcstrategy(bp)
2146941Ssam 	register struct buf *bp;
2156941Ssam {
2166941Ssam 	register struct uba_device *ui;
2176941Ssam 	register struct idcst *st;
2186941Ssam 	register int unit;
2196941Ssam 	register struct buf *dp;
2206941Ssam 	int xunit = minor(bp->b_dev) & 07;
2216941Ssam 	long bn, sz;
2226941Ssam 
2236941Ssam 	sz = (bp->b_bcount+511) >> 9;
22424739Sbloom 	unit = idcunit(bp->b_dev);
22524739Sbloom 	if (unit >= NRB) {
22624739Sbloom 		bp->b_error = ENXIO;
2276941Ssam 		goto bad;
22824739Sbloom 	}
2296941Ssam 	ui = idcdinfo[unit];
23024739Sbloom 	if (ui == 0 || ui->ui_alive == 0) {
23124739Sbloom 		bp->b_error = ENXIO;
2326941Ssam 		goto bad;
23324739Sbloom 	}
2346941Ssam 	st = &idcst[ui->ui_type];
2356941Ssam 	if (bp->b_blkno < 0 ||
23624739Sbloom 	    (bn = bp->b_blkno)+sz > st->sizes[xunit].nblocks) {
237*24784Skarels 		if (bp->b_blkno == st->sizes[xunit].nblocks) {
238*24784Skarels 			bp->b_resid = bp->b_bcount;
23924739Sbloom 			goto done;
240*24784Skarels 		}
24124739Sbloom 		bp->b_error = EINVAL;
2426941Ssam 		goto bad;
24324739Sbloom 	}
2446941Ssam 	if (ui->ui_type == 0)
2456941Ssam 		bn *= 2;
2466941Ssam 	bp->b_cylin = bn/st->nspc + st->sizes[xunit].cyloff;
2476941Ssam 	(void) spl5();
2488608Sroot 	trace("strt",bp);
2496941Ssam 	dp = &idcutab[ui->ui_unit];
2506941Ssam 	disksort(dp, bp);
2516941Ssam 	if (dp->b_active == 0) {
2528608Sroot 		trace("!act",dp);
2536941Ssam 		(void) idcustart(ui);
2546941Ssam 		bp = &ui->ui_mi->um_tab;
2556941Ssam 		if (bp->b_actf && bp->b_active == 0)
2566941Ssam 			(void) idcstart(ui->ui_mi);
2576941Ssam 	}
2586941Ssam 	(void) spl0();
2596941Ssam 	return;
2606941Ssam 
2616941Ssam bad:
2626941Ssam 	bp->b_flags |= B_ERROR;
26324739Sbloom done:
2646941Ssam 	iodone(bp);
2656941Ssam 	return;
2666941Ssam }
2676941Ssam 
2686941Ssam idcustart(ui)
2696941Ssam 	register struct uba_device *ui;
2706941Ssam {
2716941Ssam 	register struct buf *bp, *dp;
2726941Ssam 	register struct uba_ctlr *um;
2736941Ssam 	register struct idcdevice *idcaddr;
2746941Ssam 	register struct idcst *st;
2756941Ssam 	union idc_dar cyltrk;
2766941Ssam 	daddr_t bn;
2776941Ssam 	int unit;
2786941Ssam 
2796941Ssam 	if (ui == 0)
2806941Ssam 		return (0);
2816941Ssam 	dk_busy &= ~(1<<ui->ui_dk);
2826941Ssam 	dp = &idcutab[ui->ui_unit];
2836941Ssam 	um = ui->ui_mi;
2846941Ssam 	unit = ui->ui_slave;
2858608Sroot 	trace("ust", dp);
2866941Ssam 	idcaddr = (struct idcdevice *)um->um_addr;
2876941Ssam 	if (um->um_tab.b_active) {
2886941Ssam 		idc_softc.sc_softas |= 1<<unit;
2898608Sroot 		trace("umac",idc_softc.sc_softas);
2906941Ssam 		return (0);
2916941Ssam 	}
2926941Ssam 	if ((bp = dp->b_actf) == NULL) {
2938608Sroot 		trace("!bp",0);
2946941Ssam 		return (0);
2956941Ssam 	}
2966941Ssam 	if (dp->b_active) {
2978608Sroot 		trace("dpac",dp->b_active);
2986941Ssam 		goto done;
2996941Ssam 	}
3006941Ssam 	dp->b_active = 1;
3016941Ssam 	/* CHECK DRIVE READY? */
30224739Sbloom 	bn = bp->b_blkno;
3038608Sroot 	trace("seek", bn);
3046941Ssam 	if (ui->ui_type == 0)
3056941Ssam 		bn *= 2;
3066941Ssam 	st = &idcst[ui->ui_type];
3076941Ssam 	cyltrk.dar_cyl = bp->b_cylin;
3086941Ssam 	cyltrk.dar_trk = (bn / st->nsect) % st->ntrak;
3096941Ssam 	cyltrk.dar_sect = 0;
3106941Ssam 	printd("idcustart, unit %d, cyltrk 0x%x\n", unit, cyltrk.dar_dar);
3116941Ssam 	/*
3126941Ssam 	 * If on cylinder, no need to seek.
3136941Ssam 	 */
3146941Ssam 	if (cyltrk.dar_dar == idccyl[ui->ui_unit].dar_dar)
3156941Ssam 		goto done;
3166941Ssam 	/*
3176941Ssam 	 * RB80 can change heads (tracks) just by loading
3186941Ssam 	 * the disk address register, perform optimization
3196941Ssam 	 * here instead of doing a full seek.
3206941Ssam 	 */
3216941Ssam 	if (ui->ui_type && cyltrk.dar_cyl == idccyl[ui->ui_unit].dar_cyl) {
3226941Ssam 		idcaddr->idccsr = IDC_CRDY|IDC_IE|IDC_SEEK|(unit<<8);
3236941Ssam 		idcaddr->idcdar = cyltrk.dar_dar;
3246941Ssam 		idccyl[ui->ui_unit].dar_dar = cyltrk.dar_dar;
3256941Ssam 		goto done;
3266941Ssam 	}
3276941Ssam 	/*
3286941Ssam 	 * Need to do a full seek.  Select the unit, clear
3296941Ssam 	 * its attention bit, set the command, load the
3306941Ssam 	 * disk address register, and then go.
3316941Ssam 	 */
3326941Ssam 	idcaddr->idccsr =
3336941Ssam 	    IDC_CRDY|IDC_IE|IDC_SEEK|(unit<<8)|(1<<(unit+16));
3346941Ssam 	idcaddr->idcdar = cyltrk.dar_dar;
3356941Ssam 	idccyl[ui->ui_unit].dar_dar = cyltrk.dar_dar;
3366941Ssam 	printd("  seek");
3376941Ssam 	idcaddr->idccsr = IDC_IE|IDC_SEEK|(unit<<8);
3386941Ssam 	if (ui->ui_dk >= 0) {
3396941Ssam 		dk_busy |= 1<<ui->ui_dk;
3406941Ssam 		dk_seek[ui->ui_dk]++;
3416941Ssam 	}
3426941Ssam 	/*
3436941Ssam 	 * RB80's initiate seeks very quickly.  Wait for it
3446941Ssam 	 * to come ready rather than taking the interrupt.
3456941Ssam 	 */
3466941Ssam 	if (ui->ui_type) {
3476941Ssam 		if (idcwait(idcaddr, 10) == 0)
3486941Ssam 			return (1);
3496941Ssam 		idcaddr->idccsr &= ~IDC_ATTN;
3506941Ssam 		/* has the seek completed? */
3516941Ssam 		if (idcaddr->idccsr & IDC_DRDY) {
3526941Ssam 			printd(", drdy");
3536941Ssam 			idcaddr->idccsr =
3546941Ssam 			    IDC_CRDY|IDC_IE|IDC_SEEK|(unit<<8)|(1<<(unit+16));
3556941Ssam 			goto done;
3566941Ssam 		}
3576941Ssam 	}
3586941Ssam 	printd(", idccsr = 0x%x\n", idcaddr->idccsr);
3596941Ssam 	return (1);
3606941Ssam done:
3616941Ssam 	if (dp->b_active != 2) {
3628608Sroot 		trace("!=2",dp->b_active);
3636941Ssam 		dp->b_forw = NULL;
3646941Ssam 		if (um->um_tab.b_actf == NULL)
3656941Ssam 			um->um_tab.b_actf = dp;
3666941Ssam 		else {
3678608Sroot 			trace("!NUL",um->um_tab.b_actl);
3686941Ssam 			um->um_tab.b_actl->b_forw = dp;
3696941Ssam 		}
3706941Ssam 		um->um_tab.b_actl = dp;
3716941Ssam 		dp->b_active = 2;
3726941Ssam 	}
3736941Ssam 	return (0);
3746941Ssam }
3756941Ssam 
3766941Ssam idcstart(um)
3776941Ssam 	register struct uba_ctlr *um;
3786941Ssam {
3796941Ssam 	register struct buf *bp, *dp;
3806941Ssam 	register struct uba_device *ui;
3816941Ssam 	register struct idcdevice *idcaddr;
3826941Ssam 	register struct idc_softc *sc;
3836941Ssam 	struct idcst *st;
3846941Ssam 	daddr_t bn;
3856941Ssam 	int sn, tn, cmd;
3866941Ssam 
3876941Ssam loop:
3886941Ssam 	if ((dp = um->um_tab.b_actf) == NULL) {
3898608Sroot 		trace("nodp",um);
3906941Ssam 		return (0);
3916941Ssam 	}
3926941Ssam 	if ((bp = dp->b_actf) == NULL) {
3938608Sroot 		trace("nobp", dp);
3946941Ssam 		um->um_tab.b_actf = dp->b_forw;
3956941Ssam 		goto loop;
3966941Ssam 	}
3976941Ssam 	um->um_tab.b_active = 1;
39824739Sbloom 	ui = idcdinfo[idcunit(bp->b_dev)];
39924739Sbloom 	bn = bp->b_blkno;
4008608Sroot 	trace("star",bp);
4016941Ssam 	if (ui->ui_type == 0)
4026941Ssam 		bn *= 2;
4036941Ssam 	sc = &idc_softc;
4046941Ssam 	st = &idcst[ui->ui_type];
4056941Ssam 	sn = bn%st->nspc;
4066941Ssam 	tn = sn/st->nsect;
4076941Ssam 	sn %= st->nsect;
4086941Ssam 	sc->sc_sect = sn;
4096941Ssam 	sc->sc_trk = tn;
4106941Ssam 	sc->sc_cyl = bp->b_cylin;
4116941Ssam 	idcaddr = (struct idcdevice *)ui->ui_addr;
4126941Ssam 	printd("idcstart, unit %d, dar 0x%x", ui->ui_slave, sc->sc_dar);
4136941Ssam 	if (bp->b_flags & B_READ)
4146941Ssam 		cmd = IDC_IE|IDC_READ|(ui->ui_slave<<8);
4156941Ssam 	else
4166941Ssam 		cmd = IDC_IE|IDC_WRITE|(ui->ui_slave<<8);
4176941Ssam 	idcaddr->idccsr = IDC_CRDY|cmd;
4186941Ssam 	if ((idcaddr->idccsr&IDC_DRDY) == 0) {
41924739Sbloom 		printf("rb%d: not ready\n", idcunit(bp->b_dev));
4206941Ssam 		um->um_tab.b_active = 0;
4216941Ssam 		um->um_tab.b_errcnt = 0;
4226941Ssam 		dp->b_actf = bp->av_forw;
4236941Ssam 		dp->b_active = 0;
4246941Ssam 		bp->b_flags |= B_ERROR;
4256941Ssam 		iodone(bp);
4266941Ssam 		goto loop;
4276941Ssam 	}
4286941Ssam 	idccyl[ui->ui_unit].dar_dar = sc->sc_dar;
4296941Ssam 	idccyl[ui->ui_unit].dar_sect = 0;
4306941Ssam 	sn = (st->nsect - sn) * st->nbps;
4316941Ssam 	if (sn > bp->b_bcount)
4326941Ssam 		sn = bp->b_bcount;
4336941Ssam 	sc->sc_bcnt = sn;
4346941Ssam 	sc->sc_resid = bp->b_bcount;
4356941Ssam 	sc->sc_unit = ui->ui_slave;
4366941Ssam 	printd(", bcr 0x%x, cmd 0x%x\n", sn, cmd);
4376941Ssam 	um->um_cmd = cmd;
4386941Ssam 	(void) ubago(ui);
4396941Ssam 	return (1);
4406941Ssam }
4416941Ssam 
4426941Ssam idcdgo(um)
4436941Ssam 	register struct uba_ctlr *um;
4446941Ssam {
4456941Ssam 	register struct idcdevice *idcaddr = (struct idcdevice *)um->um_addr;
4466941Ssam 	register struct idc_softc *sc = &idc_softc;
4476941Ssam 
4486941Ssam 	/*
4496941Ssam 	 * VERY IMPORTANT: must load registers in this order.
4506941Ssam 	 */
4516941Ssam 	idcaddr->idcbar = sc->sc_ubaddr = um->um_ubinfo&0x3ffff;
4526941Ssam 	idcaddr->idcbcr = -sc->sc_bcnt;
4536941Ssam 	idcaddr->idcdar = sc->sc_dar;
4546941Ssam 	printd("idcdgo, ubinfo 0x%x, cmd 0x%x\n", um->um_ubinfo, um->um_cmd);
4556941Ssam 	idcaddr->idccsr = um->um_cmd;
4568608Sroot 	trace("go", um);
4576941Ssam 	um->um_tab.b_active = 2;
4586941Ssam 	/*** CLEAR SPURIOUS ATTN ON R80? ***/
4596941Ssam }
4606941Ssam 
4616941Ssam idcintr(idc)
4626941Ssam 	int idc;
4636941Ssam {
4646941Ssam 	register struct uba_ctlr *um = idcminfo[idc];
4656941Ssam 	register struct uba_device *ui;
4666941Ssam 	register struct idcdevice *idcaddr = (struct idcdevice *)um->um_addr;
4676941Ssam 	register struct idc_softc *sc = &idc_softc;
4686941Ssam 	register struct buf *bp, *dp;
4696941Ssam 	struct idcst *st;
4706941Ssam 	int unit, as, er, cmd, ds = 0;
4716941Ssam 
4726941Ssam 	printd("idcintr, idccsr 0x%x", idcaddr->idccsr);
4736941Ssam top:
4746941Ssam 	idcwticks = 0;
4758608Sroot 	trace("intr", um->um_tab.b_active);
4766941Ssam 	if (um->um_tab.b_active == 2) {
4776941Ssam 		/*
4786941Ssam 		 * Process a data transfer complete interrupt.
4796941Ssam 		 */
4806941Ssam 		um->um_tab.b_active = 1;
4816941Ssam 		dp = um->um_tab.b_actf;
4826941Ssam 		bp = dp->b_actf;
48324739Sbloom 		ui = idcdinfo[idcunit(bp->b_dev)];
4846941Ssam 		unit = ui->ui_slave;
4856941Ssam 		st = &idcst[ui->ui_type];
4866941Ssam 		idcaddr->idccsr = IDC_IE|IDC_CRDY|(unit<<8);
4876941Ssam 		if ((er = idcaddr->idccsr) & IDC_ERR) {
4886941Ssam 			if (er & IDC_DE) {
4896941Ssam 				idcaddr->idcmpr = IDCGS_GETSTAT;
4906941Ssam 				idcaddr->idccsr = IDC_GETSTAT|(unit<<8);
4918721Sroot 				(void) idcwait(idcaddr, 0);
4926941Ssam 				ds = idcaddr->idcmpr;
4936941Ssam 				idcaddr->idccsr =
4946941Ssam 				    IDC_IE|IDC_CRDY|(1<<(unit+16));
4956941Ssam 			}
4966941Ssam 			printd(", er 0x%x, ds 0x%x", er, ds);
4976941Ssam 			if (ds & IDCDS_WL) {
49824739Sbloom 				printf("rb%d: write locked\n",
49924739Sbloom 					idcunit(bp->b_dev));
5006941Ssam 				bp->b_flags |= B_ERROR;
5016941Ssam 			} else if (++um->um_tab.b_errcnt > 28 || er&IDC_HARD) {
5026941Ssam hard:
5036941Ssam 				harderr(bp, "rb");
5046941Ssam 				printf("csr=%b ds=%b\n", er, IDCCSR_BITS, ds,
5056941Ssam 				    ui->ui_type?IDCRB80DS_BITS:IDCRB02DS_BITS);
5066941Ssam 				bp->b_flags |= B_ERROR;
5076941Ssam 			} else if (er & IDC_DCK) {
5086941Ssam 				switch (er & IDC_ECS) {
5096941Ssam 				case IDC_ECS_NONE:
5106941Ssam 					break;
5116941Ssam 				case IDC_ECS_SOFT:
5126941Ssam 					idcecc(ui);
5136941Ssam 					break;
5146941Ssam 				case IDC_ECS_HARD:
5156941Ssam 				default:
5166941Ssam 					goto hard;
5176941Ssam 				}
5186941Ssam 			} else
5196941Ssam 				/* recoverable error, set up for retry */
5206941Ssam 				goto seek;
5216941Ssam 		}
5226941Ssam 		if ((sc->sc_resid -= sc->sc_bcnt) != 0) {
5236941Ssam 			sc->sc_ubaddr += sc->sc_bcnt;
5246941Ssam 			/*
5256941Ssam 			 * Current transfer is complete, have
5266941Ssam 			 * we overflowed to the next track?
5276941Ssam 			 */
5286941Ssam 			if ((sc->sc_sect += sc->sc_bcnt/st->nbps) == st->nsect) {
5296941Ssam 				sc->sc_sect = 0;
5306941Ssam 				if (++sc->sc_trk == st->ntrak) {
5316941Ssam 					sc->sc_trk = 0;
5326941Ssam 					sc->sc_cyl++;
5336941Ssam 				} else if (ui->ui_type) {
5346941Ssam 					/*
5356941Ssam 					 * RB80 can change heads just by
5366941Ssam 					 * loading the disk address register.
5376941Ssam 					 */
5386941Ssam 					idcaddr->idccsr = IDC_SEEK|IDC_CRDY|
5396941Ssam 					    IDC_IE|(unit<<8);
5406941Ssam 					printd(", change to track 0x%x", sc->sc_dar);
5416941Ssam 					idcaddr->idcdar = sc->sc_dar;
5426941Ssam 					idccyl[ui->ui_unit].dar_dar = sc->sc_dar;
5436941Ssam 					idccyl[ui->ui_unit].dar_sect = 0;
5446941Ssam 					goto cont;
5456941Ssam 				}
5466941Ssam 				/*
5476941Ssam 				 * Changing tracks on RB02 or cylinders
5486941Ssam 				 * on RB80, start a seek.
5496941Ssam 				 */
5506941Ssam seek:
5516941Ssam 				cmd = IDC_IE|IDC_SEEK|(unit<<8);
5526941Ssam 				idcaddr->idccsr = cmd|IDC_CRDY;
5536941Ssam 				idcaddr->idcdar = sc->sc_dar;
5546941Ssam 				printd(", seek to 0x%x\n", sc->sc_dar);
5556941Ssam 				idccyl[ui->ui_unit].dar_dar = sc->sc_dar;
5566941Ssam 				idccyl[ui->ui_unit].dar_sect = 0;
5576941Ssam 				sc->sc_bcnt = 0;
5586941Ssam 				idcaddr->idccsr = cmd;
5596941Ssam 				if (ui->ui_type) {
5606941Ssam 					if (idcwait(idcaddr, 10) == 0)
5616941Ssam 						return;
5626941Ssam 					idcaddr->idccsr &= ~IDC_ATTN;
5636941Ssam 					if (idcaddr->idccsr & IDC_DRDY)
5646941Ssam 						goto top;
5656941Ssam 				}
5666941Ssam 			} else {
5676941Ssam 				/*
5686941Ssam 				 * Continue transfer on current track.
5696941Ssam 				 */
5706941Ssam cont:
5716941Ssam 				sc->sc_bcnt = (st->nsect-sc->sc_sect)*st->nbps;
5726941Ssam 				if (sc->sc_bcnt > sc->sc_resid)
5736941Ssam 					sc->sc_bcnt = sc->sc_resid;
5746941Ssam 				if (bp->b_flags & B_READ)
5756941Ssam 					cmd = IDC_IE|IDC_READ|(unit<<8);
5766941Ssam 				else
5776941Ssam 					cmd = IDC_IE|IDC_WRITE|(unit<<8);
5786941Ssam 				idcaddr->idccsr = cmd|IDC_CRDY;
5796941Ssam 				idcaddr->idcbar = sc->sc_ubaddr;
5806941Ssam 				idcaddr->idcbcr = -sc->sc_bcnt;
5816941Ssam 				idcaddr->idcdar = sc->sc_dar;
5826941Ssam 				printd(", continue I/O 0x%x, 0x%x\n", sc->sc_dar, sc->sc_bcnt);
5836941Ssam 				idcaddr->idccsr = cmd;
5846941Ssam 				um->um_tab.b_active = 2;
5856941Ssam 			}
5866941Ssam 			return;
5876941Ssam 		}
5886941Ssam 		/*
5896941Ssam 		 * Entire transfer is done, clean up.
5906941Ssam 		 */
5916941Ssam 		ubadone(um);
5926941Ssam 		dk_busy &= ~(1 << ui->ui_dk);
5936941Ssam 		um->um_tab.b_active = 0;
5946941Ssam 		um->um_tab.b_errcnt = 0;
5956941Ssam 		um->um_tab.b_actf = dp->b_forw;
5966941Ssam 		dp->b_active = 0;
5976941Ssam 		dp->b_errcnt = 0;
5986941Ssam 		dp->b_actf = bp->av_forw;
5998608Sroot 		trace("done", dp); trace(&um->um_tab.b_actf, dp->b_actf);
6006941Ssam 		bp->b_resid = sc->sc_resid;
6016941Ssam 		printd(", iodone, resid 0x%x\n", bp->b_resid);
6026941Ssam 		iodone(bp);
6036941Ssam 		if (dp->b_actf)
6046941Ssam 			if (idcustart(ui))
6056941Ssam 				return;
6066941Ssam 	} else if (um->um_tab.b_active == 1) {
6076941Ssam 		/*
6086941Ssam 		 * Got an interrupt while setting up for a command
6096941Ssam 		 * or doing a mid-transfer seek.  Save any attentions
6106941Ssam 		 * for later and process a mid-transfer seek complete.
6116941Ssam 		 */
6126941Ssam 		as = idcaddr->idccsr;
6136941Ssam 		idcaddr->idccsr = IDC_IE|IDC_CRDY|(as&IDC_ATTN);
6146941Ssam 		as = (as >> 16) & 0xf;
6156941Ssam 		unit = sc->sc_unit;
6166941Ssam 		sc->sc_softas |= as & ~(1<<unit);
6176941Ssam 		if (as & (1<<unit)) {
6186941Ssam 			printd(", seek1 complete");
6196941Ssam 			um->um_tab.b_active = 2;
6206941Ssam 			goto top;
6216941Ssam 		}
6226941Ssam 		printd(", as1 %o\n", as);
6236941Ssam 		return;
6246941Ssam 	}
6256941Ssam 	/*
6266941Ssam 	 * Process any seek initiated or complete interrupts.
6276941Ssam 	 */
6286941Ssam 	as = idcaddr->idccsr;
6296941Ssam 	idcaddr->idccsr = IDC_IE|IDC_CRDY|(as&IDC_ATTN);
6306941Ssam 	as = ((as >> 16) & 0xf) | sc->sc_softas;
6316941Ssam 	sc->sc_softas = 0;
6328608Sroot 	trace("as", as);
6336941Ssam 	printd(", as %o", as);
6346941Ssam 	for (unit = 0; unit < NRB; unit++)
6356941Ssam 		if (as & (1<<unit)) {
6366941Ssam 			as &= ~(1<<unit);
6376941Ssam 			idcaddr->idccsr = IDC_IE|IDC_CRDY|(unit<<8);
6386941Ssam 			ui = idcdinfo[unit];
6396941Ssam 			if (ui) {
6406941Ssam 				printd(", attn unit %d", unit);
6416941Ssam 				if (idcaddr->idccsr & IDC_DRDY)
6426941Ssam 					if (idcustart(ui)) {
6436941Ssam 						sc->sc_softas = as;
6446941Ssam 						return;
6456941Ssam 					}
6466941Ssam 			} else {
6476941Ssam 				printd(", unsol. intr. unit %d", unit);
6486941Ssam 			}
6496941Ssam 		}
6506941Ssam 	printd("\n");
6516941Ssam 	if (um->um_tab.b_actf && um->um_tab.b_active == 0) {
6528608Sroot 		trace("stum",um->um_tab.b_actf);
6538721Sroot 		(void) idcstart(um);
6546941Ssam 	}
6556941Ssam }
6566941Ssam 
6578608Sroot idcwait(addr, n)
6586941Ssam 	register struct idcdevice *addr;
6598608Sroot 	register int n;
6606941Ssam {
6616941Ssam 	register int i;
6626941Ssam 
6638608Sroot 	while (--n && (addr->idccsr & IDC_CRDY) == 0)
6646941Ssam 		for (i = 10; i; i--)
6656941Ssam 			;
6668608Sroot 	return (n);
6676941Ssam }
6686941Ssam 
6697728Sroot idcread(dev, uio)
6706941Ssam 	dev_t dev;
6717728Sroot 	struct uio *uio;
6726941Ssam {
67324739Sbloom 	register int unit = idcunit(dev);
6746941Ssam 
6756941Ssam 	if (unit >= NRB)
6768161Sroot 		return (ENXIO);
6778161Sroot 	return (physio(idcstrategy, &ridcbuf[unit], dev, B_READ, minphys, uio));
6786941Ssam }
6796941Ssam 
6807834Sroot idcwrite(dev, uio)
6816941Ssam 	dev_t dev;
6827834Sroot 	struct uio *uio;
6836941Ssam {
68424739Sbloom 	register int unit = idcunit(dev);
6856941Ssam 
6866941Ssam 	if (unit >= NRB)
6878161Sroot 		return (ENXIO);
6888161Sroot 	return (physio(idcstrategy, &ridcbuf[unit], dev, B_WRITE, minphys, uio));
6896941Ssam }
6906941Ssam 
6916941Ssam idcecc(ui)
6926941Ssam 	register struct uba_device *ui;
6936941Ssam {
6946941Ssam 	register struct idcdevice *idc = (struct idcdevice *)ui->ui_addr;
6956941Ssam 	register struct buf *bp = idcutab[ui->ui_unit].b_actf;
6966941Ssam 	register struct uba_ctlr *um = ui->ui_mi;
6976941Ssam 	register struct idcst *st;
6986941Ssam 	register int i;
6996941Ssam 	struct uba_regs *ubp = ui->ui_hd->uh_uba;
7006941Ssam 	int bit, byte, mask;
7016941Ssam 	caddr_t addr;
7026941Ssam 	int reg, npf, o;
7036941Ssam 	int cn, tn, sn;
7046941Ssam 
7056941Ssam 	printf("idcecc: HELP!\n");
7066941Ssam 	npf = btop(idc->idcbcr + idc_softc.sc_bcnt) - 1;;
7076941Ssam 	reg = btop(idc_softc.sc_ubaddr) + npf;
7086941Ssam 	o = (int)bp->b_un.b_addr & PGOFSET;
7096941Ssam 	st = &idcst[ui->ui_type];
7106941Ssam 	cn = idc_softc.sc_cyl;
7116941Ssam 	tn = idc_softc.sc_trk;
7126941Ssam 	sn = idc_softc.sc_sect;
7136941Ssam 	um->um_tab.b_active = 1;	/* Either complete or continuing... */
71424739Sbloom 	log(KERN_RECOV, "rb%d%c: soft ecc sn%d\n", idcunit(bp->b_dev),
7156941Ssam 	    'a'+(minor(bp->b_dev)&07),
7166941Ssam 	    (cn*st->ntrak + tn) * st->nsect + sn + npf);
7176941Ssam 	mask = idc->idceccpat;
7186941Ssam 	i = idc->idceccpos - 1;		/* -1 makes 0 origin */
7196941Ssam 	bit = i&07;
7206941Ssam 	i = (i&~07)>>3;
7216941Ssam 	byte = i + o;
7226941Ssam 	while (i < 512 && (int)ptob(npf)+i < idc_softc.sc_bcnt && bit > -11) {
7236941Ssam 		addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
7246941Ssam 		    (byte & PGOFSET);
7256941Ssam 		putmemc(addr, getmemc(addr)^(mask<<bit));
7266941Ssam 		byte++;
7276941Ssam 		i++;
7286941Ssam 		bit -= 8;
7296941Ssam 	}
7306941Ssam 	idc_softc.sc_bcnt += idc->idcbcr;
7316941Ssam 	um->um_tab.b_errcnt = 0;	/* error has been corrected */
7326941Ssam 	return;
7336941Ssam }
7346941Ssam 
7356941Ssam idcreset(uban)
7366941Ssam 	int uban;
7376941Ssam {
7386941Ssam 	register struct uba_ctlr *um;
7396941Ssam 	register struct uba_device *ui;
7406941Ssam 	register unit;
7416941Ssam 
7426941Ssam 	if ((um = idcminfo[0]) == 0 || um->um_ubanum != uban ||
7436941Ssam 	    um->um_alive == 0)
7446941Ssam 		return;
7456941Ssam 	printf(" idc0");
7466941Ssam 	um->um_tab.b_active = 0;
7476941Ssam 	um->um_tab.b_actf = um->um_tab.b_actl = 0;
7486941Ssam 	if (um->um_ubinfo) {
7496941Ssam 		printf("<%d>", (um->um_ubinfo>>28)&0xf);
7509354Ssam 		um->um_ubinfo = 0;
7516941Ssam 	}
7526941Ssam 	for (unit = 0; unit < NRB; unit++) {
7536941Ssam 		if ((ui = idcdinfo[unit]) == 0 || ui->ui_alive == 0)
7546941Ssam 			continue;
7556941Ssam 		idcutab[unit].b_active = 0;
7566941Ssam 		(void) idcustart(ui);
7576941Ssam 	}
7586941Ssam 	(void) idcstart(um);
7596941Ssam }
7606941Ssam 
7616941Ssam idcwatch()
7626941Ssam {
7636941Ssam 	register struct uba_ctlr *um;
7646941Ssam 	register unit;
7656941Ssam 
7666941Ssam 	timeout(idcwatch, (caddr_t)0, hz);
7676941Ssam 	um = idcminfo[0];
7686941Ssam 	if (um == 0 || um->um_alive == 0)
7696941Ssam 		return;
7706941Ssam 	if (um->um_tab.b_active == 0) {
7716941Ssam 		for (unit = 0; unit < NRB; unit++)
7726941Ssam 			if (idcutab[unit].b_active)
7736941Ssam 				goto active;
7746941Ssam 		idcwticks = 0;
7756941Ssam 		return;
7766941Ssam 	}
7776941Ssam active:
7786941Ssam 	idcwticks++;
7796941Ssam 	if (idcwticks >= 20) {
7806941Ssam 		idcwticks = 0;
7816941Ssam 		printf("idc0: lost interrupt\n");
7826941Ssam 		idcintr(0);
7836941Ssam 	}
7846941Ssam }
7856941Ssam 
7868608Sroot /*ARGSUSED*/
7876941Ssam idcdump(dev)
7886941Ssam 	dev_t dev;
7896941Ssam {
7906941Ssam 	struct idcdevice *idcaddr;
7916941Ssam 	char *start;
79212147Sroot 	int num, blk, unit;
7936941Ssam 	struct size *sizes;
7946941Ssam 	register struct uba_regs *uba;
7956941Ssam 	register struct uba_device *ui;
7966941Ssam 	struct idcst *st;
79712778Ssam 	union idc_dar dar;
79812147Sroot 	int nspg;
7996941Ssam 
80024739Sbloom 	unit = idcunit(dev);
8016941Ssam 	if (unit >= NRB)
8026941Ssam 		return (ENXIO);
8036941Ssam #define	phys(cast, addr) ((cast)((int)addr & 0x7fffffff))
8046941Ssam 	ui = phys(struct uba_device *, idcdinfo[unit]);
8056941Ssam 	if (ui->ui_alive == 0)
8066941Ssam 		return (ENXIO);
8076941Ssam 	uba = phys(struct uba_hd *, ui->ui_hd)->uh_physuba;
8086941Ssam 	ubainit(uba);
8096941Ssam 	idcaddr = (struct idcdevice *)ui->ui_physaddr;
81012147Sroot 	if (idcwait(idcaddr, 100) == 0)
81112147Sroot 		return (EFAULT);
81212147Sroot 	/*
81312147Sroot 	 * Since we can only transfer one track at a time, and
81412147Sroot 	 * the rl02 has 256 byte sectors, all the calculations
81512147Sroot 	 * are done in terms of physical sectors (i.e. num and blk
81612147Sroot 	 * are in sectors not NBPG blocks.
81712147Sroot 	 */
81812147Sroot 	st = phys(struct idcst *, &idcst[ui->ui_type]);
8196941Ssam 	sizes = phys(struct size *, st->sizes);
82024211Sbloom 	if (dumplo < 0)
8216941Ssam 		return (EINVAL);
82224211Sbloom 	if (dumplo + maxfree >= sizes[minor(dev)&07].nblocks)
82324211Sbloom 		num = sizes[minor(dev)&07].nblocks - dumplo;
82412147Sroot 	nspg = NBPG / st->nbps;
82524211Sbloom 	num = num * nspg;
82612147Sroot 	start = 0;
82712147Sroot 
8286941Ssam 	while (num > 0) {
8296941Ssam 		register struct pte *io;
8306941Ssam 		register int i;
8316941Ssam 		daddr_t bn;
8326941Ssam 
83312147Sroot 		bn = (dumplo + btop(start)) * nspg;
83412147Sroot 		dar.dar_cyl = bn / st->nspc + sizes[minor(dev)&07].cyloff;
83512147Sroot 		bn %= st->nspc;
83612147Sroot 		dar.dar_trk = bn / st->nsect;
83712147Sroot 		dar.dar_sect = bn % st->nsect;
83812147Sroot 		blk = st->nsect - dar.dar_sect;
83912147Sroot 		if (num < blk)
84012147Sroot 			blk = num;
84112147Sroot 
8426941Ssam 		io = uba->uba_map;
84312147Sroot 		for (i = 0; i < (blk + nspg - 1) / nspg; i++)
8446941Ssam 			*(int *)io++ = (btop(start)+i) | (1<<21) | UBAMR_MRV;
8456941Ssam 		*(int *)io = 0;
84612147Sroot 
84712147Sroot 		idcaddr->idccsr = IDC_CRDY | IDC_SEEK | unit<<8;
84812147Sroot 		if ((idcaddr->idccsr&IDC_DRDY) == 0)
84912147Sroot 			return (EFAULT);
85012147Sroot 		idcaddr->idcdar = dar.dar_dar;
85112147Sroot 		idcaddr->idccsr = IDC_SEEK | unit << 8;
85212147Sroot 		while ((idcaddr->idccsr & (IDC_CRDY|IDC_DRDY))
85312147Sroot 			!= (IDC_CRDY|IDC_DRDY))
85412147Sroot 			;
85512147Sroot 		if (idcaddr->idccsr & IDC_ERR) {
85612147Sroot 			printf("rb%d: seek, csr=%b\n",
85712147Sroot 				unit, idcaddr->idccsr, IDCCSR_BITS);
8586941Ssam 			return (EIO);
85912147Sroot 		}
86012147Sroot 
86112147Sroot 		idcaddr->idccsr = IDC_CRDY | IDC_WRITE | unit<<8;
86212147Sroot 		if ((idcaddr->idccsr&IDC_DRDY) == 0)
86312147Sroot 			return (EFAULT);
86412147Sroot 		idcaddr->idcbar = 0;			/* start addr 0 */
86512147Sroot 		idcaddr->idcbcr = - (blk * st->nbps);
86612147Sroot 		idcaddr->idcdar = dar.dar_dar;
86712147Sroot 		idcaddr->idccsr = IDC_WRITE | unit << 8;
86812147Sroot 		while ((idcaddr->idccsr & (IDC_CRDY|IDC_DRDY))
86912147Sroot 			!= (IDC_CRDY|IDC_DRDY))
87012147Sroot 			;
87112147Sroot 		if (idcaddr->idccsr & IDC_ERR) {
87212147Sroot 			printf("rb%d: write, csr=%b\n",
87312147Sroot 				unit, idcaddr->idccsr, IDCCSR_BITS);
87412147Sroot 			return (EIO);
87512147Sroot 		}
87612147Sroot 
87712147Sroot 		start += blk * st->nbps;
8786941Ssam 		num -= blk;
8796941Ssam 	}
8806941Ssam 	return (0);
8816941Ssam }
88212503Ssam 
88312503Ssam idcsize(dev)
88412503Ssam 	dev_t dev;
88512503Ssam {
88624739Sbloom 	int unit = idcunit(dev);
88712503Ssam 	struct uba_device *ui;
88812503Ssam 	struct idcst *st;
88912503Ssam 
89012503Ssam 	if (unit >= NRB || (ui = idcdinfo[unit]) == 0 || ui->ui_alive == 0)
89112503Ssam 		return (-1);
89212503Ssam 	st = &idcst[ui->ui_type];
89312503Ssam 	return (st->sizes[minor(dev) & 07].nblocks);
89412503Ssam }
8956941Ssam #endif
896