code:
/*	$OpenBSD: cache.h,v 1.6 2008/03/30 12:30:01 kettenis Exp $	*/
/*	$NetBSD: cache.h,v 1.3 2000/08/01 00:28:02 eeh 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.
 *
 * 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.
 *
 *	@(#)cache.h	8.1 (Berkeley) 6/11/93
 */

enum vactype { VAC_NONE, VAC_WRITETHROUGH, VAC_WRITEBACK };

extern enum vactype vactype;	/* XXX  move into cacheinfo struct */

/*
 * Cache tags can be written in control space, and must be set to 0
 * (or invalid anyway) before turning on the cache.  The tags are
 * addressed as an array of 32-bit structures of the form:
 *
 *	struct cache_tag {
 *		u_int	:7,		(unused; must be zero)
 *			ct_cid:3,	(context ID)
 *			ct_w:1,		(write flag from PTE)
 *			ct_s:1,		(supervisor flag from PTE)
 *			ct_v:1,		(set => cache entry is valid)
 *			:3,		(unused; must be zero)
 *			ct_tid:14,	(cache tag ID)
 *			:2;		(unused; must be zero)
 *	};
 *
 * The SPARCstation 1 cache sees virtual addresses as:
 *
 *	struct cache_va {
 *		u_int	:2,		(unused; probably copies of va_tid<13>)
 *			cva_tid:14,	(tag ID)
 *			cva_line:12,	(cache line number)
 *			cva_byte:4;	(byte in cache line)
 *	};
 *
 * (The SS2 cache is similar but has half as many lines, each twice as long.)
 *
 * Note that, because the 12-bit line ID is `wider' than the page offset,
 * it is possible to have one page map to two different cache lines.
 * This can happen whenever two different physical pages have the same bits
 * in the part of the virtual address that overlaps the cache line ID, i.e.,
 * bits <15:12>.  In order to prevent cache duplication, we have to
 * make sure that no one page has more than one virtual address where
 * (va1 & 0xf000) != (va2 & 0xf000).  (The cache hardware turns off ct_v
 * when a cache miss occurs on a write, i.e., if va1 is in the cache and
 * va2 is not, and you write to va2, va1 goes out of the cache.  If va1
 * is in the cache and va2 is not, reading va2 also causes va1 to become
 * uncached, and the [same] data is then read from main memory into the
 * cache.)
 *
 * The other alternative, of course, is to disable caching of aliased
 * pages.  (In a few cases this might be faster anyway, but we do it
 * only when forced.)
 *
 * The Sun4, since it has an 8K pagesize instead of 4K, needs to check
 * bits that are one position higher.
 */

/*
 * The spitfire has a 16K two-way set associative level-1 I$ and a separate
 * 16K level-1 D$.  The I$ can be invalidated using the FLUSH instructions,
 * so we don't really need to worry about it much.  The D$ is 16K write-through
 * direct mapped virtually addressed cache with two 16-byte sub-blocks per line.
 * The E$ is a 512KB-4MB direct mapped physically indexed physically tagged cache.
 * Since the level-1 caches are write-through, they don't need flushing and can be
 * invalidated directly.
 *
 * The spitfire sees virtual addresses as:
 *
 *	struct cache_va {
 *		u_int64_t	:22,		(unused; we only have 40-bit addresses)
 *				cva_tag:28,	(tag ID)
 *				cva_line:9,	(cache line number)
 *				cva_byte:5;	(byte within line)
 *	};
 *
 * Since there is one bit of overlap between the page offset and the line index,
 * all we need to do is make sure that bit 14 of the va remains constant and we have
 * no aliasing problems.
 *
 * Let me try again.  Page size is 8K, cache size is 16K so if (va1&0x3fff != va2&0x3fff)
 * we have a problem.  Bit 14 *must* be the same for all mappings of a page to be cacheable
 * in the D$.  (The I$ is 16K 2-way associative--each bank is 8K.  No conflict there.)
 */

/* Some more well-known values: */
#define CACHE_ALIAS_MASK	0x7fff
#define CACHE_ALIAS_BITS	0x4000

/*
 * True iff a1 and a2 are `bad' aliases (will cause cache duplication).
 */
#define	BADALIAS(a1, a2) (((int)(a1) ^ (int)(a2)) & CACHE_ALIAS_BITS)

/*
 * Routines for dealing with the cache.
 */
void	cache_enable(void);		/* turn it on */
int 	cache_flush_page(paddr_t);	/* flush page from E$ */
int	cache_flush(vaddr_t, vsize_t);	/* flush region */

/* The following are for D$ flushes and are in locore.s */
#define dcache_flush_page(pa) cacheinfo.c_dcache_flush_page(pa)
void 	us_dcache_flush_page(paddr_t);	/* flush page from D$ */
void 	us3_dcache_flush_page(paddr_t);	/* flush page from D$ */
void	no_dcache_flush_page(paddr_t);

/* The following flush a range from the D$ and I$ but not E$. */
void	cache_flush_virt(vaddr_t, vsize_t);
void	cache_flush_phys(paddr_t, psize_t, int);

/*
 * Cache control information.
 */
struct cacheinfo {
	void	(*c_dcache_flush_page)(paddr_t);

	int	c_totalsize;		/* total size, in bytes */
					/* if split, MAX(icache,dcache) */
	int	c_enabled;		/* true => cache is enabled */
	int	c_hwflush;		/* true => have hardware flush */
	int	c_linesize;		/* line size, in bytes */
	int	c_l2linesize;		/* log2(linesize) */
	int	c_physical;		/* true => cache is physical */
	int 	c_split;		/* true => cache is split */
	int 	ic_totalsize;		/* instruction cache */
	int 	ic_enabled;
	int 	ic_linesize;
	int 	ic_l2linesize;
	int 	dc_totalsize;		/* data cache */
	int 	dc_enabled;
	int 	dc_linesize;
	int 	dc_l2linesize;
	int	ec_totalsize;		/* external cache info */
	int 	ec_enabled;
	int	ec_linesize;
	int	ec_l2linesize;
};
extern struct cacheinfo cacheinfo;

/*
 * Cache control statistics.
 */
struct cachestats {
	int	cs_npgflush;		/* # page flushes */
	int	cs_nraflush;		/* # range flushes */
#ifdef notyet
	int	cs_ra[65];		/* pages/range */
#endif
};


code:
/*	$OpenBSD: clock.c,v 1.57 2015/06/13 07:16:37 jsg Exp $	*/
/*	$NetBSD: clock.c,v 1.41 2001/07/24 19:29:25 eeh 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.
 */

/* Define this for a 1/4s clock to ease debugging */
/* #define INTR_DEBUG */

#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>
#ifdef GPROF
#include <sys/gmon.h>
#endif
#include <sys/sched.h>
#include <sys/timetc.h>
#include <sys/atomic.h>

#include <machine/bus.h>
#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/idprom.h>

#include <dev/clock_subr.h>
#include <dev/ic/mk48txxreg.h>

#include <sparc64/sparc64/intreg.h>
#include <sparc64/sparc64/timerreg.h>
#include <sparc64/dev/iommureg.h>
#include <sparc64/dev/sbusreg.h>
#include <dev/sbus/sbusvar.h>
#include <sparc64/dev/ebusreg.h>
#include <sparc64/dev/ebusvar.h>
#include <sparc64/dev/fhcvar.h>

extern u_int64_t cpu_clockrate;

struct clock_wenable_info {
	bus_space_tag_t		cwi_bt;
	bus_space_handle_t	cwi_bh;
	bus_size_t		cwi_size;
};

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

u_int tick_get_timecount(struct timecounter *);

struct timecounter tick_timecounter = {
	tick_get_timecount, NULL, ~0u, 0, "tick", 0, NULL
};

u_int sys_tick_get_timecount(struct timecounter *);

struct timecounter sys_tick_timecounter = {
	sys_tick_get_timecount, NULL, ~0u, 0, "sys_tick", 1000, NULL
};

/*
 * 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 */

static long tick_increment;

void	tick_start(void);
void	sys_tick_start(void);
void	stick_start(void);

int	tickintr(void *);
int	sys_tickintr(void *);
int	stickintr(void *);
int	schedintr(void *);

