xref: /plan9-contrib/sys/src/9/bcm/clock.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
15d9de2d3SDavid du Colombier /*
2*5c47fe09SDavid du Colombier  * bcm283[56] timers
35d9de2d3SDavid du Colombier  *	System timers run at 1MHz (timers 1 and 2 are used by GPU)
45d9de2d3SDavid du Colombier  *	ARM timer usually runs at 250MHz (may be slower in low power modes)
55d9de2d3SDavid du Colombier  *	Cycle counter runs at 700MHz (unless overclocked)
65d9de2d3SDavid du Colombier  *    All are free-running up-counters
7*5c47fe09SDavid du Colombier  *  Cortex-a7 has local generic timers per cpu (which we run at 1MHz)
85d9de2d3SDavid du Colombier  *
95d9de2d3SDavid du Colombier  * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
10*5c47fe09SDavid du Colombier  *   For smp on bcm2836, use local generic timer for interrupts on cpu1-3
115d9de2d3SDavid du Colombier  * Use ARM timer (32 bits) for perfticks
125d9de2d3SDavid du Colombier  * Use ARM timer to force immediate interrupt
135d9de2d3SDavid du Colombier  * Use cycle counter for cycles()
145d9de2d3SDavid du Colombier  */
155d9de2d3SDavid du Colombier 
165d9de2d3SDavid du Colombier #include "u.h"
175d9de2d3SDavid du Colombier #include "../port/lib.h"
185d9de2d3SDavid du Colombier #include "mem.h"
195d9de2d3SDavid du Colombier #include "dat.h"
205d9de2d3SDavid du Colombier #include "fns.h"
215d9de2d3SDavid du Colombier #include "io.h"
22*5c47fe09SDavid du Colombier #include "ureg.h"
23*5c47fe09SDavid du Colombier #include "arm.h"
245d9de2d3SDavid du Colombier 
255d9de2d3SDavid du Colombier enum {
265d9de2d3SDavid du Colombier 	SYSTIMERS	= VIRTIO+0x3000,
275d9de2d3SDavid du Colombier 	ARMTIMER	= VIRTIO+0xB400,
285d9de2d3SDavid du Colombier 
29*5c47fe09SDavid du Colombier 	Localctl	= 0x00,
30*5c47fe09SDavid du Colombier 	Prescaler	= 0x08,
31*5c47fe09SDavid du Colombier 	Localintpending	= 0x60,
32*5c47fe09SDavid du Colombier 
335d9de2d3SDavid du Colombier 	SystimerFreq	= 1*Mhz,
345d9de2d3SDavid du Colombier 	MaxPeriod	= SystimerFreq / HZ,
35*5c47fe09SDavid du Colombier 	MinPeriod	= 10,
36*5c47fe09SDavid du Colombier 
375d9de2d3SDavid du Colombier };
385d9de2d3SDavid du Colombier 
395d9de2d3SDavid du Colombier typedef struct Systimers Systimers;
405d9de2d3SDavid du Colombier typedef struct Armtimer Armtimer;
415d9de2d3SDavid du Colombier 
425d9de2d3SDavid du Colombier struct Systimers {
435d9de2d3SDavid du Colombier 	u32int	cs;
445d9de2d3SDavid du Colombier 	u32int	clo;
455d9de2d3SDavid du Colombier 	u32int	chi;
465d9de2d3SDavid du Colombier 	u32int	c0;
475d9de2d3SDavid du Colombier 	u32int	c1;
485d9de2d3SDavid du Colombier 	u32int	c2;
495d9de2d3SDavid du Colombier 	u32int	c3;
505d9de2d3SDavid du Colombier };
515d9de2d3SDavid du Colombier 
525d9de2d3SDavid du Colombier struct Armtimer {
535d9de2d3SDavid du Colombier 	u32int	load;
545d9de2d3SDavid du Colombier 	u32int	val;
555d9de2d3SDavid du Colombier 	u32int	ctl;
565d9de2d3SDavid du Colombier 	u32int	irqack;
575d9de2d3SDavid du Colombier 	u32int	irq;
585d9de2d3SDavid du Colombier 	u32int	maskedirq;
595d9de2d3SDavid du Colombier 	u32int	reload;
605d9de2d3SDavid du Colombier 	u32int	predivider;
615d9de2d3SDavid du Colombier 	u32int	count;
625d9de2d3SDavid du Colombier };
635d9de2d3SDavid du Colombier 
645d9de2d3SDavid du Colombier enum {
655d9de2d3SDavid du Colombier 	CntPrescaleShift= 16,	/* freq is sys_clk/(prescale+1) */
665d9de2d3SDavid du Colombier 	CntPrescaleMask	= 0xFF,
675d9de2d3SDavid du Colombier 	CntEnable	= 1<<9,
685d9de2d3SDavid du Colombier 	TmrDbgHalt	= 1<<8,
695d9de2d3SDavid du Colombier 	TmrEnable	= 1<<7,
705d9de2d3SDavid du Colombier 	TmrIntEnable	= 1<<5,
715d9de2d3SDavid du Colombier 	TmrPrescale1	= 0x00<<2,
725d9de2d3SDavid du Colombier 	TmrPrescale16	= 0x01<<2,
735d9de2d3SDavid du Colombier 	TmrPrescale256	= 0x02<<2,
745d9de2d3SDavid du Colombier 	CntWidth16	= 0<<1,
755d9de2d3SDavid du Colombier 	CntWidth32	= 1<<1,
76*5c47fe09SDavid du Colombier 
77*5c47fe09SDavid du Colombier 	/* generic timer (cortex-a7) */
78*5c47fe09SDavid du Colombier 	Enable	= 1<<0,
79*5c47fe09SDavid du Colombier 	Imask	= 1<<1,
80*5c47fe09SDavid du Colombier 	Istatus = 1<<2,
815d9de2d3SDavid du Colombier };
825d9de2d3SDavid du Colombier 
835d9de2d3SDavid du Colombier static void
clockintr(Ureg * ureg,void *)845d9de2d3SDavid du Colombier clockintr(Ureg *ureg, void *)
855d9de2d3SDavid du Colombier {
865d9de2d3SDavid du Colombier 	Systimers *tn;
875d9de2d3SDavid du Colombier 
88*5c47fe09SDavid du Colombier 	if(m->machno != 0)
89*5c47fe09SDavid du Colombier 		panic("cpu%d: unexpected system timer interrupt", m->machno);
905d9de2d3SDavid du Colombier 	tn = (Systimers*)SYSTIMERS;
915d9de2d3SDavid du Colombier 	/* dismiss interrupt */
92*5c47fe09SDavid du Colombier 	tn->c3 = tn->clo - 1;
935d9de2d3SDavid du Colombier 	tn->cs = 1<<3;
945d9de2d3SDavid du Colombier 	timerintr(ureg, 0);
955d9de2d3SDavid du Colombier }
965d9de2d3SDavid du Colombier 
97*5c47fe09SDavid du Colombier static void
localclockintr(Ureg * ureg,void *)98*5c47fe09SDavid du Colombier localclockintr(Ureg *ureg, void *)
99*5c47fe09SDavid du Colombier {
100*5c47fe09SDavid du Colombier 	if(m->machno == 0)
101*5c47fe09SDavid du Colombier 		panic("cpu0: Unexpected local generic timer interrupt");
102*5c47fe09SDavid du Colombier 	cpwrtimerphysctl(Imask|Enable);
103*5c47fe09SDavid du Colombier 	timerintr(ureg, 0);
104*5c47fe09SDavid du Colombier }
105*5c47fe09SDavid du Colombier 
1065d9de2d3SDavid du Colombier void
clockshutdown(void)1075d9de2d3SDavid du Colombier clockshutdown(void)
1085d9de2d3SDavid du Colombier {
1095d9de2d3SDavid du Colombier 	Armtimer *tm;
1105d9de2d3SDavid du Colombier 
1115d9de2d3SDavid du Colombier 	tm = (Armtimer*)ARMTIMER;
1125d9de2d3SDavid du Colombier 	tm->ctl = 0;
113*5c47fe09SDavid du Colombier 	if(cpuserver)
114*5c47fe09SDavid du Colombier 		wdogfeed();
115*5c47fe09SDavid du Colombier 	else
116da091934SDavid du Colombier 		wdogoff();
1175d9de2d3SDavid du Colombier }
1185d9de2d3SDavid du Colombier 
1195d9de2d3SDavid du Colombier void
clockinit(void)1205d9de2d3SDavid du Colombier clockinit(void)
1215d9de2d3SDavid du Colombier {
1225d9de2d3SDavid du Colombier 	Systimers *tn;
1235d9de2d3SDavid du Colombier 	Armtimer *tm;
1245d9de2d3SDavid du Colombier 	u32int t0, t1, tstart, tend;
1255d9de2d3SDavid du Colombier 
126*5c47fe09SDavid du Colombier 	if(((cprdfeat1() >> 16) & 0xF) != 0) {
127*5c47fe09SDavid du Colombier 		/* generic timer supported */
128*5c47fe09SDavid du Colombier 		if(m->machno == 0){
129*5c47fe09SDavid du Colombier 			/* input clock is 19.2MHz or 54MHz crystal */
130*5c47fe09SDavid du Colombier 			*(ulong*)(ARMLOCAL + Localctl) = 0;
131*5c47fe09SDavid du Colombier 			/* divide by (2^31/Prescaler) for 1Mhz */
132*5c47fe09SDavid du Colombier 			*(ulong*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/soc.oscfreq)&~1UL;
133*5c47fe09SDavid du Colombier 		}
134*5c47fe09SDavid du Colombier 		cpwrtimerphysctl(Imask);
135*5c47fe09SDavid du Colombier 	}
1365d9de2d3SDavid du Colombier 
137*5c47fe09SDavid du Colombier 	tn = (Systimers*)SYSTIMERS;
1385d9de2d3SDavid du Colombier 	tstart = tn->clo;
1395d9de2d3SDavid du Colombier 	do{
1405d9de2d3SDavid du Colombier 		t0 = lcycles();
1415d9de2d3SDavid du Colombier 	}while(tn->clo == tstart);
1425d9de2d3SDavid du Colombier 	tend = tstart + 10000;
1435d9de2d3SDavid du Colombier 	do{
1445d9de2d3SDavid du Colombier 		t1 = lcycles();
1455d9de2d3SDavid du Colombier 	}while(tn->clo != tend);
1465d9de2d3SDavid du Colombier 	t1 -= t0;
1475d9de2d3SDavid du Colombier 	m->cpuhz = 100 * t1;
1485d9de2d3SDavid du Colombier 	m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
1495d9de2d3SDavid du Colombier 	m->cyclefreq = m->cpuhz;
150*5c47fe09SDavid du Colombier 	if(m->machno == 0){
1515d9de2d3SDavid du Colombier 		tn->c3 = tn->clo - 1;
152*5c47fe09SDavid du Colombier 		tm = (Armtimer*)ARMTIMER;
153*5c47fe09SDavid du Colombier 		tm->load = 0;
154*5c47fe09SDavid du Colombier 		tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
1555d9de2d3SDavid du Colombier 		intrenable(IRQtimer3, clockintr, nil, 0, "clock");
156*5c47fe09SDavid du Colombier 	}else
157*5c47fe09SDavid du Colombier 		intrenable(IRQcntpns, localclockintr, nil, 0, "clock");
1585d9de2d3SDavid du Colombier }
1595d9de2d3SDavid du Colombier 
1605d9de2d3SDavid du Colombier void
timerset(uvlong next)1615d9de2d3SDavid du Colombier timerset(uvlong next)
1625d9de2d3SDavid du Colombier {
1635d9de2d3SDavid du Colombier 	Systimers *tn;
164*5c47fe09SDavid du Colombier 	uvlong now;
165*5c47fe09SDavid du Colombier 	long period;
1665d9de2d3SDavid du Colombier 
1675d9de2d3SDavid du Colombier 	now = fastticks(nil);
168*5c47fe09SDavid du Colombier 	period = next - now;
1695d9de2d3SDavid du Colombier 	if(period < MinPeriod)
170*5c47fe09SDavid du Colombier 		period = MinPeriod;
1715d9de2d3SDavid du Colombier 	else if(period > MaxPeriod)
172*5c47fe09SDavid du Colombier 		period = MaxPeriod;
173*5c47fe09SDavid du Colombier 	if(m->machno > 0){
174*5c47fe09SDavid du Colombier 		cpwrtimerphysval(period);
175*5c47fe09SDavid du Colombier 		cpwrtimerphysctl(Enable);
176*5c47fe09SDavid du Colombier 	}else{
177*5c47fe09SDavid du Colombier 		tn = (Systimers*)SYSTIMERS;
178*5c47fe09SDavid du Colombier 		tn->c3 = tn->clo + period;
179*5c47fe09SDavid du Colombier 	}
1805d9de2d3SDavid du Colombier }
1815d9de2d3SDavid du Colombier 
1825d9de2d3SDavid du Colombier uvlong
fastticks(uvlong * hz)1835d9de2d3SDavid du Colombier fastticks(uvlong *hz)
1845d9de2d3SDavid du Colombier {
1855d9de2d3SDavid du Colombier 	Systimers *tn;
1865d9de2d3SDavid du Colombier 	ulong lo, hi;
187*5c47fe09SDavid du Colombier 	uvlong now;
1885d9de2d3SDavid du Colombier 
1895d9de2d3SDavid du Colombier 	if(hz)
1905d9de2d3SDavid du Colombier 		*hz = SystimerFreq;
191*5c47fe09SDavid du Colombier 	tn = (Systimers*)SYSTIMERS;
1925d9de2d3SDavid du Colombier 	do{
1935d9de2d3SDavid du Colombier 		hi = tn->chi;
1945d9de2d3SDavid du Colombier 		lo = tn->clo;
1955d9de2d3SDavid du Colombier 	}while(tn->chi != hi);
196*5c47fe09SDavid du Colombier 	now = (uvlong)hi<<32 | lo;
197*5c47fe09SDavid du Colombier 	return now;
1985d9de2d3SDavid du Colombier }
1995d9de2d3SDavid du Colombier 
2005d9de2d3SDavid du Colombier ulong
perfticks(void)2015d9de2d3SDavid du Colombier perfticks(void)
2025d9de2d3SDavid du Colombier {
2035d9de2d3SDavid du Colombier 	Armtimer *tm;
2045d9de2d3SDavid du Colombier 
2055d9de2d3SDavid du Colombier 	tm = (Armtimer*)ARMTIMER;
2065d9de2d3SDavid du Colombier 	return tm->count;
2075d9de2d3SDavid du Colombier }
2085d9de2d3SDavid du Colombier 
2095d9de2d3SDavid du Colombier void
armtimerset(int n)2105d9de2d3SDavid du Colombier armtimerset(int n)
2115d9de2d3SDavid du Colombier {
2125d9de2d3SDavid du Colombier 	Armtimer *tm;
2135d9de2d3SDavid du Colombier 
2145d9de2d3SDavid du Colombier 	tm = (Armtimer*)ARMTIMER;
2155d9de2d3SDavid du Colombier 	if(n > 0){
2165d9de2d3SDavid du Colombier 		tm->ctl |= TmrEnable|TmrIntEnable;
2175d9de2d3SDavid du Colombier 		tm->load = n;
2185d9de2d3SDavid du Colombier 	}else{
2195d9de2d3SDavid du Colombier 		tm->load = 0;
2205d9de2d3SDavid du Colombier 		tm->ctl &= ~(TmrEnable|TmrIntEnable);
2215d9de2d3SDavid du Colombier 		tm->irq = 1;
2225d9de2d3SDavid du Colombier 	}
2235d9de2d3SDavid du Colombier }
2245d9de2d3SDavid du Colombier 
2255d9de2d3SDavid du Colombier ulong
s(void)2265d9de2d3SDavid du Colombier µs(void)
2275d9de2d3SDavid du Colombier {
2285d9de2d3SDavid du Colombier 	if(SystimerFreq != 1*Mhz)
2295d9de2d3SDavid du Colombier 		return fastticks2us(fastticks(nil));
230*5c47fe09SDavid du Colombier 	return ((Systimers*)SYSTIMERS)->clo;
2315d9de2d3SDavid du Colombier }
2325d9de2d3SDavid du Colombier 
2335d9de2d3SDavid du Colombier void
microdelay(int n)2345d9de2d3SDavid du Colombier microdelay(int n)
2355d9de2d3SDavid du Colombier {
2365d9de2d3SDavid du Colombier 	Systimers *tn;
2375d9de2d3SDavid du Colombier 	u32int now, diff;
2385d9de2d3SDavid du Colombier 
2395d9de2d3SDavid du Colombier 	diff = n + 1;
240*5c47fe09SDavid du Colombier 	tn = (Systimers*)SYSTIMERS;
2415d9de2d3SDavid du Colombier 	now = tn->clo;
2425d9de2d3SDavid du Colombier 	while(tn->clo - now < diff)
2435d9de2d3SDavid du Colombier 		;
2445d9de2d3SDavid du Colombier }
2455d9de2d3SDavid du Colombier 
2465d9de2d3SDavid du Colombier void
delay(int n)2475d9de2d3SDavid du Colombier delay(int n)
2485d9de2d3SDavid du Colombier {
2495d9de2d3SDavid du Colombier 	while(--n >= 0)
2505d9de2d3SDavid du Colombier 		microdelay(1000);
2515d9de2d3SDavid du Colombier }
252