V10/lsys/md/machmflow.c

/*
 * machine-specific hardware routines,
 * these for MicroVAX II
 */

#include "sys/param.h"
#include "sys/pte.h"
#include "sys/mtpr.h"
#include "sys/vm.h"
#include "sys/qbio.h"
#include "sys/clock.h"

#define	IORESET	0x37		/* reset all IO connections */
#define	PARENB	01		/* mser: enable parity error traps */

/*
 * bits in console mailbox
 */

#define	HALTACT	03	/* halt action */
#define	HMRBOOT	0	/* halt: restart, then boot (if halts disabled!) */
#define	HMRST	01	/* halt: restart regardless (but never boot) */
#define	HMBOOT	02	/* halt: boot */
#define	BOOTIH	04	/* boot inhibit */
#define	RSTIH	010	/* restart inhibit */

char *iospace;
int delayfact = 1;		/* factor for DELAY macro */

/*
 * adjust physical top of memory to useful top of memory:
 * preserve space for console program (1024 bytes)
 * and memory bitmap (one bit per 512-byte page; why do we save it?)
 */
machmem(hi)
int hi;
{
	return(hi - (hi/(NBPG*NBBY)) - 1024);
}

/*
 * miscellaneous machine-dependent initialization
 * called just after mapping turned on
 *
 * - make instruction emulation code accessible from user space
 * - reset and enable IO
 */
machinit()
{
	register int *p;		/* pun; really struct pte */
	register char *e;
	register struct iomflow *q;
	extern char _emulbeg, _emulend;

	e = &_emulbeg;
	p = (int *)&Sysmap[btop((int)e & ~KSTART)];
	do {
		*p = (*p &~ PG_PROT) | PG_URKR;
		p++;
		e += NBPG;
	} while (e < &_emulend);
	mtpr(IORESET, 0);
	q = (struct iomflow *)iospace;
	q->c.mser = q->c.mser | PARENB;		/* clear stale error bits */
}

/*
 * stray interrupt handling:
 * just decrypt it and return
 */
strayintr(v)
int v;
{
	if (v < 0x200)
		printf("stray interrupt at 0x%x\n", v);
	else
		printf("stray Q-bus interrupt at 0%o\n", v-0x200);
}

/*
 * how big is io space?
 */

mchiopsize()
{
	return (sizeof(struct iomflow));
}

/*
 * set up the page tables for iospace
 * called while the system page table is being assembled;
 * memory mapping is off
 * pt is the first page table of an area
 * mapping what mchiopsize returned
 */

mchiopinit(pt)
struct pte *pt;
{
	register long *p;	/* pun, for efficiency */
	register long b;
	register int i;

	p = (long *)pt;
	*p++ = PG_V|PG_KW|btop(0x20080000);	/* cpu regs */
	*p++ = PG_V|PG_KW|btop(0x200b8000);	/* watch chip regs */
	b = btop(0x20088000);			/* Q-bus map */
	for (i = 0; i < (8192*sizeof(long))/NBPG; i++)
		*p++ = PG_V|PG_KW|b++;
	b = btop(0x20000000);			/* Q-bus io regs */
	for (i = 0; i < 8192/NBPG; i++)
		*p++ = PG_V|PG_KW|b++;
}

/*
 * return the IO regs for a Q-bus adapter
 * (there's really only one, but only this code knows that)
 */

caddr_t
qbaaddr(u)
int u;
{
	if (u != 0)
		return (0);
	return ((caddr_t)&((struct iomflow *)iospace)->u[u]);
}

/*
 * arrange for a restart on halt
 * -- it would be slightly preferable
 * to fall back to a boot if the restart fails;
 * alas, to do that on MicroVAX II,
 * you must disable console halts
 */

setrestart()
{
	register short *p;

	p = &((struct iomflow *)iospace)->w.cpmbx;
	*p &=~ (BOOTIH|RSTIH|HALTACT);
	*p |= HMRST;		/* always just restart */
}

