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