code:
/*	$OpenBSD: clock.c,v 1.32 2015/02/10 22:49:16 miod Exp $	*/
/*	$NetBSD: clock.c,v 1.52 1997/05/24 20:16:05 pk Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 * Copyright (c) 1994 Gordon W. Ross
 * Copyright (c) 1993 Adam Glass
 * Copyright (c) 1996 Paul Kranenburg
 * Copyright (c) 1996
 * 	The President and Fellows of Harvard College. All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by Harvard University.
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 *	This product includes software developed by Paul Kranenburg.
 *	This product includes software developed by Harvard University.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)clock.c	8.1 (Berkeley) 6/11/93
 *
 */

/*
 * Clock driver.  This is the id prom and eeprom driver as well
 * and includes the timer register functions too.
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <sys/timetc.h>
#ifdef GPROF
#include <sys/gmon.h>
#endif

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/eeprom.h>
#include <machine/cpu.h>

#include <sparc/sparc/vaddrs.h>
#include <sparc/sparc/cpuvar.h>
#include <sparc/sparc/clockreg.h>
#include <sparc/sparc/intreg.h>
#include <sparc/sparc/timerreg.h>

/*
 * Statistics clock interval and variance, in usec.  Variance must be a
 * power of two.  Since this gives us an even number, not an odd number,
 * we discard one case and compensate.  That is, a variance of 1024 would
 * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
 * This is symmetric about the point 512, or statvar/2, and thus averages
 * to that value (assuming uniform random numbers).
 */
/* XXX fix comment to match value */
int statvar = 8192;
int statmin;			/* statclock interval - 1/2*variance */
int timerok;

#include <dev/ic/intersil7170.h>

extern struct idprom idprom;

#define intersil_command(run, interrupt) \
    (run | interrupt | INTERSIL_CMD_FREQ_32K | INTERSIL_CMD_24HR_MODE | \
     INTERSIL_CMD_NORMAL_MODE)

#define intersil_disable(CLOCK) \
    CLOCK->clk_cmd_reg = \
    intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IDISABLE)

#define intersil_enable(CLOCK) \
    CLOCK->clk_cmd_reg = \
    intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE)

#define intersil_clear(CLOCK) CLOCK->clk_intr_reg

#if defined(SUN4)
/*
 * OCLOCK support: 4/100's and 4/200's have the old clock.
 */
static int oldclk = 0;
struct intersil7170 *i7;

long	oclk_get_secs(void);
void	oclk_get_dt(struct intersil_dt *);
void	oclk_set_dt(struct intersil_dt *);
void	oclk_set_secs(long);

int	oclockmatch(struct device *, void *, void *);
void	oclockattach(struct device *, struct device *, void *);

struct cfattach oclock_ca = {
	sizeof(struct device), oclockmatch, oclockattach
};

struct cfdriver oclock_cd = {
	NULL, "oclock", DV_DULL
};

u_int	oclock_get_timecount(struct timecounter *);
struct timecounter oclock_timecounter = {
	.tc_get_timecount = oclock_get_timecount,
	.tc_counter_mask = 0xffffffff,
	.tc_name = "oclock",
	.tc_quality = 0
};
#endif

/*
 * Sun 4 machines use the old-style (a'la Sun 3) EEPROM.  On the
 * 4/100's and 4/200's, this is at a separate obio space.  On the
 * 4/300's and 4/400's, however, it is the cl_nvram[] chunk of the
 * Mostek chip.  Therefore, eeprom_match will only return true on
 * the 100/200 models, and the eeprom will be attached separately.
 * On the 300/400 models, the eeprom will be dealt with when the clock is
 * attached.
 */
char		*eeprom_va = NULL;
#if defined(SUN4)
static int	eeprom_busy = 0;
static int	eeprom_wanted = 0;
static int	eeprom_nvram = 0;	/* non-zero if eeprom is on Mostek */
int	eeprom_take(void);
void	eeprom_give(void);
int	eeprom_update(char *, size_t, size_t);
#endif

int	eeprom_match(struct device *, void *, void *);
void	eeprom_attach(struct device *, struct device *, void *);

struct cfattach eeprom_ca = {
	sizeof(struct device), eeprom_match, eeprom_attach
};

struct	cfdriver eeprom_cd = {
	NULL, "eeprom", DV_DULL
};

int	clockmatch(struct device *, void *, void *);
void	clockattach(struct device *, struct device *, void *);

struct cfattach clock_ca = {
	sizeof(struct device), clockmatch, clockattach
};

struct cfdriver clock_cd = {
	NULL, "clock", DV_DULL
};

int	timermatch(struct device *, void *, void *);
void	timerattach(struct device *, struct device *, void *);

struct timer_4m	*timerreg_4m;	/* XXX - need more cleanup */
struct counter_4m	*counterreg_4m;
#define	timerreg4		((struct timerreg_4 *)TIMERREG_VA)

struct cfattach timer_ca = {
	sizeof(struct device), timermatch, timerattach
};

struct cfdriver timer_cd = {
	NULL, "timer", DV_DULL
};

/*
 * timecounter local state
 */
static struct counter {
	volatile u_int *cntreg;	/* counter register */
	u_int limit;		/* limit we count up to */
	u_int offset;		/* accumulated offset due to wraps */
	u_int shift;		/* scaling for valid bits */
} cntr;

u_int	timer_get_timecount(struct timecounter *);
struct timecounter timer_timecounter = {
	.tc_get_timecount = timer_get_timecount,
	.tc_counter_mask = 0xffffffff,
	.tc_name = "counter-timer",
	.tc_quality = 100,
	.tc_priv = &cntr
};

void clk_wenable(int);
void myetheraddr(u_char *);

int timerblurb = 10; /* Guess a value; used before clock is attached */

static struct intrhand level10 = { .ih_fun = clockintr, .ih_arg = NULL };
static struct intrhand level14 = { .ih_fun = statintr, .ih_arg = NULL };

#if defined(SUN4)
/*
 * old clock match routine
 */
int
oclockmatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct confargs *ca = aux;

	/* Only these sun4s have oclock */
	if (!CPU_ISSUN4 ||
	    (cpuinfo.cpu_type != CPUTYP_4_100 &&
	     cpuinfo.cpu_type != CPUTYP_4_200))
		return (0);

	/* Check configuration name */
	if (strcmp(oclock_cd.cd_name, ca->ca_ra.ra_name) != 0)
		return (0);

	/* Make sure there is something there */
	if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
		return (0);

	return (1);
}

/* ARGSUSED */
void
oclockattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;
	struct idprom *idp;
	int h;

	oldclk = 1;  /* we've got an oldie! */

	i7 = (struct intersil7170 *) mapiodev(ra->ra_reg, 0, sizeof(*i7));

	idp = &idprom;
	h = idp->id_machine << 24;
	h |= idp->id_hostid[0] << 16;
	h |= idp->id_hostid[1] << 8;
	h |= idp->id_hostid[2];
	hostid = h;

	/*
	 * calibrate delay()
	 */
	intreg_clr_44c(IE_L14 | IE_L10);	/* disable all clock intrs */
	for (timerblurb = 1; ; timerblurb++) {
		volatile register char *ireg = &i7->clk_intr_reg;
		int ival;
		*ireg = INTERSIL_INTER_CSECONDS; /* 1/100 sec */
		intersil_enable(i7);		 /* enable clock */
		while ((*ireg & INTERSIL_INTER_PENDING) == 0)
			/* sync with interrupt */;
		while ((*ireg & INTERSIL_INTER_PENDING) == 0)
			/* XXX: do it again, seems to need it */;
		delay(10000);			/* Probe 1/100 sec delay */
		ival = *ireg;			/* clear, save value */
		intersil_disable(i7);		/* disable clock */
		if (ival & INTERSIL_INTER_PENDING) {
			printf(": delay constant %d%s\n", timerblurb,
				(timerblurb == 1) ? " [TOO SMALL?]" : "");
			break;
		}
		if (timerblurb > 10) {
			printf("\noclock: calibration failing; clamped at %d\n",
			       timerblurb);
			break;
		}
	}

	oclock_timecounter.tc_frequency = hz;
	tc_init(&oclock_timecounter);

	intr_establish(10, &level10, IPL_CLOCK, "clock");
	intr_establish(14, &level14, IPL_STATCLOCK, "prof");
}

u_int
oclock_get_timecount(struct timecounter *tc)
{
	return level10.ih_count.ec_count;
}
#endif /* SUN4 */

/*
 * Sun 4/100, 4/200 EEPROM match routine.
 */
int
eeprom_match(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct cfdata *cf = vcf;
	struct confargs *ca = aux;

	if (!CPU_ISSUN4)
		return (0);

	if (cf->cf_unit != 0)
		return (0);

	if (cpuinfo.cpu_type != CPUTYP_4_100 &&
	    cpuinfo.cpu_type != CPUTYP_4_200)
		return (0);

	if (strcmp(eeprom_cd.cd_name, ca->ca_ra.ra_name) != 0)
		return (0);

	/*
	 * Make sure there's something there...
	 * This is especially important if we want to
	 * use the same kernel on a 4/100 as a 4/200.
	 */
	if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
		return (0);

	/* Passed all tests */
	return (1);
}

void
eeprom_attach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
#if defined(SUN4)
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;

	printf("\n");

	eeprom_va = (char *)mapiodev(ra->ra_reg, 0, EEPROM_SIZE);

	eeprom_nvram = 0;
#endif /* SUN4 */
}

/*
 * The OPENPROM calls the clock the "eeprom", so we have to have our
 * own special match function to call it the "clock".
 */
int
clockmatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct confargs *ca = aux;

	if (CPU_ISSUN4) {
		/* Only these sun4s have "clock" (others have "oclock") */
		if (cpuinfo.cpu_type != CPUTYP_4_300 &&
		    cpuinfo.cpu_type != CPUTYP_4_400)
			return (0);

		if (strcmp(clock_cd.cd_name, ca->ca_ra.ra_name) != 0)
			return (0);

		/* Make sure there is something there */
		if (probeget(ca->ca_ra.ra_vaddr, 1) == -1)
			return (0);

		return (1);
	}

	return (strcmp("eeprom", ca->ca_ra.ra_name) == 0);
}

/* ARGSUSED */
void
clockattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	int h;
	struct clockreg *cl;
	struct idprom *idp;
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;
	char *prop = NULL;

	if (CPU_ISSUN4)
		prop = "mk48t02";
	else if (!CPU_ISSUN4)
		prop = getpropstring(ra->ra_node, "model");

	printf(": %s (eeprom)\n", prop);

	/*
	 * We ignore any existing virtual address as we need to map
	 * this read-only and make it read-write only temporarily,
	 * whenever we read or write the clock chip.  The clock also
	 * contains the ID ``PROM'', and I have already had the pleasure
	 * of reloading the cpu type, Ethernet address, etc, by hand from
	 * the console FORTH interpreter.  I intend not to enjoy it again.
	 */
	if (strcmp(prop, "mk48t08") == 0) {
		/*
		 * the MK48T08 is 8K
		 */
		cl = (struct clockreg *)mapiodev(ra->ra_reg, 0, 8192);
		pmap_changeprot(pmap_kernel(), (vaddr_t)cl, PROT_READ, 1);
		pmap_changeprot(pmap_kernel(), (vaddr_t)cl + 4096,
				PROT_READ, 1);
		cl = (struct clockreg *)((int)cl + CLK_MK48T08_OFF);
	} else {
		/*
		 * the MK48T02 is 2K
		 */
		cl = (struct clockreg *)mapiodev(ra->ra_reg, 0,
						 sizeof *clockreg);
		pmap_changeprot(pmap_kernel(), (vaddr_t)cl, PROT_READ, 1);
	}
	idp = &cl->cl_idprom;

#if defined(SUN4)
	if (CPU_ISSUN4) {
		idp = &idprom;

		if (cpuinfo.cpu_type == CPUTYP_4_300 ||
		    cpuinfo.cpu_type == CPUTYP_4_400) {
			eeprom_va = (char *)cl->cl_nvram;
			eeprom_nvram = 1;
		}
	}
#endif

	h = idp->id_machine << 24;
	h |= idp->id_hostid[0] << 16;
	h |= idp->id_hostid[1] << 8;
	h |= idp->id_hostid[2];
	hostid = h;
	clockreg = cl;
}

/*
 * The OPENPROM calls the timer the "counter-timer".
 */
int
timermatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct confargs *ca = aux;

	if (CPU_ISSUN4) {
		if (cpuinfo.cpu_type != CPUTYP_4_300 &&
		    cpuinfo.cpu_type != CPUTYP_4_400)
			return (0);

		if (strcmp("timer", ca->ca_ra.ra_name) != 0)
			return (0);

		/* Make sure there is something there */
		if (probeget(ca->ca_ra.ra_vaddr, 4) == -1)
			return (0);

		return (1);
	}

	if (CPU_ISSUN4C || CPU_ISSUN4E) {
		return (strcmp("counter-timer", ca->ca_ra.ra_name) == 0);
	}

	if (CPU_ISSUN4M) {
		return (strcmp("counter", ca->ca_ra.ra_name) == 0);
	}

	return (0);
}

/* ARGSUSED */
void
timerattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;
	volatile int *cnt = NULL, *lim = NULL;
		/* XXX: must init to NULL to avoid stupid gcc -Wall warning */
	u_int prec = 0, t0;

	if (CPU_ISSUN4M) {
		(void)mapdev(&ra->ra_reg[ra->ra_nreg-1], TIMERREG_VA, 0,
			     sizeof(struct timer_4m));
		(void)mapdev(&ra->ra_reg[0], COUNTERREG_VA, 0,
			     sizeof(struct counter_4m));
		timerreg_4m = (struct timer_4m *)TIMERREG_VA;
		counterreg_4m = (struct counter_4m *)COUNTERREG_VA;

		/* Put processor counter in "timer" mode */
		timerreg_4m->t_cfg = 0;

		cnt = &timerreg_4m->t_counter;
		lim = &timerreg_4m->t_limit;
	}

	if (CPU_ISSUN4OR4COR4E) {
		/*
		 * This time, we ignore any existing virtual address because
		 * we have a fixed virtual address for the timer, to make
		 * time counter operation faster (in SUN4/SUN4C/SUN4E kernel
		 * only).
		 */
		(void)mapdev(ra->ra_reg, TIMERREG_VA, 0,
			     sizeof(struct timerreg_4));

		cnt = &timerreg4->t_c10.t_counter;
		lim = &timerreg4->t_c10.t_limit;
	}

	timerok = 1;

	/*
	 * Calibrate delay() by tweaking the magic constant
	 * until a delay(100) actually reads (at least) 100 us on the clock.
	 * Note: sun4m clocks tick with 500ns periods.
	 */

	for (timerblurb = 1; ; timerblurb++) {
		volatile int discard;
		u_int t1;

		/* Reset counter register by writing some large limit value */
		discard = *lim;
		*lim = tmr_ustolim(TMR_MASK-1);

		t0 = *cnt;
		delay(100);
		t1 = *cnt;

		prec |= (t0 ^ t1) | (*cnt ^ *cnt);

		if (t1 & TMR_LIMIT)
			panic("delay calibration");

		t0 = (t0 >> TMR_SHIFT) & TMR_MASK;
		t1 = (t1 >> TMR_SHIFT) & TMR_MASK;

		if (t1 >= t0 + 100)
			break;

	}

	/* find lowest active bit */
	for (t0 = 0; t0 < TMR_SHIFT; t0++)
		if ((1 << t0) & prec)
			break;

	cntr.shift = t0;
	cntr.limit = tick << (TMR_SHIFT - cntr.shift);
	cntr.cntreg = cnt;
	timer_timecounter.tc_frequency = 1000000 << (TMR_SHIFT - cntr.shift);
	tc_init(&timer_timecounter);

	printf(": delay constant %d, frequency %llu Hz\n",
	    timerblurb, (unsigned long long)timer_timecounter.tc_frequency);

	intr_establish(10, &level10, IPL_CLOCK, "clock");
	intr_establish(14, &level14, IPL_STATCLOCK, "prof");
}