/*
 * arrange for a boot, now or on next halt
 * -- sometimes called with mapping disabled
 */

setboot()
{
	register short *p;

	if (mfpr(MAPEN))
		p = &((struct iomflow *)iospace)->w.cpmbx;
	else
		p = (short *)0x200b801c;
	*p &=~ (BOOTIH|HALTACT);
	*p |= HMBOOT;		/* halt mode `boot' */
}

/*
 * time-of-year clock:
 * in MicroVAX, it's a watch chip
 * just use it as a counter with funny digits
 */

#define	BYEAR	82	/* arbitrary non-leap-year base */
#define	TODRRES	100	/* TODR units per second */

#define	UIP	0200	/* csra - update in progress */
#define	AMAG	040	/* csra - magic clock modes */
#define	SET	0200	/* csrb - set clock */
#define	DM	04	/* csrb - binary (not bcd) */
#define	M24	02	/* csrb - 24-hour mode */
#define	VRT	0200	/* csrd - valid clock */

#define	BOFF	(SET|DM|M24)	/* stop clock and ready for setting */
#define	BON	(DM|M24)	/* let clock go again */

static char dmsize[] = {
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
};

struct ty {	/* just for convenience */
	char sec, min, hr, day, mon, yr;
};

gettodr()
{
	register int s, i;
	register struct watchregs *regs;
	struct ty ty;

	regs = &((struct iomflow *)iospace)->w;
	while(regs->csra & UIP)
		;
	/*
	 * we now have 244us to read the clock
	 */
	if ((regs->csrd & VRT) == 0) {	/* invalid time */
		regs->csrb = BOFF;
		regs->csra = AMAG;
		regs->yr = 0;		/* just make it wrong */
		regs->csrb = BON;
		return (0);		/* and return a wrong answer */
	}
	s = spl7();
	ty.sec = regs->sec;
	ty.min = regs->min;
	ty.hr = regs->hr;
	ty.day = regs->day;
	ty.mon = regs->mon;
	ty.yr = regs->yr;
	splx(s);
	if (ty.sec < 0 || ty.sec > 59
	||  ty.min < 0 || ty.min > 59
	||  ty.hr < 0 || ty.hr > 23
	||  ty.day < 1 || ty.day > 31
	||  ty.mon < 1 || ty.mon > 12
	||  ty.yr < BYEAR || ty.yr > BYEAR+1)
		return 0;
	if (ty.yr > BYEAR)
		ty.mon += 12;	/* overflow */
	s = 0;
	for(i = 0; i < ty.mon-1; i++)
		s += dmsize[i];
	s += ty.day-1;
	s = 24*s + ty.hr;
	s = 60*s + ty.min;
	s = 60*s + ty.sec;
	return (s*TODRRES+TODRZERO);
}

/*
 * This routine is used to set the MicroVAX-II toy register.
 */
settodr(tim)
{
	register int yd;
	register struct watchregs *regs;
	struct ty ty;

	regs = &((struct iomflow *)iospace)->w;
	tim = (tim - TODRZERO)/TODRRES;		/* truncated -- too bad */
	ty.sec = tim % 60;
	tim /= 60;
	ty.min = tim % 60;
	tim /= 60;
	ty.hr = tim % 24;
	tim /= 24;
	yd = tim % 365;
	tim /= 365;
	ty.yr = BYEAR + tim;
	for (ty.mon = 0; yd > dmsize[ty.mon]; ty.mon++)
		yd -= dmsize[ty.mon];
	ty.day = yd;
	regs->csrb = BOFF;
	regs->csra = AMAG;
	regs->sec = ty.sec;
	regs->min = ty.min;
	regs->hr = ty.hr;
	regs->day = ty.day+1;
	regs->mon = ty.mon+1;
	regs->yr = ty.yr;
	regs->csrb = BON;
}