static struct intrhand level10 = { clockintr };
static struct intrhand level0 = { tickintr };
static struct intrhand level14 = { statintr };
static struct intrhand schedint = { schedintr };

/*
 * clock (eeprom) attaches at the sbus or the ebus (PCI)
 */
static int	clockmatch_sbus(struct device *, void *, void *);
static void	clockattach_sbus(struct device *, struct device *, void *);
static int	clockmatch_ebus(struct device *, void *, void *);
static void	clockattach_ebus(struct device *, struct device *, void *);
static int	clockmatch_fhc(struct device *, void *, void *);
static void	clockattach_fhc(struct device *, struct device *, void *);
static void	clockattach(int, bus_space_tag_t, bus_space_handle_t);

struct cfattach clock_sbus_ca = {
	sizeof(struct device), clockmatch_sbus, clockattach_sbus
};

struct cfattach clock_ebus_ca = {
	sizeof(struct device), clockmatch_ebus, clockattach_ebus
};

struct cfattach clock_fhc_ca = {
	sizeof(struct device), clockmatch_fhc, clockattach_fhc
};

/* Global TOD clock handle & idprom pointer */
todr_chip_handle_t todr_handle = NULL;
static struct idprom *idprom;

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

struct timerreg_4u	timerreg_4u;	/* XXX - need more cleanup */

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

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

int clock_bus_wenable(struct todr_chip_handle *, int);
struct chiptime;
void myetheraddr(u_char *);
struct idprom *getidprom(void);
int chiptotime(int, int, int, int, int, int);
void timetochip(struct chiptime *);
void stopcounter(struct timer_4u *);

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

/*
 * The OPENPROM calls the clock the "eeprom", so we have to have our
 * own special match function to call it the "clock".
 */
static int
clockmatch_sbus(parent, cf, aux)
	struct device *parent;
	void *cf;
	void *aux;
{
	struct sbus_attach_args *sa = aux;

	return (strcmp("eeprom", sa->sa_name) == 0);
}

static int
clockmatch_ebus(parent, cf, aux)
	struct device *parent;
	void *cf;
	void *aux;
{
	struct ebus_attach_args *ea = aux;

	return (strcmp("eeprom", ea->ea_name) == 0);
}

static int
clockmatch_fhc(parent, cf, aux)
	struct device *parent;
	void *cf;
	void *aux;
{
	struct fhc_attach_args *fa = aux;

	return (strcmp("eeprom", fa->fa_name) == 0);
}

/*
 * Attach a clock (really `eeprom') to the sbus or ebus.
 *
 * 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.
 *
 * the MK48T02 is 2K.  the MK48T08 is 8K, and the MK48T59 is
 * supposed to be identical to it.
 *
 * This is *UGLY*!  We probably have multiple mappings.  But I do
 * know that this all fits inside an 8K page, so I'll just map in
 * once.
 *
 * What we really need is some way to record the bus attach args
 * so we can call *_bus_map() later with BUS_SPACE_MAP_READONLY
 * or not to write enable/disable the device registers.  This is
 * a non-trivial operation.
 */

/* ARGSUSED */
static void
clockattach_sbus(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct sbus_attach_args *sa = aux;
	bus_space_tag_t bt = sa->sa_bustag;
	int sz;
	static struct clock_wenable_info cwi;

	/* use sa->sa_regs[0].size? */
	sz = 8192;

	if (sbus_bus_map(bt,
			 sa->sa_slot,
			 (sa->sa_offset & ~NBPG),
			 sz,
			 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY,
			 0, &cwi.cwi_bh) != 0) {
		printf("%s: can't map register\n", self->dv_xname);
		return;
	}
	clockattach(sa->sa_node, bt, cwi.cwi_bh);

	/* Save info for the clock wenable call. */
	cwi.cwi_bt = bt;
	cwi.cwi_size = sz;
	todr_handle->bus_cookie = &cwi;
	todr_handle->todr_setwen = clock_bus_wenable;
}

/*
 * Write en/dis-able clock registers.  We coordinate so that several
 * writers can run simultaneously.
 * XXX There is still a race here.  The page change and the "writers"
 * change are not atomic.
 */
int
clock_bus_wenable(handle, onoff)
	struct todr_chip_handle *handle;
	int onoff;
{
	int s, err = 0;
	int prot; /* nonzero => change prot */
	volatile static int writers;
	struct clock_wenable_info *cwi = handle->bus_cookie;

	s = splhigh();
	if (onoff)
		prot = writers++ == 0 ? 1 : 0;
	else
		prot = --writers == 0 ? 1 : 0;
	splx(s);

	if (prot) {
		err = bus_space_protect(cwi->cwi_bt, cwi->cwi_bh, cwi->cwi_size,
		    onoff ? 0 : BUS_SPACE_MAP_READONLY);
		if (err)
			printf("clock_wenable_info: WARNING -- cannot %s "
			    "page protection\n", onoff ? "disable" : "enable");
	}
	return (err);
}

/* ARGSUSED */
static void
clockattach_ebus(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct ebus_attach_args *ea = aux;
	bus_space_tag_t bt;
	int sz;
	static struct clock_wenable_info cwi;

	/* hard code to 8K? */
	sz = ea->ea_regs[0].size;

	if (ea->ea_nvaddrs) {
		if (bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0,
		    BUS_SPACE_MAP_PROMADDRESS, &cwi.cwi_bh) != 0) {
			printf("%s: can't map register\n", self->dv_xname);
			return;
		}
		bt = ea->ea_memtag;
	} else if (ebus_bus_map(ea->ea_iotag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), sz, 0, 0, &cwi.cwi_bh) == 0) {
		bt = ea->ea_iotag;
	} else if (ebus_bus_map(ea->ea_memtag, 0,
	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), sz,
	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY,
	    0, &cwi.cwi_bh) == 0) {
		bt = ea->ea_memtag;
	} else {
		printf("%s: can't map register\n", self->dv_xname);
		return;
	}

	clockattach(ea->ea_node, bt, cwi.cwi_bh);

	/* Save info for the clock wenable call. */
	cwi.cwi_bt = bt;
	cwi.cwi_size = sz;
	todr_handle->bus_cookie = &cwi;
	todr_handle->todr_setwen = (ea->ea_memtag == bt) ?
	    clock_bus_wenable : NULL;
}

static void
clockattach_fhc(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct fhc_attach_args *fa = aux;
	bus_space_tag_t bt = fa->fa_bustag;
	int sz;
	static struct clock_wenable_info cwi;

	/* use sa->sa_regs[0].size? */
	sz = 8192;

	if (fhc_bus_map(bt, fa->fa_reg[0].fbr_slot,
	    (fa->fa_reg[0].fbr_offset & ~NBPG), fa->fa_reg[0].fbr_size,
	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY, &cwi.cwi_bh) != 0) {
		printf("%s: can't map register\n", self->dv_xname);
		return;
	}

	clockattach(fa->fa_node, bt, cwi.cwi_bh);

	/* Save info for the clock wenable call. */
	cwi.cwi_bt = bt;
	cwi.cwi_size = sz;
	todr_handle->bus_cookie = &cwi;
	todr_handle->todr_setwen = clock_bus_wenable;
}

static void
clockattach(node, bt, bh)
	int node;
	bus_space_tag_t bt;
	bus_space_handle_t bh;
{
	char *model;
	struct idprom *idp;
	int h;

	model = getpropstring(node, "model");

#ifdef DIAGNOSTIC
	if (model == NULL)
		panic("clockattach: no model property");
#endif

	/* Our TOD clock year 0 is 1968 */
	if ((todr_handle = mk48txx_attach(bt, bh, model, 1968)) == NULL)
		panic("Can't attach %s tod clock", model);

#define IDPROM_OFFSET (8*1024 - 40)	/* XXX - get nvram sz from driver */
	if (idprom == NULL) {
		idp = getidprom();
		if (idp == NULL)
			idp = (struct idprom *)(bus_space_vaddr(bt, bh) +
			    IDPROM_OFFSET);
		idprom = idp;
	} else
		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;
	printf("\n");
}

