]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm64/clock.c
arm64: use generic timer virtual counter for cycles()
[plan9front.git] / sys / src / 9 / bcm64 / clock.c
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  *    All are free-running up-counters
6  *
7  * Use system timer 3 (64 bits) for hzclock interrupts and fastticks
8  *   For smp on bcm2836, use local generic timer for interrupts on cpu1-3
9  * Use ARM timer (32 bits) for perfticks
10  * Use ARM timer to force immediate interrupt
11  * Use performance cycle counter for lcycles()
12  * Use generic timer virtual counter for cycles()
13  */
14
15 #include "u.h"
16 #include "../port/lib.h"
17 #include "mem.h"
18 #include "dat.h"
19 #include "fns.h"
20 #include "io.h"
21 #include "ureg.h"
22 #include "sysreg.h"
23
24 enum {
25         SYSTIMERS       = VIRTIO+0x3000,
26         ARMTIMER        = VIRTIO+0xB400,
27
28         Localctl        = 0x00,
29         Prescaler       = 0x08,
30         GPUirqroute     = 0x0C,
31
32         SystimerFreq    = 1*Mhz,
33         MaxPeriod       = SystimerFreq / HZ,
34         MinPeriod       = 10,
35 };
36
37 typedef struct Systimers Systimers;
38 typedef struct Armtimer Armtimer;
39
40 struct Systimers {
41         u32int  cs;
42         u32int  clo;
43         u32int  chi;
44         u32int  c0;
45         u32int  c1;
46         u32int  c2;
47         u32int  c3;
48 };
49
50 struct Armtimer {
51         u32int  load;
52         u32int  val;
53         u32int  ctl;
54         u32int  irqack;
55         u32int  irq;
56         u32int  maskedirq;
57         u32int  reload;
58         u32int  predivider;
59         u32int  count;
60 };
61
62 enum {
63         CntPrescaleShift= 16,   /* freq is sys_clk/(prescale+1) */
64         CntPrescaleMask = 0xFF,
65         CntEnable       = 1<<9,
66         TmrDbgHalt      = 1<<8,
67         TmrEnable       = 1<<7,
68         TmrIntEnable    = 1<<5,
69         TmrPrescale1    = 0x00<<2,
70         TmrPrescale16   = 0x01<<2,
71         TmrPrescale256  = 0x02<<2,
72         CntWidth16      = 0<<1,
73         CntWidth32      = 1<<1,
74
75         /* generic timer (cortex-a7) */
76         Enable  = 1<<0,
77         Imask   = 1<<1,
78         Istatus = 1<<2,
79 };
80
81 static void
82 clockintr(Ureg *ureg, void *)
83 {
84         Systimers *tn;
85
86         if(m->machno != 0)
87                 panic("cpu%d: unexpected system timer interrupt", m->machno);
88         tn = (Systimers*)SYSTIMERS;
89         /* dismiss interrupt */
90         tn->cs = 1<<3;
91         timerintr(ureg, 0);
92 }
93
94 static void
95 localclockintr(Ureg *ureg, void *)
96 {
97         if(m->machno == 0)
98                 panic("cpu0: Unexpected local generic timer interrupt");
99         timerintr(ureg, 0);
100 }
101
102 void
103 clockshutdown(void)
104 {
105         Armtimer *tm;
106
107         tm = (Armtimer*)ARMTIMER;
108         tm->ctl = 0;
109 }
110
111 void
112 clockinit(void)
113 {
114         Systimers *tn;
115         Armtimer *tm;
116         ulong t0, t1, tstart, tend;
117
118         syswr(PMCR_EL0, 1<<6 | 7);
119         syswr(PMCNTENSET, 1<<31);
120         syswr(PMUSERENR_EL0, 1<<2);
121         syswr(CNTKCTL_EL1, 1<<1);
122
123         syswr(CNTP_TVAL_EL0, ~0UL);
124         if(m->machno == 0){
125                 syswr(CNTP_CTL_EL0, Imask);
126
127                 *(u32int*)(ARMLOCAL + GPUirqroute) = 0;
128
129                 /* input clock to OSC */
130                 *(u32int*)(ARMLOCAL + Localctl) = 0;
131
132                 /* divide by (2^31/Prescaler) */
133                 *(u32int*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/soc.oscfreq)&~1UL;
134         } else {
135                 syswr(CNTP_CTL_EL0, Enable);
136                 intrenable(IRQcntpns, localclockintr, nil, BUSUNKNOWN, "clock");
137         }
138
139         tn = (Systimers*)SYSTIMERS;
140         tstart = tn->clo;
141         do{
142                 t0 = lcycles();
143         }while(tn->clo == tstart);
144         tend = tstart + (SystimerFreq/100);
145         do{
146                 t1 = lcycles();
147         }while(tn->clo < tend);
148         t1 -= t0;
149         m->cpuhz = 100 * t1;
150         m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
151
152         /*
153          * Cyclefreq used to be the same as cpuhz as
154          * we where using the PMCCNTR_EL0 which counts
155          * per core cpu cycles. But is is kind of useless
156          * in userspace because each core has a differnet
157          * counter and it stops when the core is idle (WFI).
158          * So we change it to use the generic timer
159          * virtual counter register CNTVCT_EL0 instead
160          * running at the same frequency as system timer.
161          * (CNTFRQ_EL0 is WRONG on raspberry pi).
162          */
163         m->cyclefreq = SystimerFreq;
164
165         if(m->machno == 0){
166                 tn->cs = 1<<3;
167                 tn->c3 = tn->clo - 1;
168                 intrenable(IRQtimer3, clockintr, nil, BUSUNKNOWN, "clock");
169
170                 tm = (Armtimer*)ARMTIMER;
171                 tm->load = 0;
172                 tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
173         }
174 }
175
176 void
177 timerset(uvlong next)
178 {
179         Systimers *tn;
180         uvlong now;
181         long period;
182
183         now = fastticks(nil);
184         period = next - now;
185         if(period < MinPeriod)
186                 period = MinPeriod;
187         else if(period > MaxPeriod)
188                 period = MaxPeriod;
189         if(m->machno)
190                 syswr(CNTP_TVAL_EL0, period);
191         else{
192                 tn = (Systimers*)SYSTIMERS;
193                 tn->c3 = tn->clo + period;
194         }
195 }
196
197 uvlong
198 fastticks(uvlong *hz)
199 {
200         Systimers *tn;
201         ulong lo, hi;
202         uvlong now;
203
204         if(hz)
205                 *hz = SystimerFreq;
206         tn = (Systimers*)SYSTIMERS;
207         do{
208                 hi = tn->chi;
209                 lo = tn->clo;
210         }while(tn->chi != hi);
211         now = (uvlong)hi<<32 | lo;
212         return now;
213 }
214
215 ulong
216 perfticks(void)
217 {
218         Armtimer *tm;
219
220         tm = (Armtimer*)ARMTIMER;
221         return tm->count;
222 }
223
224 void
225 armtimerset(int n)
226 {
227         Armtimer *tm;
228
229         tm = (Armtimer*)ARMTIMER;
230         if(n > 0){
231                 tm->ctl |= TmrEnable|TmrIntEnable;
232                 tm->load = n;
233         }else{
234                 tm->load = 0;
235                 tm->ctl &= ~(TmrEnable|TmrIntEnable);
236                 tm->irq = 1;
237         }
238 }
239
240 ulong
241 µs(void)
242 {
243         if(SystimerFreq != 1*Mhz)
244                 return fastticks2us(fastticks(nil));
245         return ((Systimers*)SYSTIMERS)->clo;
246 }
247
248 void
249 microdelay(int n)
250 {
251         ulong now;
252
253         now = µs();
254         while(µs() - now < n);
255 }
256
257 void
258 delay(int n)
259 {
260         while(--n >= 0)
261                 microdelay(1000);
262 }
263
264 void
265 synccycles(void)
266 {
267         static Ref r1, r2;
268         int s;
269
270         s = splhi();
271         r2.ref = 0;
272         incref(&r1);
273         while(r1.ref != conf.nmach)
274                 ;
275 //      syswr(PMCR_EL0, 1<<6 | 7);
276         incref(&r2);
277         while(r2.ref != conf.nmach)
278                 ;
279         r1.ref = 0;
280         splx(s);
281 }