xref: /plan9/sys/src/9/bcm/clock.c (revision da091934ad5a991038edbfec621ba4743b1dcd18)
15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier  * bcm2835 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
75d9de2d3SDavid du Colombier  *
85d9de2d3SDavid du Colombier  * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
95d9de2d3SDavid du Colombier  * Use ARM timer (32 bits) for perfticks
105d9de2d3SDavid du Colombier  * Use ARM timer to force immediate interrupt
115d9de2d3SDavid du Colombier  * Use cycle counter for cycles()
125d9de2d3SDavid du Colombier  */
135d9de2d3SDavid du Colombier 
145d9de2d3SDavid du Colombier #include "u.h"
155d9de2d3SDavid du Colombier #include "../port/lib.h"
165d9de2d3SDavid du Colombier #include "mem.h"
175d9de2d3SDavid du Colombier #include "dat.h"
185d9de2d3SDavid du Colombier #include "fns.h"
195d9de2d3SDavid du Colombier #include "io.h"
205d9de2d3SDavid du Colombier 
215d9de2d3SDavid du Colombier enum {
225d9de2d3SDavid du Colombier 	SYSTIMERS	= VIRTIO+0x3000,
235d9de2d3SDavid du Colombier 	ARMTIMER	= VIRTIO+0xB400,
245d9de2d3SDavid du Colombier 
255d9de2d3SDavid du Colombier 	SystimerFreq	= 1*Mhz,
265d9de2d3SDavid du Colombier 	MaxPeriod	= SystimerFreq / HZ,
275d9de2d3SDavid du Colombier 	MinPeriod	= SystimerFreq / (100*HZ),
285d9de2d3SDavid du Colombier };
295d9de2d3SDavid du Colombier 
305d9de2d3SDavid du Colombier typedef struct Systimers Systimers;
315d9de2d3SDavid du Colombier typedef struct Armtimer Armtimer;
325d9de2d3SDavid du Colombier 
335d9de2d3SDavid du Colombier struct Systimers {
345d9de2d3SDavid du Colombier 	u32int	cs;
355d9de2d3SDavid du Colombier 	u32int	clo;
365d9de2d3SDavid du Colombier 	u32int	chi;
375d9de2d3SDavid du Colombier 	u32int	c0;
385d9de2d3SDavid du Colombier 	u32int	c1;
395d9de2d3SDavid du Colombier 	u32int	c2;
405d9de2d3SDavid du Colombier 	u32int	c3;
415d9de2d3SDavid du Colombier };
425d9de2d3SDavid du Colombier 
435d9de2d3SDavid du Colombier struct Armtimer {
445d9de2d3SDavid du Colombier 	u32int	load;
455d9de2d3SDavid du Colombier 	u32int	val;
465d9de2d3SDavid du Colombier 	u32int	ctl;
475d9de2d3SDavid du Colombier 	u32int	irqack;
485d9de2d3SDavid du Colombier 	u32int	irq;
495d9de2d3SDavid du Colombier 	u32int	maskedirq;
505d9de2d3SDavid du Colombier 	u32int	reload;
515d9de2d3SDavid du Colombier 	u32int	predivider;
525d9de2d3SDavid du Colombier 	u32int	count;
535d9de2d3SDavid du Colombier };
545d9de2d3SDavid du Colombier 
555d9de2d3SDavid du Colombier enum {
565d9de2d3SDavid du Colombier 	CntPrescaleShift= 16,	/* freq is sys_clk/(prescale+1) */
575d9de2d3SDavid du Colombier 	CntPrescaleMask	= 0xFF,
585d9de2d3SDavid du Colombier 	CntEnable	= 1<<9,
595d9de2d3SDavid du Colombier 	TmrDbgHalt	= 1<<8,
605d9de2d3SDavid du Colombier 	TmrEnable	= 1<<7,
615d9de2d3SDavid du Colombier 	TmrIntEnable	= 1<<5,
625d9de2d3SDavid du Colombier 	TmrPrescale1	= 0x00<<2,
635d9de2d3SDavid du Colombier 	TmrPrescale16	= 0x01<<2,
645d9de2d3SDavid du Colombier 	TmrPrescale256	= 0x02<<2,
655d9de2d3SDavid du Colombier 	CntWidth16	= 0<<1,
665d9de2d3SDavid du Colombier 	CntWidth32	= 1<<1,
675d9de2d3SDavid du Colombier };
685d9de2d3SDavid du Colombier 
695d9de2d3SDavid du Colombier static void
clockintr(Ureg * ureg,void *)705d9de2d3SDavid du Colombier clockintr(Ureg *ureg, void *)
715d9de2d3SDavid du Colombier {
725d9de2d3SDavid du Colombier 	Systimers *tn;
735d9de2d3SDavid du Colombier 
745d9de2d3SDavid du Colombier 	tn = (Systimers*)SYSTIMERS;
755d9de2d3SDavid du Colombier 	/* dismiss interrupt */
765d9de2d3SDavid du Colombier 	tn->cs = 1<<3;
775d9de2d3SDavid du Colombier 	timerintr(ureg, 0);
785d9de2d3SDavid du Colombier }
795d9de2d3SDavid du Colombier 
805d9de2d3SDavid du Colombier void
clockshutdown(void)815d9de2d3SDavid du Colombier clockshutdown(void)
825d9de2d3SDavid du Colombier {
835d9de2d3SDavid du Colombier 	Armtimer *tm;
845d9de2d3SDavid du Colombier 
855d9de2d3SDavid du Colombier 	tm = (Armtimer*)ARMTIMER;
865d9de2d3SDavid du Colombier 	tm->ctl = 0;
87*da091934SDavid du Colombier 	wdogoff();
885d9de2d3SDavid du Colombier }
895d9de2d3SDavid du Colombier 
905d9de2d3SDavid du Colombier void
clockinit(void)915d9de2d3SDavid du Colombier clockinit(void)
925d9de2d3SDavid du Colombier {
935d9de2d3SDavid du Colombier 	Systimers *tn;
945d9de2d3SDavid du Colombier 	Armtimer *tm;
955d9de2d3SDavid du Colombier 	u32int t0, t1, tstart, tend;
965d9de2d3SDavid du Colombier 
975d9de2d3SDavid du Colombier 	tn = (Systimers*)SYSTIMERS;
985d9de2d3SDavid du Colombier 	tm = (Armtimer*)ARMTIMER;
995d9de2d3SDavid du Colombier 	tm->load = 0;
1005d9de2d3SDavid du Colombier 	tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
1015d9de2d3SDavid du Colombier 
1025d9de2d3SDavid du Colombier 	tstart = tn->clo;
1035d9de2d3SDavid du Colombier 	do{
1045d9de2d3SDavid du Colombier 		t0 = lcycles();
1055d9de2d3SDavid du Colombier 	}while(tn->clo == tstart);
1065d9de2d3SDavid du Colombier 	tend = tstart + 10000;
1075d9de2d3SDavid du Colombier 	do{
1085d9de2d3SDavid du Colombier 		t1 = lcycles();
1095d9de2d3SDavid du Colombier 	}while(tn->clo != tend);
1105d9de2d3SDavid du Colombier 	t1 -= t0;
1115d9de2d3SDavid du Colombier 	m->cpuhz = 100 * t1;
1125d9de2d3SDavid du Colombier 	m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
1135d9de2d3SDavid du Colombier 	m->cyclefreq = m->cpuhz;
1145d9de2d3SDavid du Colombier 
1155d9de2d3SDavid du Colombier 	tn->c3 = tn->clo - 1;
1165d9de2d3SDavid du Colombier 	intrenable(IRQtimer3, clockintr, nil, 0, "clock");
1175d9de2d3SDavid du Colombier }
1185d9de2d3SDavid du Colombier 
1195d9de2d3SDavid du Colombier void
timerset(uvlong next)1205d9de2d3SDavid du Colombier timerset(uvlong next)
1215d9de2d3SDavid du Colombier {
1225d9de2d3SDavid du Colombier 	Systimers *tn;
1235d9de2d3SDavid du Colombier 	vlong now, period;
1245d9de2d3SDavid du Colombier 
1255d9de2d3SDavid du Colombier 	tn = (Systimers*)SYSTIMERS;
1265d9de2d3SDavid du Colombier 	now = fastticks(nil);
1275d9de2d3SDavid du Colombier 	period = next - fastticks(nil);
1285d9de2d3SDavid du Colombier 	if(period < MinPeriod)
1295d9de2d3SDavid du Colombier 		next = now + MinPeriod;
1305d9de2d3SDavid du Colombier 	else if(period > MaxPeriod)
1315d9de2d3SDavid du Colombier 		next = now + MaxPeriod;
1325d9de2d3SDavid du Colombier 	tn->c3 = (ulong)next;
1335d9de2d3SDavid du Colombier }
1345d9de2d3SDavid du Colombier 
1355d9de2d3SDavid du Colombier uvlong
fastticks(uvlong * hz)1365d9de2d3SDavid du Colombier fastticks(uvlong *hz)
1375d9de2d3SDavid du Colombier {
1385d9de2d3SDavid du Colombier 	Systimers *tn;
1395d9de2d3SDavid du Colombier 	ulong lo, hi;
1405d9de2d3SDavid du Colombier 
1415d9de2d3SDavid du Colombier 	tn = (Systimers*)SYSTIMERS;
1425d9de2d3SDavid du Colombier 	if(hz)
1435d9de2d3SDavid du Colombier 		*hz = SystimerFreq;
1445d9de2d3SDavid du Colombier 	do{
1455d9de2d3SDavid du Colombier 		hi = tn->chi;
1465d9de2d3SDavid du Colombier 		lo = tn->clo;
1475d9de2d3SDavid du Colombier 	}while(tn->chi != hi);
1485d9de2d3SDavid du Colombier 	m->fastclock = (uvlong)hi<<32 | lo;
1495d9de2d3SDavid du Colombier 	return m->fastclock;
1505d9de2d3SDavid du Colombier }
1515d9de2d3SDavid du Colombier 
1525d9de2d3SDavid du Colombier ulong
perfticks(void)1535d9de2d3SDavid du Colombier perfticks(void)
1545d9de2d3SDavid du Colombier {
1555d9de2d3SDavid du Colombier 	Armtimer *tm;
1565d9de2d3SDavid du Colombier 
1575d9de2d3SDavid du Colombier 	tm = (Armtimer*)ARMTIMER;
1585d9de2d3SDavid du Colombier 	return tm->count;
1595d9de2d3SDavid du Colombier }
1605d9de2d3SDavid du Colombier 
1615d9de2d3SDavid du Colombier void
armtimerset(int n)1625d9de2d3SDavid du Colombier armtimerset(int n)
1635d9de2d3SDavid du Colombier {
1645d9de2d3SDavid du Colombier 	Armtimer *tm;
1655d9de2d3SDavid du Colombier 
1665d9de2d3SDavid du Colombier 	tm = (Armtimer*)ARMTIMER;
1675d9de2d3SDavid du Colombier 	if(n > 0){
1685d9de2d3SDavid du Colombier 		tm->ctl |= TmrEnable|TmrIntEnable;
1695d9de2d3SDavid du Colombier 		tm->load = n;
1705d9de2d3SDavid du Colombier 	}else{
1715d9de2d3SDavid du Colombier 		tm->load = 0;
1725d9de2d3SDavid du Colombier 		tm->ctl &= ~(TmrEnable|TmrIntEnable);
1735d9de2d3SDavid du Colombier 		tm->irq = 1;
1745d9de2d3SDavid du Colombier 	}
1755d9de2d3SDavid du Colombier }
1765d9de2d3SDavid du Colombier 
1775d9de2d3SDavid du Colombier ulong
s(void)1785d9de2d3SDavid du Colombier µs(void)
1795d9de2d3SDavid du Colombier {
1805d9de2d3SDavid du Colombier 	if(SystimerFreq != 1*Mhz)
1815d9de2d3SDavid du Colombier 		return fastticks2us(fastticks(nil));
1825d9de2d3SDavid du Colombier 	return fastticks(nil);
1835d9de2d3SDavid du Colombier }
1845d9de2d3SDavid du Colombier 
1855d9de2d3SDavid du Colombier void
microdelay(int n)1865d9de2d3SDavid du Colombier microdelay(int n)
1875d9de2d3SDavid du Colombier {
1885d9de2d3SDavid du Colombier 	Systimers *tn;
1895d9de2d3SDavid du Colombier 	u32int now, diff;
1905d9de2d3SDavid du Colombier 
1915d9de2d3SDavid du Colombier 	tn = (Systimers*)SYSTIMERS;
1925d9de2d3SDavid du Colombier 	diff = n + 1;
1935d9de2d3SDavid du Colombier 	now = tn->clo;
1945d9de2d3SDavid du Colombier 	while(tn->clo - now < diff)
1955d9de2d3SDavid du Colombier 		;
1965d9de2d3SDavid du Colombier }
1975d9de2d3SDavid du Colombier 
1985d9de2d3SDavid du Colombier void
delay(int n)1995d9de2d3SDavid du Colombier delay(int n)
2005d9de2d3SDavid du Colombier {
2015d9de2d3SDavid du Colombier 	while(--n >= 0)
2025d9de2d3SDavid du Colombier 		microdelay(1000);
2035d9de2d3SDavid du Colombier }
204