struct idprom *
getidprom()
{
	struct idprom *idp = NULL;
	int node, n;

	node = findroot();
	if (getprop(node, "idprom", sizeof(*idp), &n, (void **)&idp) != 0)
		return (NULL);
	if (n != 1) {
		free(idp, M_DEVBUF, 0);
		return (NULL);
	}
	return (idp);
}

/*
 * The sun4u OPENPROMs call the timer the "counter-timer", except for
 * the lame UltraSPARC IIi PCI machines that don't have them.
 */
static int
timermatch(parent, cf, aux)
	struct device *parent;
	void *cf;
	void *aux;
{
#ifndef MULTIPROCESSOR
	struct mainbus_attach_args *ma = aux;

	if (!timerreg_4u.t_timer || !timerreg_4u.t_clrintr)
		return (strcmp("counter-timer", ma->ma_name) == 0);
	else
#endif
		return (0);
}

static void
timerattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct mainbus_attach_args *ma = aux;
	u_int *va = ma->ma_address;

	/*
	 * What we should have are 3 sets of registers that reside on
	 * different parts of SYSIO or PSYCHO.  We'll use the prom
	 * mappings cause we can't get rid of them and set up appropriate
	 * pointers on the timerreg_4u structure.
	 */
	timerreg_4u.t_timer = (struct timer_4u *)(u_long)va[0];
	timerreg_4u.t_clrintr = (int64_t *)(u_long)va[1];
	timerreg_4u.t_mapintr = (int64_t *)(u_long)va[2];

	/* Install the appropriate interrupt vector here */
	level10.ih_number = INTVEC(ma->ma_interrupts[0]);
	level10.ih_clr = (void *)&timerreg_4u.t_clrintr[0];
	level10.ih_map = (void *)&timerreg_4u.t_mapintr[0];
	strlcpy(level10.ih_name, "clock", sizeof(level10.ih_name));
	intr_establish(10, &level10);

	level14.ih_number = INTVEC(ma->ma_interrupts[1]);
	level14.ih_clr = (void *)&timerreg_4u.t_clrintr[1];
	level14.ih_map = (void *)&timerreg_4u.t_mapintr[1];
	strlcpy(level14.ih_name, "prof", sizeof(level14.ih_name));
	intr_establish(14, &level14);

	printf(" ivec 0x%llx, 0x%llx\n", INTVEC(level10.ih_number),
	    INTVEC(level14.ih_number));
}

void
stopcounter(creg)
	struct timer_4u *creg;
{
	/* Stop the clock */
	volatile int discard;
	discard = creg->t_limit;
	creg->t_limit = 0;
}

/*
 * XXX this belongs elsewhere
 */
void
myetheraddr(cp)
	u_char *cp;
{
	struct idprom *idp;

	if ((idp = idprom) == NULL) {
		int node, n;

		node = findroot();
		if (getprop(node, "idprom", sizeof *idp, &n, (void **)&idp) ||
		    n != 1) {
			printf("\nmyetheraddr: clock not setup yet, "
			       "and no idprom property in /\n");
			return;
		}
	}

	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];
	if (idprom == NULL)
		free(idp, M_DEVBUF, 0);
}

/*
 * 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(void)
{
	int statint, minint;
#ifdef DEBUG
	extern int intrdebug;
#endif
	u_int sys_tick_rate;
	int impl = 0;

#ifdef DEBUG
	/* Set a 1s clock */
	if (intrdebug) {
		hz = 1;
		tick = 1000000 / hz;
		printf("intrdebug set: 1Hz clock\n");
	}
#endif

	if (1000000 % hz) {
		printf("cannot get %d Hz clock; using 100 Hz\n", hz);
		hz = 100;
		tick = 1000000 / hz;
	}

	/* Make sure we have a sane cpu_clockrate -- we'll need it */
	if (!cpu_clockrate)
		/* Default to 200MHz clock XXXXX */
		cpu_clockrate = 200000000;

	tick_timecounter.tc_frequency = cpu_clockrate;
	tc_init(&tick_timecounter);

	/*
	 * UltraSPARC IIe processors do have a STICK register, but it
	 * lives on the PCI host bridge and isn't accessable through
	 * ASR24.
	 */
	if (CPU_ISSUN4U || CPU_ISSUN4US)
		impl = (getver() & VER_IMPL) >> VER_IMPL_SHIFT;

	sys_tick_rate = getpropint(findroot(), "stick-frequency", 0);
	if (sys_tick_rate > 0 && impl != IMPL_HUMMINGBIRD) {
		sys_tick_timecounter.tc_frequency = sys_tick_rate;
		tc_init(&sys_tick_timecounter);
	}

	/*
	 * Now handle machines w/o counter-timers.
	 */

	if (!timerreg_4u.t_timer || !timerreg_4u.t_clrintr) {
		struct cpu_info *ci;

		/* We don't have a counter-timer -- use %tick */
		level0.ih_clr = 0;

		/*
		 * Establish a level 10 interrupt handler
		 *
		 * We will have a conflict with the softint handler,
		 * so we set the ih_number to 1.
		 */
		level0.ih_number = 1;
		strlcpy(level0.ih_name, "clock", sizeof(level0.ih_name));
		intr_establish(10, &level0);

		/* We only have one timer so we have no statclock */
		stathz = 0;

		if (sys_tick_rate > 0) {
			tick_increment = sys_tick_rate / hz;
			if (impl == IMPL_HUMMINGBIRD) {
				level0.ih_fun = stickintr;
				cpu_start_clock = stick_start;
			} else {
				level0.ih_fun = sys_tickintr;
				cpu_start_clock = sys_tick_start;
			}
		} else {
			/* set the next interrupt time */
			tick_increment = cpu_clockrate / hz;
			level0.ih_fun = tickintr;
			cpu_start_clock = tick_start;
		}

		for (ci = cpus; ci != NULL; ci = ci->ci_next)
			memcpy(&ci->ci_tickintr, &level0, sizeof(level0));

		cpu_start_clock();

		return;
	}

	if (stathz == 0)
		stathz = hz;
	if (1000000 % stathz) {
		printf("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;

	/*
	 * Establish scheduler softint.
	 */
	schedint.ih_pil = PIL_SCHED;
	schedint.ih_clr = NULL;
	schedint.ih_arg = 0;
	schedint.ih_pending = 0;
	schedhz = stathz/4;

	/*
	 * Enable timers
	 *
	 * Also need to map the interrupts cause we're not a child of the sbus.
	 * N.B. By default timer[0] is disabled and timer[1] is enabled.
	 */
	stxa((vaddr_t)&timerreg_4u.t_timer[0].t_limit, ASI_NUCLEUS,
	     tmr_ustolim(tick)|TMR_LIM_IEN|TMR_LIM_PERIODIC|TMR_LIM_RELOAD);
	stxa((vaddr_t)&timerreg_4u.t_mapintr[0], ASI_NUCLEUS,
	     timerreg_4u.t_mapintr[0]|INTMAP_V);

#ifdef DEBUG
	if (intrdebug)
		/* Neglect to enable timer */
		stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS,
		     tmr_ustolim(statint)|TMR_LIM_RELOAD);
	else
#endif
		stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS,
		     tmr_ustolim(statint)|TMR_LIM_IEN|TMR_LIM_RELOAD);
	stxa((vaddr_t)&timerreg_4u.t_mapintr[1], ASI_NUCLEUS,
	     timerreg_4u.t_mapintr[1]|INTMAP_V);

	statmin = statint - (statvar >> 1);

}

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

/*
 * Level 10 (clock) interrupts.  If we are using the FORTH PROM for
 * console input, we need to check for that here as well, and generate
 * a software interrupt to read it.
 */
#ifdef	DEBUG
static int clockcheck = 0;
#endif
int
clockintr(cap)
	void *cap;
{
#ifdef DEBUG
	static int64_t tick_base = 0;
	struct timeval ctime;
	int64_t t;

	t = tick() & TICK_TICKS;

	microtime(&ctime);
	if (!tick_base) {
		tick_base = (ctime.tv_sec * 1000000LL + ctime.tv_usec)
			* 1000000LL / cpu_clockrate;
		tick_base -= t;
	} else if (clockcheck) {
		int64_t tk = t;
		int64_t clk = (ctime.tv_sec * 1000000LL + ctime.tv_usec);
		t -= tick_base;
		t = t * 1000000LL / cpu_clockrate;
		if (t - clk > hz) {
			printf("Clock lost an interrupt!\n");
			printf("Actual: %llx Expected: %llx tick %llx "
			    "tick_base %llx\n", (long long)t, (long long)clk,
			    (long long)tk, (long long)tick_base);
#ifdef DDB
			Debugger();
#endif
			tick_base = 0;
		}
	}
#endif
	/* Let locore.s clear the interrupt for us. */
	hardclock((struct clockframe *)cap);

	return (1);
}