u_int
timer_get_timecount(struct timecounter *tc)
{
	struct counter *ctr = (struct counter *)tc->tc_priv;
	u_int carry, cnt, res;
	int s;

	s = splhigh();
	res = cnt = *ctr->cntreg;
	res &= ~TMR_LIMIT;
	if (cnt != res)
		carry = ctr->limit;	/* a clock interrupt is pending */
	else
		carry = 0;
	res >>= ctr->shift;
	res += carry + ctr->offset;
	splx(s);

	return res;
}

/*
 * Write en/dis-able clock registers.  We coordinate so that several
 * writers can run simultaneously.
 */
void
clk_wenable(onoff)
	int onoff;
{
	int s;
	vm_prot_t prot;/* nonzero => change prot */
	static int writers;

	s = splhigh();
	if (onoff)
		prot = writers++ == 0 ? PROT_READ | PROT_WRITE : 0;
	else
		prot = --writers == 0 ? PROT_READ : 0;
	splx(s);
	if (prot)
		pmap_changeprot(pmap_kernel(), (vaddr_t)clockreg & ~(NBPG-1),
				prot, 1);
}

/*
 * XXX this belongs elsewhere
 */
void
myetheraddr(cp)
	u_char *cp;
{
	struct clockreg *cl = clockreg;
	struct idprom *idp = &cl->cl_idprom;

#if defined(SUN4)
	if (CPU_ISSUN4)
		idp = &idprom;
#endif

	cp[0] = idp->id_ether[0];
	cp[1] = idp->id_ether[1];
	cp[2] = idp->id_ether[2];
	cp[3] = idp->id_ether[3];
	cp[4] = idp->id_ether[4];
	cp[5] = idp->id_ether[5];
}

/*
 * Set up the real-time and statistics clocks.  Leave stathz 0 only if
 * no alternative timer is available.
 *
 * The frequencies of these clocks must be an even number of microseconds.
 */
void
cpu_initclocks()
{
	int statint, minint;

#if defined(SUN4)
	if (oldclk) {
		int dummy;

		if (hz != 100) {
			printf("oclock0: cannot get %d Hz clock; using 100 Hz\n", hz);
		}

		profhz = hz = 100;
		tick = 1000000 / hz;

		i7->clk_intr_reg = INTERSIL_INTER_CSECONDS; /* 1/100 sec */

		intreg_clr_44c(IE_L14 | IE_L10);/* disable all clock intrs */
		intersil_disable(i7);		/* disable clock */
		dummy = intersil_clear(i7);	/* clear interrupts */
		intreg_set_44c(IE_L10);		/* enable l10 interrupt */
		intersil_enable(i7);		/* enable clock */

		return;
	}
#endif /* SUN4 */

	if (1000000 % hz) {
		printf("clock0: cannot get %d Hz clock; using 100 Hz\n", hz);
		hz = 100;
		tick = 1000000 / hz;
	}
	if (stathz == 0)
		stathz = hz;
	if (1000000 % stathz) {
		printf("clock0: cannot get %d Hz statclock; using 100 Hz\n", stathz);
		stathz = 100;
	}
	profhz = stathz;		/* always */

	statint = 1000000 / stathz;
	minint = statint / 2 + 100;
	while (statvar > minint)
		statvar >>= 1;

	if (CPU_ISSUN4M) {
		timerreg_4m->t_limit = tmr_ustolim4m(tick);
		counterreg_4m->t_limit = tmr_ustolim4m(statint);
	}

	if (CPU_ISSUN4OR4COR4E) {
		timerreg4->t_c10.t_limit = tmr_ustolim(tick);
		timerreg4->t_c14.t_limit = tmr_ustolim(statint);
	}

	statmin = statint - (statvar >> 1);

#if defined(SUN4M)
	if (CPU_ISSUN4M)
		intreg_clr_4m(SINTR_T);
#endif

#if defined(SUN4) || defined(SUN4C) || defined(SUN4E)
	if (CPU_ISSUN4OR4COR4E)
		intreg_set_44c(IE_L14 | IE_L10);
#endif
}

/*
 * Dummy setstatclockrate(), since we know profhz==hz.
 */
/* ARGSUSED */
void
setstatclockrate(newhz)
	int newhz;
{
	/* nothing */
}

/*
 * Level 10 (clock) interrupts.
 */
int
clockintr(cap)
	void *cap;
{
	volatile int discard;
	int s;

	/*
	 * Protect the clearing of the clock interrupt.  If we don't
	 * do this, and we're interrupted (by the zs, for example),
	 * the clock stops!
	 * XXX WHY DOES THIS HAPPEN?
	 */
	s = splhigh();

#if defined(SUN4)
	if (oldclk) {
		discard = intersil_clear(i7);
		intreg_clr_44c(IE_L10);	/* clear interrupt */
		intreg_set_44c(IE_L10);	/* enable interrupt */
		goto forward;
	}
#endif
#if defined(SUN4M)
	/* read the limit register to clear the interrupt */
	if (CPU_ISSUN4M) {
		discard = timerreg_4m->t_limit;
	}
#endif
#if defined(SUN4) || defined(SUN4C) || defined(SUN4E)
	if (CPU_ISSUN4OR4COR4E) {
		discard = timerreg4->t_c10.t_limit;
	}
#endif
#if defined(SUN4)
forward:
#endif
	cntr.offset += cntr.limit;
	splx(s);

	/*
	 * XXX Clock interrupts are enabled (and therefore serviceable)
	 * XXX before initclocks() has completed.
	 */
	if (cold == 0)
		hardclock((struct clockframe *)cap);

	return (1);
}

/*
 * Level 14 (stat clock) interrupt handler.
 */
int
statintr(cap)
	void *cap;
{
	volatile int discard;
	u_long newint, r, var;

#if defined(SUN4)
	if (oldclk) {
		panic("oldclk statintr");
		return (1);
	}
#endif

	/* read the limit register to clear the interrupt */
	if (CPU_ISSUN4M) {
		discard = counterreg_4m->t_limit;
		if (timerok == 0) {
			/* Stop the clock */
			counterreg_4m->t_limit = 0;
			counterreg_4m->t_ss = 0;
			timerreg_4m->t_cfg = TMR_CFG_USER;
			return 1;
		}
	}

	if (CPU_ISSUN4OR4COR4E) {
		discard = timerreg4->t_c14.t_limit;
	}
	statclock((struct clockframe *)cap);

	/*
	 * Compute new randomized interval.  The intervals are uniformly
	 * distributed on [statint - statvar / 2, statint + statvar / 2],
	 * and therefore have mean statint, giving a stathz frequency clock.
	 */
	var = statvar;
	do {
		r = random() & (var - 1);
	} while (r == 0);
	newint = statmin + r;

	if (CPU_ISSUN4M) {
		counterreg_4m->t_limit = tmr_ustolim4m(newint);
	}

	if (CPU_ISSUN4OR4COR4E) {
		timerreg4->t_c14.t_limit = tmr_ustolim(newint);
	}
	return (1);
}

/*
 * Set up the system's time, given a `reasonable' time value.
 */
void
inittodr(base)
	time_t base;
{
	struct clockreg *cl = clockreg;
	struct clock_ymdhms dt;
	int badbase = 0, waszero = base == 0;
	char *bad = NULL;
	struct timespec ts;

	ts.tv_sec = ts.tv_nsec = 0;

	if (base < 5 * SECYR) {
		/*
		 * If base is 0, assume filesystem time is just unknown
		 * in stead of preposterous. Don't bark.
		 */
		if (base != 0)
			printf("WARNING: preposterous time in file system\n");
		/* not going to use it anyway, if the chip is readable */
		base = (2012 - 1970) * SECYR;
		badbase = 1;
	}
#if defined(SUN4)
	if (oldclk) {
		ts.tv_sec = oclk_get_secs();
		goto forward;
	}
#endif
	clk_wenable(1);
	cl->cl_csr |= CLK_READ;		/* enable read (stop time) */
	dt.dt_sec = FROMBCD(cl->cl_sec);
	dt.dt_min = FROMBCD(cl->cl_min);
	dt.dt_hour = FROMBCD(cl->cl_hour);
	dt.dt_day = FROMBCD(cl->cl_mday);
	dt.dt_mon = FROMBCD(cl->cl_month);
	dt.dt_year = FROMBCD(cl->cl_year) + CLOCK_BASE_YEAR;
	cl->cl_csr &= ~CLK_READ;	/* time wears on */
	clk_wenable(0);
	ts.tv_sec = clock_ymdhms_to_secs(&dt);

#if defined(SUN4)
forward:
#endif
	if (ts.tv_sec == 0) {
		/*
		 * Believe the time in the file system for lack of
		 * anything better, resetting the clock.
		 */
		bad = "WARNING: bad date in battery clock";
		ts.tv_sec = base;
		tc_setclock(&ts);
		if (!badbase)
			resettodr();
	} else {
		time_t deltat = ts.tv_sec - base;

		tc_setclock(&ts);
		if (deltat < 0)
			deltat = -deltat;
		if (waszero || deltat < 2 * SECDAY)
			return;

#ifndef SMALL_KERNEL
		printf("WARNING: clock %s %lld days",
		    ts.tv_sec < base ? "lost" : "gained",
		    (long long)(deltat / SECDAY));
		bad = "";
#endif
	}
	if (bad) {
		printf("%s", bad);
		printf(" -- CHECK AND RESET THE DATE!\n");
	}
}

/*
 * Reset the clock based on the current time.
 * Used when the current clock is preposterous, when the time is changed,
 * and when rebooting.  Do nothing if the time is not yet known, e.g.,
 * when crashing during autoconfig.
 */
void
resettodr()
{
	struct clockreg *cl;
	struct clock_ymdhms dt;

#if defined(SUN4)
	if (oldclk) {
		if (!time_second || i7 == NULL)
			return;
		oclk_set_secs(time_second);
		return;
	}
#endif

	if (!time_second || (cl = clockreg) == NULL)
		return;

	clock_secs_to_ymdhms(time_second, &dt);

	clk_wenable(1);
	cl->cl_csr |= CLK_WRITE;	/* enable write */
	cl->cl_sec = TOBCD(dt.dt_sec);
	cl->cl_min = TOBCD(dt.dt_min);
	cl->cl_hour = TOBCD(dt.dt_hour);
	cl->cl_wday = TOBCD(dt.dt_wday);
	cl->cl_mday = TOBCD(dt.dt_day);
	cl->cl_month = TOBCD(dt.dt_mon);
	cl->cl_year = TOBCD(dt.dt_year - CLOCK_BASE_YEAR);
	cl->cl_csr &= ~CLK_WRITE;	/* load them up */
	clk_wenable(0);
}

#if defined(SUN4)
/*
 * Now routines to get and set clock as POSIX time.
 */
long
oclk_get_secs()
{
        struct intersil_dt idt;
	struct clock_ymdhms dt;

        oclk_get_dt(&idt);
	dt.dt_sec = idt.dt_sec;
	dt.dt_min = idt.dt_min;
	dt.dt_hour = idt.dt_hour;
	dt.dt_day = idt.dt_day;
	dt.dt_mon = idt.dt_month;
	dt.dt_year = idt.dt_year + CLOCK_BASE_YEAR;
        return clock_ymdhms_to_secs(&dt);
}

void
oclk_set_secs(secs)
	long secs;
{
        struct intersil_dt idt;
	struct clock_ymdhms dt;

	clock_secs_to_ymdhms(secs, &dt);

	idt.dt_hour = dt.dt_hour;
	idt.dt_min = dt.dt_min;
	idt.dt_sec = dt.dt_sec;
	idt.dt_month = dt.dt_mon;
	idt.dt_day = dt.dt_day;
	idt.dt_year = dt.dt_year - CLOCK_BASE_YEAR;
	idt.dt_dow = dt.dt_wday;
        oclk_set_dt(&idt);
}

/*
 * Routine to copy state into and out of the clock.
 * The clock registers have to be read or written
 * in sequential order (or so it appears). -gwr
 */
void
oclk_get_dt(dt)
	struct intersil_dt *dt;
{
        int s;
        register volatile char *src, *dst;

        src = (char *) &i7->counters;

        s = splhigh();
        i7->clk_cmd_reg =
                intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);

        dst = (char *) dt;
        dt++;   /* end marker */
        do {
                *dst++ = *src++;
        } while (dst < (char *)dt);

        i7->clk_cmd_reg =
                intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
        splx(s);
}

void
oclk_set_dt(dt)
	struct intersil_dt *dt;
{
        int s;
        register volatile char *src, *dst;

        dst = (char *) &i7->counters;

        s = splhigh();
        i7->clk_cmd_reg =
                intersil_command(INTERSIL_CMD_STOP, INTERSIL_CMD_IENABLE);

        src = (char *) dt;
        dt++;   /* end marker */
        do {
                *dst++ = *src++;
        } while (src < (char *)dt);

        i7->clk_cmd_reg =
                intersil_command(INTERSIL_CMD_RUN, INTERSIL_CMD_IENABLE);
        splx(s);
}
#endif /* SUN4 */

/*
 * XXX: these may actually belong somewhere else, but since the
 * EEPROM is so closely tied to the clock on some models, perhaps
 * it needs to stay here...
 */
int
eeprom_uio(uio)
	struct uio *uio;
{
#if defined(SUN4)
	int error;
	off_t off;
	size_t cnt, bcnt;
	caddr_t buf = NULL;

	if (!CPU_ISSUN4)
		return (ENODEV);

	off = uio->uio_offset;
	if (off < 0 || off > EEPROM_SIZE)
		return (EFAULT);

