xref: /plan9/sys/src/9/bcm/clock.c (revision da091934ad5a991038edbfec621ba4743b1dcd18)
1 /*
2  * bcm2835 timers
3  *	System timers run at 1MHz (timers 1 and 2 are used by GPU)
4  *	ARM timer usually runs at 250MHz (may be slower in low power modes)
5  *	Cycle counter runs at 700MHz (unless overclocked)
6  *    All are free-running up-counters
7  *
8  * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
9  * Use ARM timer (32 bits) for perfticks
10  * Use ARM timer to force immediate interrupt
11  * Use cycle counter for cycles()
12  */
13 
14 #include "u.h"
15 #include "../port/lib.h"
16 #include "mem.h"
17 #include "dat.h"
18 #include "fns.h"
19 #include "io.h"
20 
21 enum {
22 	SYSTIMERS	= VIRTIO+0x3000,
23 	ARMTIMER	= VIRTIO+0xB400,
24 
25 	SystimerFreq	= 1*Mhz,
26 	MaxPeriod	= SystimerFreq / HZ,
27 	MinPeriod	= SystimerFreq / (100*HZ),
28 };
29 
30 typedef struct Systimers Systimers;
31 typedef struct Armtimer Armtimer;
32 
33 struct Systimers {
34 	u32int	cs;
35 	u32int	clo;
36 	u32int	chi;
37 	u32int	c0;
38 	u32int	c1;
39 	u32int	c2;
40 	u32int	c3;
41 };
42 
43 struct Armtimer {
44 	u32int	load;
45 	u32int	val;
46 	u32int	ctl;
47 	u32int	irqack;
48 	u32int	irq;
49 	u32int	maskedirq;
50 	u32int	reload;
51 	u32int	predivider;
52 	u32int	count;
53 };
54 
55 enum {
56 	CntPrescaleShift= 16,	/* freq is sys_clk/(prescale+1) */
57 	CntPrescaleMask	= 0xFF,
58 	CntEnable	= 1<<9,
59 	TmrDbgHalt	= 1<<8,
60 	TmrEnable	= 1<<7,
61 	TmrIntEnable	= 1<<5,
62 	TmrPrescale1	= 0x00<<2,
63 	TmrPrescale16	= 0x01<<2,
64 	TmrPrescale256	= 0x02<<2,
65 	CntWidth16	= 0<<1,
66 	CntWidth32	= 1<<1,
67 };
68 
69 static void
clockintr(Ureg * ureg,void *)70 clockintr(Ureg *ureg, void *)
71 {
72 	Systimers *tn;
73 
74 	tn = (Systimers*)SYSTIMERS;
75 	/* dismiss interrupt */
76 	tn->cs = 1<<3;
77 	timerintr(ureg, 0);
78 }
79 
80 void
clockshutdown(void)81 clockshutdown(void)
82 {
83 	Armtimer *tm;
84 
85 	tm = (Armtimer*)ARMTIMER;
86 	tm->ctl = 0;
87 	wdogoff();
88 }
89 
90 void
clockinit(void)91 clockinit(void)
92 {
93 	Systimers *tn;
94 	Armtimer *tm;
95 	u32int t0, t1, tstart, tend;
96 
97 	tn = (Systimers*)SYSTIMERS;
98 	tm = (Armtimer*)ARMTIMER;
99 	tm->load = 0;
100 	tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
101 
102 	tstart = tn->clo;
103 	do{
104 		t0 = lcycles();
105 	}while(tn->clo == tstart);
106 	tend = tstart + 10000;
107 	do{
108 		t1 = lcycles();
109 	}while(tn->clo != tend);
110 	t1 -= t0;
111 	m->cpuhz = 100 * t1;
112 	m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
113 	m->cyclefreq = m->cpuhz;
114 
115 	tn->c3 = tn->clo - 1;
116 	intrenable(IRQtimer3, clockintr, nil, 0, "clock");
117 }
118 
119 void
timerset(uvlong next)120 timerset(uvlong next)
121 {
122 	Systimers *tn;
123 	vlong now, period;
124 
125 	tn = (Systimers*)SYSTIMERS;
126 	now = fastticks(nil);
127 	period = next - fastticks(nil);
128 	if(period < MinPeriod)
129 		next = now + MinPeriod;
130 	else if(period > MaxPeriod)
131 		next = now + MaxPeriod;
132 	tn->c3 = (ulong)next;
133 }
134 
135 uvlong
fastticks(uvlong * hz)136 fastticks(uvlong *hz)
137 {
138 	Systimers *tn;
139 	ulong lo, hi;
140 
141 	tn = (Systimers*)SYSTIMERS;
142 	if(hz)
143 		*hz = SystimerFreq;
144 	do{
145 		hi = tn->chi;
146 		lo = tn->clo;
147 	}while(tn->chi != hi);
148 	m->fastclock = (uvlong)hi<<32 | lo;
149 	return m->fastclock;
150 }
151 
152 ulong
perfticks(void)153 perfticks(void)
154 {
155 	Armtimer *tm;
156 
157 	tm = (Armtimer*)ARMTIMER;
158 	return tm->count;
159 }
160 
161 void
armtimerset(int n)162 armtimerset(int n)
163 {
164 	Armtimer *tm;
165 
166 	tm = (Armtimer*)ARMTIMER;
167 	if(n > 0){
168 		tm->ctl |= TmrEnable|TmrIntEnable;
169 		tm->load = n;
170 	}else{
171 		tm->load = 0;
172 		tm->ctl &= ~(TmrEnable|TmrIntEnable);
173 		tm->irq = 1;
174 	}
175 }
176 
177 ulong
s(void)178 µs(void)
179 {
180 	if(SystimerFreq != 1*Mhz)
181 		return fastticks2us(fastticks(nil));
182 	return fastticks(nil);
183 }
184 
185 void
microdelay(int n)186 microdelay(int n)
187 {
188 	Systimers *tn;
189 	u32int now, diff;
190 
191 	tn = (Systimers*)SYSTIMERS;
192 	diff = n + 1;
193 	now = tn->clo;
194 	while(tn->clo - now < diff)
195 		;
196 }
197 
198 void
delay(int n)199 delay(int n)
200 {
201 	while(--n >= 0)
202 		microdelay(1000);
203 }
204