/*
 * Level 10 (clock) interrupts.  If we are using the FORTH PROM for
 * console input, we need to check for that here as well, and generate
 * a software interrupt to read it.
 *
 * %tick is really a level-14 interrupt.  We need to remap this in
 * locore.s to a level 10.
 */
int
tickintr(cap)
	void *cap;
{
	struct cpu_info *ci = curcpu();
	u_int64_t s;

	/*
	 * No need to worry about overflow; %tick is architecturally
	 * defined not to do that for at least 10 years.
	 */
	while (ci->ci_tick < tick()) {
		ci->ci_tick += tick_increment;
		hardclock((struct clockframe *)cap);
		atomic_add_long((unsigned long *)&level0.ih_count.ec_count, 1);
	}

	/* Reset the interrupt. */
	s = intr_disable();
	tickcmpr_set(ci->ci_tick);
	intr_restore(s);

	return (1);
}

int
sys_tickintr(cap)
	void *cap;
{
	struct cpu_info *ci = curcpu();
	u_int64_t s;

	/*
	 * Do we need to worry about overflow here?
	 */
	while (ci->ci_tick < sys_tick()) {
		ci->ci_tick += tick_increment;
		hardclock((struct clockframe *)cap);
		atomic_add_long((unsigned long *)&level0.ih_count.ec_count, 1);
	}

	/* Reset the interrupt. */
	s = intr_disable();
	sys_tickcmpr_set(ci->ci_tick);
	intr_restore(s);

	return (1);
}

int
stickintr(cap)
	void *cap;
{
	struct cpu_info *ci = curcpu();
	u_int64_t s;

	/*
	 * Do we need to worry about overflow here?
	 */
	while (ci->ci_tick < stick()) {
		ci->ci_tick += tick_increment;
		hardclock((struct clockframe *)cap);
		atomic_add_long((unsigned long *)&level0.ih_count.ec_count, 1);
	}

	/* Reset the interrupt. */
	s = intr_disable();
	stickcmpr_set(ci->ci_tick);
	intr_restore(s);

	return (1);
}

/*
 * Level 14 (stat clock) interrupt handler.
 */
int
statintr(cap)
	void *cap;
{
	u_long newint, r, var;
	struct cpu_info *ci = curcpu();

#ifdef NOT_DEBUG
	printf("statclock: count %x:%x, limit %x:%x\n",
	       timerreg_4u.t_timer[1].t_count, timerreg_4u.t_timer[1].t_limit);
#endif
#ifdef NOT_DEBUG
	prom_printf("!");
#endif
	statclock((struct clockframe *)cap);
#ifdef NOTDEF_DEBUG
	/* Don't re-schedule the IRQ */
	return 1;
#endif
	/*
	 * 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 (schedhz)
		if ((++ci->ci_schedstate.spc_schedticks & 3) == 0)
			send_softint(-1, PIL_SCHED, &schedint);
	stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS,
	     tmr_ustolim(newint)|TMR_LIM_IEN|TMR_LIM_RELOAD);

	return (1);
}

int
schedintr(arg)
	void *arg;
{
	if (curproc)
		schedclock(curproc);
	return (1);
}

/*
 * `sparc_clock_time_is_ok' is used in cpu_reboot() to determine
 * whether it is appropriate to call resettodr() to consolidate
 * pending time adjustments.
 */
int sparc_clock_time_is_ok;

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

	tv.tv_sec = tv.tv_usec = 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 = 21*SECYR + 186*SECDAY + SECDAY/2;
		badbase = 1;
	}

	if (todr_handle != NULL)
		todr_gettime(todr_handle, &tv);

	if (tv.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";
		tv.tv_sec = base;
		tv.tv_usec = 0;
		if (!badbase)
			resettodr();
	} else {
		time_t deltat = tv.tv_sec - base;

		sparc_clock_time_is_ok = 1;

		if (deltat < 0)
			deltat = -deltat;
		if (!(waszero || deltat < 2 * SECDAY)) {
#ifndef SMALL_KERNEL
			printf("WARNING: clock %s %lld days",
			    tv.tv_sec < base ? "lost" : "gained",
			    (long long)(deltat / SECDAY));
			bad = "";
#endif
		}
	}

	ts.tv_sec = tv.tv_sec;
	ts.tv_nsec = tv.tv_usec * 1000;
	tc_setclock(&ts);

	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 timeval tv;

	if (time_second == 1)
		return;

	microtime(&tv);

	sparc_clock_time_is_ok = 1;
	if (todr_handle == 0 || todr_settime(todr_handle, &tv) != 0)
		printf("Cannot set time in time-of-day clock\n");
}

void
tick_start(void)
{
	struct cpu_info *ci = curcpu();
	u_int64_t s;

	/*
	 * Try to make the tick interrupts as synchronously as possible on
	 * all CPUs to avoid inaccuracies for migrating processes.
	 */

	s = intr_disable();
	ci->ci_tick = roundup(tick(), tick_increment);
	tickcmpr_set(ci->ci_tick);
	intr_restore(s);
}

void
sys_tick_start(void)
{
	struct cpu_info *ci = curcpu();
	u_int64_t s;

	/*
	 * Try to make the tick interrupts as synchronously as possible on
	 * all CPUs to avoid inaccuracies for migrating processes.
	 */

	s = intr_disable();
	ci->ci_tick = roundup(sys_tick(), tick_increment);
	sys_tickcmpr_set(ci->ci_tick);
	intr_restore(s);
}

void
stick_start(void)
{
	struct cpu_info *ci = curcpu();
	u_int64_t s;

	/*
	 * Try to make the tick interrupts as synchronously as possible on
	 * all CPUs to avoid inaccuracies for migrating processes.
	 */

	s = intr_disable();
	ci->ci_tick = roundup(stick(), tick_increment);
	stickcmpr_set(ci->ci_tick);
	intr_restore(s);
}

u_int
tick_get_timecount(struct timecounter *tc)
{
	u_int64_t tick;

	__asm volatile("rd %%tick, %0" : "=r" (tick) :);

	return (tick & ~0u);
}

u_int
sys_tick_get_timecount(struct timecounter *tc)
{
	u_int64_t tick;

	__asm volatile("rd %%sys_tick, %0" : "=r" (tick) :);

	return (tick & ~0u);
}

license: unknown


code:
/*	$OpenBSD: conf.c,v 1.74 2014/12/11 19:44:17 tedu Exp $	*/
/*	$NetBSD: conf.c,v 1.17 2001/03/26 12:33:26 lukem 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 "midi.h"
#include "audio.h"
#include "video.h"
#include "vnd.h"
#include "ch.h"
#include "sd.h"
#include "st.h"
#include "cd.h"
#include "uk.h"
#include "wd.h"

#ifdef notyet
#include "fb.h"
#endif
#include "zstty.h"
#include "sab.h"
#include "pcons.h"
#include "vcons.h"
#include "vcctty.h"
#include "sbbc.h"
#include "com.h"
#include "lpt.h"
#include "bpp.h"
#include "magma.h"		/* has NMTTY and NMBPP */
#include "spif.h"		/* has NSTTY and NSBPP */
#include "uperf.h"
#include "vldcp.h"
#include "vdsp.h"

#include "fdc.h"		/* has NFDC and NFD; see files.sparc */

#include "drm.h"
cdev_decl(drm);

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

#ifdef USER_PCICONF
#include "pci.h"
cdev_decl(pci);
#endif

#include "rd.h"

#include "usb.h"
#include "uhid.h"
#include "ugen.h"
#include "ulpt.h"
#include "ucom.h"

#include "pf.h"

#include "ksyms.h"

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

