xref: /plan9-contrib/sys/src/9/bcm/clock.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1 /*
2  * bcm283[56] 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  *  Cortex-a7 has local generic timers per cpu (which we run at 1MHz)
8  *
9  * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
10  *   For smp on bcm2836, use local generic timer for interrupts on cpu1-3
11  * Use ARM timer (32 bits) for perfticks
12  * Use ARM timer to force immediate interrupt
13  * Use cycle counter for cycles()
14  */
15 
16 #include "u.h"
17 #include "../port/lib.h"
18 #include "mem.h"
19 #include "dat.h"
20 #include "fns.h"
21 #include "io.h"
22 #include "ureg.h"
23 #include "arm.h"
24 
25 enum {
26 	SYSTIMERS	= VIRTIO+0x3000,
27 	ARMTIMER	= VIRTIO+0xB400,
28 
29 	Localctl	= 0x00,
30 	Prescaler	= 0x08,
31 	Localintpending	= 0x60,
32 
33 	SystimerFreq	= 1*Mhz,
34 	MaxPeriod	= SystimerFreq / HZ,
35 	MinPeriod	= 10,
36 
37 };
38 
39 typedef struct Systimers Systimers;
40 typedef struct Armtimer Armtimer;
41 
42 struct Systimers {
43 	u32int	cs;
44 	u32int	clo;
45 	u32int	chi;
46 	u32int	c0;
47 	u32int	c1;
48 	u32int	c2;
49 	u32int	c3;
50 };
51 
52 struct Armtimer {
53 	u32int	load;
54 	u32int	val;
55 	u32int	ctl;
56 	u32int	irqack;
57 	u32int	irq;
58 	u32int	maskedirq;
59 	u32int	reload;
60 	u32int	predivider;
61 	u32int	count;
62 };
63 
64 enum {
65 	CntPrescaleShift= 16,	/* freq is sys_clk/(prescale+1) */
66 	CntPrescaleMask	= 0xFF,
67 	CntEnable	= 1<<9,
68 	TmrDbgHalt	= 1<<8,
69 	TmrEnable	= 1<<7,
70 	TmrIntEnable	= 1<<5,
71 	TmrPrescale1	= 0x00<<2,
72 	TmrPrescale16	= 0x01<<2,
73 	TmrPrescale256	= 0x02<<2,
74 	CntWidth16	= 0<<1,
75 	CntWidth32	= 1<<1,
76 
77 	/* generic timer (cortex-a7) */
78 	Enable	= 1<<0,
79 	Imask	= 1<<1,
80 	Istatus = 1<<2,
81 };
82 
83 static void
clockintr(Ureg * ureg,void *)84 clockintr(Ureg *ureg, void *)
85 {
86 	Systimers *tn;
87 
88 	if(m->machno != 0)
89 		panic("cpu%d: unexpected system timer interrupt", m->machno);
90 	tn = (Systimers*)SYSTIMERS;
91 	/* dismiss interrupt */
92 	tn->c3 = tn->clo - 1;
93 	tn->cs = 1<<3;
94 	timerintr(ureg, 0);
95 }
96 
97 static void
localclockintr(Ureg * ureg,void *)98 localclockintr(Ureg *ureg, void *)
99 {
100 	if(m->machno == 0)
101 		panic("cpu0: Unexpected local generic timer interrupt");
102 	cpwrtimerphysctl(Imask|Enable);
103 	timerintr(ureg, 0);
104 }
105 
106 void
clockshutdown(void)107 clockshutdown(void)
108 {
109 	Armtimer *tm;
110 
111 	tm = (Armtimer*)ARMTIMER;
112 	tm->ctl = 0;
113 	if(cpuserver)
114 		wdogfeed();
115 	else
116 		wdogoff();
117 }
118 
119 void
clockinit(void)120 clockinit(void)
121 {
122 	Systimers *tn;
123 	Armtimer *tm;
124 	u32int t0, t1, tstart, tend;
125 
126 	if(((cprdfeat1() >> 16) & 0xF) != 0) {
127 		/* generic timer supported */
128 		if(m->machno == 0){
129 			/* input clock is 19.2MHz or 54MHz crystal */
130 			*(ulong*)(ARMLOCAL + Localctl) = 0;
131 			/* divide by (2^31/Prescaler) for 1Mhz */
132 			*(ulong*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/soc.oscfreq)&~1UL;
133 		}
134 		cpwrtimerphysctl(Imask);
135 	}
136 
137 	tn = (Systimers*)SYSTIMERS;
138 	tstart = tn->clo;
139 	do{
140 		t0 = lcycles();
141 	}while(tn->clo == tstart);
142 	tend = tstart + 10000;
143 	do{
144 		t1 = lcycles();
145 	}while(tn->clo != tend);
146 	t1 -= t0;
147 	m->cpuhz = 100 * t1;
148 	m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
149 	m->cyclefreq = m->cpuhz;
150 	if(m->machno == 0){
151 		tn->c3 = tn->clo - 1;
152 		tm = (Armtimer*)ARMTIMER;
153 		tm->load = 0;
154 		tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
155 		intrenable(IRQtimer3, clockintr, nil, 0, "clock");
156 	}else
157 		intrenable(IRQcntpns, localclockintr, nil, 0, "clock");
158 }
159 
160 void
timerset(uvlong next)161 timerset(uvlong next)
162 {
163 	Systimers *tn;
164 	uvlong now;
165 	long period;
166 
167 	now = fastticks(nil);
168 	period = next - now;
169 	if(period < MinPeriod)
170 		period = MinPeriod;
171 	else if(period > MaxPeriod)
172 		period = MaxPeriod;
173 	if(m->machno > 0){
174 		cpwrtimerphysval(period);
175 		cpwrtimerphysctl(Enable);
176 	}else{
177 		tn = (Systimers*)SYSTIMERS;
178 		tn->c3 = tn->clo + period;
179 	}
180 }
181 
182 uvlong
fastticks(uvlong * hz)183 fastticks(uvlong *hz)
184 {
185 	Systimers *tn;
186 	ulong lo, hi;
187 	uvlong now;
188 
189 	if(hz)
190 		*hz = SystimerFreq;
191 	tn = (Systimers*)SYSTIMERS;
192 	do{
193 		hi = tn->chi;
194 		lo = tn->clo;
195 	}while(tn->chi != hi);
196 	now = (uvlong)hi<<32 | lo;
197 	return now;
198 }
199 
200 ulong
perfticks(void)201 perfticks(void)
202 {
203 	Armtimer *tm;
204 
205 	tm = (Armtimer*)ARMTIMER;
206 	return tm->count;
207 }
208 
209 void
armtimerset(int n)210 armtimerset(int n)
211 {
212 	Armtimer *tm;
213 
214 	tm = (Armtimer*)ARMTIMER;
215 	if(n > 0){
216 		tm->ctl |= TmrEnable|TmrIntEnable;
217 		tm->load = n;
218 	}else{
219 		tm->load = 0;
220 		tm->ctl &= ~(TmrEnable|TmrIntEnable);
221 		tm->irq = 1;
222 	}
223 }
224 
225 ulong
s(void)226 µs(void)
227 {
228 	if(SystimerFreq != 1*Mhz)
229 		return fastticks2us(fastticks(nil));
230 	return ((Systimers*)SYSTIMERS)->clo;
231 }
232 
233 void
microdelay(int n)234 microdelay(int n)
235 {
236 	Systimers *tn;
237 	u32int now, diff;
238 
239 	diff = n + 1;
240 	tn = (Systimers*)SYSTIMERS;
241 	now = tn->clo;
242 	while(tn->clo - now < diff)
243 		;
244 }
245 
246 void
delay(int n)247 delay(int n)
248 {
249 	while(--n >= 0)
250 		microdelay(1000);
251 }
252