	cnt = uio->uio_resid;
	if (cnt > (EEPROM_SIZE - off))
		cnt = (EEPROM_SIZE - off);

	if ((error = eeprom_take()) != 0)
		return (error);

	if (eeprom_va == NULL) {
		error = ENXIO;
		goto out;
	}

	/*
	 * The EEPROM can only be accessed one byte at a time, yet
	 * uiomove() will attempt long-word access.  To circumvent
	 * this, we byte-by-byte copy the eeprom contents into a
	 * temporary buffer.
	 */
	buf = malloc(EEPROM_SIZE, M_DEVBUF, M_WAITOK);

	if (uio->uio_rw == UIO_READ)
		for (bcnt = 0; bcnt < EEPROM_SIZE; ++bcnt)
			*(char *)(buf + bcnt) = *(char *)(eeprom_va + bcnt);

	if ((error = uiomove(buf + off, cnt, uio)) != 0)
		goto out;

	if (uio->uio_rw != UIO_READ)
		error = eeprom_update(buf, (size_t)off, cnt);

 out:
	if (buf)
		free(buf, M_DEVBUF, 0);
	eeprom_give();
	return (error);
#else /* ! SUN4 */
	return (ENODEV);
#endif /* SUN4 */
}

#if defined(SUN4)
/*
 * Update the EEPROM from the passed buf.
 */
int
eeprom_update(buf, off, cnt)
	char *buf;
	size_t off, cnt;
{
	int error = 0;
	volatile char *ep;
	char *bp;

	if (eeprom_va == NULL)
		return (ENXIO);

	ep = eeprom_va + off;
	bp = buf + off;

	if (eeprom_nvram)
		clk_wenable(1);

	while (cnt > 0) {
		/*
		 * DO NOT WRITE IT UNLESS WE HAVE TO because the
		 * EEPROM has a limited number of write cycles.
		 * After some number of writes it just fails!
		 */
		if (*ep != *bp) {
			*ep = *bp;
			/*
			 * We have written the EEPROM, so now we must
			 * sleep for at least 10 milliseconds while
			 * holding the lock to prevent all access to
			 * the EEPROM while it recovers.
			 */
			(void)tsleep(eeprom_va, PZERO - 1, "eeprom", hz/50);
		}
		/* Make sure the write worked. */
		if (*ep != *bp) {
			error = EIO;
			goto out;
		}
		++ep;
		++bp;
		--cnt;
	}
 out:
	if (eeprom_nvram)
		clk_wenable(0);

	return (error);
}

/* Take a lock on the eeprom. */
int
eeprom_take()
{
	int error = 0;

	while (eeprom_busy) {
		eeprom_wanted = 1;
		error = tsleep(&eeprom_busy, PZERO | PCATCH, "eeprom", 0);
		eeprom_wanted = 0;
		if (error)	/* interrupted */
			goto out;
	}
	eeprom_busy = 1;
 out:
	return (error);
}

/* Give a lock on the eeprom away. */
void
eeprom_give()
{

	eeprom_busy = 0;
	if (eeprom_wanted) {
		eeprom_wanted = 0;
		wakeup(&eeprom_busy);
	}
}
#endif /* SUN4 */


code:
/*	$OpenBSD: clockreg.h,v 1.5 2007/04/10 17:47:55 miod Exp $	*/
/*	$NetBSD: clockreg.h,v 1.5 1994/11/20 20:54:07 deraadt Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)clockreg.h	8.1 (Berkeley) 6/11/93
 */

#include <machine/idprom.h>

/*
 * Sun-4/Sun-4c/Sun-4m clock Mostek TOD clock.
 */

/*
 * Mostek MK48T02 clock.
 *
 * The clock includes 2040 bytes of RAM, the last 32 of which serve to
 * identify the kind of Sun 4c this is.
 *
 * or, the Mostek MK48T08 clock.
 *
 * This is used in the Sun 4m machines. It is identical to the MK48T02,
 * except for being 8K in size. The following structure, then, describes
 * the last 2K of its 8K address space. We simply ignore the first 6K..
 */
struct clockreg {
	char	cl_nvram[2008];		/* `free' nonvolatile memory */
	struct	idprom	cl_idprom;	/* `id prom' */
	volatile u_char	cl_csr;		/* control register */
	volatile u_char	cl_sec;		/* seconds (0..59; BCD) */
	volatile u_char	cl_min;		/* minutes (0..59; BCD) */
	volatile u_char	cl_hour;	/* hour (0..23; BCD) */
	volatile u_char	cl_wday;	/* weekday (1..7) */
	volatile u_char	cl_mday;	/* day in month (1..31; BCD) */
	volatile u_char	cl_month;	/* month (1..12; BCD) */
	volatile u_char	cl_year;	/* year (0..99; BCD) */
};

/* bits in cl_csr */
#define	CLK_WRITE	0x80		/* want to write */
#define	CLK_READ	0x40		/* want to read (freeze clock) */

#define CLK_MK48T08_OFF	6*1024		/* struct clockreg is 6K forward */

struct clockreg *clockreg;

/*
 * Machine dependent base year:
 * Note: must be < 1970
 */
#define CLOCK_BASE_YEAR 1968

license: unknown


code:
/*	$OpenBSD: conf.c,v 1.64 2014/10/09 04:10:03 tedu Exp $	*/
/*	$NetBSD: conf.c,v 1.40 1996/04/11 19:20:03 thorpej Exp $ */

/*
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)conf.c	8.3 (Berkeley) 11/14/93
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/ioctl.h>
#include <sys/vnode.h>
#include <sys/tty.h>
#include <sys/conf.h>

#include <machine/conf.h>

#include "bio.h"
#include "pty.h"
#include "bpfilter.h"
#include "tun.h"
#include "audio.h"
#include "vnd.h"
#include "ch.h"
#include "uk.h"
#include "sd.h"
#include "st.h"
#include "cd.h"
#include "rd.h"
#include "presto.h"

#include "zstty.h"

#include "fdc.h"		/* has NFDC and NFD; see files.sparc */
#include "xd.h"
#include "xy.h"
#include "bpp.h"
#include "magma.h"		/* has NMTTY and NMBPP */
#include "spif.h"		/* has NSTTY and NSBPP */
#include "scf.h"
#include "flash.h"
#include "fga.h"
#include "daadio.h"
#include "com.h"

#include "wsdisplay.h"
#include "wskbd.h"
#include "wsmouse.h"
#include "wsmux.h"

#include "ksyms.h"

struct bdevsw	bdevsw[] =
{
	bdev_notdef(),			/* 0 */
	bdev_notdef(),			/* 1 */
	bdev_notdef(),			/* 2 */
	bdev_disk_init(NXY,xy),		/* 3: SMD disk */
	bdev_swap_init(1,sw),		/* 4 swap pseudo-device */
	bdev_notdef(),			/* 5 */
	bdev_notdef(),			/* 6 */
	bdev_disk_init(NSD,sd),		/* 7: SCSI disk */
	bdev_disk_init(NVND,vnd),	/* 8: vnode disk driver */
	bdev_notdef(),			/* 9: was: concatenated disk driver */
	bdev_disk_init(NXD,xd),		/* 10: SMD disk */
	bdev_tape_init(NST,st),		/* 11: SCSI tape */
	bdev_notdef(),			/* 12 */
	bdev_notdef(),			/* 13 */
	bdev_notdef(),			/* 14 */
	bdev_notdef(),			/* 15 */
	bdev_disk_init(NFD,fd),		/* 16: floppy disk */
	bdev_disk_init(NRD,rd),		/* 17: ram disk driver */
	bdev_disk_init(NCD,cd),		/* 18: SCSI CD-ROM */
	bdev_notdef(),			/* 19 */
	bdev_notdef(),			/* 20 */
	bdev_notdef(),			/* 21 */
	bdev_notdef(),			/* 22 */
	bdev_notdef(),			/* 23 */
	bdev_notdef(),			/* 24 */
	bdev_notdef(),			/* 25 was: RAIDframe disk driver */
	bdev_disk_init(NPRESTO,presto),	/* 26: Prestoserve NVRAM */
};
int	nblkdev = nitems(bdevsw);

#include "pf.h"
#include "systrace.h"
#include "tctrl.h"
#include "vscsi.h"
#include "pppx.h"
#include "hotplug.h"
#include "fuse.h"

struct cdevsw	cdevsw[] =
{
	cdev_cn_init(1,cn),		/* 0: virtual console */
	cdev_notdef(),			/* 1 */
	cdev_ctty_init(1,ctty),		/* 2: controlling terminal */
	cdev_mm_init(1,mm),		/* 3: /dev/{null,mem,kmem,...} */
	cdev_notdef(),			/* 4 */
	cdev_notdef(),			/* 5 */
	cdev_notdef(),			/* 6 */
	cdev_notdef(),			/* 7 was /dev/drum */
	cdev_notdef(),			/* 8 */
	cdev_disk_init(NXY,xy),		/* 9: SMD disk */
	cdev_notdef(),			/* 10 */
	cdev_notdef(),			/* 11 */
	cdev_tty_init(NZSTTY,zs),	/* 12: zs serial */
	cdev_notdef(),			/* 13: was /dev/mouse */
	cdev_notdef(),			/* 14 */
	cdev_notdef(),			/* 15: sun /dev/winNNN */
	cdev_log_init(1,log),		/* 16: /dev/klog */
	cdev_disk_init(NSD,sd),		/* 17: SCSI disk */
	cdev_tape_init(NST,st),		/* 18: SCSI tape */
	cdev_ch_init(NCH,ch),		/* 19: SCSI autochanger */
	cdev_tty_init(NPTY,pts),	/* 20: pseudo-tty slave */
	cdev_ptc_init(NPTY,ptc),	/* 21: pseudo-tty master */
	cdev_notdef(),			/* 22: was /dev/fb */
	cdev_notdef(),			/* 23: was: concatenated disk driver */
	cdev_fd_init(1,filedesc),	/* 24: file descriptor pseudo-device */
	cdev_disk_init(NPRESTO,presto),	/* 25: Prestoserve NVRAM */
	cdev_notdef(),			/* 26 */
	cdev_notdef(),			/* 27: was /dev/bwtwo */
	cdev_notdef(),			/* 28 */
	cdev_notdef(),			/* 29: was /dev/kbd */
	cdev_apm_init(NTCTRL,apm),	/* 30: tctrl APM interface */
	cdev_notdef(),			/* 31: was /dev/cgtwo */
	cdev_notdef(),			/* 32: should be /dev/gpone */
	cdev_notdef(),			/* 33 */
	cdev_notdef(),			/* 34 */
	cdev_notdef(),			/* 35 */
	cdev_tty_init(NCOM,com),	/* 36: SPARCbook modem */
	cdev_notdef(),			/* 37 */
	cdev_notdef(),			/* 38 */
	cdev_notdef(),			/* 39: was /dev/cgfour */
	cdev_notdef(),			/* 40 */
	cdev_notdef(),			/* 41 */
	cdev_disk_init(NXD,xd),		/* 42: SMD disk */
	cdev_notdef(),			/* 43 */
	cdev_notdef(),			/* 44 */
	cdev_notdef(),			/* 45 */
	cdev_notdef(),			/* 46 */
	cdev_notdef(),			/* 47 */
	cdev_notdef(),			/* 48 */
	cdev_notdef(),			/* 49 */
	cdev_systrace_init(NSYSTRACE,systrace),	/* 50 system call tracing */
	cdev_notdef(),			/* 51 */
	cdev_notdef(),			/* 52 */
	cdev_notdef(),			/* 53 */
	cdev_disk_init(NFD,fd),		/* 54: floppy disk */
	cdev_notdef(),			/* 55: was /dev/cgthree */
	cdev_notdef(),			/* 56 */
	cdev_notdef(),			/* 57 */
	cdev_disk_init(NCD,cd),		/* 58: SCSI CD-ROM */
	cdev_pf_init(NPF,pf),		/* 59: packet filter */
	cdev_notdef(),			/* 60 */
	cdev_notdef(),			/* 61 */
	cdev_notdef(),			/* 62 */
	cdev_notdef(),			/* 63 */
	cdev_notdef(),			/* 64: was /dev/cgeight */
	cdev_notdef(),			/* 65 */
	cdev_notdef(),			/* 66 */
	cdev_notdef(),			/* 67: was /dev/cgsix */
	cdev_notdef(),			/* 68 */
	cdev_gen_init(NAUDIO,audio),	/* 69: /dev/audio */
#if defined(SUN4) || defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	cdev_openprom_init(1,openprom),	/* 70: /dev/openprom */
#else
	cdev_notdef(),			/* 70 */
#endif
	cdev_notdef(),			/* 71 */
	cdev_notdef(),			/* 72 */
	cdev_notdef(),			/* 73 */
	cdev_notdef(),			/* 74 */
	cdev_notdef(),			/* 75 */
	cdev_notdef(),			/* 76 */
	cdev_notdef(),			/* 77 */
	cdev_wsdisplay_init(NWSDISPLAY,	/* 78: frame buffers, etc. */
	    wsdisplay),
	cdev_mouse_init(NWSKBD, wskbd),	/* 79: keyboards */
	cdev_mouse_init(NWSMOUSE, wsmouse), /* 80: mice */
	cdev_mouse_init(NWSMUX, wsmux),	/* 81: ws multiplexer */
	cdev_fuse_init(NFUSE, fuse),	/* 82: fuse */
	cdev_notdef(),			/* 83 */
	cdev_notdef(),			/* 84 */
	cdev_notdef(),			/* 85 */
	cdev_notdef(),			/* 86 */
	cdev_notdef(),			/* 87 */
	cdev_notdef(),			/* 88 */
	cdev_notdef(),			/* 89 */
	cdev_notdef(),			/* 90 */
	cdev_notdef(),			/* 91 */
	cdev_notdef(),			/* 92 */
	cdev_notdef(),			/* 93 */
	cdev_notdef(),			/* 94 */
	cdev_notdef(),			/* 95 */
	cdev_notdef(),			/* 96 */
	cdev_daadio_init(NDAADIO,daadio), /* 97: daadio */
	cdev_fga_init(NFGA,fga),	/* 98: fga */
	cdev_notdef(),			/* 99: was /dev/cgfourteen */
	cdev_tty_init(NMTTY,mtty),	/* 100: magma */
	cdev_gen_init(NMBPP,mbpp),	/* 101: magma */
	cdev_tty_init(NSTTY,stty),	/* 102: spif */
	cdev_gen_init(NSBPP,sbpp),	/* 103: spif */
	cdev_bpp_init(NBPP,bpp),	/* 104: bpp */
	cdev_bpf_init(NBPFILTER,bpf),	/* 105: packet filter */
	cdev_disk_init(NRD,rd),		/* 106: ram disk driver */
	cdev_scf_init(NSCF,scf),	/* 107: sysconfig regs */
	cdev_flash_init(NFLASH,flash),	/* 108: flash memory */
	cdev_notdef(),			/* 109: was /dev/tcx */
	cdev_disk_init(NVND,vnd),	/* 110: vnode disk driver */
	cdev_tun_init(NTUN,tun),	/* 111: network tunnel */
	cdev_notdef(),			/* 112: was LKM */
	cdev_notdef(),			/* 113 */
	cdev_notdef(),			/* 114 */
	cdev_notdef(),			/* 115 */
	cdev_notdef(),			/* 116 */
	cdev_notdef(),			/* 117 */
	cdev_notdef(),			/* 118 */
	cdev_random_init(1,random),	/* 119: random generator */
	cdev_uk_init(NUK,uk),		/* 120: unknown SCSI */
	cdev_notdef(),			/* 121 */
	cdev_ksyms_init(NKSYMS,ksyms),	/* 122: Kernel symbols device */
	cdev_notdef(),			/* 123 was: RAIDframe disk driver */
	cdev_bio_init(NBIO,bio),	/* 124: ioctl tunnel */
	cdev_ptm_init(NPTY,ptm),	/* 125: pseudo-tty ptm device */
	cdev_notdef(),			/* 126 */
	cdev_notdef(),			/* 127 */
	cdev_vscsi_init(NVSCSI,vscsi),	/* 128: vscsi */
	cdev_disk_init(1,diskmap),	/* 129: disk mapper */
	cdev_pppx_init(NPPPX,pppx),	/* 130: pppx */
	cdev_hotplug_init(NHOTPLUG,hotplug),	/* 131: devices hot plugging */
};
int	nchrdev = nitems(cdevsw);