struct bdevsw	bdevsw[] =
{
	bdev_notdef(),			/* 0 */
	bdev_notdef(),			/* 1 */
	bdev_notdef(),			/* 2 */
	bdev_notdef(),			/* 3: SMD disk -- not this arch */
	bdev_swap_init(1,sw),		/* 4 swap pseudo-device */
	bdev_disk_init(NRD,rd),		/* 5: ram disk */
	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_notdef(),			/* 10: SMD disk -- not this arch */
	bdev_tape_init(NST,st),		/* 11: SCSI tape */
	bdev_disk_init(NWD,wd),		/* 12: IDE disk */
	bdev_notdef(),			/* 13 */
	bdev_notdef(),			/* 14 */
	bdev_notdef(),			/* 15 */
	bdev_disk_init(NFD,fd),		/* 16: floppy disk */
	bdev_notdef(),			/* 17 */
	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 */
};
int	nblkdev = nitems(bdevsw);

struct cdevsw	cdevsw[] =
{
	cdev_cn_init(1,cn),		/* 0: virtual console */
	cdev_notdef(),			/* 1: tapemaster tape */
	cdev_ctty_init(1,ctty),		/* 2: controlling terminal */
	cdev_mm_init(1,mm),		/* 3: /dev/{null,mem,kmem,...} */
	cdev_notdef(),			/* 4 */
	cdev_notdef(),			/* 5: tapemaster tape */
	cdev_notdef(),			/* 6: systech/versatec */
	cdev_notdef(),			/* 7 was /dev/drum */
	cdev_notdef(),			/* 8: Archive QIC-11 tape */
	cdev_notdef(),			/* 9: SMD disk on Xylogics 450/451 */
	cdev_notdef(),			/* 10: systech multi-terminal board */
	cdev_notdef(),			/* 11: DES encryption chip */
	cdev_tty_init(NZSTTY,zs),	/* 12: Zilog 8530 serial port */
	cdev_notdef(),			/* 13: /dev/mouse */
	cdev_notdef(),			/* 14: cgone */
	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 */
	cdev_notdef(),			/* 23: was: concatenated disk driver */
	cdev_fd_init(1,filedesc),	/* 24: file descriptor pseudo-device */
	cdev_uperf_init(NUPERF,uperf),	/* 25: performance counters */
	cdev_disk_init(NWD,wd),		/* 26: IDE disk */
	cdev_notdef(),			/* 27 */
	cdev_notdef(),			/* 28: Systech VPC-2200 versatec/centronics */
	cdev_notdef(),			/* 29 */
	cdev_notdef(),			/* 30: Xylogics tape */
	cdev_notdef(),			/* 31: /dev/cgtwo */
	cdev_notdef(),			/* 32: should be /dev/gpone */
	cdev_notdef(),			/* 33 */
	cdev_notdef(),			/* 34 */
	cdev_notdef(),			/* 35 */
	cdev_tty_init(NCOM,com),	/* 36: NS16x50 compatible ports */
	cdev_lpt_init(NLPT,lpt),	/* 37: parallel printer */
	cdev_notdef(),			/* 38 */
	cdev_notdef(),			/* 39 */
	cdev_notdef(),			/* 40 */
	cdev_notdef(),			/* 41 */
	cdev_notdef(),			/* 42: SMD disk */
	cdev_notdef(),			/* 43 */
	cdev_video_init(NVIDEO,video),	/* 44: generic video I/O */
	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 */
#ifdef USER_PCICONF
	cdev_pci_init(NPCI,pci),	/* 52: PCI user */
#else
	cdev_notdef(),			/* 52 */
#endif
	cdev_notdef(),			/* 53 */
	cdev_disk_init(NFD,fd),		/* 54: floppy disk */
	cdev_notdef(),			/* 55 */
	cdev_notdef(),			/* 56 */
	cdev_notdef(),			/* 57 */
	cdev_disk_init(NCD,cd),		/* 58: SCSI CD-ROM */
	cdev_notdef(),			/* 59 */
	cdev_uk_init(NUK,uk),		/* 60: SCSI unknown */
	cdev_disk_init(NRD,rd),		/* 61: memory disk */
	cdev_notdef(),			/* 62 */
	cdev_notdef(),			/* 63 */
	cdev_notdef(),			/* 64: /dev/cgeight */
	cdev_notdef(),			/* 65 */
	cdev_notdef(),			/* 66 */
	cdev_notdef(),			/* 67 */
	cdev_midi_init(NMIDI,midi),	/* 68: /dev/rmidi */
	cdev_audio_init(NAUDIO,audio),	/* 69: /dev/audio */
	cdev_openprom_init(1,openprom),	/* 70: /dev/openprom */
	cdev_tty_init(NMTTY,mtty),	/* 71: magma serial ports */
	cdev_gen_init(NMBPP,mbpp),	/* 72: magma parallel ports */
	cdev_pf_init(NPF,pf),		/* 73: packet filter */
	cdev_notdef(),			/* 74: ALTQ (deprecated) */
	cdev_notdef(),			/* 75: was: /dev/crypto */
	cdev_ksyms_init(NKSYMS,ksyms),	/* 76 *: Kernel symbols device */
	cdev_tty_init(NSABTTY,sabtty),	/* 77: sab82532 serial ports */
	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 multiplexor */
	cdev_notdef(),			/* 82 */
	cdev_notdef(),			/* 83 */
	cdev_notdef(),			/* 84 */
	cdev_notdef(),			/* 85 */
	cdev_notdef(),			/* 86 */
	cdev_drm_init(NDRM,drm),	/* 87: drm */
	cdev_notdef(),			/* 88 */
	cdev_notdef(),			/* 89 */
	cdev_usb_init(NUSB,usb),	/* 90: USB controller */
	cdev_usbdev_init(NUHID,uhid),	/* 91: USB generic HID */
	cdev_usbdev_init(NUGEN,ugen),	/* 92: USB generic driver */
	cdev_ulpt_init(NULPT,ulpt),	/* 93: USB printers */
	cdev_notdef(),			/* 94 */
	cdev_tty_init(NUCOM,ucom),	/* 95: USB tty */
	cdev_notdef(),			/* 96: was USB scanners */
	cdev_notdef(),			/* 97 */
	cdev_notdef(),			/* 98 */
	cdev_notdef(),			/* 99 */
	cdev_notdef(),			/* 100 */
	cdev_notdef(),			/* 101 */
	cdev_notdef(),			/* 102 */
	cdev_notdef(),			/* 103 */
	cdev_notdef(),			/* 104 */
	cdev_bpf_init(NBPFILTER,bpf),	/* 105: packet filter */
	cdev_notdef(),			/* 106 */
	cdev_bpp_init(NBPP,bpp),	/* 107: on-board parallel port */
	cdev_tty_init(NSTTY,stty),	/* 108: spif serial ports */
	cdev_gen_init(NSBPP,sbpp),	/* 109: spif parallel ports */
	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 data source */
	cdev_bio_init(NBIO,bio),	/* 120: ioctl tunnel */
	cdev_notdef(),			/* 121 was: RAIDframe disk driver */
	cdev_tty_init(NPCONS,pcons),	/* 122: PROM console */
	cdev_ptm_init(NPTY,ptm),	/* 123: pseudo-tty ptm device */
	cdev_hotplug_init(NHOTPLUG,hotplug), /* 124: devices hot plugging */
	cdev_tty_init(NVCONS,vcons),	/* 125: virtual console */
	cdev_tty_init(NSBBC,sbbc),	/* 126: SBBC console */
	cdev_tty_init(NVCCTTY,vcctty),	/* 127: virtual console concentrator */
	cdev_vscsi_init(NVSCSI,vscsi),	/* 128: vscsi */
	cdev_notdef(),
	cdev_disk_init(1,diskmap),	/* 130: disk mapper */
	cdev_pppx_init(NPPPX,pppx),	/* 131: pppx */
	cdev_gen_init(NVLDCP,vldcp),	/* 132: vldcp */
	cdev_vdsp_init(NVDSP,vdsp),	/* 133: vdsp */
	cdev_fuse_init(NFUSE,fuse),	/* 134: fuse */
};
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(void)
{
	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 */	NODEV,
	/* 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 */	NODEV,
	/* 26 */	12,
	/* 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 */	NODEV,
	/* 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 */	5,
	/* 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 */	NODEV,
	/*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 */	25,
	/*122 */	NODEV,
};
int nchrtoblktbl = nitems(chrtoblktbl);


