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