int	mem_no = 3; 	/* major device number of memory special file */

/*
 * Swapdev is a fake device implemented
 * in sw.c used only internally to get to swstrategy.
 * It cannot be provided to the users, because the
 * swstrategy routine munches the b_dev and b_blkno entries
 * before calling the appropriate driver.  This would horribly
 * confuse, e.g. the hashing routines. Instead, /dev/drum is
 * provided as a character (raw) device.
 */
dev_t	swapdev = makedev(4, 0);

/*
 * Routine that identifies /dev/mem and /dev/kmem.
 *
 * A minimal stub routine can always return 0.
 */
int
iskmemdev(dev)
	dev_t dev;
{

	return (major(dev) == mem_no && minor(dev) < 2);
}

int
iszerodev(dev)
	dev_t dev;
{
	return (major(dev) == mem_no && minor(dev) == 12);
}

dev_t
getnulldev()
{
	return makedev(mem_no, 2);
}

int chrtoblktbl[] = {
	/*VCHR*/	/*VBLK*/
	/*  0 */	NODEV,
	/*  1 */	NODEV,
	/*  2 */	NODEV,
	/*  3 */	NODEV,
	/*  4 */	NODEV,
	/*  5 */	NODEV,
	/*  6 */	NODEV,
	/*  7 */	NODEV,
	/*  8 */	NODEV,
	/*  9 */	3,
	/* 10 */	NODEV,
	/* 11 */	NODEV,
	/* 12 */	NODEV,
	/* 13 */	NODEV,
	/* 14 */	NODEV,
	/* 15 */	NODEV,
	/* 16 */	NODEV,
	/* 17 */	7,
	/* 18 */	11,
	/* 19 */	NODEV,
	/* 20 */	NODEV,
	/* 21 */	NODEV,
	/* 22 */	NODEV,
	/* 23 */	9,
	/* 24 */	NODEV,
	/* 25 */	26,
	/* 26 */	NODEV,
	/* 27 */	NODEV,
	/* 28 */	NODEV,
	/* 29 */	NODEV,
	/* 30 */	NODEV,
	/* 31 */	NODEV,
	/* 32 */	NODEV,
	/* 33 */	NODEV,
	/* 34 */	NODEV,
	/* 35 */	NODEV,
	/* 36 */	NODEV,
	/* 37 */	NODEV,
	/* 38 */	NODEV,
	/* 39 */	NODEV,
	/* 40 */	NODEV,
	/* 41 */	NODEV,
	/* 42 */	10,
	/* 43 */	NODEV,
	/* 44 */	NODEV,
	/* 45 */	NODEV,
	/* 46 */	NODEV,
	/* 47 */	NODEV,
	/* 48 */	NODEV,
	/* 49 */	NODEV,
	/* 50 */	NODEV,
	/* 51 */	NODEV,
	/* 52 */	NODEV,
	/* 53 */	NODEV,
	/* 54 */	16,
	/* 55 */	NODEV,
	/* 56 */	NODEV,
	/* 57 */	NODEV,
	/* 58 */	18,
	/* 59 */	NODEV,
	/* 60 */	NODEV,
	/* 61 */	NODEV,
	/* 62 */	NODEV,
	/* 63 */	NODEV,
	/* 64 */	NODEV,
	/* 65 */	NODEV,
	/* 66 */	NODEV,
	/* 67 */	NODEV,
	/* 68 */	NODEV,
	/* 69 */	NODEV,
	/* 70 */	NODEV,
	/* 71 */	NODEV,
	/* 72 */	NODEV,
	/* 73 */	NODEV,
	/* 74 */	NODEV,
	/* 75 */	NODEV,
	/* 76 */	NODEV,
	/* 77 */	NODEV,
	/* 78 */	NODEV,
	/* 79 */	NODEV,
	/* 80 */	NODEV,
	/* 81 */	NODEV,
	/* 82 */	NODEV,
	/* 83 */	NODEV,
	/* 84 */	NODEV,
	/* 85 */	NODEV,
	/* 86 */	NODEV,
	/* 87 */	NODEV,
	/* 88 */	NODEV,
	/* 89 */	NODEV,
	/* 90 */	NODEV,
	/* 91 */	NODEV,
	/* 92 */	NODEV,
	/* 93 */	NODEV,
	/* 94 */	NODEV,
	/* 95 */	NODEV,
	/* 96 */	NODEV,
	/* 97 */	NODEV,
	/* 98 */	NODEV,
	/* 99 */	NODEV,
	/*100 */	NODEV,
	/*101 */	NODEV,
	/*102 */	NODEV,
	/*103 */	NODEV,
	/*104 */	NODEV,
	/*105 */	NODEV,
	/*106 */	17,
	/*107 */	NODEV,
	/*108 */	NODEV,
	/*109 */	NODEV,
	/*110 */	8,
	/*111 */	NODEV,
	/*112 */	NODEV,
	/*113 */	NODEV,
	/*114 */	NODEV,
	/*115 */	NODEV,
	/*116 */	NODEV,
	/*117 */	NODEV,
	/*118 */	NODEV,
	/*119 */	NODEV,
	/*120 */	NODEV,
	/*121 */	NODEV,
	/*122 */	NODEV,
	/*123 */	25,
};
int nchrtoblktbl = nitems(chrtoblktbl);


code:
/*	$OpenBSD: consinit.c,v 1.1 2015/02/28 17:54:54 miod Exp $	*/
/*	$NetBSD: zs.c,v 1.50 1997/10/18 00:00:40 gwr Exp $	*/

/*-
 * Copyright (c) 1996 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Gordon W. Ross.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Zilog Z8530 Dual UART driver (machine-dependent part)
 *
 * Runs two serial lines per chip using slave drivers.
 * Plain tty/async lines use the zstty slave.
 * Sun keyboard/mouse uses the zskbd/zsms slaves.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>

#include <machine/autoconf.h>
#include <machine/bsd_openprom.h>
#include <machine/eeprom.h>
#if defined(SUN4)
#include <machine/oldmon.h>
#endif
#include <machine/z8530var.h>

#include <dev/cons.h>

#include <sparc/dev/cons.h>

#ifdef solbourne
#include <machine/prom.h>
#endif

#include "zskbd.h"
#include "zs.h"

/* Make life easier for the initialized arrays here. */
#if NZS < 3
#undef  NZS
#define NZS 3
#endif

/* Flags from cninit() */
extern int zs_hwflags[NZS][2];

/*****************************************************************/

#if !defined(solbourne)

cons_decl(prom);

/*
 * The console is set to this one initially,
 * which lets us use the PROM until consinit()
 * is called to select a real console.
 */
struct consdev consdev_prom = {
	promcnprobe,
	promcninit,
	promcngetc,
	promcnputc,
	nullcnpollc,
};

/*
 * The console table pointer is statically initialized
 * to point to the PROM (output only) table, so that
 * early calls to printf will work.
 */
struct consdev *cn_tab = &consdev_prom;

void
promcnprobe(cn)
	struct consdev *cn;
{
	cn->cn_dev = makedev(0, 0);
	cn->cn_pri = CN_MIDPRI;
}

void
promcninit(cn)
	struct consdev *cn;
{
}

/*
 * PROM console input putchar.
 */
int
promcngetc(dev)
	dev_t dev;
{
	int s, c;

	if (promvec->pv_romvec_vers > 2) {
		int n = 0;
		unsigned char c0;

		s = splhigh();
		while (n <= 0) {
			n = (*promvec->pv_v2devops.v2_read)
			        (*promvec->pv_v2bootargs.v2_fd0, &c0, 1);
		}
		splx(s);

		c = c0;
	} else {
#if defined(SUN4)
		/* SUN4 PROM: must turn off local echo */
		extern struct om_vector *oldpvec;
		int saveecho = 0;
#endif
		s = splhigh();
#if defined(SUN4)
		if (CPU_ISSUN4) {
			saveecho = *(oldpvec->echo);
			*(oldpvec->echo) = 0;
		}
#endif
		c = (*promvec->pv_getchar)();
#if defined(SUN4)
		if (CPU_ISSUN4)
			*(oldpvec->echo) = saveecho;
#endif
		splx(s);
	}

	if (c == '\r')
		c = '\n';

	return (c);
}

/*
 * PROM console output putchar.
 */
void
promcnputc(dev, c)
	dev_t dev;
	int c;
{
	int s;
	char c0 = (c & 0x7f);

	s = splhigh();
	if (promvec->pv_romvec_vers > 2)
		(*promvec->pv_v2devops.v2_write)
			(*promvec->pv_v2bootargs.v2_fd1, &c0, 1);
	else
		(*promvec->pv_putchar)(c);
	splx(s);
}

#endif	/* !solbourne */

/*****************************************************************/

char *prom_inSrc_name[] = {
	"keyboard/display",
	"ttya", "ttyb",
	"ttyc", "ttyd" };

extern struct consdev zscn;
int zstty_unit;

/*
 * This function replaces sys/dev/cninit.c
 * Determine which device is the console using
 * the PROM "input source" and "output sink".
 */
void
consinit()
{
	struct zschan *zc;
	struct consdev *console = cn_tab;
	int channel, zs_unit;
	int inSource, outSink;

#if !defined(solbourne)
	if (promvec->pv_romvec_vers > 2) {
		/* We need to probe the PROM device tree */
		int node,fd;
		char buffer[128];
		struct nodeops *no;
		struct v2devops *op;
		char *cp;
		extern int fbnode;

		inSource = outSink = -1;
		no = promvec->pv_nodeops;
		op = &promvec->pv_v2devops;

		node = findroot();
		if (no->no_proplen(node, "stdin-path") >= sizeof(buffer)) {
			printf("consinit: increase buffer size and recompile\n");
			goto setup_output;
		}
		/* XXX: fix above */

		no->no_getprop(node, "stdin-path",buffer);

		/*
		 * Open an "instance" of this device.
		 * You'd think it would be appropriate to call v2_close()
		 * on the handle when we're done with it. But that seems
		 * to cause the device to shut down somehow; for the moment,
		 * we simply leave it open...
		 */
		if ((fd = op->v2_open(buffer)) == 0 ||
		     (node = op->v2_fd_phandle(fd)) == 0) {
			printf("consinit: bogus stdin path %s.\n",buffer);
			goto setup_output;
		}
		if (no->no_proplen(node,"keyboard") >= 0) {
			inSource = PROMDEV_KBD;
			goto setup_output;
		}
		if (strcmp(getpropstring(node,"device_type"), "serial") != 0) {
			/* not a serial, not keyboard. what is it?!? */
			inSource = -1;
			goto setup_output;
		}
		/*
		 * At this point we assume the device path is in the form
		 *   ....device@x,y:a for ttya and ...device@x,y:b for ttyb.
		 * If it isn't, we defer to the ROM
		 */
		cp = buffer;
		while (*cp)
		    cp++;
		cp -= 2;
#ifdef DEBUG
		if (cp < buffer)
		    panic("consinit: bad stdin path %s",buffer);
#endif
		/* XXX: only allows tty's a->z, assumes PROMDEV_TTYx contig */
		if (cp[0]==':' && cp[1] >= 'a' && cp[1] <= 'z')
		    inSource = PROMDEV_TTYA + (cp[1] - 'a');
		/* else use rom */
setup_output:
		node = findroot();
		if (no->no_proplen(node, "stdout-path") >= sizeof(buffer)) {
			printf("consinit: increase buffer size and recompile\n");
			goto setup_console;
		}
		/* XXX: fix above */

		no->no_getprop(node, "stdout-path", buffer);

		if ((fd = op->v2_open(buffer)) == 0 ||
		     (node = op->v2_fd_phandle(fd)) == 0) {
			printf("consinit: bogus stdout path %s.\n",buffer);
			goto setup_output;
		}
		if (strcmp(getpropstring(node,"device_type"),"display") == 0) {
			/* frame buffer output */
			outSink = PROMDEV_SCREEN;
			fbnode = node;
		} else if (strcmp(getpropstring(node,"device_type"), "serial")
			   != 0) {
			/* not screen, not serial. Whatzit? */
			outSink = -1;
		} else { /* serial console. which? */
			/*
			 * At this point we assume the device path is in the
			 * form:
			 * ....device@x,y:a for ttya, etc.
			 * If it isn't, we defer to the ROM
			 */
			cp = buffer;
			while (*cp)
			    cp++;
			cp -= 2;
#ifdef DEBUG
			if (cp < buffer)
				panic("consinit: bad stdout path %s",buffer);
#endif
			/* XXX: only allows tty's a->z, assumes PROMDEV_TTYx contig */
			if (cp[0]==':' && cp[1] >= 'a' && cp[1] <= 'z')
			    outSink = PROMDEV_TTYA + (cp[1] - 'a');
			else outSink = -1;
		}
	} else {
		inSource = *promvec->pv_stdin;
		outSink  = *promvec->pv_stdout;
	}
setup_console:
#endif	/* !solbourne */
#ifdef solbourne
	if (CPU_ISKAP) {
		const char *dev;

		inSource = PROMDEV_TTYA;	/* default */
		dev = prom_getenv(ENV_INPUTDEVICE);
		if (dev != NULL) {
			if (strcmp(dev, "ttyb") == 0)
				inSource = PROMDEV_TTYB;
			if (strcmp(dev, "keyboard") == 0)
				inSource = PROMDEV_KBD;
		}

		outSink = PROMDEV_TTYA;	/* default */
		dev = prom_getenv(ENV_OUTPUTDEVICE);
		if (dev != NULL) {
			if (strcmp(dev, "ttyb") == 0)
				outSink = PROMDEV_TTYB;
			if (strcmp(dev, "screen") == 0)
				outSink = PROMDEV_SCREEN;
		}
	}
#endif

	if (inSource != outSink) {
		printf("cninit: mismatched PROM output selector\n");
		/*
		 * In case of mismatch, force the console to be on
		 * serial.
		 * There are three possible mismatches:
		 * - input and output on different serial lines:
		 *   use the output line.
		 * - input on keyboard, output on serial:
		 *   use the output line (this allows systems configured
		 *   for glass console, which frame buffers have been removed,
		 *   to still work if the keyboard is left plugged).
		 * - input on serial, output on video:
		 *   use the input line, since we don't know if a keyboard
		 *   is connected.
		 */
		if (outSink == PROMDEV_TTYA || outSink == PROMDEV_TTYB)
			inSource = outSink;
		else
			outSink = inSource;
	}

	switch (inSource) {
	default:
		printf("cninit: invalid inSource=%d\n", inSource);
		callrom();
		inSource = PROMDEV_KBD;
		/* FALLTHROUGH */

	case PROMDEV_KBD: /* keyboard/display */
#if NZSKBD > 0
		zs_unit = 1;
		channel = 0;
		break;
#else	/* NZSKBD */
		printf("cninit: kdb/display not configured\n");
		callrom();
		inSource = PROMDEV_TTYA;
		/* FALLTHROUGH */
#endif	/* NZSKBD */

	case PROMDEV_TTYA:
	case PROMDEV_TTYB:
		zstty_unit = inSource - PROMDEV_TTYA;
		zs_unit = 0;
		channel = zstty_unit & 1;
		console = &zscn;
		break;

	}
	/* Now that inSource has been validated, print it. */
	printf("console is %s\n", prom_inSrc_name[inSource]);

	zc = zs_get_chan_addr(zs_unit, channel);
	if (zc == NULL) {
		printf("cninit: zs not mapped.\n");
		return;
	}
	zs_conschan = zc;
	zs_hwflags[zs_unit][channel] = ZS_HWFLAG_CONSOLE;
	/* switch to selected console */
	cn_tab = console;
	(*cn_tab->cn_probe)(cn_tab);
	(*cn_tab->cn_init)(cn_tab);
#ifdef	KGDB
	zs_kgdb_init();
#endif
}