code:
/*	$OpenBSD: cpu.c,v 1.63 2014/11/16 12:30:59 deraadt Exp $	*/
/*	$NetBSD: cpu.c,v 1.13 2001/05/26 21:27:15 chs 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/device.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <dev/rndvar.h>

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/reg.h>
#include <machine/trap.h>
#include <machine/hypervisor.h>
#include <machine/openfirm.h>
#include <machine/pmap.h>
#include <machine/sparc64.h>

#include <sparc64/sparc64/cache.h>

#include <sparc64/dev/starfire.h>

/* This is declared here so that you must include a CPU for the cache code. */
struct cacheinfo cacheinfo = {
	us_dcache_flush_page
};

void (*cpu_start_clock)(void);

/* Linked list of all CPUs in system. */
struct cpu_info *cpus = NULL;

struct cpu_info *alloc_cpuinfo(struct mainbus_attach_args *);

/* The following are used externally (sysctl_hw). */
char	machine[] = MACHINE;		/* from <machine/param.h> */
char	cpu_model[100];

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

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

void cpu_init(struct cpu_info *ci);
void cpu_hatch(void);

int sparc64_cpuspeed(int *);

int hummingbird_div(uint64_t);
uint64_t hummingbird_estar_mode(int);
void hummingbird_enable_self_refresh(void);
void hummingbird_disable_self_refresh(void);
void hummingbird_set_refresh_count(int, int);
void hummingbird_setperf(int);
void hummingbird_init(struct cpu_info *ci);

#define	IU_IMPL(v)	((((u_int64_t)(v))&VER_IMPL) >> VER_IMPL_SHIFT)
#define	IU_VERS(v)	((((u_int64_t)(v))&VER_MASK) >> VER_MASK_SHIFT)

struct cpu_info *
alloc_cpuinfo(struct mainbus_attach_args *ma)
{
	paddr_t pa0, pa;
	vaddr_t va, va0;
	vsize_t sz = 8 * PAGE_SIZE;
	int portid;
	struct cpu_info *cpi, *ci;
	extern paddr_t cpu0paddr;

	portid = getpropint(ma->ma_node, "upa-portid", -1);
	if (portid == -1)
		portid = getpropint(ma->ma_node, "portid", -1);
	if (portid == -1)
		portid = getpropint(ma->ma_node, "cpuid", -1);
	if (portid == -1 && ma->ma_nreg > 0)
		portid = (ma->ma_reg[0].ur_paddr >> 32) & 0x0fffffff;
	if (portid == -1)
		panic("alloc_cpuinfo: portid");

	for (cpi = cpus; cpi != NULL; cpi = cpi->ci_next)
		if (cpi->ci_upaid == portid)
			return cpi;

	va = uvm_km_valloc_align(kernel_map, sz, 8 * PAGE_SIZE, 0);
	if (va == 0)
		panic("alloc_cpuinfo: no virtual space");
	va0 = va;

	pa0 = cpu0paddr;
	cpu0paddr += sz;

	for (pa = pa0; pa < cpu0paddr; pa += PAGE_SIZE, va += PAGE_SIZE)
		pmap_kenter_pa(va, pa, PROT_READ | PROT_WRITE);

	pmap_update(pmap_kernel());

	cpi = (struct cpu_info *)(va0 + CPUINFO_VA - INTSTACK);

	memset((void *)va0, 0, sz);

	/*
	 * Initialize cpuinfo structure.
	 *
	 * Arrange pcb, idle stack and interrupt stack in the same
	 * way as is done for the boot CPU in pmap.c.
	 */
	cpi->ci_next = NULL;
	cpi->ci_curproc = NULL;
	cpi->ci_number = ncpus++;
	cpi->ci_upaid = portid;
	cpi->ci_fpproc = NULL;
#ifdef MULTIPROCESSOR
	cpi->ci_spinup = cpu_hatch;				/* XXX */
#else
	cpi->ci_spinup = NULL;
#endif

	cpi->ci_initstack = cpi;
	cpi->ci_paddr = pa0;
#ifdef SUN4V
	cpi->ci_mmfsa = pa0;
#endif
	cpi->ci_self = cpi;
	cpi->ci_node = ma->ma_node;

	sched_init_cpu(cpi);

	/*
	 * Finally, add itself to the list of active cpus.
	 */
	for (ci = cpus; ci->ci_next != NULL; ci = ci->ci_next)
		;
	ci->ci_next = cpi;
	return (cpi);
}

int
cpu_match(parent, vcf, aux)
	struct device *parent;
	void *vcf;
	void *aux;
{
	struct mainbus_attach_args *ma = aux;
#ifndef MULTIPROCESSOR
	int portid;
#endif
	char buf[32];

	if (OF_getprop(ma->ma_node, "device_type", buf, sizeof(buf)) <= 0 ||
	    strcmp(buf, "cpu") != 0)
		return (0);

#ifndef MULTIPROCESSOR
	/*
	 * On singleprocessor kernels, only match the CPU we're
	 * running on.
	 */
	portid = getpropint(ma->ma_node, "upa-portid", -1);
	if (portid == -1)
		portid = getpropint(ma->ma_node, "portid", -1);
	if (portid == -1)
		portid = getpropint(ma->ma_node, "cpuid", -1);
	if (portid == -1 && ma->ma_nreg > 0)
		portid = (ma->ma_reg[0].ur_paddr >> 32) & 0xff;
	if (portid == -1)
		return (0);

	if (portid != cpus->ci_upaid)
		return (0);
#endif

	return (1);
}

/*
 * 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, dev, aux)
	struct device *parent;
	struct device *dev;
	void *aux;
{
	int node;
	u_int clk;
	int impl, vers;
	struct mainbus_attach_args *ma = aux;
	struct cpu_info *ci;
	const char *sep;
	register int i, l;
	u_int64_t ver = 0;
	extern u_int64_t cpu_clockrate[];

	if (CPU_ISSUN4U || CPU_ISSUN4US)
		ver = getver();
	impl = IU_IMPL(ver);
	vers = IU_VERS(ver);

	/* tell them what we have */
	if (strcmp(parent->dv_cfdata->cf_driver->cd_name, "core") == 0)
		node = OF_parent(ma->ma_node);
	else
		node = ma->ma_node;

	/*
	 * Allocate cpu_info structure if needed.
	 */
	ci = alloc_cpuinfo(ma);
	ci->ci_node = ma->ma_node;

	clk = getpropint(node, "clock-frequency", 0);
	if (clk == 0) {
		/*
		 * Try to find it in the OpenPROM root...
		 */
		clk = getpropint(findroot(), "clock-frequency", 0);
	}
	if (clk) {
		cpu_clockrate[0] = clk; /* Tell OS what frequency we run on */
		cpu_clockrate[1] = clk/1000000;
	}
	snprintf(cpu_model, sizeof cpu_model, "%s (rev %d.%d) @ %s MHz",
	    ma->ma_name, vers >> 4, vers & 0xf, clockfreq(clk));
	printf(": %s\n", cpu_model);

	cpu_cpuspeed = sparc64_cpuspeed;

	if (ci->ci_upaid == cpu_myid())
		cpu_init(ci);

	cacheinfo.c_physical = 1; /* Dunno... */
	cacheinfo.c_split = 1;
	l = getpropint(node, "icache-line-size", 0);
	if (l == 0)
		l = getpropint(node, "l1-icache-line-size", 0);
	cacheinfo.ic_linesize = l;
	for (i = 0; (1 << i) < l && l; i++)
		/* void */;
	if ((1 << i) != l && l)
		panic("bad icache line size %d", l);
	cacheinfo.ic_l2linesize = i;
	cacheinfo.ic_totalsize = getpropint(node, "icache-size", 0);
	if (cacheinfo.ic_totalsize == 0)
		cacheinfo.ic_totalsize = getpropint(node, "l1-icache-size", 0);
	if (cacheinfo.ic_totalsize == 0)
		cacheinfo.ic_totalsize = l *
		    getpropint(node, "icache-nlines", 64) *
		    getpropint(node, "icache-associativity", 1);

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

	l = getpropint(node, "ecache-line-size", 0);
	if (l == 0)
		l = getpropint(node, "l2-cache-line-size", 0);
	cacheinfo.ec_linesize = l;
	for (i = 0; (1 << i) < l && l; i++)
		/* void */;
	if ((1 << i) != l && l)
		panic("bad ecache line size %d", l);
	cacheinfo.ec_l2linesize = i;
	cacheinfo.ec_totalsize = getpropint(node, "ecache-size", 0);
	if (cacheinfo.ec_totalsize == 0)
		cacheinfo.ec_totalsize = getpropint(node, "l2-cache-size", 0);
	if (cacheinfo.ec_totalsize == 0)
		cacheinfo.ec_totalsize = l *
		    getpropint(node, "ecache-nlines", 32768) *
		    getpropint(node, "ecache-associativity", 1);

	/*
	 * XXX - The following will have to do until
	 * we have per-cpu cache handling.
	 */
	cacheinfo.c_l2linesize =
		min(cacheinfo.ic_l2linesize,
		    cacheinfo.dc_l2linesize);
	cacheinfo.c_linesize =
		min(cacheinfo.ic_linesize,
		    cacheinfo.dc_linesize);
	cacheinfo.c_totalsize =
		cacheinfo.ic_totalsize +
		cacheinfo.dc_totalsize;

	if (cacheinfo.c_totalsize == 0)
		return;

	sep = " ";
	printf("%s: physical", dev->dv_xname);
	if (cacheinfo.ic_totalsize > 0) {
		printf("%s%ldK instruction (%ld b/l)", sep,
		       (long)cacheinfo.ic_totalsize/1024,
		       (long)cacheinfo.ic_linesize);
		sep = ", ";
	}
	if (cacheinfo.dc_totalsize > 0) {
		printf("%s%ldK data (%ld b/l)", sep,
		       (long)cacheinfo.dc_totalsize/1024,
		       (long)cacheinfo.dc_linesize);
		sep = ", ";
	}
	if (cacheinfo.ec_totalsize > 0) {
		printf("%s%ldK external (%ld b/l)", sep,
		       (long)cacheinfo.ec_totalsize/1024,
		       (long)cacheinfo.ec_linesize);
	}

