xref: /plan9/sys/src/9/omap/clock.c (revision df2dbabf397493e64ca7440c63ccb06027111868)
18e32b400SDavid du Colombier /*
28e32b400SDavid du Colombier  * omap3530 clocks
38e32b400SDavid du Colombier  *
48e32b400SDavid du Colombier  * timers count up to zero.
58e32b400SDavid du Colombier  *
68e32b400SDavid du Colombier  * the source clock signals for the timers are sometimes selectable.  for
78e32b400SDavid du Colombier  * WDTIMER[23] and GPTIMER12, it's always the 32kHz clock.  for the
88e32b400SDavid du Colombier  * others, it can be the 32kHz clock or the system clock.  we use only
98e32b400SDavid du Colombier  * WDTIMER2 and GPTIMER[12], and configure GPTIMER[12] in archomap.c to
108e32b400SDavid du Colombier  * use the 32kHZ clock.  WDTIMER1 is not accessible to us on GP
118e32b400SDavid du Colombier  * (general-purpose) omaps.
128e32b400SDavid du Colombier  */
138e32b400SDavid du Colombier #include "u.h"
148e32b400SDavid du Colombier #include "../port/lib.h"
158e32b400SDavid du Colombier #include "mem.h"
168e32b400SDavid du Colombier #include "dat.h"
178e32b400SDavid du Colombier #include "fns.h"
188e32b400SDavid du Colombier #include "arm.h"
198e32b400SDavid du Colombier 
208e32b400SDavid du Colombier enum {
218e32b400SDavid du Colombier 	Debug		= 0,
228e32b400SDavid du Colombier 
238e32b400SDavid du Colombier 	Tn0		= PHYSTIMER1,
248e32b400SDavid du Colombier 	Tn1		= PHYSTIMER2,
258e32b400SDavid du Colombier 
26436938c0SDavid du Colombier 	/* irq 36 is watchdog timer module 3 overflow */
278e32b400SDavid du Colombier 	Tn0irq		= 37,			/* base IRQ for all timers */
288e32b400SDavid du Colombier 
298e32b400SDavid du Colombier 	Freebase	= 1,			/* base of free-running timer */
308e32b400SDavid du Colombier 
318e32b400SDavid du Colombier 	/*
328e32b400SDavid du Colombier 	 * clock is 32K (32,768) Hz, so one tick is 30.517µs,
33436938c0SDavid du Colombier 	 * so 327.68 ticks is 10ms, 32.768 ticks is 1ms.
348e32b400SDavid du Colombier 	 */
358e32b400SDavid du Colombier 	Clockfreqbase	= 32 * 1024,		/* base rate in Hz */
368e32b400SDavid du Colombier 	Tcycles		= Clockfreqbase / HZ,	/* cycles per clock tick */
378e32b400SDavid du Colombier 
388e32b400SDavid du Colombier 	MinPeriod	= (Tcycles / 100 < 2? 2: Tcycles / 100),
398e32b400SDavid du Colombier 	MaxPeriod	= Tcycles,
408e32b400SDavid du Colombier 
41047f1f95SDavid du Colombier 	Dogtimeout	= 20 * Clockfreqbase,	/* was 4 s.; must be ≤ 21 s. */
428e32b400SDavid du Colombier };
438e32b400SDavid du Colombier 
448e32b400SDavid du Colombier enum {
458e32b400SDavid du Colombier 	/* ticpcfg bits */
468e32b400SDavid du Colombier 	Noidle		= 1<<3,
478e32b400SDavid du Colombier 	Softreset	= 1<<1,
488e32b400SDavid du Colombier 
498e32b400SDavid du Colombier 	/* tistat bits */
508e32b400SDavid du Colombier 	Resetdone	= 1<<0,
518e32b400SDavid du Colombier 
528e32b400SDavid du Colombier 	/* tisr/tier bits */
538e32b400SDavid du Colombier 	Ovf_it		= 1<<1,		/* gp: overflow intr */
548e32b400SDavid du Colombier 	Mat_it		= 1<<0,		/* gp: match intr */
558e32b400SDavid du Colombier 	Wdovf_it	= 1<<0,		/* wdog: overflow intr */
568e32b400SDavid du Colombier 
578e32b400SDavid du Colombier 	/* tclr bits */
588e32b400SDavid du Colombier 	Ar		= 1<<1,		/* gp only: autoreload mode overflow */
598e32b400SDavid du Colombier 	St		= 1<<0,		/* gp only: start the timer */
608e32b400SDavid du Colombier };
618e32b400SDavid du Colombier 
628e32b400SDavid du Colombier /* omap35x timer registers */
638e32b400SDavid du Colombier typedef struct Timerregs Timerregs;
648e32b400SDavid du Colombier struct Timerregs {
658e32b400SDavid du Colombier 	/* common to all timers, gp and watchdog */
668e32b400SDavid du Colombier 	uchar	pad0[0x10];
678e32b400SDavid du Colombier 	ulong	ticpcfg;
688e32b400SDavid du Colombier 	ulong	tistat;		/* ro: low bit: reset done */
698e32b400SDavid du Colombier 	ulong	tisr;
708e32b400SDavid du Colombier 	ulong	tier;
718e32b400SDavid du Colombier 	ulong	twer;
728e32b400SDavid du Colombier 	ulong	tclr;
738e32b400SDavid du Colombier 	ulong	tcrr;		/* counter: cycles to zero */
748e32b400SDavid du Colombier 	ulong	tldr;
758e32b400SDavid du Colombier 	ulong	ttgr;		/* trigger */
768e32b400SDavid du Colombier 	ulong	twps;		/* ro: write posted pending */
778e32b400SDavid du Colombier 
788e32b400SDavid du Colombier 	/* gp timers only, unused by us */
798e32b400SDavid du Colombier 	ulong	tmar;		/* value to compare with counter */
808e32b400SDavid du Colombier 	ulong	tcar1;		/* ro */
818e32b400SDavid du Colombier 	ulong	tsicr;
828e32b400SDavid du Colombier 	ulong	tcar2;		/* ro */
838e32b400SDavid du Colombier 	union {
848e32b400SDavid du Colombier 		ulong	tpir;	/* gp: 1 ms tick generation: +ve */
858e32b400SDavid du Colombier 		ulong	wspr;	/* wdog: start/stop control */
868e32b400SDavid du Colombier 	};
878e32b400SDavid du Colombier 	ulong	tnir;		/* 1 ms tick generation: -ve */
888e32b400SDavid du Colombier 	ulong	tcvr;		/* 1 ms tick generation: next counter value */
898e32b400SDavid du Colombier 	ulong	tocr;		/* intr mask for n ticks */
908e32b400SDavid du Colombier 	ulong	towr;
918e32b400SDavid du Colombier };
928e32b400SDavid du Colombier 
938e32b400SDavid du Colombier static int ticks; /* for sanity checking; m->ticks doesn't always get called */
948e32b400SDavid du Colombier static Lock clklck;
958e32b400SDavid du Colombier 
968e32b400SDavid du Colombier static ulong	rdcycles(void), rdbaseticks(void);
978e32b400SDavid du Colombier 
988e32b400SDavid du Colombier /* write a watchdog timer's start/stop register */
998e32b400SDavid du Colombier static void
wdogwrss(Timerregs * tn,ulong val)1008e32b400SDavid du Colombier wdogwrss(Timerregs *tn, ulong val)
1018e32b400SDavid du Colombier {
1028e32b400SDavid du Colombier 	while (tn->twps & (1 << 4))	/* pending write to start/stop reg? */
1038e32b400SDavid du Colombier 		;
1048e32b400SDavid du Colombier 	tn->wspr = val;
1058e32b400SDavid du Colombier 	coherence();
1068e32b400SDavid du Colombier 	while (tn->twps & (1 << 4))	/* pending write to start/stop reg? */
1078e32b400SDavid du Colombier 		;
1088e32b400SDavid du Colombier }
1098e32b400SDavid du Colombier 
1108e32b400SDavid du Colombier static void
resetwait(Timerregs * tn)1118e32b400SDavid du Colombier resetwait(Timerregs *tn)
1128e32b400SDavid du Colombier {
1138e32b400SDavid du Colombier 	long bound;
1148e32b400SDavid du Colombier 
115*df2dbabfSDavid du Colombier 	for (bound = 400*Mhz; !(tn->tistat & Resetdone) && bound > 0; bound--)
1168e32b400SDavid du Colombier 		;
1178e32b400SDavid du Colombier 	if (bound <= 0)
1188e32b400SDavid du Colombier 		iprint("clock reset didn't complete\n");
1198e32b400SDavid du Colombier }
1208e32b400SDavid du Colombier 
1218e32b400SDavid du Colombier 
1228e32b400SDavid du Colombier static void
wdogoff(Timerregs * tn)1238e32b400SDavid du Colombier wdogoff(Timerregs *tn)
1248e32b400SDavid du Colombier {
1258e32b400SDavid du Colombier 	resetwait(tn);
1268e32b400SDavid du Colombier 
1278e32b400SDavid du Colombier 	wdogwrss(tn, 0xaaaa);		/* magic off sequence */
1288e32b400SDavid du Colombier 	wdogwrss(tn, 0x5555);
1298e32b400SDavid du Colombier 
1308e32b400SDavid du Colombier 	tn->tldr = 1;
1318e32b400SDavid du Colombier 	coherence();
1328e32b400SDavid du Colombier 	tn->tcrr = 1;			/* paranoia */
1338e32b400SDavid du Colombier 	coherence();
1348e32b400SDavid du Colombier }
1358e32b400SDavid du Colombier 
1368e32b400SDavid du Colombier static void wdogassure(void);
1378e32b400SDavid du Colombier 
1388e32b400SDavid du Colombier static void
wdogon(Timerregs * tn)1398e32b400SDavid du Colombier wdogon(Timerregs *tn)
1408e32b400SDavid du Colombier {
1418e32b400SDavid du Colombier 	static int beenhere;
1428e32b400SDavid du Colombier 
1438e32b400SDavid du Colombier 	resetwait(tn);
1448e32b400SDavid du Colombier 	tn->tldr = -Dogtimeout;
1458e32b400SDavid du Colombier 	tn->tcrr = -Dogtimeout;
1468e32b400SDavid du Colombier 	coherence();
1478e32b400SDavid du Colombier 	wdogwrss(tn, 0xbbbb);		/* magic on sequence */
1488e32b400SDavid du Colombier 	wdogwrss(tn, 0x4444);		/* magic on sequence */
1498e32b400SDavid du Colombier 
1508e32b400SDavid du Colombier 	if (!beenhere) {
1518e32b400SDavid du Colombier 		beenhere = 1;
1528e32b400SDavid du Colombier 		/* touching the dog is not quick, so do it infrequently */
1538e32b400SDavid du Colombier 		addclock0link(wdogassure, HZ);
1548e32b400SDavid du Colombier 	}
1558e32b400SDavid du Colombier }
1568e32b400SDavid du Colombier 
1578e32b400SDavid du Colombier static void
wdogassure(void)1588e32b400SDavid du Colombier wdogassure(void)		/* reset the watch dog's counter */
1598e32b400SDavid du Colombier {
1608e32b400SDavid du Colombier 	Timerregs *tn;
1618e32b400SDavid du Colombier 
1628e32b400SDavid du Colombier 	tn = (Timerregs *)PHYSWDOG;
1638e32b400SDavid du Colombier 	wdogoff(tn);
1648e32b400SDavid du Colombier 
1658e32b400SDavid du Colombier 	tn->tcrr = -Dogtimeout;
1668e32b400SDavid du Colombier 	coherence();
1678e32b400SDavid du Colombier 
1688e32b400SDavid du Colombier 	wdogon(tn);
1698e32b400SDavid du Colombier }
1708e32b400SDavid du Colombier 
1718e32b400SDavid du Colombier static void
clockintr(Ureg * ureg,void * arg)1728e32b400SDavid du Colombier clockintr(Ureg* ureg, void *arg)
1738e32b400SDavid du Colombier {
1748e32b400SDavid du Colombier 	Timerregs *tn;
1758e32b400SDavid du Colombier 	static int nesting;
1768e32b400SDavid du Colombier 
1778e32b400SDavid du Colombier 	ticks++;
1788e32b400SDavid du Colombier 	coherence();
1798e32b400SDavid du Colombier 
1808e32b400SDavid du Colombier 	if (nesting == 0) {	/* if the clock interrupted itself, bail out */
1818e32b400SDavid du Colombier 		++nesting;
1828e32b400SDavid du Colombier 		timerintr(ureg, 0);
1838e32b400SDavid du Colombier 		--nesting;
1848e32b400SDavid du Colombier 	}
1858e32b400SDavid du Colombier 
1868e32b400SDavid du Colombier 	tn = arg;
1878e32b400SDavid du Colombier 	tn->tisr = Ovf_it;			/* dismiss the interrupt */
1888e32b400SDavid du Colombier 	coherence();
1898e32b400SDavid du Colombier }
1908e32b400SDavid du Colombier 
1918e32b400SDavid du Colombier static void
clockreset(Timerregs * tn)1928e32b400SDavid du Colombier clockreset(Timerregs *tn)
1938e32b400SDavid du Colombier {
1948e32b400SDavid du Colombier 	if (probeaddr((uintptr)&tn->ticpcfg) < 0)
1958e32b400SDavid du Colombier 		panic("no clock at %#p", tn);
1968e32b400SDavid du Colombier 	tn->ticpcfg = Softreset | Noidle;
1978e32b400SDavid du Colombier 	coherence();
1988e32b400SDavid du Colombier 	resetwait(tn);
1998e32b400SDavid du Colombier 	tn->tier = tn->tclr = 0;
2008e32b400SDavid du Colombier 	coherence();
2018e32b400SDavid du Colombier }
2028e32b400SDavid du Colombier 
2038e32b400SDavid du Colombier /* stop clock interrupts and disable the watchdog timer */
2048e32b400SDavid du Colombier void
clockshutdown(void)2058e32b400SDavid du Colombier clockshutdown(void)
2068e32b400SDavid du Colombier {
2078e32b400SDavid du Colombier 	clockreset((Timerregs *)PHYSWDT2);
2088e32b400SDavid du Colombier 	wdogoff((Timerregs *)PHYSWDT2);
2098e32b400SDavid du Colombier 	clockreset((Timerregs *)PHYSWDT3);
2108e32b400SDavid du Colombier 	wdogoff((Timerregs *)PHYSWDT3);
2118e32b400SDavid du Colombier 
2128e32b400SDavid du Colombier 	clockreset((Timerregs *)Tn0);
2138e32b400SDavid du Colombier 	clockreset((Timerregs *)Tn1);
2148e32b400SDavid du Colombier }
2158e32b400SDavid du Colombier 
2168e32b400SDavid du Colombier enum {
217*df2dbabfSDavid du Colombier 	Instrs		= 10*Mhz,
2188e32b400SDavid du Colombier };
2198e32b400SDavid du Colombier 
2208e32b400SDavid du Colombier static long
issue1loop(void)2218e32b400SDavid du Colombier issue1loop(void)
2228e32b400SDavid du Colombier {
2238e32b400SDavid du Colombier 	register int i;
2248e32b400SDavid du Colombier 	long st;
2258e32b400SDavid du Colombier 
2268e32b400SDavid du Colombier 	st = rdbaseticks();
2278e32b400SDavid du Colombier 	i = Instrs;
2288e32b400SDavid du Colombier 	do {
2298e32b400SDavid du Colombier 		--i; --i; --i; --i; --i;
2308e32b400SDavid du Colombier 		--i; --i; --i; --i;
2318e32b400SDavid du Colombier 	} while(--i >= 0);
2328e32b400SDavid du Colombier 	return rdbaseticks() - st;
2338e32b400SDavid du Colombier }
2348e32b400SDavid du Colombier 
2358e32b400SDavid du Colombier static long
issue2loop(void)2368e32b400SDavid du Colombier issue2loop(void)
2378e32b400SDavid du Colombier {
2388e32b400SDavid du Colombier 	register int i, j;
2398e32b400SDavid du Colombier 	long st;
2408e32b400SDavid du Colombier 
2418e32b400SDavid du Colombier 	st = rdbaseticks();
2428e32b400SDavid du Colombier 	i = Instrs / 2;
2438e32b400SDavid du Colombier 	j = 0;
2448e32b400SDavid du Colombier 	do {
2458e32b400SDavid du Colombier 		--i; --j; --i; --j;
2468e32b400SDavid du Colombier 		--i; --j; --i; --j;
2478e32b400SDavid du Colombier 		--j;
2488e32b400SDavid du Colombier 	} while(--i >= 0);
2498e32b400SDavid du Colombier 	return rdbaseticks() - st;
2508e32b400SDavid du Colombier }
2518e32b400SDavid du Colombier 
2528e32b400SDavid du Colombier /* estimate instructions/s. using 32kHz clock */
2538e32b400SDavid du Colombier static void
guessmips(long (* loop)(void),char * lab)2548e32b400SDavid du Colombier guessmips(long (*loop)(void), char *lab)
2558e32b400SDavid du Colombier {
2568e32b400SDavid du Colombier 	int s;
2578e32b400SDavid du Colombier 	long tcks;
2588e32b400SDavid du Colombier 
2598e32b400SDavid du Colombier 	do {
2608e32b400SDavid du Colombier 		s = splhi();
2618e32b400SDavid du Colombier 		tcks = loop();
2628e32b400SDavid du Colombier 		splx(s);
2638e32b400SDavid du Colombier 		if (tcks < 0)
2648e32b400SDavid du Colombier 			iprint("again...");
2658e32b400SDavid du Colombier 	} while (tcks < 0);
2668e32b400SDavid du Colombier 	/*
2678e32b400SDavid du Colombier 	 * Instrs instructions took tcks ticks @ Clockfreqbase Hz.
2688e32b400SDavid du Colombier 	 */
2698e32b400SDavid du Colombier 	s = ((vlong)Clockfreqbase * Instrs) / tcks / 1000000;
2708e32b400SDavid du Colombier 	if (Debug)
2718e32b400SDavid du Colombier 		iprint("%ud mips (%s-issue)", s, lab);
2728e32b400SDavid du Colombier 	USED(s);
2738e32b400SDavid du Colombier }
2748e32b400SDavid du Colombier 
2758e32b400SDavid du Colombier void
clockinit(void)2768e32b400SDavid du Colombier clockinit(void)
2778e32b400SDavid du Colombier {
2788e32b400SDavid du Colombier 	int i, s;
2798e32b400SDavid du Colombier 	Timerregs *tn;
2808e32b400SDavid du Colombier 
2818e32b400SDavid du Colombier 	clockshutdown();
2828e32b400SDavid du Colombier 
2838e32b400SDavid du Colombier 	/* turn cycle counter on */
2848e32b400SDavid du Colombier 	cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31);
2858e32b400SDavid du Colombier 
2868e32b400SDavid du Colombier 	/* turn all counters on and clear the cycle counter */
2878e32b400SDavid du Colombier 	cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1);
2888e32b400SDavid du Colombier 
2898e32b400SDavid du Colombier 	/* let users read the cycle counter directly */
2908e32b400SDavid du Colombier 	cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1);
2918e32b400SDavid du Colombier 
2928e32b400SDavid du Colombier 	ilock(&clklck);
2938e32b400SDavid du Colombier 	m->fastclock = 1;
2948e32b400SDavid du Colombier 	m->ticks = ticks = 0;
2958e32b400SDavid du Colombier 
2968e32b400SDavid du Colombier 	/*
2978e32b400SDavid du Colombier 	 * T0 is a freerunning timer (cycle counter); it wraps,
2988e32b400SDavid du Colombier 	 * automatically reloads, and does not dispatch interrupts.
2998e32b400SDavid du Colombier 	 */
3008e32b400SDavid du Colombier 	tn = (Timerregs *)Tn0;
3018e32b400SDavid du Colombier 	tn->tcrr = Freebase;			/* count up to 0 */
3028e32b400SDavid du Colombier 	tn->tldr = Freebase;
3038e32b400SDavid du Colombier 	coherence();
3048e32b400SDavid du Colombier 	tn->tclr = Ar | St;
3058e32b400SDavid du Colombier 	iunlock(&clklck);
3068e32b400SDavid du Colombier 
3078e32b400SDavid du Colombier 	/*
3088e32b400SDavid du Colombier 	 * T1 is the interrupting timer and does not participate
3098e32b400SDavid du Colombier 	 * in measuring time.  It is initially set to HZ.
3108e32b400SDavid du Colombier 	 */
3118e32b400SDavid du Colombier 	tn = (Timerregs *)Tn1;
3128e32b400SDavid du Colombier 	irqenable(Tn0irq+1, clockintr, tn, "clock");
3138e32b400SDavid du Colombier 	ilock(&clklck);
3148e32b400SDavid du Colombier 	tn->tcrr = -Tcycles;			/* approx.; count up to 0 */
3158e32b400SDavid du Colombier 	tn->tldr = -Tcycles;
3168e32b400SDavid du Colombier 	coherence();
3178e32b400SDavid du Colombier 	tn->tclr = Ar | St;
3188e32b400SDavid du Colombier 	coherence();
3198e32b400SDavid du Colombier 	tn->tier = Ovf_it;
3208e32b400SDavid du Colombier 	coherence();
3218e32b400SDavid du Colombier 	iunlock(&clklck);
3228e32b400SDavid du Colombier 
3238e32b400SDavid du Colombier 	/*
3248e32b400SDavid du Colombier 	 * verify sanity of timer1
3258e32b400SDavid du Colombier 	 */
3268e32b400SDavid du Colombier 	s = spllo();			/* risky */
3278e32b400SDavid du Colombier 	for (i = 0; i < 5 && ticks == 0; i++) {
3288e32b400SDavid du Colombier 		delay(10);
3298e32b400SDavid du Colombier 		cachedwbinvse(&ticks, sizeof ticks);
3308e32b400SDavid du Colombier 	}
3318e32b400SDavid du Colombier 	splx(s);
3328e32b400SDavid du Colombier 	if (ticks == 0) {
3338e32b400SDavid du Colombier 		if (tn->tcrr == 0)
3348e32b400SDavid du Colombier 			panic("clock not interrupting");
3358e32b400SDavid du Colombier 		else if (tn->tcrr == tn->tldr)
3368e32b400SDavid du Colombier 			panic("clock not ticking at all");
3378e32b400SDavid du Colombier #ifdef PARANOID
3388e32b400SDavid du Colombier 		else
3398e32b400SDavid du Colombier 			panic("clock running very slowly");
3408e32b400SDavid du Colombier #endif
3418e32b400SDavid du Colombier 	}
3428e32b400SDavid du Colombier 
3438e32b400SDavid du Colombier 	guessmips(issue1loop, "single");
3448e32b400SDavid du Colombier 	if (Debug)
3458e32b400SDavid du Colombier 		iprint(", ");
3468e32b400SDavid du Colombier 	guessmips(issue2loop, "dual");
3478e32b400SDavid du Colombier 	if (Debug)
3488e32b400SDavid du Colombier 		iprint("\n");
3498e32b400SDavid du Colombier 
3508e32b400SDavid du Colombier 	/*
3518e32b400SDavid du Colombier 	 * m->delayloop should be the number of delay loop iterations
3528e32b400SDavid du Colombier 	 * needed to consume 1 ms.  2 is min. instructions in the delay loop.
3538e32b400SDavid du Colombier 	 */
3548e32b400SDavid du Colombier 	m->delayloop = m->cpuhz / (1000 * 2);
3558e32b400SDavid du Colombier //	iprint("m->delayloop = %lud\n", m->delayloop);
3568e32b400SDavid du Colombier 
3578e32b400SDavid du Colombier 	/*
3588e32b400SDavid du Colombier 	 *  desynchronize the processor clocks so that they all don't
3598e32b400SDavid du Colombier 	 *  try to resched at the same time.
3608e32b400SDavid du Colombier 	 */
3618e32b400SDavid du Colombier 	delay(m->machno*2);
3628e32b400SDavid du Colombier }
3638e32b400SDavid du Colombier 
3648e32b400SDavid du Colombier void
watchdoginit(void)3658e32b400SDavid du Colombier watchdoginit(void)
3668e32b400SDavid du Colombier {
3678e32b400SDavid du Colombier 	wdogassure();
3688e32b400SDavid du Colombier }
3698e32b400SDavid du Colombier 
3708e32b400SDavid du Colombier ulong
s(void)3718e32b400SDavid du Colombier µs(void)
3728e32b400SDavid du Colombier {
3738e32b400SDavid du Colombier 	return fastticks2us(fastticks(nil));
3748e32b400SDavid du Colombier }
3758e32b400SDavid du Colombier 
3768e32b400SDavid du Colombier void
timerset(Tval next)3778e32b400SDavid du Colombier timerset(Tval next)
3788e32b400SDavid du Colombier {
3798e32b400SDavid du Colombier 	long offset;
3808e32b400SDavid du Colombier 	Timerregs *tn = (Timerregs *)Tn1;
3818e32b400SDavid du Colombier 	static Lock setlck;
3828e32b400SDavid du Colombier 
3838e32b400SDavid du Colombier 	ilock(&setlck);
3848e32b400SDavid du Colombier 	offset = next - fastticks(nil);
3858e32b400SDavid du Colombier 	if(offset < MinPeriod)
3868e32b400SDavid du Colombier 		offset = MinPeriod;
3878e32b400SDavid du Colombier 	else if(offset > MaxPeriod)
3888e32b400SDavid du Colombier 		offset = MaxPeriod;
3898e32b400SDavid du Colombier 	tn->tcrr = -offset;
3908e32b400SDavid du Colombier 	coherence();
3918e32b400SDavid du Colombier 	iunlock(&setlck);
3928e32b400SDavid du Colombier }
3938e32b400SDavid du Colombier 
3948e32b400SDavid du Colombier static ulong
rdcycles(void)3958e32b400SDavid du Colombier rdcycles(void)
3968e32b400SDavid du Colombier {
3978e32b400SDavid du Colombier 	ulong v;
3988e32b400SDavid du Colombier 
3998e32b400SDavid du Colombier 	/* reads 32-bit cycle counter (counting up) */
4008e32b400SDavid du Colombier 	v = cprdsc(0, CpCLD, CpCLDcyc, 0);
4018e32b400SDavid du Colombier 	/* keep it positive; prevent m->fastclock ever going to 0 */
4028e32b400SDavid du Colombier 	return v == 0? 1: v;
4038e32b400SDavid du Colombier }
4048e32b400SDavid du Colombier 
4058e32b400SDavid du Colombier static ulong
rdbaseticks(void)4068e32b400SDavid du Colombier rdbaseticks(void)
4078e32b400SDavid du Colombier {
4088e32b400SDavid du Colombier 	ulong v;
4098e32b400SDavid du Colombier 
4108e32b400SDavid du Colombier 	v = ((Timerregs *)Tn0)->tcrr;		/* tcrr should be counting up */
4118e32b400SDavid du Colombier 	/* keep it positive; prevent m->fastclock ever going to 0 */
4128e32b400SDavid du Colombier 	return v == 0? 1: v;
4138e32b400SDavid du Colombier }
4148e32b400SDavid du Colombier 
4158e32b400SDavid du Colombier ulong
perfticks(void)4168e32b400SDavid du Colombier perfticks(void)
4178e32b400SDavid du Colombier {
4188e32b400SDavid du Colombier 	return rdcycles();
4198e32b400SDavid du Colombier }
4208e32b400SDavid du Colombier 
4218e32b400SDavid du Colombier long
lcycles(void)4228e32b400SDavid du Colombier lcycles(void)
4238e32b400SDavid du Colombier {
4248e32b400SDavid du Colombier 	return perfticks();
4258e32b400SDavid du Colombier }
4268e32b400SDavid du Colombier 
4278e32b400SDavid du Colombier /*
4288e32b400SDavid du Colombier  * until 5[cal] inline vlong ops, avoid them where possible,
4298e32b400SDavid du Colombier  * they are currently slow function calls.
4308e32b400SDavid du Colombier  */
4318e32b400SDavid du Colombier typedef union Counter Counter;
4328e32b400SDavid du Colombier union Counter {
4338e32b400SDavid du Colombier 	uvlong	uvl;
4348e32b400SDavid du Colombier 	struct {			/* little-endian */
4358e32b400SDavid du Colombier 		ulong	low;
4368e32b400SDavid du Colombier 		ulong	high;
4378e32b400SDavid du Colombier 	};
4388e32b400SDavid du Colombier };
4398e32b400SDavid du Colombier 
4408e32b400SDavid du Colombier enum {
4418e32b400SDavid du Colombier 	Fastvlongops	= 0,
4428e32b400SDavid du Colombier };
4438e32b400SDavid du Colombier 
4448e32b400SDavid du Colombier uvlong
fastticks(uvlong * hz)4458e32b400SDavid du Colombier fastticks(uvlong *hz)
4468e32b400SDavid du Colombier {
4478e32b400SDavid du Colombier 	Counter now, sclnow;
4488e32b400SDavid du Colombier 
4498e32b400SDavid du Colombier 	if(hz)
4508e32b400SDavid du Colombier 		*hz = m->cpuhz;
4518e32b400SDavid du Colombier 	ilock(&clklck);
4528e32b400SDavid du Colombier 	if (m->ticks > HZ/10 && m->fastclock == 0)
4538e32b400SDavid du Colombier 		panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",
4548e32b400SDavid du Colombier 			m->ticks, m->fastclock);
4558e32b400SDavid du Colombier 
4568e32b400SDavid du Colombier 	now.uvl = m->fastclock;
4578e32b400SDavid du Colombier 	now.low = rdcycles();
4588e32b400SDavid du Colombier 	if(now.uvl < m->fastclock)	/* low bits must have wrapped */
4598e32b400SDavid du Colombier 		now.high++;
4608e32b400SDavid du Colombier 	m->fastclock = now.uvl;
4618e32b400SDavid du Colombier 	coherence();
4628e32b400SDavid du Colombier 
4638e32b400SDavid du Colombier 	sclnow.uvl = now.uvl;
4648e32b400SDavid du Colombier 	iunlock(&clklck);
4658e32b400SDavid du Colombier 	return sclnow.uvl;
4668e32b400SDavid du Colombier }
4678e32b400SDavid du Colombier 
4688e32b400SDavid du Colombier void
microdelay(int l)4698e32b400SDavid du Colombier microdelay(int l)
4708e32b400SDavid du Colombier {
4718e32b400SDavid du Colombier 	int i;
4728e32b400SDavid du Colombier 
4738e32b400SDavid du Colombier 	l = l * (vlong)m->delayloop / 1000;
4748e32b400SDavid du Colombier 	if(l <= 0)
4758e32b400SDavid du Colombier 		l = 1;
4768e32b400SDavid du Colombier 	for(i = 0; i < l; i++)
4778e32b400SDavid du Colombier 		;
4788e32b400SDavid du Colombier }
4798e32b400SDavid du Colombier 
4808e32b400SDavid du Colombier void
delay(int l)4818e32b400SDavid du Colombier delay(int l)
4828e32b400SDavid du Colombier {
4838e32b400SDavid du Colombier 	ulong i, j;
4848e32b400SDavid du Colombier 
4858e32b400SDavid du Colombier 	j = m->delayloop;
4868e32b400SDavid du Colombier 	while(l-- > 0)
4878e32b400SDavid du Colombier 		for(i=0; i < j; i++)
4888e32b400SDavid du Colombier 			;
4898e32b400SDavid du Colombier }
490