code:
/*	$OpenBSD: cpu.c,v 1.50 2014/04/30 04:20:31 miod Exp $	*/
/*	$NetBSD: cpu.c,v 1.56 1997/09/15 20:52:36 pk Exp $ */

/*
 * Copyright (c) 1996
 *	The President and Fellows of Harvard College. All rights reserved.
 * Copyright (c) 1992, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This software was developed by the Computer Systems Engineering group
 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
 * contributed to Berkeley.
 *
 * All advertising materials mentioning features or use of this software
 * must display the following acknowledgement:
 *	This product includes software developed by Harvard University.
 *	This product includes software developed by the University of
 *	California, Lawrence Berkeley Laboratory.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Aaron Brown and
 *	Harvard University.
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)cpu.c	8.5 (Berkeley) 11/23/93
 *
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/reg.h>
#include <machine/ctlreg.h>
#include <machine/trap.h>
#include <machine/pmap.h>

#include <machine/oldmon.h>
#include <machine/idprom.h>

#include <sparc/sparc/cache.h>
#include <sparc/sparc/asm.h>
#include <sparc/sparc/cpuvar.h>
#include <sparc/sparc/memreg.h>

#ifdef solbourne
#include <machine/idt.h>
#include <machine/kap.h>
#include <machine/prom.h>
#endif

/* The following are used externally (sysctl_hw). */
char	machine[] = MACHINE;		/* from <machine/param.h> */
char	*cpu_class = "sun4";
char	cpu_model[130];
char	cpu_hotfix[40];
extern char mainbus_model[];		/* from autoconf.c */

int	foundfpu;			/* from machine/cpu.h */

/* The CPU configuration driver. */
void cpu_attach(struct device *, struct device *, void *);
int  cpu_match(struct device *, void *, void *);

struct cfattach cpu_ca = {
	sizeof(struct cpu_softc), cpu_match, cpu_attach
};

struct cfdriver cpu_cd = {
	NULL, "cpu", DV_CPU
};

char *fsrtoname(int, int, int, char *, size_t);
void cache_print(struct cpu_softc *);
void cpu_spinup(struct cpu_softc *);
void fpu_init(struct cpu_softc *);
void replacemul(void);

#define	IU_IMPL(psr)	((u_int)(psr) >> 28)
#define	IU_VERS(psr)	(((psr) >> 24) & 0xf)

#define SRMMU_IMPL(mmusr)	((u_int)(mmusr) >> 28)
#define SRMMU_VERS(mmusr)	(((mmusr) >> 24) & 0xf)

#ifdef notdef
/*
 * IU implementations are parceled out to vendors (with some slight
 * glitches).  Printing these is cute but takes too much space.
 */
static char *iu_vendor[16] = {
	"Fujitsu",	/* and also LSI Logic */
	"ROSS",		/* ROSS (ex-Cypress) */
	"BIT",
	"LSIL",		/* LSI Logic finally got their own */
	"TI",		/* Texas Instruments */
	"Matsushita",
	"Philips",
	"Harvest",	/* Harvest VLSI Design Center */
	"SPEC",		/* Systems and Processes Engineering Corporation */
	"Weitek",
	"vendor#10",
	"vendor#11",
	"vendor#12",
	"vendor#13",
	"vendor#14",
	"vendor#15"
};
#endif

int
cpu_match(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	register struct cfdata *cf = vcf;
	register struct confargs *ca = aux;

	return (strcmp(cf->cf_driver->cd_name, ca->ca_ra.ra_name) == 0);
}

/*
 * Attach the CPU.
 * Discover interesting goop about the virtual address cache
 * (slightly funny place to do it, but this is where it is to be found).
 */
void
cpu_attach(parent, self, aux)
	struct device *parent;
	struct device *self;
	void *aux;
{
	struct cpu_softc *sc = (struct cpu_softc *)self;
	register int node;
	register char *fpuname;
	struct confargs *ca = aux;
	char fpbuf[40];
	char model[100];

	sc->node = node = ca->ca_ra.ra_node;

	/*
	 * First, find out if we're attaching the boot CPU.
	 */
	if (node == 0)
		sc->master = 1;
	else {
		sc->mid = getpropint(node, "mid", 0);
		if (sc->mid == 0 || sc->mid == getmid() + 8 /*XXX*/)
			sc->master = 1;
	}

	if (sc->master) {
		/*
		 * Gross, but some things in cpuinfo may already have
		 * been setup by early routines like pmap_bootstrap().
		 */
		bcopy(&sc->dv, &cpuinfo, sizeof(sc->dv));
		bcopy(&cpuinfo, sc, sizeof(cpuinfo));
	}

#if defined(SUN4C) || defined(SUN4D) || defined(SUN4E) || defined(SUN4M)
	switch (cputyp) {
#if defined(SUN4C)
	case CPU_SUN4C:
		cpu_class = "sun4c";
		break;
#endif /* defined(SUN4C) */
#if defined(SUN4D)
	case CPU_SUN4D:
		cpu_class = "sun4d";
		break;
#endif /* defined(SUN4D) */
#if defined(SUN4E)
	case CPU_SUN4E:
		cpu_class = "sun4e";
		break;
#endif /* defined(SUN4E) */
#if defined(SUN4M)
	case CPU_SUN4M:
		cpu_class = "sun4m";
		break;
#endif /* defined(SUN4M) */
	}
#endif /* SUN4C || SUN4D || SUN4E || SUN4M */

	getcpuinfo(sc, node);

	fpuname = "no";
	if (sc->master) {
		if (sc->hotfix)
			sc->hotfix(sc);

		fpu_init(sc);
		if (foundfpu)
			fpuname = fsrtoname(sc->cpu_impl, sc->cpu_vers,
			    sc->fpuvers, fpbuf, sizeof fpbuf);
	}
	/* XXX - multi-processor: take care of `cpu_model' and `foundfpu' */

	snprintf(model, sizeof model, "%s @ %s MHz, %s FPU", sc->cpu_name,
	    clockfreq(sc->hz), fpuname);
	printf(": %s", model);
	snprintf(cpu_model, sizeof cpu_model, "%s, %s", mainbus_model, model);

	if (cpu_hotfix[0])
		printf("; %s", cpu_hotfix);
	printf("\n");

	if (sc->cacheinfo.c_totalsize != 0)
		cache_print(sc);

	sc->ci.ci_softc = sc;

	if (sc->master) {
		int s;

		bcopy(sc, &cpuinfo, sizeof(cpuinfo));
		/*
		 * Enable the cache
		 *
		 * Disable all interrupts because we don't want anything
		 * nasty to happen to the pagetables while the cache is
		 * enabled and we haven't uncached them yet.
		 */
		if (sc->cacheinfo.c_totalsize != 0) {
			s = splhigh();
			sc->cache_enable();
			pmap_cache_enable();
			splx(s);
		}
		return;
	}

	/* Now start this CPU */
}

#if 0
void
cpu_(sc)
	struct cpu_softc *sc;
{
	if (sc->hotfix)
		sc->hotfix(sc);

	/* Initialize FPU */
	fpu_init(sc);

	/* Enable the cache */
	sc->cache_enable();
}
#endif

void
cpu_spinup(sc)
	struct cpu_softc *sc;
{
#if 0
	pmap_cpusetup();
#endif
}

void
fpu_init(sc)
	struct cpu_softc *sc;
{
	struct fpstate fpstate __aligned(8);

	/*
	 * Get the FSR and clear any exceptions.  If we do not unload
	 * the queue here and it is left over from a previous crash, we
	 * will panic in the first loadfpstate(), due to a sequence
	 * error, so we need to dump the whole state anyway.
	 *
	 * If there is no FPU, trap.c will advance over all the stores,
	 * so we initialize fs_fsr here.
	 */

	/* 7 is reserved for "none" */
	fpstate.fs_fsr = 7 << FSR_VER_SHIFT;
	savefpstate(&fpstate);
	sc->fpuvers =
	    (fpstate.fs_fsr >> FSR_VER_SHIFT) & (FSR_VER >> FSR_VER_SHIFT);

	if (sc->fpuvers != 7)
		foundfpu = 1;
}

void
cache_print(sc)
	struct cpu_softc *sc;
{
	struct cacheinfo *ci = &sc->cacheinfo;
	char *sep = "";

	printf("%s: ", sc->dv.dv_xname);

	if (ci->c_split) {
		printf("%s", (ci->c_physical ? "physical " : ""));
		if (ci->ic_totalsize > 0) {
			printf("%s%dK instruction (%d b/l)", sep,
			    ci->ic_totalsize/1024, ci->ic_linesize);
			sep = ", ";
		}
		if (ci->dc_totalsize > 0) {
			printf("%s%dK data (%d b/l)", sep,
			    ci->dc_totalsize/1024, ci->dc_linesize);
			sep = ", ";
		}
	} else if (ci->c_physical) {
		/* combined, physical */
		printf("physical %dK combined cache (%d bytes/line)",
		    ci->c_totalsize/1024, ci->c_linesize);
		sep = ", ";
	} else {
		/* combined, virtual */
		printf("%dK byte write-%s, %d bytes/line, %cw flush",
		    ci->c_totalsize/1024,
		    (ci->c_vactype == VAC_WRITETHROUGH) ? "through" : "back",
		    ci->c_linesize, ci->c_hwflush ? 'h' : 's');
		sep = ", ";
	}

	if (ci->ec_totalsize > 0) {
		printf("%s%dK external (%d b/l)", sep,
		    ci->ec_totalsize/1024, ci->ec_linesize);
	}
	if (sep)	/* printed at least one field.. */
		printf(" ");
}

/*------------*/

void cpumatch_unknown(struct cpu_softc *, struct module_info *, int);
void cpumatch_sun4(struct cpu_softc *, struct module_info *, int);
void cpumatch_sun4c(struct cpu_softc *, struct module_info *, int);
void cpumatch_ms(struct cpu_softc *, struct module_info *, int);
void cpumatch_viking(struct cpu_softc *, struct module_info *, int);
void cpumatch_hypersparc(struct cpu_softc *, struct module_info *, int);
void cpumatch_turbosparc(struct cpu_softc *, struct module_info *, int);
void cpumatch_kap(struct cpu_softc *, struct module_info *, int);

void getcacheinfo_sun4(struct cpu_softc *, int node);
void getcacheinfo_sun4c(struct cpu_softc *, int node);
void getcacheinfo_obp(struct cpu_softc *, int node);
void getcacheinfo_kap(struct cpu_softc *, int node);

void sun4_hotfix(struct cpu_softc *);
void viking_hotfix(struct cpu_softc *);
void turbosparc_hotfix(struct cpu_softc *);
void swift_hotfix(struct cpu_softc *);

void ms1_mmu_enable(void);
void viking_mmu_enable(void);
void swift_mmu_enable(void);
void hypersparc_mmu_enable(void);

void srmmu_get_syncflt(void);
void ms1_get_syncflt(void);
void viking_get_syncflt(void);
void swift_get_syncflt(void);
void turbosparc_get_syncflt(void);
void hypersparc_get_syncflt(void);
void cypress_get_syncflt(void);

int srmmu_get_asyncflt(u_int *, u_int *);
int hypersparc_get_asyncflt(u_int *, u_int *);
int cypress_get_asyncflt(u_int *, u_int *);
int no_asyncflt_regs(u_int *, u_int *);

struct module_info module_unknown = {
	CPUTYP_UNKNOWN,
	VAC_UNKNOWN,
	cpumatch_unknown
};

void
cpumatch_unknown(sc, mp, node)
	struct cpu_softc *sc;
	struct module_info *mp;
	int	node;
{
	panic("Unknown CPU type: "
	      "cpu: impl %d, vers %d; mmu: impl %d, vers %d",
		sc->cpu_impl, sc->cpu_vers,
		sc->mmu_impl, sc->mmu_vers);
}

#if defined(SUN4)
struct module_info module_sun4 = {
	CPUTYP_UNKNOWN,
	VAC_WRITETHROUGH,
	cpumatch_sun4,
	getcacheinfo_sun4,
	sun4_hotfix,
	0,
	sun4_cache_enable,
	0,			/* ncontext set in `match' function */
	0,			/* get_syncflt(); unused in sun4 */
	0,			/* get_asyncflt(); unused in sun4 */
	sun4_cache_flush,
	sun4_vcache_flush_page,
	sun4_vcache_flush_segment,
	sun4_vcache_flush_region,
	sun4_vcache_flush_context,
	noop_pcache_flush_line,
	noop_pure_vcache_flush,
	noop_cache_flush_all,
	0
};