#ifndef SMALL_KERNEL
	if (impl == IMPL_HUMMINGBIRD)
		hummingbird_init(ci);
#endif

	printf("\n");
	cache_enable();
}

int
cpu_myid(void)
{
	char buf[32];
	int impl;

#ifdef SUN4V
	if (CPU_ISSUN4V) {
		uint64_t myid;

		hv_cpu_myid(&myid);
		return myid;
	}
#endif

	if (OF_getprop(findroot(), "name", buf, sizeof(buf)) > 0 &&
	    strcmp(buf, "SUNW,Ultra-Enterprise-10000") == 0)
		return lduwa(0x1fff40000d0UL, ASI_PHYS_NON_CACHED);

	impl = (getver() & VER_IMPL) >> VER_IMPL_SHIFT;
	switch (impl) {
	case IMPL_OLYMPUS_C:
	case IMPL_JUPITER:
		return CPU_JUPITERID;
	case IMPL_CHEETAH:
	case IMPL_CHEETAH_PLUS:
	case IMPL_JAGUAR:
	case IMPL_PANTHER:
		return CPU_FIREPLANEID;
	default:
		return CPU_UPAID;
	}
}

void
cpu_init(struct cpu_info *ci)
{
#ifdef SUN4V
	paddr_t pa = ci->ci_paddr;
	int err;
#endif

	if (CPU_ISSUN4U || CPU_ISSUN4US) {
		tick_enable();
		return;
	}

#ifdef SUN4V
#define MONDO_QUEUE_SIZE	32
#define QUEUE_ENTRY_SIZE	64

	pa += CPUINFO_VA - INTSTACK;
	pa += PAGE_SIZE;

	ci->ci_cpumq = pa;
	err = hv_cpu_qconf(CPU_MONDO_QUEUE, ci->ci_cpumq, MONDO_QUEUE_SIZE);
	if (err != H_EOK)
		panic("Unable to set cpu mondo queue: %d", err);
	pa += MONDO_QUEUE_SIZE * QUEUE_ENTRY_SIZE;

	ci->ci_devmq = pa;
	err = hv_cpu_qconf(DEVICE_MONDO_QUEUE, ci->ci_devmq, MONDO_QUEUE_SIZE);
	if (err != H_EOK)
		panic("Unable to set device mondo queue: %d", err);
	pa += MONDO_QUEUE_SIZE * QUEUE_ENTRY_SIZE;

	ci->ci_mondo = pa;
	pa += 64;

	ci->ci_cpuset = pa;
	pa += 64;
#endif
}

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

int
sparc64_cpuspeed(int *freq)
{
	extern u_int64_t cpu_clockrate[];

	*freq = cpu_clockrate[1];
	return (0);
}

#ifndef SMALL_KERNEL

/*
 * Hummingbird (UltraSPARC-IIe) has a clock control unit that enables
 * Energy Star mode.  This only works in combination with unbuffered
 * DIMMs so it is not supported on all machines with UltraSPARC-IIe
 * CPUs.
 */

/* Memory_Control_0 (MC0) register. */
#define HB_MC0			0x1fe0000f010ULL
#define  HB_MC0_SELF_REFRESH		0x00010000
#define  HB_MC0_REFRESH_COUNT_MASK	0x00007f00
#define  HB_MC0_REFRESH_COUNT_SHIFT	8
#define  HB_MC0_REFRESH_COUNT(reg) \
  (((reg) & HB_MC0_REFRESH_COUNT_MASK) >> HB_MC0_REFRESH_COUNT_SHIFT)
#define  HB_MC0_REFRESH_CLOCKS_PER_COUNT	64ULL
#define  HB_MC0_REFRESH_INTERVAL	7800ULL

/* Energy Star register. */
#define HB_ESTAR		0x1fe0000f080ULL
#define  HB_ESTAR_MODE_MASK		0x00000007
#define  HB_ESTAR_MODE_DIV_1		0x00000000
#define  HB_ESTAR_MODE_DIV_2		0x00000001
#define  HB_ESTAR_MODE_DIV_4		0x00000003
#define  HB_ESTAR_MODE_DIV_6		0x00000002
#define  HB_ESTAR_MODE_DIV_8		0x00000004
#define  HB_ESTAR_NUM_MODES		5

int hummingbird_divisors[HB_ESTAR_NUM_MODES];

int
hummingbird_div(uint64_t estar_mode)
{
	switch(estar_mode) {
	case HB_ESTAR_MODE_DIV_1:
		return 1;
	case HB_ESTAR_MODE_DIV_2:
		return 2;
	case HB_ESTAR_MODE_DIV_4:
		return 4;
	case HB_ESTAR_MODE_DIV_6:
		return 6;
	case HB_ESTAR_MODE_DIV_8:
		return 8;
	default:
		panic("bad E-Star mode");
	}
}

uint64_t
hummingbird_estar_mode(int div)
{
	switch(div) {
	case 1:
		return HB_ESTAR_MODE_DIV_1;
	case 2:
		return HB_ESTAR_MODE_DIV_2;
	case 4:
		return HB_ESTAR_MODE_DIV_4;
	case 6:
		return HB_ESTAR_MODE_DIV_6;
	case 8:
		return HB_ESTAR_MODE_DIV_8;
	default:
		panic("bad clock divisor");
	}
}

void
hummingbird_enable_self_refresh(void)
{
	uint64_t reg;

	reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED);
	reg |= HB_MC0_SELF_REFRESH;
	stxa(HB_MC0, ASI_PHYS_NON_CACHED, reg);
	reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED);
}

void
hummingbird_disable_self_refresh(void)
{
	uint64_t reg;

	reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED);
	reg &= ~HB_MC0_SELF_REFRESH;
	stxa(HB_MC0, ASI_PHYS_NON_CACHED, reg);
	reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED);
}

