]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm64/clock.c
bcm64: use exclusive monitor on nrdy to wake up idlehands()
[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  *      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  *   For smp on bcm2836, use local generic timer for interrupts on cpu1-3
10  * Use ARM timer (32 bits) for perfticks
11  * Use ARM timer to force immediate interrupt
12  * Use cycle 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
122         syswr(CNTP_TVAL_EL0, ~0UL);
123         if(m->machno == 0){
124                 syswr(CNTP_CTL_EL0, Imask);
125
126                 *(u32int*)(ARMLOCAL + GPUirqroute) = 0;
127
128                 /* input clock is 19.2Mhz crystal */
129                 *(u32int*)(ARMLOCAL + Localctl) = 0;
130                 /* divide by (2^31/Prescaler) */
131                 *(u32int*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/19200000)&~1UL;
132         } else {
133                 syswr(CNTP_CTL_EL0, Enable);
134                 intrenable(IRQcntpns, localclockintr, nil, 0, "clock");
135         }
136
137         tn = (Systimers*)SYSTIMERS;
138         tstart = tn->clo;
139         do{
140                 t0 = lcycles();
141         }while(tn->clo == tstart);
142         tend = tstart + (SystimerFreq/100);
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
151         if(m->machno == 0){
152                 tn->cs = 1<<3;
153                 tn->c3 = tn->clo - 1;
154                 intrenable(IRQtimer3, clockintr, nil, 0, "clock");
155
156                 tm = (Armtimer*)ARMTIMER;
157                 tm->load = 0;
158                 tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
159         }
160 }
161
162 void
163 timerset(uvlong next)
164 {
165         Systimers *tn;
166         uvlong now;
167         long period;
168
169         now = fastticks(nil);
170         period = next - now;
171         if(period < MinPeriod)
172                 period = MinPeriod;
173         else if(period > MaxPeriod)
174                 period = MaxPeriod;
175         if(m->machno)
176                 syswr(CNTP_TVAL_EL0, period);
177         else{
178                 tn = (Systimers*)SYSTIMERS;
179                 tn->c3 = tn->clo + period;
180         }
181 }
182
183 uvlong
184 fastticks(uvlong *hz)
185 {
186         Systimers *tn;
187         ulong lo, hi;
188         uvlong now;
189
190         if(hz)
191                 *hz = SystimerFreq;
192         tn = (Systimers*)SYSTIMERS;
193         do{
194                 hi = tn->chi;
195                 lo = tn->clo;
196         }while(tn->chi != hi);
197         now = (uvlong)hi<<32 | lo;
198         return now;
199 }
200
201 ulong
202 perfticks(void)
203 {
204         Armtimer *tm;
205
206         tm = (Armtimer*)ARMTIMER;
207         return tm->count;
208 }
209
210 void
211 armtimerset(int n)
212 {
213         Armtimer *tm;
214
215         tm = (Armtimer*)ARMTIMER;
216         if(n > 0){
217                 tm->ctl |= TmrEnable|TmrIntEnable;
218                 tm->load = n;
219         }else{
220                 tm->load = 0;
221                 tm->ctl &= ~(TmrEnable|TmrIntEnable);
222                 tm->irq = 1;
223         }
224 }
225
226 ulong
227 µs(void)
228 {
229         if(SystimerFreq != 1*Mhz)
230                 return fastticks2us(fastticks(nil));
231         return ((Systimers*)SYSTIMERS)->clo;
232 }
233
234 void
235 microdelay(int n)
236 {
237         ulong now;
238
239         now = µs();
240         while(µs() - now < n);
241 }
242
243 void
244 delay(int n)
245 {
246         while(--n >= 0)
247                 microdelay(1000);
248 }
249
250 void
251 synccycles(void)
252 {
253         static Ref r1, r2;
254         int s;
255
256         s = splhi();
257         r2.ref = 0;
258         incref(&r1);
259         while(r1.ref != conf.nmach)
260                 ;
261 //      syswr(PMCR_EL0, 1<<6 | 7);
262         incref(&r2);
263         while(r2.ref != conf.nmach)
264                 ;
265         r1.ref = 0;
266         splx(s);
267 }