void
getcacheinfo_sun4(sc, node)
	struct cpu_softc *sc;
	int	node;
{
	struct cacheinfo *ci = &sc->cacheinfo;

	switch (sc->cpu_type) {
	case CPUTYP_4_100:
		ci->c_vactype = VAC_NONE;
		ci->c_totalsize = 0;
		ci->c_hwflush = 0;
		ci->c_linesize = 0;
		ci->c_l2linesize = 0;
		ci->c_split = 0;
		ci->c_nlines = 0;

		/* Override cache flush functions */
		sc->cache_flush = noop_cache_flush;
		sc->vcache_flush_page = noop_vcache_flush_page;
		sc->vcache_flush_segment = noop_vcache_flush_segment;
		sc->vcache_flush_region = noop_vcache_flush_region;
		sc->vcache_flush_context = noop_vcache_flush_context;
		break;
	case CPUTYP_4_200:
		ci->c_vactype = VAC_WRITEBACK;
		ci->c_totalsize = 128*1024;
		ci->c_hwflush = 0;
		ci->c_linesize = 16;
		ci->c_l2linesize = 4;
		ci->c_split = 0;
		ci->c_nlines = ci->c_totalsize << ci->c_l2linesize;
		break;
	case CPUTYP_4_300:
		ci->c_vactype = VAC_WRITEBACK;
		ci->c_totalsize = 128*1024;
		ci->c_hwflush = 0;
		ci->c_linesize = 16;
		ci->c_l2linesize = 4;
		ci->c_split = 0;
		ci->c_nlines = ci->c_totalsize << ci->c_l2linesize;
		sc->flags |= CPUFLG_SUN4CACHEBUG;
		break;
	case CPUTYP_4_400:
		ci->c_vactype = VAC_WRITEBACK;
		ci->c_totalsize = 128 * 1024;
		ci->c_hwflush = 0;
		ci->c_linesize = 32;
		ci->c_l2linesize = 5;
		ci->c_split = 0;
		ci->c_nlines = ci->c_totalsize << ci->c_l2linesize;
		break;
	}
}

struct	idprom idprom;
void	getidprom(struct idprom *, int size);

void
cpumatch_sun4(sc, mp, node)
	struct cpu_softc *sc;
	struct module_info *mp;
	int	node;
{

	getidprom(&idprom, sizeof(idprom));
	switch (idprom.id_machine) {
	/* XXX: don't know about Sun4 types */
	case ID_SUN4_100:
		sc->cpu_type = CPUTYP_4_100;
		sc->classlvl = 100;
		sc->mmu_ncontext = 8;
		sc->mmu_nsegment = 256;
/*XXX*/		sc->hz = 14280000;
		break;
	case ID_SUN4_200:
		sc->cpu_type = CPUTYP_4_200;
		sc->classlvl = 200;
		sc->mmu_nsegment = 512;
		sc->mmu_ncontext = 16;
/*XXX*/		sc->hz = 16670000;
		break;
	case ID_SUN4_300:
		sc->cpu_type = CPUTYP_4_300;
		sc->classlvl = 300;
		sc->mmu_nsegment = 256;
		sc->mmu_ncontext = 16;
/*XXX*/		sc->hz = 25000000;
		break;
	case ID_SUN4_400:
		sc->cpu_type = CPUTYP_4_400;
		sc->classlvl = 400;
		sc->mmu_nsegment = 1024;
		sc->mmu_ncontext = 64;
		sc->mmu_nregion = 256;
/*XXX*/		sc->hz = 33000000;
		sc->sun4_mmu3l = 1;
		break;
	}

}
#endif /* SUN4 */

#if defined(SUN4C) || defined(SUN4E)
struct module_info module_sun4c = {
	CPUTYP_UNKNOWN,
	VAC_WRITETHROUGH,
	cpumatch_sun4c,
	getcacheinfo_sun4c,
	sun4_hotfix,
	0,
	sun4_cache_enable,
	0,			/* ncontext set in `match' function */
	0,			/* get_syncflt(); unused in sun4c */
	0,			/* get_asyncflt(); unused in sun4c */
	sun4_cache_flush,
	sun4_vcache_flush_page,
	sun4_vcache_flush_segment,
	sun4_vcache_flush_region,
	sun4_vcache_flush_context,
	noop_pcache_flush_line,
	noop_pure_vcache_flush,
	noop_cache_flush_all,
	0
};

void
cpumatch_sun4c(sc, mp, node)
	struct cpu_softc *sc;
	struct module_info *mp;
	int	node;
{
	int	rnode;

	rnode = findroot();
	sc->mmu_npmeg = sc->mmu_nsegment =
		getpropint(rnode, "mmu-npmg", CPU_ISSUN4E ? 256 : 128);
	sc->mmu_ncontext = getpropint(rnode, "mmu-nctx", 8);

	/* Get clock frequency */
	sc->hz = getpropint(rnode, "clock-frequency", 0);
}

void
getcacheinfo_sun4c(sc, node)
	struct cpu_softc *sc;
	int node;
{
	struct cacheinfo *ci = &sc->cacheinfo;
	int i, l;

	if (node == 0)
		/* Bootstrapping */
		return;

	/* Sun4c's have only virtually-addressed caches */
	ci->c_physical = 0;
	ci->c_totalsize = getpropint(node, "vac-size", 65536);
	/*
	 * Note: vac-hwflush is spelled with an underscore
	 * on the 4/75s.
	 */
	ci->c_hwflush =
		getpropint(node, "vac_hwflush", 0) |
		getpropint(node, "vac-hwflush", 0);

	ci->c_linesize = l = getpropint(node, "vac-linesize", 16);
	for (i = 0; (1 << i) < l; i++)
		/* void */;
	if ((1 << i) != l)
		panic("bad cache line size %d", l);
	ci->c_l2linesize = i;
	ci->c_associativity = 1;
	ci->c_nlines = ci->c_totalsize << i;

	ci->c_vactype = VAC_WRITETHROUGH;

	/*
	 * Machines with "buserr-type" 1 have a bug in the cache
	 * chip that affects traps.  (I wish I knew more about this
	 * mysterious buserr-type variable....)
	 */
	if (getpropint(node, "buserr-type", 0) == 1)
		sc->flags |= CPUFLG_SUN4CACHEBUG;
}
#endif /* SUN4C || SUN4E */

#if defined(solbourne)
struct module_info module_kap = {
	CPUTYP_UNKNOWN,
	VAC_WRITEBACK,
	cpumatch_kap,
	getcacheinfo_kap,
	NULL,
	0,				/* mmu_enable */
	kap_cache_enable,
	0,				/* ncontext is irrelevant here */
	0,
	0,
	kap_cache_flush,
	kap_vcache_flush_page,
	noop_vcache_flush_segment,	/* unused */
	noop_vcache_flush_region,	/* unused */
	kap_vcache_flush_context,
	noop_pcache_flush_line,
	noop_pure_vcache_flush,
	noop_cache_flush_all,
	0
};

void
cpumatch_kap(sc, mp, node)
	struct cpu_softc *sc;
	struct module_info *mp;
	int	node;
{
	extern int timerblurb;

	sc->mmu_npmeg = sc->mmu_ncontext = 0;	/* do not matter for idt */

	/*
	 * Check for the clock speed in the board diagnostic register.
	 * While there, knowing that there are only two possible values,
	 * fill the delay constant.
	 */
	if ((lda(GLU_DIAG, ASI_PHYS_IO) >> 24) & GD_36MHZ) {
		sc->hz = 36000000;
		timerblurb = 14;	/* about 14.40 */
	} else {
		sc->hz = 33000000;
		timerblurb = 13;	/* about 13.20 */
	}

	if (node != 0) {
		sysmodel = getpropint(node, "cpu", 0);
		switch (sysmodel) {
		case SYS_S4000:
			break;
		case SYS_S4100:
			/* XXX do something about the L2 cache */
			break;
		default:
			panic("cpumatch_kap: unrecognized sysmodel %x",
			    sysmodel);
		}
	}
}

void
getcacheinfo_kap(sc, node)
	struct cpu_softc *sc;
	int node;
{
	struct cacheinfo *ci = &sc->cacheinfo;

	/*
	 * The KAP processor has 3KB icache and 2KB dcache.
	 * It is divided in 3 (icache) or 2 (dcache) banks
	 * of 256 lines, each line being 4 bytes.
	 * Both caches are virtually addressed.
	 */

	ci->ic_linesize = 12;
	ci->ic_l2linesize = 3;	/* XXX */
	ci->ic_nlines = DCACHE_LINES;
	ci->ic_associativity = 1;
	ci->ic_totalsize =
	    ci->ic_nlines * ci->ic_linesize * ci->ic_associativity;

	ci->dc_enabled = 1;
	ci->dc_linesize = 8;
	ci->dc_l2linesize = 3;
	ci->dc_nlines = DCACHE_LINES;
	ci->dc_associativity = 1;
	ci->dc_totalsize =
	    ci->dc_nlines * ci->dc_linesize * ci->dc_associativity;

	ci->c_totalsize = ci->ic_totalsize + ci->dc_totalsize;
	/* ci->c_enabled */
	ci->c_hwflush = 0;
	ci->c_linesize = 8;	/* min */
	ci->c_l2linesize = 3;	/* min */
	ci->c_nlines = DCACHE_LINES;
	ci->c_physical = 0;
	ci->c_associativity = 1;
	ci->c_split = 1;

	/* no L2 cache (except on 4100 but we don't handle it yet) */

	ci->c_vactype = VAC_WRITEBACK;
}
#endif

void
sun4_hotfix(sc)
	struct cpu_softc *sc;
{
	if ((sc->flags & CPUFLG_SUN4CACHEBUG) != 0) {
		kvm_uncache((caddr_t)trapbase, 1);
		strlcpy(cpu_hotfix, "cache chip bug - trap page uncached",
		    sizeof cpu_hotfix);
	}

}

#if defined(SUN4M)
void
getcacheinfo_obp(sc, node)
	struct	cpu_softc *sc;
	int	node;
{
	struct cacheinfo *ci = &sc->cacheinfo;
	int i, l;

	if (node == 0)
		/* Bootstrapping */
		return;

	/*
	 * Determine the Sun4m cache organization.
	 */
	ci->c_physical = node_has_property(node, "cache-physical?");

	if (getpropint(node, "ncaches", 1) == 2)
		ci->c_split = 1;
	else
		ci->c_split = 0;

	/* hwflush is used only by sun4/4c code */
	ci->c_hwflush = 0;

	if (node_has_property(node, "icache-nlines") &&
	    node_has_property(node, "dcache-nlines") &&
	    ci->c_split) {
		/* Harvard architecture: get I and D cache sizes */
		ci->ic_nlines = getpropint(node, "icache-nlines", 0);
		ci->ic_linesize = l =
			getpropint(node, "icache-line-size", 0);
		for (i = 0; (1 << i) < l && l; i++)
			/* void */;
		if ((1 << i) != l && l)
			panic("bad icache line size %d", l);
		ci->ic_l2linesize = i;
		ci->ic_associativity =
			getpropint(node, "icache-associativity", 1);
		ci->ic_totalsize = l * ci->ic_nlines * ci->ic_associativity;

		ci->dc_nlines = getpropint(node, "dcache-nlines", 0);
		ci->dc_linesize = l =
			getpropint(node, "dcache-line-size",0);
		for (i = 0; (1 << i) < l && l; i++)
			/* void */;
		if ((1 << i) != l && l)
			panic("bad dcache line size %d", l);
		ci->dc_l2linesize = i;
		ci->dc_associativity =
			getpropint(node, "dcache-associativity", 1);
		ci->dc_totalsize = l * ci->dc_nlines * ci->dc_associativity;

		ci->c_l2linesize = min(ci->ic_l2linesize, ci->dc_l2linesize);
		ci->c_linesize = min(ci->ic_linesize, ci->dc_linesize);
		ci->c_totalsize = ci->ic_totalsize + ci->dc_totalsize;
	} else {
		/* unified I/D cache */
		ci->c_nlines = getpropint(node, "cache-nlines", 128);
		ci->c_linesize = l =
			getpropint(node, "cache-line-size", 0);
		for (i = 0; (1 << i) < l && l; i++)
			/* void */;
		if ((1 << i) != l && l)
			panic("bad cache line size %d", l);
		ci->c_l2linesize = i;
		ci->c_totalsize = l *
			ci->c_nlines *
			getpropint(node, "cache-associativity", 1);
	}

	if (node_has_property(node, "ecache-nlines")) {
		/* we have a L2 "e"xternal cache */
		ci->ec_nlines = getpropint(node, "ecache-nlines", 32768);
		ci->ec_linesize = l = getpropint(node, "ecache-line-size", 0);
		for (i = 0; (1 << i) < l && l; i++)
			/* void */;
		if ((1 << i) != l && l)
			panic("bad ecache line size %d", l);
		ci->ec_l2linesize = i;
		ci->ec_associativity =
			getpropint(node, "ecache-associativity", 1);
		ci->ec_totalsize = l * ci->ec_nlines * ci->ec_associativity;
	}
	if (ci->c_totalsize == 0)
		printf("warning: couldn't identify cache\n");
}

/*
 * We use the max. number of contexts on the micro and
 * hyper SPARCs. The SuperSPARC would let us use up to 65536
 * contexts (by powers of 2), but we keep it at 4096 since
 * the table must be aligned to #context*4. With 4K contexts,
 * we waste at most 16K of memory. Note that the context
 * table is *always* page-aligned, so there can always be
 * 1024 contexts without sacrificing memory space (given
 * that the chip supports 1024 contexts).
 *
 * Currently known limits: MS1=64, MS2=256, HS=4096, SS=65536
 * 	some old SS's=4096
 */

/* TI Microsparc I */
struct module_info module_ms1 = {
	CPUTYP_MS1,
	VAC_NONE,
	cpumatch_ms,
	getcacheinfo_obp,
	0,
	ms1_mmu_enable,
	ms1_cache_enable,
	64,
	ms1_get_syncflt,
	no_asyncflt_regs,
	ms1_cache_flush,
	noop_vcache_flush_page,
	noop_vcache_flush_segment,
	noop_vcache_flush_region,
	noop_vcache_flush_context,
	noop_pcache_flush_line,
	noop_pure_vcache_flush,
	ms1_cache_flush_all,
	memerr4m
};

void
ms1_mmu_enable()
{
}