void
hummingbird_set_refresh_count(int div, int new_div)
{
	extern u_int64_t cpu_clockrate[];
	uint64_t count, new_count;
	uint64_t delta;
	uint64_t reg;

	reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED);
	count = HB_MC0_REFRESH_COUNT(reg);
	new_count = (HB_MC0_REFRESH_INTERVAL * cpu_clockrate[0]) /
		(HB_MC0_REFRESH_CLOCKS_PER_COUNT * new_div * 1000000000);
	reg &= ~HB_MC0_REFRESH_COUNT_MASK;
	reg |= (new_count << HB_MC0_REFRESH_COUNT_SHIFT);
	stxa(HB_MC0, ASI_PHYS_NON_CACHED, reg);
	reg = ldxa(HB_MC0, ASI_PHYS_NON_CACHED);

	if (new_div > div && (reg & HB_MC0_SELF_REFRESH) == 0) {
		delta = HB_MC0_REFRESH_CLOCKS_PER_COUNT *
		    ((count + new_count) * 1000000UL * div) / cpu_clockrate[0];
		delay(delta + 1);
	}
}

void
hummingbird_setperf(int level)
{
	extern u_int64_t cpu_clockrate[];
	uint64_t estar_mode, new_estar_mode;
	uint64_t reg, s;
	int div, new_div, i;

	new_estar_mode = HB_ESTAR_MODE_DIV_1;
	for (i = 0; i < HB_ESTAR_NUM_MODES && hummingbird_divisors[i]; i++) {
		if (level <= 100 / hummingbird_divisors[i])
			new_estar_mode =
			    hummingbird_estar_mode(hummingbird_divisors[i]);
	}

	reg = ldxa(HB_ESTAR, ASI_PHYS_NON_CACHED);
	estar_mode = reg & HB_ESTAR_MODE_MASK;
	if (estar_mode == new_estar_mode)
		return;

	reg &= ~HB_ESTAR_MODE_MASK;
	div = hummingbird_div(estar_mode);
	new_div = hummingbird_div(new_estar_mode);

	s = intr_disable();
	if (estar_mode == HB_ESTAR_MODE_DIV_1 &&
	    new_estar_mode == HB_ESTAR_MODE_DIV_2) {
		hummingbird_set_refresh_count(1, 2);
		stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_2);
		delay(1);
		hummingbird_enable_self_refresh();
	} else if (estar_mode == HB_ESTAR_MODE_DIV_2 &&
	    new_estar_mode == HB_ESTAR_MODE_DIV_1) {
		hummingbird_disable_self_refresh();
		stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_1);
		delay(1);
		hummingbird_set_refresh_count(2, 1);
	} else if (estar_mode == HB_ESTAR_MODE_DIV_1) {
		/*
		 * Transition to 1/2 speed first, then to
		 * lower speed.
		 */
		hummingbird_set_refresh_count(1, 2);
		stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_2);
		delay(1);
		hummingbird_enable_self_refresh();

		hummingbird_set_refresh_count(2, new_div);
		stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | new_estar_mode);
		delay(1);
	} else if (new_estar_mode == HB_ESTAR_MODE_DIV_1) {
		/*
		 * Transition to 1/2 speed first, then to
		 * full speed.
		 */
		stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_2);
		delay(1);
		hummingbird_set_refresh_count(div, 2);

		hummingbird_disable_self_refresh();
		stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | HB_ESTAR_MODE_DIV_1);
		delay(1);
		hummingbird_set_refresh_count(2, 1);
	} else if (div < new_div) {
		hummingbird_set_refresh_count(div, new_div);
		stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | new_estar_mode);
		delay(1);
	} else if (div > new_div) {
		stxa(HB_ESTAR, ASI_PHYS_NON_CACHED, reg | new_estar_mode);
		delay(1);
		hummingbird_set_refresh_count(div, new_div);
	}
	cpu_clockrate[1] = cpu_clockrate[0] / (new_div * 1000000);
	intr_restore(s);
}

void
hummingbird_init(struct cpu_info *ci)
{
	/*
	 * The "clock-divisors" property seems to indicate which
	 * frequency scalings are supported on a particular model.
	 */
	if (OF_getprop(ci->ci_node, "clock-divisors",
	    &hummingbird_divisors, sizeof(hummingbird_divisors)) <= 0)
		return;

	cpu_setperf = hummingbird_setperf;
}
#endif

#ifdef MULTIPROCESSOR
void cpu_mp_startup(void);

void
cpu_boot_secondary_processors(void)
{
	struct cpu_info *ci;
	int cpuid, i;
	char buf[32];

	if (OF_getprop(findroot(), "name", buf, sizeof(buf)) > 0 &&
	    strcmp(buf, "SUNW,Ultra-Enterprise-10000") == 0) {
		for (ci = cpus; ci != NULL; ci = ci->ci_next)
			ci->ci_itid = STARFIRE_UPAID2HWMID(ci->ci_upaid);
	} else {
		for (ci = cpus; ci != NULL; ci = ci->ci_next)
			ci->ci_itid = ci->ci_upaid;
	}

	for (ci = cpus; ci != NULL; ci = ci->ci_next) {
		if (ci->ci_upaid == cpu_myid())
			continue;
		ci->ci_randseed = (arc4random() & 0x7fffffff) + 1;

		if (CPU_ISSUN4V)
			cpuid = ci->ci_upaid;
		else
			cpuid = getpropint(ci->ci_node, "cpuid", -1);

		if (OF_test("SUNW,start-cpu-by-cpuid") == 0) {
			prom_start_cpu_by_cpuid(cpuid,
			    (void *)cpu_mp_startup, ci->ci_paddr);
		} else {
			prom_start_cpu(ci->ci_node,
			    (void *)cpu_mp_startup, ci->ci_paddr);
		}

		for (i = 0; i < 2000; i++) {
			sparc_membar(Sync);
			if (ci->ci_flags & CPUF_RUNNING)
				break;
			delay(10000);
		}
	}
}

void
cpu_hatch(void)
{
	struct cpu_info *ci = curcpu();
	int s;

	cpu_init(ci);

	ci->ci_flags |= CPUF_RUNNING;
	sparc_membar(Sync);

	s = splhigh();
	nanouptime(&ci->ci_schedstate.spc_runtime);
	splx(s);

	cpu_start_clock();

	SCHED_LOCK(s);
	cpu_switchto(NULL, sched_chooseproc());
}
#endif

void
need_resched(struct cpu_info *ci)
{
	ci->ci_want_resched = 1;

	/* There's a risk we'll be called before the idle threads start */
	if (ci->ci_curproc) {
		aston(ci->ci_curproc);
		if (ci != curcpu())
			cpu_unidle(ci);
	}
}

/*
 * Idle loop.
 *
 * We disable and reenable the interrupts in every cycle of the idle loop.
 * Since hv_cpu_yield doesn't actually reenable interrupts, it just wakes
 * up if an interrupt would have happened, but it's our responsibility to
 * unblock interrupts.
 */

void
cpu_idle_enter(void)
{
	if (CPU_ISSUN4V) {
		sparc_wrpr(pstate, sparc_rdpr(pstate) & ~PSTATE_IE, 0);
	}
}

void
cpu_idle_cycle(void)
{
#ifdef SUN4V
	if (CPU_ISSUN4V) {
		hv_cpu_yield();
		sparc_wrpr(pstate, sparc_rdpr(pstate) | PSTATE_IE, 0);
		sparc_wrpr(pstate, sparc_rdpr(pstate) & ~PSTATE_IE, 0);
	}
#endif

	/*
	 * On processors with multiple threads we simply force a
	 * thread switch.  Using the sleep instruction seems to work
	 * just as well as using the suspend instruction and makes the
	 * code a little bit less complicated.
	 */
	__asm volatile(
		"999:	nop					\n"
		"	.section .sun4u_mtp_patch, \"ax\"	\n"
		"	.word	999b				\n"
		"	.word	0x81b01060	! sleep		\n"
		"	.previous				\n"
		: : : "memory");
}

void
cpu_idle_leave(void)
{
	if (CPU_ISSUN4V) {
		sparc_wrpr(pstate, sparc_rdpr(pstate) | PSTATE_IE, 0);
	}
}