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