/* TI Microsparc II */
struct module_info module_ms2 = {
	CPUTYP_MS2,
	VAC_WRITETHROUGH,
	cpumatch_ms,
	getcacheinfo_obp,
	0, /* was swift_hotfix, */
	0,
	swift_cache_enable,
	256,
	srmmu_get_syncflt,
	srmmu_get_asyncflt,
	srmmu_cache_flush,
	srmmu_vcache_flush_page,
	srmmu_vcache_flush_segment,
	srmmu_vcache_flush_region,
	srmmu_vcache_flush_context,
	noop_pcache_flush_line,
	noop_pure_vcache_flush,
	srmmu_cache_flush_all,
	memerr4m
};

struct module_info module_swift = {
	CPUTYP_MS2,
	VAC_WRITETHROUGH,
	cpumatch_ms,
	getcacheinfo_obp,
	swift_hotfix,
	0,
	swift_cache_enable,
	256,
	swift_get_syncflt,
	no_asyncflt_regs,
	srmmu_cache_flush,
	srmmu_vcache_flush_page,
	srmmu_vcache_flush_segment,
	srmmu_vcache_flush_region,
	srmmu_vcache_flush_context,
	srmmu_pcache_flush_line,
	noop_pure_vcache_flush,
	srmmu_cache_flush_all,
	memerr4m
};

void
cpumatch_ms(sc, mp, node)
	struct cpu_softc *sc;
	struct module_info *mp;
	int	node;
{
	replacemul();
}

void
swift_hotfix(sc)
	struct cpu_softc *sc;
{
	int pcr = lda(SRMMU_PCR, ASI_SRMMU);

	/* Turn on branch prediction */
	pcr |= SWIFT_PCR_BF;
	sta(SRMMU_PCR, ASI_SRMMU, pcr);
}

void
swift_mmu_enable()
{
}

struct module_info module_viking = {
	CPUTYP_UNKNOWN,		/* set in cpumatch() */
	VAC_NONE,
	cpumatch_viking,
	getcacheinfo_obp,
	viking_hotfix,
	viking_mmu_enable,
	viking_cache_enable,
	4096,
	viking_get_syncflt,
	no_asyncflt_regs,
	/* supersparcs use cached DVMA, no need to flush */
	noop_cache_flush,
	noop_vcache_flush_page,
	noop_vcache_flush_segment,
	noop_vcache_flush_region,
	noop_vcache_flush_context,
	viking_pcache_flush_line,
	noop_pure_vcache_flush,
	noop_cache_flush_all,
	viking_memerr
};

void
cpumatch_viking(sc, mp, node)
	struct cpu_softc *sc;
	struct module_info *mp;
	int	node;
{
	replacemul();

	if (node == 0)
		viking_hotfix(sc);
}

void
viking_hotfix(sc)
	struct cpu_softc *sc;
{
	int pcr = lda(SRMMU_PCR, ASI_SRMMU);

	/* Test if we're directly on the MBus */
	if ((pcr & VIKING_PCR_MB) == 0) {
		sc->mxcc = 1;
		sc->flags |= CPUFLG_CACHE_MANDATORY;
		/*
		 * Ok to cache PTEs; set the flag here, so we don't
		 * uncache in pmap_bootstrap().
		 */
		if ((pcr & VIKING_PCR_TC) == 0)
			printf("[viking: PCR_TC is off]");
		else
			sc->flags |= CPUFLG_CACHEPAGETABLES;
	} else {
		sc->cache_flush = viking_cache_flush;
		sc->pcache_flush_line = viking_pcache_flush_line;
	}

	/* XXX! */
	if (sc->mxcc)
		sc->cpu_type = CPUTYP_SS1_MBUS_MXCC;
	else
		sc->cpu_type = CPUTYP_SS1_MBUS_NOMXCC;
}

void
viking_mmu_enable()
{
	int pcr;

	pcr = lda(SRMMU_PCR, ASI_SRMMU);

	if (cpuinfo.mxcc) {
		if ((pcr & VIKING_PCR_TC) == 0) {
			printf("[viking: turn on PCR_TC]");
		}
		pcr |= VIKING_PCR_TC;
	} else
		pcr &= ~VIKING_PCR_TC;
	sta(SRMMU_PCR, ASI_SRMMU, pcr);
}

/* ROSS Hypersparc */
struct module_info module_hypersparc = {
	CPUTYP_UNKNOWN,
	VAC_WRITEBACK,
	cpumatch_hypersparc,
	getcacheinfo_obp,
	0,
	hypersparc_mmu_enable,
	hypersparc_cache_enable,
	4096,
	hypersparc_get_syncflt,
	hypersparc_get_asyncflt,
	srmmu_cache_flush,
	srmmu_vcache_flush_page,
	srmmu_vcache_flush_segment,
	srmmu_vcache_flush_region,
	srmmu_vcache_flush_context,
	srmmu_pcache_flush_line,
	hypersparc_pure_vcache_flush,
	hypersparc_cache_flush_all,
	hypersparc_memerr
};

void
cpumatch_hypersparc(sc, mp, node)
	struct cpu_softc *sc;
	struct module_info *mp;
	int	node;
{
	sc->cpu_type = CPUTYP_HS_MBUS;/*XXX*/

	if (node == 0)
		sta(0, ASI_HICACHECLR, 0);

	replacemul();
}

void
hypersparc_mmu_enable()
{
#if 0
	int pcr;

	pcr = lda(SRMMU_PCR, ASI_SRMMU);
	pcr |= HYPERSPARC_PCR_C;
	pcr &= ~HYPERSPARC_PCR_CE;

	sta(SRMMU_PCR, ASI_SRMMU, pcr);
#endif
}

/* Cypress 605 */
struct module_info module_cypress = {
	CPUTYP_CYPRESS,
	VAC_WRITEBACK,
	0,
	getcacheinfo_obp,
	0,
	0,
	cypress_cache_enable,
	4096,
	cypress_get_syncflt,
	cypress_get_asyncflt,
	srmmu_cache_flush,
	srmmu_vcache_flush_page,
	srmmu_vcache_flush_segment,
	srmmu_vcache_flush_region,
	srmmu_vcache_flush_context,
	srmmu_pcache_flush_line,
	noop_pure_vcache_flush,
	cypress_cache_flush_all,
	memerr4m
};

/* Fujitsu Turbosparc */
struct module_info module_turbosparc = {	/* UNTESTED */
	CPUTYP_MS2,
	VAC_WRITEBACK,
	cpumatch_turbosparc,
	getcacheinfo_obp,
	turbosparc_hotfix,
	0,
	turbosparc_cache_enable,
	256,
	turbosparc_get_syncflt,
	no_asyncflt_regs,
	srmmu_cache_flush,
	srmmu_vcache_flush_page,
	srmmu_vcache_flush_segment,
	srmmu_vcache_flush_region,
	srmmu_vcache_flush_context,
	srmmu_pcache_flush_line,
	noop_pure_vcache_flush,
	srmmu_cache_flush_all,
	memerr4m
};

void
cpumatch_turbosparc(sc, mp, node)
	struct cpu_softc *sc;
	struct module_info *mp;
	int	node;
{
	int i;

	if (node == 0 || sc->master == 0)
		return;

	i = getpsr();
	if (sc->cpu_vers == IU_VERS(i))
		return;

	/*
	 * A cloaked Turbosparc: clear any items in cpuinfo that
	 * might have been set to uS2 versions during bootstrap.
	 */
	sc->cpu_name = 0;
	sc->mmu_ncontext = 0;
	sc->cpu_type = 0;
	sc->cacheinfo.c_vactype = 0;
	sc->hotfix = 0;
	sc->mmu_enable = 0;
	sc->cache_enable = 0;
	sc->get_syncflt = 0;
	sc->cache_flush = 0;
	sc->vcache_flush_page = 0;
	sc->vcache_flush_segment = 0;
	sc->vcache_flush_region = 0;
	sc->vcache_flush_context = 0;
	sc->pcache_flush_line = 0;

	replacemul();
}

void
turbosparc_hotfix(sc)
	struct cpu_softc *sc;
{
	int pcf;

	pcf = lda(SRMMU_PCFG, ASI_SRMMU);
	if (pcf & TURBOSPARC_PCFG_US2) {
		/* Turn off uS2 emulation bit */
		pcf &= ~TURBOSPARC_PCFG_US2;
		sta(SRMMU_PCFG, ASI_SRMMU, pcf);
	}
}
#endif /* SUN4M */

#define	ANY	-1	/* match any version */

struct cpu_conf {
	int	arch;
	int	cpu_impl;
	int	cpu_vers;
	int	mmu_impl;
	int	mmu_vers;
	char	*name;
	struct	module_info *minfo;
} cpu_conf[] = {
#if defined(SUN4)
	{ CPU_SUN4, 0, 0, ANY, ANY, "MB86900/1A or L64801", &module_sun4 },
	{ CPU_SUN4, 1, 0, ANY, ANY, "L64811", &module_sun4 },
	{ CPU_SUN4, 1, 1, ANY, ANY, "CY7C601", &module_sun4 },
#endif

#if defined(SUN4C)
	{ CPU_SUN4C, 0, 0, ANY, ANY, "MB86900/1A or L64801", &module_sun4c },
	{ CPU_SUN4C, 1, 0, ANY, ANY, "L64811", &module_sun4c },
	{ CPU_SUN4C, 1, 1, ANY, ANY, "CY7C601", &module_sun4c },
	{ CPU_SUN4C, 9, 0, ANY, ANY, "W8601/8701 or MB86903", &module_sun4c },
#endif

#if defined(SUN4E)
	{ CPU_SUN4E, 0, 0, ANY, ANY, "MB86900/1A or L64801", &module_sun4c },
#endif

#if defined(SUN4M)
	{ CPU_SUN4M, 0, 4, 0, 4, "MB86904", &module_swift },
	{ CPU_SUN4M, 0, 5, 0, 5, "MB86907", &module_turbosparc },
	{ CPU_SUN4M, 1, 1, 1, 0, "CY7C601/604", &module_cypress },
	{ CPU_SUN4M, 1, 1, 1, 0xb, "CY7C601/605 (v.b)", &module_cypress },
	{ CPU_SUN4M, 1, 1, 1, 0xc, "CY7C601/605 (v.c)", &module_cypress },
	{ CPU_SUN4M, 1, 1, 1, 0xf, "CY7C601/605 (v.f)", &module_cypress },
	{ CPU_SUN4M, 1, 3, 1, ANY, "CY7C611", &module_cypress },
	{ CPU_SUN4M, 1, 0xe, 1, 7, "RT620/625", &module_hypersparc },
	{ CPU_SUN4M, 1, 0xf, 1, 7, "RT620/625", &module_hypersparc },
	{ CPU_SUN4M, 4, 0, 0, ANY, "TMS390Z50 v0 or TMS390Z55", &module_viking },
	{ CPU_SUN4M, 4, 1, 0, ANY, "TMS390Z50 v1", &module_viking },
	{ CPU_SUN4M, 4, 1, 4, ANY, "TMS390S10", &module_ms1 },
	{ CPU_SUN4M, 4, 2, 0, ANY, "TI_MS2", &module_ms2 },
	{ CPU_SUN4M, 4, 3, ANY, ANY, "TI_4_3", &module_viking },
	{ CPU_SUN4M, 4, 4, ANY, ANY, "TI_4_4", &module_viking },
#endif

#if defined(solbourne)
	{ CPU_KAP, 5, 0, ANY, ANY, "KAP", &module_kap },
#endif

	{ ANY, ANY, ANY, ANY, ANY, "Unknown", &module_unknown }
};

void
getcpuinfo(sc, node)
	struct cpu_softc *sc;
	int	node;
{
	struct cpu_conf *mp;
	int i;
	int cpu_impl, cpu_vers;
	int mmu_impl, mmu_vers;

	/*
	 * Set up main criteria for selection from the CPU configuration
	 * table: the CPU implementation/version fields from the PSR
	 * register, and -- on sun4m machines -- the MMU
	 * implementation/version from the SCR register.
	 */
	if (sc->master) {
		i = getpsr();
		if (node == 0 ||
		    (cpu_impl =
		     getpropint(node, "psr-implementation", -1)) == -1)
			cpu_impl = IU_IMPL(i);

		if (node == 0 ||
		    (cpu_vers = getpropint(node, "psr-version", -1)) == -1)
			cpu_vers = IU_VERS(i);

		if (CPU_ISSUN4M) {
			i = lda(SRMMU_PCR, ASI_SRMMU);
			if (node == 0 ||
			    (mmu_impl =
			     getpropint(node, "implementation", -1)) == -1)
				mmu_impl = SRMMU_IMPL(i);

			if (node == 0 ||
			    (mmu_vers = getpropint(node, "version", -1)) == -1)
				mmu_vers = SRMMU_VERS(i);
		} else {
			mmu_impl = ANY;
			mmu_vers = ANY;
		}
	} else {
		/*
		 * Get CPU version/implementation from ROM. If not
		 * available, assume same as boot CPU.
		 */
		cpu_impl = getpropint(node, "psr-implementation", -1);
		if (cpu_impl == -1)
			cpu_impl = cpuinfo.cpu_impl;
		cpu_vers = getpropint(node, "psr-version", -1);
		if (cpu_vers == -1)
			cpu_vers = cpuinfo.cpu_vers;

		/* Get MMU version/implementation from ROM always */
		mmu_impl = getpropint(node, "implementation", -1);
		mmu_vers = getpropint(node, "version", -1);
	}

	for (mp = cpu_conf; ; mp++) {
		if (mp->arch != cputyp && mp->arch != ANY)
			continue;

#define MATCH(x)	(mp->x == x || mp->x == ANY)
		if (!MATCH(cpu_impl) ||
		    !MATCH(cpu_vers) ||
		    !MATCH(mmu_impl) ||
		    !MATCH(mmu_vers))
			continue;
#undef MATCH

		/*
		 * Got CPU type.
		 */
		sc->cpu_impl = cpu_impl;
		sc->cpu_vers = cpu_vers;
		sc->mmu_impl = mmu_impl;
		sc->mmu_vers = mmu_vers;

		if (mp->minfo->cpu_match) {
			/* Additional fixups */
			mp->minfo->cpu_match(sc, mp->minfo, node);
		}
		if (sc->cpu_name == 0)
			sc->cpu_name = mp->name;

		if (sc->mmu_ncontext == 0)
			sc->mmu_ncontext = mp->minfo->ncontext;

		if (sc->cpu_type == 0)
			sc->cpu_type = mp->minfo->cpu_type;

		if (sc->cacheinfo.c_vactype == VAC_UNKNOWN)
			sc->cacheinfo.c_vactype = mp->minfo->vactype;

		mp->minfo->getcacheinfo(sc, node);

		if (node && sc->hz == 0 && !CPU_ISSUN4/*XXX*/) {
			sc->hz = getpropint(node, "clock-frequency", 0);
			if (sc->hz == 0) {
				/*
				 * Try to find it in the OpenPROM root...
				 */
				sc->hz = getpropint(findroot(),
						    "clock-frequency", 0);
			}
		}

		/*
		 * Copy CPU/MMU/Cache specific routines into cpu_softc.
		 */
#define MPCOPY(x)	if (sc->x == 0) sc->x = mp->minfo->x;
		MPCOPY(hotfix);
		MPCOPY(mmu_enable);
		MPCOPY(cache_enable);
		MPCOPY(get_syncflt);
		MPCOPY(get_asyncflt);
		MPCOPY(cache_flush);
		MPCOPY(vcache_flush_page);
		MPCOPY(vcache_flush_segment);
		MPCOPY(vcache_flush_region);
		MPCOPY(vcache_flush_context);
		MPCOPY(pcache_flush_line);
		MPCOPY(pure_vcache_flush);
		MPCOPY(cache_flush_all);
		MPCOPY(memerr);
#undef MPCOPY
		return;
	}
	panic("Out of CPUs");
}

