xref: /plan9/sys/src/9/teg2/clock.c (revision 35ef7fca89ece0f426d92c2ffdbb91d2c786f969)
13de6a9c0SDavid du Colombier /*
23de6a9c0SDavid du Colombier  * cortex-a clocks; excludes tegra 2 SoC clocks
33de6a9c0SDavid du Colombier  *
43de6a9c0SDavid du Colombier  * cortex-a processors include private `global' and local timers
53de6a9c0SDavid du Colombier  * at soc.scu + 0x200 (global) and + 0x600 (local).
63de6a9c0SDavid du Colombier  * the global timer is a single count-up timer shared by all cores
73de6a9c0SDavid du Colombier  * but with per-cpu comparator and auto-increment registers.
83de6a9c0SDavid du Colombier  * a local count-down timer can be used as a watchdog.
93de6a9c0SDavid du Colombier  *
103de6a9c0SDavid du Colombier  * v7 arch provides a 32-bit count-up cycle counter (at about 1GHz in our case)
113de6a9c0SDavid du Colombier  * but it's unsuitable as our source of fastticks, because it stops advancing
123de6a9c0SDavid du Colombier  * when the cpu is suspended by WFI.
133de6a9c0SDavid du Colombier  */
143de6a9c0SDavid du Colombier #include "u.h"
153de6a9c0SDavid du Colombier #include "../port/lib.h"
163de6a9c0SDavid du Colombier #include "mem.h"
173de6a9c0SDavid du Colombier #include "dat.h"
183de6a9c0SDavid du Colombier #include "fns.h"
193de6a9c0SDavid du Colombier #include "arm.h"
203de6a9c0SDavid du Colombier 
213de6a9c0SDavid du Colombier enum {
223de6a9c0SDavid du Colombier 	Debug		= 0,
233de6a9c0SDavid du Colombier 
243de6a9c0SDavid du Colombier 	Basetickfreq	= Mhz,			/* soc.µs rate in Hz */
253de6a9c0SDavid du Colombier 	/* the local timers seem to run at half the expected rate */
263de6a9c0SDavid du Colombier 	Clockfreqbase	= 250*Mhz / 2,	/* private timer rate (PERIPHCLK/2) */
273de6a9c0SDavid du Colombier 	Tcycles		= Clockfreqbase / HZ,	/* cycles per clock tick */
283de6a9c0SDavid du Colombier 
293de6a9c0SDavid du Colombier 	MinPeriod	= Tcycles / 100,
303de6a9c0SDavid du Colombier 	MaxPeriod	= Tcycles,
313de6a9c0SDavid du Colombier 
323de6a9c0SDavid du Colombier 	Dogtimeout	= Dogsectimeout * Clockfreqbase,
333de6a9c0SDavid du Colombier };
343de6a9c0SDavid du Colombier 
353de6a9c0SDavid du Colombier typedef struct Ltimer Ltimer;
363de6a9c0SDavid du Colombier typedef struct Pglbtmr Pglbtmr;
373de6a9c0SDavid du Colombier typedef struct Ploctmr Ploctmr;
383de6a9c0SDavid du Colombier 
393de6a9c0SDavid du Colombier /*
403de6a9c0SDavid du Colombier  * cortex-a private-intr local timer registers.  all cpus see their
413de6a9c0SDavid du Colombier  * own local timers at the same base address.
423de6a9c0SDavid du Colombier  */
433de6a9c0SDavid du Colombier struct Ltimer {
443de6a9c0SDavid du Colombier 	ulong	load;		/* new value + 1 */
453de6a9c0SDavid du Colombier 	ulong	cnt;		/* counts down */
463de6a9c0SDavid du Colombier 	ulong	ctl;
473de6a9c0SDavid du Colombier 	ulong	isr;
483de6a9c0SDavid du Colombier 
493de6a9c0SDavid du Colombier 	/* watchdog only */
503de6a9c0SDavid du Colombier 	ulong	wdrst;
513de6a9c0SDavid du Colombier 	ulong	wddis;		/* wo */
523de6a9c0SDavid du Colombier 
533de6a9c0SDavid du Colombier 	ulong	_pad0[2];
543de6a9c0SDavid du Colombier };
553de6a9c0SDavid du Colombier struct Ploctmr {
563de6a9c0SDavid du Colombier 	Ltimer	loc;
573de6a9c0SDavid du Colombier 	Ltimer	wd;
583de6a9c0SDavid du Colombier };
593de6a9c0SDavid du Colombier 
603de6a9c0SDavid du Colombier enum {
613de6a9c0SDavid du Colombier 	/* ctl bits */
623de6a9c0SDavid du Colombier 	Tmrena	= 1<<0,		/* timer enabled */
633de6a9c0SDavid du Colombier 	Wdogena = Tmrena,	/* watchdog enabled */
643de6a9c0SDavid du Colombier 	Xreload	= 1<<1,		/* reload on intr; periodic interrupts */
653de6a9c0SDavid du Colombier 	Tintena	= 1<<2,		/* enable irq 29 at cnt==0 (30 for watchdog) */
663de6a9c0SDavid du Colombier 	Wdog	= 1<<3,		/* watchdog, not timer, mode */
673de6a9c0SDavid du Colombier 	Xsclrshift = 8,
683de6a9c0SDavid du Colombier 	Xsclrmask = MASK(8),
693de6a9c0SDavid du Colombier 
703de6a9c0SDavid du Colombier 	/* isr bits */
713de6a9c0SDavid du Colombier 	Xisrclk	= 1<<0,		/* write to clear */
723de6a9c0SDavid du Colombier 
733de6a9c0SDavid du Colombier 	/* wdrst bits */
743de6a9c0SDavid du Colombier 	Wdrst	= 1<<0,
753de6a9c0SDavid du Colombier 
763de6a9c0SDavid du Colombier 	/* wddis values */
773de6a9c0SDavid du Colombier 	Wdon	= 1,
783de6a9c0SDavid du Colombier 	Wdoff1	= 0x12345678,	/* send these two to switch to timer mode */
793de6a9c0SDavid du Colombier 	Wdoff2	= 0x87654321,
803de6a9c0SDavid du Colombier };
813de6a9c0SDavid du Colombier 
823de6a9c0SDavid du Colombier /* cortex-a private-intr globl timer registers */
833de6a9c0SDavid du Colombier struct Pglbtmr {
843de6a9c0SDavid du Colombier 	ulong	cnt[2];		/* counts up; little-endian uvlong */
853de6a9c0SDavid du Colombier 	ulong	ctl;
863de6a9c0SDavid du Colombier 	ulong	isr;
873de6a9c0SDavid du Colombier 	ulong	cmp[2];		/* little-endian uvlong */
883de6a9c0SDavid du Colombier 	ulong	inc;
893de6a9c0SDavid du Colombier };
903de6a9c0SDavid du Colombier 
913de6a9c0SDavid du Colombier enum {
923de6a9c0SDavid du Colombier 	/* unique ctl bits (otherwise see X* above) */
933de6a9c0SDavid du Colombier 	Gcmp	= 1<<1,
943de6a9c0SDavid du Colombier //	Gtintena= 1<<2,		/* enable irq 27 */
953de6a9c0SDavid du Colombier 	Gincr	= 1<<3,
963de6a9c0SDavid du Colombier };
973de6a9c0SDavid du Colombier 
983de6a9c0SDavid du Colombier /*
99*35ef7fcaSDavid du Colombier  * until 5[cl] inline vlong ops, avoid them where possible,
1003de6a9c0SDavid du Colombier  * they are currently slow function calls.
1013de6a9c0SDavid du Colombier  */
102*35ef7fcaSDavid du Colombier typedef union Vlong Vlong;
103*35ef7fcaSDavid du Colombier union Vlong {
1043de6a9c0SDavid du Colombier 	uvlong	uvl;
1053de6a9c0SDavid du Colombier 	struct {			/* little-endian */
1063de6a9c0SDavid du Colombier 		ulong	low;
1073de6a9c0SDavid du Colombier 		ulong	high;
1083de6a9c0SDavid du Colombier 	};
1093de6a9c0SDavid du Colombier };
1103de6a9c0SDavid du Colombier 
1113de6a9c0SDavid du Colombier static int fired;
1123de6a9c0SDavid du Colombier static int ticking[MAXMACH];
1133de6a9c0SDavid du Colombier 
1143de6a9c0SDavid du Colombier /* no lock is needed to update our local timer.  splhi keeps it tight. */
1153de6a9c0SDavid du Colombier static void
setltimer(Ltimer * tn,ulong ticks)1163de6a9c0SDavid du Colombier setltimer(Ltimer *tn, ulong ticks)
1173de6a9c0SDavid du Colombier {
1183de6a9c0SDavid du Colombier 	int s;
1193de6a9c0SDavid du Colombier 
1203de6a9c0SDavid du Colombier 	assert(ticks <= Clockfreqbase);
1213de6a9c0SDavid du Colombier 	s = splhi();
1223de6a9c0SDavid du Colombier 	tn->load = ticks - 1;
1233de6a9c0SDavid du Colombier 	coherence();
1243de6a9c0SDavid du Colombier 	tn->ctl = Tmrena | Tintena | Xreload;
1253de6a9c0SDavid du Colombier 	coherence();
1263de6a9c0SDavid du Colombier 	splx(s);
1273de6a9c0SDavid du Colombier }
1283de6a9c0SDavid du Colombier 
1293de6a9c0SDavid du Colombier static void
ckstuck(int cpu,long myticks,long histicks)1303de6a9c0SDavid du Colombier ckstuck(int cpu, long myticks, long histicks)
1313de6a9c0SDavid du Colombier {
1323de6a9c0SDavid du Colombier 	if (labs(histicks - myticks) > HZ) {
1333de6a9c0SDavid du Colombier //		iprint("cpu%d: clock ticks %ld (vs myticks %ld cpu0 %ld); "
1343de6a9c0SDavid du Colombier //			"apparently stopped\n",
1353de6a9c0SDavid du Colombier //			cpu, histicks, myticks, MACHP(0)->ticks);
1363de6a9c0SDavid du Colombier 		if (!ticking[cpu])
1373de6a9c0SDavid du Colombier 			panic("cpu%d: clock not interrupting", cpu);
1383de6a9c0SDavid du Colombier 	}
1393de6a9c0SDavid du Colombier }
1403de6a9c0SDavid du Colombier 
1413de6a9c0SDavid du Colombier static void
mpclocksanity(void)1423de6a9c0SDavid du Colombier mpclocksanity(void)
1433de6a9c0SDavid du Colombier {
1443de6a9c0SDavid du Colombier 	int cpu, mycpu;
1453de6a9c0SDavid du Colombier 	long myticks, histicks;
1463de6a9c0SDavid du Colombier 
1473de6a9c0SDavid du Colombier 	if (conf.nmach <= 1 || active.exiting || navailcpus == 0)
1483de6a9c0SDavid du Colombier 		return;
1493de6a9c0SDavid du Colombier 
1503de6a9c0SDavid du Colombier 	mycpu = m->machno;
1513de6a9c0SDavid du Colombier 	myticks = m->ticks;
1523de6a9c0SDavid du Colombier 	if (myticks == HZ)
1533de6a9c0SDavid du Colombier 		ticking[mycpu] = 1;
1543de6a9c0SDavid du Colombier 
1553de6a9c0SDavid du Colombier 	if (myticks < 5*HZ)
1563de6a9c0SDavid du Colombier 		return;
1573de6a9c0SDavid du Colombier 
1583de6a9c0SDavid du Colombier 	for (cpu = 0; cpu < navailcpus; cpu++) {
1593de6a9c0SDavid du Colombier 		if (cpu == mycpu)
1603de6a9c0SDavid du Colombier 			continue;
1613de6a9c0SDavid du Colombier 		histicks = MACHP(cpu)->ticks;
1623de6a9c0SDavid du Colombier 		if (myticks == 5*HZ || histicks > 1)
1633de6a9c0SDavid du Colombier 			ckstuck(cpu, myticks, histicks);
1643de6a9c0SDavid du Colombier 	}
1653de6a9c0SDavid du Colombier }
1663de6a9c0SDavid du Colombier 
1673de6a9c0SDavid du Colombier static void
clockintr(Ureg * ureg,void * arg)1683de6a9c0SDavid du Colombier clockintr(Ureg* ureg, void *arg)
1693de6a9c0SDavid du Colombier {
1703de6a9c0SDavid du Colombier 	Ltimer *wd, *tn;
1713de6a9c0SDavid du Colombier 	Ploctmr *lt;
1723de6a9c0SDavid du Colombier 
1733de6a9c0SDavid du Colombier 	lt = (Ploctmr *)arg;
1743de6a9c0SDavid du Colombier 	tn = &lt->loc;
1753de6a9c0SDavid du Colombier 	tn->isr = Xisrclk;
1763de6a9c0SDavid du Colombier 	coherence();
1773de6a9c0SDavid du Colombier 
1783de6a9c0SDavid du Colombier 	timerintr(ureg, 0);
1793de6a9c0SDavid du Colombier 
1803de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
1813de6a9c0SDavid du Colombier 	/* appease the dogs */
1823de6a9c0SDavid du Colombier 	wd = &lt->wd;
1833de6a9c0SDavid du Colombier 	if (wd->cnt == 0 &&
1843de6a9c0SDavid du Colombier 	    (wd->ctl & (Wdog | Wdogena | Tintena)) == (Wdog | Wdogena))
1853de6a9c0SDavid du Colombier 		panic("cpu%d: zero watchdog count but no system reset",
1863de6a9c0SDavid du Colombier 			m->machno);
1873de6a9c0SDavid du Colombier 	wd->load = Dogtimeout - 1;
1883de6a9c0SDavid du Colombier 	coherence();
1893de6a9c0SDavid du Colombier #endif
1903de6a9c0SDavid du Colombier 	SET(wd); USED(wd);
1913de6a9c0SDavid du Colombier 	tegclockintr();
1923de6a9c0SDavid du Colombier 
1933de6a9c0SDavid du Colombier 	mpclocksanity();
1943de6a9c0SDavid du Colombier }
1953de6a9c0SDavid du Colombier 
1963de6a9c0SDavid du Colombier void
clockprod(Ureg * ureg)1973de6a9c0SDavid du Colombier clockprod(Ureg *ureg)
1983de6a9c0SDavid du Colombier {
1993de6a9c0SDavid du Colombier 	Ltimer *tn;
2003de6a9c0SDavid du Colombier 
2013de6a9c0SDavid du Colombier 	timerintr(ureg, 0);
2023de6a9c0SDavid du Colombier 	tegclockintr();
2033de6a9c0SDavid du Colombier 	if (m->machno != 0) {		/* cpu1 gets stuck */
2043de6a9c0SDavid du Colombier 		tn = &((Ploctmr *)soc.loctmr)->loc;
2053de6a9c0SDavid du Colombier 		setltimer(tn, Tcycles);
2063de6a9c0SDavid du Colombier 	}
2073de6a9c0SDavid du Colombier }
2083de6a9c0SDavid du Colombier 
2093de6a9c0SDavid du Colombier static void
clockreset(Ltimer * tn)2103de6a9c0SDavid du Colombier clockreset(Ltimer *tn)
2113de6a9c0SDavid du Colombier {
2123de6a9c0SDavid du Colombier 	if (probeaddr((uintptr)tn) < 0)
2133de6a9c0SDavid du Colombier 		panic("no clock at %#p", tn);
2143de6a9c0SDavid du Colombier 	tn->ctl = 0;
2153de6a9c0SDavid du Colombier 	coherence();
2163de6a9c0SDavid du Colombier }
2173de6a9c0SDavid du Colombier 
2183de6a9c0SDavid du Colombier void
watchdogoff(Ltimer * wd)2193de6a9c0SDavid du Colombier watchdogoff(Ltimer *wd)
2203de6a9c0SDavid du Colombier {
2213de6a9c0SDavid du Colombier 	wd->ctl &= ~Wdogena;
2223de6a9c0SDavid du Colombier 	coherence();
2233de6a9c0SDavid du Colombier 	wd->wddis = Wdoff1;
2243de6a9c0SDavid du Colombier 	coherence();
2253de6a9c0SDavid du Colombier 	wd->wddis = Wdoff2;
2263de6a9c0SDavid du Colombier 	coherence();
2273de6a9c0SDavid du Colombier }
2283de6a9c0SDavid du Colombier 
2293de6a9c0SDavid du Colombier /* clear any pending watchdog intrs or causes */
2303de6a9c0SDavid du Colombier void
wdogclrintr(Ltimer * wd)2313de6a9c0SDavid du Colombier wdogclrintr(Ltimer *wd)
2323de6a9c0SDavid du Colombier {
2333de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
2343de6a9c0SDavid du Colombier 	wd->isr = Xisrclk;
2353de6a9c0SDavid du Colombier 	coherence();
2363de6a9c0SDavid du Colombier 	wd->wdrst = Wdrst;
2373de6a9c0SDavid du Colombier 	coherence();
2383de6a9c0SDavid du Colombier #endif
2393de6a9c0SDavid du Colombier 	USED(wd);
2403de6a9c0SDavid du Colombier }
2413de6a9c0SDavid du Colombier 
2423de6a9c0SDavid du Colombier /*
2433de6a9c0SDavid du Colombier  * stop clock interrupts on this cpu and disable the local watchdog timer,
2443de6a9c0SDavid du Colombier  * and, if on cpu0, shutdown the shared tegra2 watchdog timer.
2453de6a9c0SDavid du Colombier  */
2463de6a9c0SDavid du Colombier void
clockshutdown(void)2473de6a9c0SDavid du Colombier clockshutdown(void)
2483de6a9c0SDavid du Colombier {
2493de6a9c0SDavid du Colombier 	Ploctmr *lt;
2503de6a9c0SDavid du Colombier 
2513de6a9c0SDavid du Colombier 	lt = (Ploctmr *)soc.loctmr;
2523de6a9c0SDavid du Colombier 	clockreset(&lt->loc);
2533de6a9c0SDavid du Colombier 	watchdogoff(&lt->wd);
2543de6a9c0SDavid du Colombier 
2553de6a9c0SDavid du Colombier 	tegclockshutdown();
2563de6a9c0SDavid du Colombier }
2573de6a9c0SDavid du Colombier 
2583de6a9c0SDavid du Colombier enum {
2593de6a9c0SDavid du Colombier 	Instrs		= 10*Mhz,
2603de6a9c0SDavid du Colombier };
2613de6a9c0SDavid du Colombier 
2623de6a9c0SDavid du Colombier /* we assume that perfticks are microseconds */
2633de6a9c0SDavid du Colombier static long
issue1loop(void)2643de6a9c0SDavid du Colombier issue1loop(void)
2653de6a9c0SDavid du Colombier {
2663de6a9c0SDavid du Colombier 	register int i;
2673de6a9c0SDavid du Colombier 	long st;
2683de6a9c0SDavid du Colombier 
2693de6a9c0SDavid du Colombier 	i = Instrs;
2703de6a9c0SDavid du Colombier 	st = perfticks();
2713de6a9c0SDavid du Colombier 	do {
2723de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2733de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2743de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2753de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2763de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2773de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2783de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2793de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2803de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2813de6a9c0SDavid du Colombier 		--i; --i; --i; --i; --i; --i; --i; --i; --i;
2823de6a9c0SDavid du Colombier 	} while(--i >= 0);
2833de6a9c0SDavid du Colombier 	return perfticks() - st;
2843de6a9c0SDavid du Colombier }
2853de6a9c0SDavid du Colombier 
2863de6a9c0SDavid du Colombier static long
issue2loop(void)2873de6a9c0SDavid du Colombier issue2loop(void)
2883de6a9c0SDavid du Colombier {
2893de6a9c0SDavid du Colombier 	register int i, j;
2903de6a9c0SDavid du Colombier 	long st;
2913de6a9c0SDavid du Colombier 
2923de6a9c0SDavid du Colombier 	i = Instrs / 2;			/* j gets half the decrements */
2933de6a9c0SDavid du Colombier 	j = 0;
2943de6a9c0SDavid du Colombier 	st = perfticks();
2953de6a9c0SDavid du Colombier 	do {
2963de6a9c0SDavid du Colombier 		     --j; --i; --j; --i; --j; --i; --j; --i; --j;
2973de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
2983de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
2993de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3003de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3013de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3023de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3033de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3043de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3053de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3063de6a9c0SDavid du Colombier 
3073de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3083de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3093de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3103de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3113de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3123de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3133de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3143de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3153de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3163de6a9c0SDavid du Colombier 		--i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3173de6a9c0SDavid du Colombier 	} while(--i >= 0);
3183de6a9c0SDavid du Colombier 	return perfticks() - st;
3193de6a9c0SDavid du Colombier }
3203de6a9c0SDavid du Colombier 
3213de6a9c0SDavid du Colombier /* estimate instructions/s. */
3223de6a9c0SDavid du Colombier static void
guessmips(long (* loop)(void),char * lab)3233de6a9c0SDavid du Colombier guessmips(long (*loop)(void), char *lab)
3243de6a9c0SDavid du Colombier {
3253de6a9c0SDavid du Colombier 	int s;
3263de6a9c0SDavid du Colombier 	long tcks;
3273de6a9c0SDavid du Colombier 
3283de6a9c0SDavid du Colombier 	do {
3293de6a9c0SDavid du Colombier 		s = splhi();
3303de6a9c0SDavid du Colombier 		tcks = loop();
3313de6a9c0SDavid du Colombier 		splx(s);
3323de6a9c0SDavid du Colombier 		if (tcks < 0)
3333de6a9c0SDavid du Colombier 			iprint("again...");
3343de6a9c0SDavid du Colombier 	} while (tcks < 0);
3353de6a9c0SDavid du Colombier 	/*
3363de6a9c0SDavid du Colombier 	 * Instrs instructions took tcks ticks @ Basetickfreq Hz.
3373de6a9c0SDavid du Colombier 	 * round the result.
3383de6a9c0SDavid du Colombier 	 */
3393de6a9c0SDavid du Colombier 	s = (((vlong)Basetickfreq * Instrs) / tcks + 500000) / 1000000;
3403de6a9c0SDavid du Colombier 	if (Debug)
3413de6a9c0SDavid du Colombier 		iprint("%ud mips (%s-issue)", s, lab);
3423de6a9c0SDavid du Colombier 	USED(s);
3433de6a9c0SDavid du Colombier }
3443de6a9c0SDavid du Colombier 
3453de6a9c0SDavid du Colombier void
wdogintr(Ureg *,void * ltmr)3463de6a9c0SDavid du Colombier wdogintr(Ureg *, void *ltmr)
3473de6a9c0SDavid du Colombier {
3483de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
3493de6a9c0SDavid du Colombier 	Ltimer *wd;
3503de6a9c0SDavid du Colombier 
3513de6a9c0SDavid du Colombier 	wd = ltmr;
3523de6a9c0SDavid du Colombier 	fired++;
3533de6a9c0SDavid du Colombier 	wdogclrintr(wd);
3543de6a9c0SDavid du Colombier #endif
3553de6a9c0SDavid du Colombier 	USED(ltmr);
3563de6a9c0SDavid du Colombier }
3573de6a9c0SDavid du Colombier 
3583de6a9c0SDavid du Colombier static void
ckcounting(Ltimer * lt)3593de6a9c0SDavid du Colombier ckcounting(Ltimer *lt)
3603de6a9c0SDavid du Colombier {
3613de6a9c0SDavid du Colombier 	ulong old;
3623de6a9c0SDavid du Colombier 
3633de6a9c0SDavid du Colombier 	old = lt->cnt;
3643de6a9c0SDavid du Colombier 	if (old == lt->cnt)
3653de6a9c0SDavid du Colombier 		delay(1);
3663de6a9c0SDavid du Colombier 	if (old == lt->cnt)
3673de6a9c0SDavid du Colombier 		panic("cpu%d: watchdog timer not counting down", m->machno);
3683de6a9c0SDavid du Colombier }
3693de6a9c0SDavid du Colombier 
3703de6a9c0SDavid du Colombier /* test fire with interrupt to see that it's working */
3713de6a9c0SDavid du Colombier static void
ckwatchdog(Ltimer * wd)3723de6a9c0SDavid du Colombier ckwatchdog(Ltimer *wd)
3733de6a9c0SDavid du Colombier {
3743de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
3753de6a9c0SDavid du Colombier 	int s;
3763de6a9c0SDavid du Colombier 
3773de6a9c0SDavid du Colombier 	fired = 0;
3783de6a9c0SDavid du Colombier 	wd->load = Tcycles - 1;
3793de6a9c0SDavid du Colombier 	coherence();
3803de6a9c0SDavid du Colombier 	/* Tintena is supposed to be ignored in watchdog mode */
3813de6a9c0SDavid du Colombier 	wd->ctl |= Wdogena | Tintena;
3823de6a9c0SDavid du Colombier 	coherence();
3833de6a9c0SDavid du Colombier 
3843de6a9c0SDavid du Colombier 	ckcounting(wd);
3853de6a9c0SDavid du Colombier 
3863de6a9c0SDavid du Colombier 	s = spllo();
3873de6a9c0SDavid du Colombier 	delay(2 * 1000/HZ);
3883de6a9c0SDavid du Colombier 	splx(s);
3893de6a9c0SDavid du Colombier 	if (!fired)
3903de6a9c0SDavid du Colombier 		/* useless local watchdog */
3913de6a9c0SDavid du Colombier 		iprint("cpu%d: local watchdog failed to interrupt\n", m->machno);
3923de6a9c0SDavid du Colombier 	/* clean up */
3933de6a9c0SDavid du Colombier 	wd->ctl &= ~Wdogena;
3943de6a9c0SDavid du Colombier 	coherence();
3953de6a9c0SDavid du Colombier #endif
3963de6a9c0SDavid du Colombier 	USED(wd);
3973de6a9c0SDavid du Colombier }
3983de6a9c0SDavid du Colombier 
3993de6a9c0SDavid du Colombier static void
startwatchdog(void)4003de6a9c0SDavid du Colombier startwatchdog(void)
4013de6a9c0SDavid du Colombier {
4023de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
4033de6a9c0SDavid du Colombier 	Ltimer *wd;
4043de6a9c0SDavid du Colombier 	Ploctmr *lt;
4053de6a9c0SDavid du Colombier 
4063de6a9c0SDavid du Colombier 	lt = (Ploctmr *)soc.loctmr;
4073de6a9c0SDavid du Colombier 	wd = &lt->wd;
4083de6a9c0SDavid du Colombier 	watchdogoff(wd);
4093de6a9c0SDavid du Colombier 	wdogclrintr(wd);
4103de6a9c0SDavid du Colombier 	irqenable(Wdtmrirq, wdogintr, wd, "watchdog");
4113de6a9c0SDavid du Colombier 
4123de6a9c0SDavid du Colombier 	ckwatchdog(wd);
4133de6a9c0SDavid du Colombier 
4143de6a9c0SDavid du Colombier 	/* set up for normal use, causing reset */
4153de6a9c0SDavid du Colombier 	wd->ctl &= ~Tintena;			/* reset, don't interrupt */
4163de6a9c0SDavid du Colombier 	coherence();
4173de6a9c0SDavid du Colombier 	wd->ctl |= Wdog;
4183de6a9c0SDavid du Colombier 	coherence();
4193de6a9c0SDavid du Colombier 	wd->load = Dogtimeout - 1;
4203de6a9c0SDavid du Colombier 	coherence();
4213de6a9c0SDavid du Colombier 	wd->ctl |= Wdogena;
4223de6a9c0SDavid du Colombier 	coherence();
4233de6a9c0SDavid du Colombier 
4243de6a9c0SDavid du Colombier 	ckcounting(wd);
4253de6a9c0SDavid du Colombier #endif
4263de6a9c0SDavid du Colombier }
4273de6a9c0SDavid du Colombier 
4283de6a9c0SDavid du Colombier static void
clock0init(Ltimer * tn)4293de6a9c0SDavid du Colombier clock0init(Ltimer *tn)
4303de6a9c0SDavid du Colombier {
4313de6a9c0SDavid du Colombier 	int s;
4323de6a9c0SDavid du Colombier 	ulong old, fticks;
4333de6a9c0SDavid du Colombier 
4343de6a9c0SDavid du Colombier 	/*
4353de6a9c0SDavid du Colombier 	 * calibrate fastclock
4363de6a9c0SDavid du Colombier 	 */
4373de6a9c0SDavid du Colombier 	s = splhi();
4383de6a9c0SDavid du Colombier 	tn->load = ~0ul >> 1;
4393de6a9c0SDavid du Colombier 	coherence();
4403de6a9c0SDavid du Colombier 	tn->ctl = Tmrena;
4413de6a9c0SDavid du Colombier 	coherence();
4423de6a9c0SDavid du Colombier 
4433de6a9c0SDavid du Colombier 	old = perfticks();
4443de6a9c0SDavid du Colombier 	fticks = tn->cnt;
4453de6a9c0SDavid du Colombier 	delay(1);
4463de6a9c0SDavid du Colombier 	fticks = abs(tn->cnt - fticks);
4473de6a9c0SDavid du Colombier 	old = perfticks() - old;
4483de6a9c0SDavid du Colombier 	splx(s);
4493de6a9c0SDavid du Colombier 	if (Debug)
4503de6a9c0SDavid du Colombier 		iprint("cpu%d: fastclock %ld/%ldµs = %ld fastticks/µs (MHz)\n",
4513de6a9c0SDavid du Colombier 			m->machno, fticks, old, (fticks + old/2 - 1) / old);
4523de6a9c0SDavid du Colombier 	USED(fticks, old);
4533de6a9c0SDavid du Colombier 
4543de6a9c0SDavid du Colombier 	if (Debug)
4553de6a9c0SDavid du Colombier 		iprint("cpu%d: ", m->machno);
4563de6a9c0SDavid du Colombier 	guessmips(issue1loop, "single");
4573de6a9c0SDavid du Colombier 	if (Debug)
4583de6a9c0SDavid du Colombier 		iprint(", ");
4593de6a9c0SDavid du Colombier 	guessmips(issue2loop, "dual");
4603de6a9c0SDavid du Colombier 	if (Debug)
4613de6a9c0SDavid du Colombier 		iprint("\n");
4623de6a9c0SDavid du Colombier 
4633de6a9c0SDavid du Colombier 	/*
4643de6a9c0SDavid du Colombier 	 * m->delayloop should be the number of delay loop iterations
4653de6a9c0SDavid du Colombier 	 * needed to consume 1 ms.  2 is instr'ns in the delay loop.
4663de6a9c0SDavid du Colombier 	 */
4673de6a9c0SDavid du Colombier 	m->delayloop = m->cpuhz / (1000 * 2);
4683de6a9c0SDavid du Colombier //	iprint("cpu%d: m->delayloop = %lud\n", m->machno, m->delayloop);
4693de6a9c0SDavid du Colombier 
4703de6a9c0SDavid du Colombier 	tegclock0init();
4713de6a9c0SDavid du Colombier }
4723de6a9c0SDavid du Colombier 
4733de6a9c0SDavid du Colombier /*
4743de6a9c0SDavid du Colombier  * the local timer is the interrupting timer and does not
4753de6a9c0SDavid du Colombier  * participate in measuring time.  It is initially set to HZ.
4763de6a9c0SDavid du Colombier  */
4773de6a9c0SDavid du Colombier void
clockinit(void)4783de6a9c0SDavid du Colombier clockinit(void)
4793de6a9c0SDavid du Colombier {
4803de6a9c0SDavid du Colombier 	ulong old;
4813de6a9c0SDavid du Colombier 	Ltimer *tn;
4823de6a9c0SDavid du Colombier 	Ploctmr *lt;
4833de6a9c0SDavid du Colombier 
4843de6a9c0SDavid du Colombier 	clockshutdown();
4853de6a9c0SDavid du Colombier 
4863de6a9c0SDavid du Colombier 	/* turn my cycle counter on */
4873de6a9c0SDavid du Colombier 	cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31);
4883de6a9c0SDavid du Colombier 
4893de6a9c0SDavid du Colombier 	/* turn all my counters on and clear my cycle counter */
4903de6a9c0SDavid du Colombier 	cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1);
4913de6a9c0SDavid du Colombier 
4923de6a9c0SDavid du Colombier 	/* let users read my cycle counter directly */
4933de6a9c0SDavid du Colombier 	cpwrsc(0, CpCLD, CpCLDuser, CpCLDenapmnc, 1);
4943de6a9c0SDavid du Colombier 
4953de6a9c0SDavid du Colombier 	/* verify µs counter sanity */
4963de6a9c0SDavid du Colombier 	tegclockinit();
4973de6a9c0SDavid du Colombier 
4983de6a9c0SDavid du Colombier 	lt = (Ploctmr *)soc.loctmr;
4993de6a9c0SDavid du Colombier 	tn = &lt->loc;
5003de6a9c0SDavid du Colombier 	if (m->machno == 0)
5013de6a9c0SDavid du Colombier 		irqenable(Loctmrirq, clockintr, lt, "clock");
5023de6a9c0SDavid du Colombier 	else
5033de6a9c0SDavid du Colombier 		intcunmask(Loctmrirq);
5043de6a9c0SDavid du Colombier 
5053de6a9c0SDavid du Colombier 	/*
5063de6a9c0SDavid du Colombier 	 * verify sanity of local timer
5073de6a9c0SDavid du Colombier 	 */
5083de6a9c0SDavid du Colombier 	tn->load = Clockfreqbase / 1000;
5093de6a9c0SDavid du Colombier 	tn->isr = Xisrclk;
5103de6a9c0SDavid du Colombier 	coherence();
5113de6a9c0SDavid du Colombier 	tn->ctl = Tmrena;
5123de6a9c0SDavid du Colombier 	coherence();
5133de6a9c0SDavid du Colombier 
5143de6a9c0SDavid du Colombier 	old = tn->cnt;
5153de6a9c0SDavid du Colombier 	delay(5);
5163de6a9c0SDavid du Colombier 	/* m->ticks won't be incremented here because timersinit hasn't run. */
5173de6a9c0SDavid du Colombier 	if (tn->cnt == old)
5183de6a9c0SDavid du Colombier 		panic("cpu%d: clock not ticking at all", m->machno);
5193de6a9c0SDavid du Colombier 	else if ((long)tn->cnt > 0)
5203de6a9c0SDavid du Colombier 		panic("cpu%d: clock ticking slowly", m->machno);
5213de6a9c0SDavid du Colombier 
5223de6a9c0SDavid du Colombier 	if (m->machno == 0)
5233de6a9c0SDavid du Colombier 		clock0init(tn);
5243de6a9c0SDavid du Colombier 
5253de6a9c0SDavid du Colombier 	/* if pci gets stuck, maybe one of the many watchdogs will nuke us. */
5263de6a9c0SDavid du Colombier 	startwatchdog();
5273de6a9c0SDavid du Colombier 
5283de6a9c0SDavid du Colombier 	/*
5293de6a9c0SDavid du Colombier 	 *  desynchronize the processor clocks so that they all don't
5303de6a9c0SDavid du Colombier 	 *  try to resched at the same time.
5313de6a9c0SDavid du Colombier 	 */
5323de6a9c0SDavid du Colombier 	delay(m->machno*2);
5333de6a9c0SDavid du Colombier 	setltimer(tn, Tcycles);
5343de6a9c0SDavid du Colombier }
5353de6a9c0SDavid du Colombier 
5363de6a9c0SDavid du Colombier /* our fastticks are at 1MHz (Basetickfreq), so the conversion is trivial. */
5373de6a9c0SDavid du Colombier ulong
s(void)5383de6a9c0SDavid du Colombier µs(void)
5393de6a9c0SDavid du Colombier {
5403de6a9c0SDavid du Colombier 	return fastticks2us(fastticks(nil));
5413de6a9c0SDavid du Colombier }
5423de6a9c0SDavid du Colombier 
5433de6a9c0SDavid du Colombier /* Tval is supposed to be in fastticks units. */
5443de6a9c0SDavid du Colombier void
timerset(Tval next)5453de6a9c0SDavid du Colombier timerset(Tval next)
5463de6a9c0SDavid du Colombier {
5473de6a9c0SDavid du Colombier 	int s;
5483de6a9c0SDavid du Colombier 	long offset;
5493de6a9c0SDavid du Colombier 	Ltimer *tn;
5503de6a9c0SDavid du Colombier 
5513de6a9c0SDavid du Colombier 	tn = &((Ploctmr *)soc.loctmr)->loc;
5523de6a9c0SDavid du Colombier 	s = splhi();
5533de6a9c0SDavid du Colombier 	offset = fastticks2us(next - fastticks(nil));
5543de6a9c0SDavid du Colombier 	/* offset is now in µs (MHz); convert to Clockfreqbase Hz. */
5553de6a9c0SDavid du Colombier 	offset *= Clockfreqbase / Mhz;
5563de6a9c0SDavid du Colombier 	if(offset < MinPeriod)
5573de6a9c0SDavid du Colombier 		offset = MinPeriod;
5583de6a9c0SDavid du Colombier 	else if(offset > MaxPeriod)
5593de6a9c0SDavid du Colombier 		offset = MaxPeriod;
5603de6a9c0SDavid du Colombier 
5613de6a9c0SDavid du Colombier 	setltimer(tn, offset);
5623de6a9c0SDavid du Colombier 	splx(s);
5633de6a9c0SDavid du Colombier }
5643de6a9c0SDavid du Colombier 
5653de6a9c0SDavid du Colombier static ulong
cpucycles(void)5663de6a9c0SDavid du Colombier cpucycles(void)	/* cpu clock rate, except when waiting for intr (unused) */
5673de6a9c0SDavid du Colombier {
5683de6a9c0SDavid du Colombier 	ulong v;
5693de6a9c0SDavid du Colombier 
5703de6a9c0SDavid du Colombier 	/* reads 32-bit cycle counter (counting up) */
5713de6a9c0SDavid du Colombier //	v = cprdsc(0, CpCLD, CpCLDcyc, 0);
5723de6a9c0SDavid du Colombier 	v = getcyc();				/* fast asm */
5733de6a9c0SDavid du Colombier 	/* keep it non-negative; prevent m->fastclock ever going to 0 */
5743de6a9c0SDavid du Colombier 	return v == 0? 1: v;
5753de6a9c0SDavid du Colombier }
5763de6a9c0SDavid du Colombier 
5773de6a9c0SDavid du Colombier long
lcycles(void)5783de6a9c0SDavid du Colombier lcycles(void)
5793de6a9c0SDavid du Colombier {
5803de6a9c0SDavid du Colombier 	return perfticks();
5813de6a9c0SDavid du Colombier }
5823de6a9c0SDavid du Colombier 
5833de6a9c0SDavid du Colombier uvlong
fastticks(uvlong * hz)5843de6a9c0SDavid du Colombier fastticks(uvlong *hz)
5853de6a9c0SDavid du Colombier {
586*35ef7fcaSDavid du Colombier 	int s;
587*35ef7fcaSDavid du Colombier 	ulong newticks;
588*35ef7fcaSDavid du Colombier 	Vlong *fcp;
5893de6a9c0SDavid du Colombier 
5903de6a9c0SDavid du Colombier 	if(hz)
5913de6a9c0SDavid du Colombier 		*hz = Basetickfreq;
5923de6a9c0SDavid du Colombier 
593*35ef7fcaSDavid du Colombier 	fcp = (Vlong *)&m->fastclock;
594*35ef7fcaSDavid du Colombier 	/* avoid reentry on interrupt or trap, to prevent recursion */
595*35ef7fcaSDavid du Colombier 	s = splhi();
596*35ef7fcaSDavid du Colombier 	newticks = perfticks();
597*35ef7fcaSDavid du Colombier 	if(newticks < fcp->low)		/* low word must have wrapped */
598*35ef7fcaSDavid du Colombier 		fcp->high++;
599*35ef7fcaSDavid du Colombier 	fcp->low = newticks;
600*35ef7fcaSDavid du Colombier 	splx(s);
601*35ef7fcaSDavid du Colombier 
602*35ef7fcaSDavid du Colombier 	if (fcp->low == 0 && fcp->high == 0 && m->ticks > HZ/10)
603*35ef7fcaSDavid du Colombier 		panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",
604*35ef7fcaSDavid du Colombier 			m->ticks, m->fastclock);
605*35ef7fcaSDavid du Colombier 	return m->fastclock;
6063de6a9c0SDavid du Colombier }
6073de6a9c0SDavid du Colombier 
6083de6a9c0SDavid du Colombier void
microdelay(int l)6093de6a9c0SDavid du Colombier microdelay(int l)
6103de6a9c0SDavid du Colombier {
6113de6a9c0SDavid du Colombier 	for (l = l * (vlong)m->delayloop / 1000; --l >= 0; )
6123de6a9c0SDavid du Colombier 		;
6133de6a9c0SDavid du Colombier }
6143de6a9c0SDavid du Colombier 
6153de6a9c0SDavid du Colombier void
delay(int l)6163de6a9c0SDavid du Colombier delay(int l)
6173de6a9c0SDavid du Colombier {
6183de6a9c0SDavid du Colombier 	int i, d;
6193de6a9c0SDavid du Colombier 
6203de6a9c0SDavid du Colombier 	d = m->delayloop;
6213de6a9c0SDavid du Colombier 	while(--l >= 0)
6223de6a9c0SDavid du Colombier 		for (i = d; --i >= 0; )
6233de6a9c0SDavid du Colombier 			;
6243de6a9c0SDavid du Colombier }
625