/*
 * The following tables convert <IU impl, IU version, FPU version> triples
 * into names for the CPU and FPU chip.  In most cases we do not need to
 * inspect the FPU version to name the IU chip, but there is one exception
 * (for Tsunami), and this makes the tables the same.
 *
 * The table contents (and much of the structure here) are from Guy Harris.
 *
 */
struct info {
	int	valid;
	int	iu_impl;
	int	iu_vers;
	int	fpu_vers;
	char	*name;
};

/* NB: table order matters here; specific numbers must appear before ANY. */
static struct info fpu_types[] = {
	/*
	 * Vendor 0, IU Fujitsu0.
	 */
	{ 1, 0x0, ANY, 0, "MB86910 or WTL1164/5" },
	{ 1, 0x0, ANY, 1, "MB86911 or WTL1164/5" },
	{ 1, 0x0, ANY, 2, "L64802 or ACT8847" },
	{ 1, 0x0, ANY, 3, "WTL3170/2" },
	{ 1, 0x0, 4,   4, "on-chip" },		/* Swift */
	{ 1, 0x0, 5,   5, "on-chip" },		/* TurboSparc */
	{ 1, 0x0, ANY, 4, "L64804" },

	/*
	 * Vendor 1, IU ROSS0/1 or Pinnacle.
	 */
	{ 1, 0x1, 0xf, 0, "on-chip" },		/* Pinnacle */
	{ 1, 0x1, 0xe, 0, "on-chip" },		/* Hypersparc RT 625/626 */
	{ 1, 0x1, ANY, 0, "L64812 or ACT8847" },
	{ 1, 0x1, ANY, 1, "L64814" },
	{ 1, 0x1, ANY, 2, "TMS390C602A" },
	{ 1, 0x1, ANY, 3, "RT602 or WTL3171" },

	/*
	 * Vendor 2, IU BIT0.
	 */
	{ 1, 0x2, ANY, 0, "B5010 or B5110/20 or B5210" },

	/*
	 * Vendor 4, Texas Instruments.
	 */
	{ 1, 0x4, ANY, 0, "on-chip" },		/* Viking */
	{ 1, 0x4, ANY, 4, "on-chip" },		/* Tsunami */

	/*
	 * Vendor 5, IU Matsushita0.
	 */
	{ 1, 0x5, ANY, 0, "on-chip" },

	/*
	 * Vendor 9, Weitek.
	 */
	{ 1, 0x9, ANY, 3, "on-chip" },

	{ 0 }
};

char *
fsrtoname(impl, vers, fver, buf, buflen)
	register int impl, vers, fver;
	char *buf;
	size_t buflen;
{
	register struct info *p;

	for (p = fpu_types; p->valid; p++)
		if (p->iu_impl == impl &&
		    (p->iu_vers == vers || p->iu_vers == ANY) &&
		    (p->fpu_vers == fver))
			return (p->name);
	snprintf(buf, buflen, "version 0x%x", fver);
	return (buf);
}

/*
 * Whack the slow sun4/sun4c {,u}{mul,div,rem} functions with
 * fast V8 ones
 * We are called once before pmap_bootstrap and once after. We only do stuff
 * in the "before" case. We happen to know that the kernel text is not
 * write-protected then.
 * XXX - investigate cache flushing, right now we can't do it because the
 *       flushes try to do va -> pa conversions.
 */
extern int _mulreplace, _mulreplace_end, _mul;
extern int _umulreplace, _umulreplace_end, _umul;
extern int _divreplace, _divreplace_end, _div;
extern int _udivreplace, _udivreplace_end, _udiv;
extern int _remreplace, _remreplace_end, _rem;
extern int _uremreplace, _uremreplace_end, _urem;
int	v8mul;	/* flag whether cpu has hardware mul, div, and rem */

static const struct replace {
	void *from, *frome, *to;
} ireplace[] = {
	{ &_mulreplace, &_mulreplace_end, &_mul },
	{ &_umulreplace, &_umulreplace_end, &_umul },
	{ &_divreplace, &_divreplace_end, &_div },
	{ &_udivreplace, &_udivreplace_end, &_udiv },
	{ &_remreplace, &_remreplace_end, &_rem },
 	{ &_uremreplace, &_uremreplace_end, &_urem },
};

void
replacemul()
{
	static int replacedone = 0;
	int i, s;

	if (replacedone)
		return;
	replacedone = 1;

	s = splhigh();
	for (i = 0; i < sizeof(ireplace)/sizeof(ireplace[0]); i++)
		bcopy(ireplace[i].from, ireplace[i].to,
		    ireplace[i].frome - ireplace[i].from);
	splx(s);
	v8mul = 1;
}


code:
/*	$OpenBSD: cpuvar.h,v 1.20 2014/07/11 10:53:07 uebayasi Exp $	*/
/*	$NetBSD: cpuvar.h,v 1.4 1997/07/06 21:14:25 pk Exp $ */

/*
 *  Copyright (c) 1996 The NetBSD Foundation, Inc.
 *  All rights reserved.
 *
 *  This code is derived from software contributed to The NetBSD Foundation
 *  by Paul Kranenburg.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 *  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 *  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 *  PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _SPARC_CPUVAR_H
#define _SPARC_CPUVAR_H

#include <sys/device.h>
#include <sys/sched.h>

#include <sparc/sparc/cache.h>	/* for cacheinfo */

/*
 * CPU/MMU module information.
 * There is one of these for each "mainline" CPU module we support.
 * The information contained in the structure is used only during
 * auto-configuration of the CPUs; some fields are copied into the
 * per-cpu data structure (cpu_softc) for easy access during normal
 * operation.
 */
struct cpu_softc;
struct module_info {
	int  cpu_type;
	enum vactype vactype;
	void (*cpu_match)(struct cpu_softc *, struct module_info *, int);
	void (*getcacheinfo)(struct cpu_softc *sc, int node);
	void (*hotfix)(struct cpu_softc *);
	void (*mmu_enable)(void);
	void (*cache_enable)(void);
	int  ncontext;			/* max. # of contexts (we use) */

	void (*get_syncflt)(void);
	int  (*get_asyncflt)(u_int *, u_int *);
	void (*cache_flush)(caddr_t, u_int);
	void (*vcache_flush_page)(int);
	void (*vcache_flush_segment)(int, int);
	void (*vcache_flush_region)(int);
	void (*vcache_flush_context)(void);
	void (*pcache_flush_line)(int, int);
	void (*pure_vcache_flush)(void);
	void (*cache_flush_all)(void);
	void (*memerr)(unsigned, u_int, u_int, struct trapframe *);
};

struct cpu_softc;
struct cpu_info {
	struct cpu_softc *ci_softc;

	struct proc *ci_curproc;
	struct cpu_info *ci_next;

	struct schedstate_percpu ci_schedstate;
	u_int32_t 		ci_randseed;

#ifdef DIAGNOSTIC
	int	ci_mutex_level;
#endif
};

#define curcpu() (&cpuinfo.ci)
#define cpu_number() (cpuinfo.mid)
#define CPU_IS_PRIMARY(ci)	((ci)->ci_softc->master)
#define CPU_INFO_ITERATOR	int
#define CPU_INFO_FOREACH(cii, ci) \
	for (cii = 0, ci = curcpu(); ci != NULL; ci = ci->ci_next)
#define CPU_INFO_UNIT(ci) ((ci)->ci_softc ? (ci)->ci_softc->dv.dv_unit : 0)
#define MAXCPUS	1
#define cpu_unidle(ci)

#define CPU_BUSY_CYCLE()	do {} while (0)

/*
 * The cpu_softc structure. This structure maintains information about one
 * currently installed CPU (there may be several of these if the machine
 * supports multiple CPUs, as on some Sun4m architectures). The information
 * in this structure supersedes the old "cpumod", "mmumod", and similar
 * fields.
 */

struct cpu_softc {
	struct device	dv;		/* generic device info */

	struct cpu_info ci;

	int		node;		/* PROM node for this CPU */

	/* CPU information */
	char		*cpu_name;	/* CPU model */
	int		cpu_impl;	/* CPU implementation code */
	int		cpu_vers;	/* CPU version code */
	int		mmu_impl;	/* MMU implementation code */
	int		mmu_vers;	/* MMU version code */
	int		master;		/* 1 if this is bootup CPU */

	int		mid;		/* Module ID for MP systems */
	int		mbus;		/* 1 if CPU is on MBus */
	int		mxcc;		/* 1 if a MBus-level MXCC is present */

	caddr_t		mailbox;	/* VA of CPU's mailbox */

	int		mmu_ncontext;	/* Number of contexts supported */
	int		mmu_nregion; 	/* Number of regions supported */
	int		mmu_nsegment;	/* [4/4c] Segments */
	int		mmu_npmeg;	/* [4/4c] Pmegs */
	int		sun4_mmu3l;	/* [4]: 3-level MMU present */
#if defined(SUN4_MMU3L)
#define HASSUN4_MMU3L	(cpuinfo.sun4_mmu3l)
#else
#define HASSUN4_MMU3L	(0)
#endif

	/* Context administration */
	int		*ctx_tbl;	/* [4m] SRMMU-edible context table */
	union ctxinfo	*ctxinfo;
	union ctxinfo	*ctx_freelist;  /* context free list */
	int		ctx_kick;	/* allocation rover when none free */
	int		ctx_kickdir;	/* ctx_kick roves both directions */

	/* MMU tables that map `cpuinfo'' on each CPU */
	int		*L1_ptps;	/* XXX */

/* XXX - of these, we currently use only cpu_type */
	int		arch;		/* Architecture: CPU_SUN4x */
	int		class;		/* Class: SuperSPARC, microSPARC... */
	int		classlvl;	/* Iteration in class: 1, 2, etc. */
	int		classsublvl;	/* stepping in class (version) */
	int		cpu_type;	/* Type: see CPUTYP_xxx below */

	int		hz;		/* Clock speed */

	/* Cache information */
	struct cacheinfo	cacheinfo;	/* see cache.h */

	/* FPU information */
	int		fpupresent;	/* true if FPU is present */
	int		fpuvers;	/* FPU revision */

	/* various flags to workaround anomalies in chips */
	int		flags;		/* see CPUFLG_xxx, below */

	/*
	 * The following pointers point to processes that are somehow
	 * associated with this CPU--running on it, using its FPU,
	 * etc.
	 *
	 * XXXMP: much more needs to go here
	 */
	struct	proc 	*fpproc;		/* FPU owner */

	/*
	 * The following are function pointers to do interesting CPU-dependent
	 * things without having to do type-tests all the time
	 */

	/* bootup things: access to physical memory */
	u_int	(*read_physmem)(u_int addr, int space);
	void	(*write_physmem)(u_int addr, u_int data);
	void	(*cache_tablewalks)(void);
	void	(*mmu_enable)(void);
	void	(*hotfix)(struct cpu_softc *);

	/* locore defined: */
	void	(*get_syncflt)(void);		/* Not C-callable */
	int	(*get_asyncflt)(u_int *, u_int *);

       	/* Synchronous Fault Status; temporary storage */
       	struct {
		int     sfsr;
		int     sfva;
	} syncfltdump;

	/* Cache handling functions */
	void	(*cache_enable)(void);
	void	(*cache_flush)(caddr_t, u_int);
	void	(*vcache_flush_page)(int);
	void	(*vcache_flush_segment)(int, int);
	void	(*vcache_flush_region)(int);
	void	(*vcache_flush_context)(void);
	void	(*pcache_flush_line)(int, int);
	void	(*pure_vcache_flush)(void);
	void	(*cache_flush_all)(void);

#ifdef SUN4M
	/* hardware-assisted block operation routines */
	void		(*hwbcopy)(const void *from, void *to, size_t len);
	void		(*hwbzero)(void *buf, size_t len);

	/* routine to clear mbus-sbus buffers */
	void		(*mbusflush)(void);
#endif

	/*
	 * Memory error handler; parity errors, unhandled NMIs and other
	 * unrecoverable faults end up here.
	 */
	void    (*memerr)(unsigned, u_int, u_int, struct trapframe *);
	/* XXX: Add more here! */
};

/*
 * CPU types. When nonzero, these enable system-specific behaviour.
 * The general form is
 * 	CPUTYP_proctype_bustype_cachetype_etc_etc
 */
#define CPUTYP_UNKNOWN		0

/* sun4 models */
#define CPUTYP_4_100		1 	/* Sun4/100 */
#define CPUTYP_4_200		2	/* Sun4/200 */
#define CPUTYP_4_300		3	/* Sun4/300 */
#define CPUTYP_4_400		4	/* Sun4/400 */

/* rough sun4m families; not really used */
#define CPUTYP_SS1_MBUS_MXCC	21	/* SuperSPARC-I, MBus, MXCC (SS10) */
#define CPUTYP_SS1_MBUS_NOMXCC	23	/* SuperSPARC-I, on MBus w/o MXCC */
#define CPUTYP_MS2		24	/* MicroSPARC-2 */
#define CPUTYP_MS1		25 	/* MicroSPARC-1 */
#define CPUTYP_HS_MBUS		26	/* MBus-based HyperSPARC */
#define CPUTYP_CYPRESS		27	/* MBus-based Cypress */

/*
 * CPU flags
 */
#define CPUFLG_CACHEPAGETABLES	0x1	/* caching pagetables OK on Sun4m */
#define CPUFLG_CACHEIOMMUTABLES	0x2	/* caching IOMMU translations OK */
#define CPUFLG_CACHEDVMA	0x4	/* DVMA goes through cache */
#define CPUFLG_SUN4CACHEBUG	0x8	/* trap page can't be cached */
#define CPUFLG_CACHE_MANDATORY	0x10	/* if cache is on, don't use
					   uncached access */

/*
 * Related function prototypes
 */
void getcpuinfo(struct cpu_softc *sc, int node);
void mmu_install_tables(struct cpu_softc *);
void pmap_alloc_cpu(struct cpu_softc *);

#define cpuinfo	(*(struct cpu_softc *)CPUINFO_VA)
#endif	/* _SPARC_CPUVAR_H */