]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm/clock.c
bcm: cleanup timer code
[plan9front.git] / sys / src / 9 / bcm / 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  *  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
32         SystimerFreq    = 1*Mhz,
33         MaxPeriod       = SystimerFreq / HZ,
34         MinPeriod       = 10,
35
36 };
37
38 typedef struct Systimers Systimers;
39 typedef struct Armtimer Armtimer;
40
41 struct Systimers {
42         u32int  cs;
43         u32int  clo;
44         u32int  chi;
45         u32int  c0;
46         u32int  c1;
47         u32int  c2;
48         u32int  c3;
49 };
50
51 struct Armtimer {
52         u32int  load;
53         u32int  val;
54         u32int  ctl;
55         u32int  irqack;
56         u32int  irq;
57         u32int  maskedirq;
58         u32int  reload;
59         u32int  predivider;
60         u32int  count;
61 };
62
63 enum {
64         CntPrescaleShift= 16,   /* freq is sys_clk/(prescale+1) */
65         CntPrescaleMask = 0xFF,
66         CntEnable       = 1<<9,
67         TmrDbgHalt      = 1<<8,
68         TmrEnable       = 1<<7,
69         TmrIntEnable    = 1<<5,
70         TmrPrescale1    = 0x00<<2,
71         TmrPrescale16   = 0x01<<2,
72         TmrPrescale256  = 0x02<<2,
73         CntWidth16      = 0<<1,
74         CntWidth32      = 1<<1,
75
76         /* generic timer (cortex-a7) */
77         Enable  = 1<<0,
78         Imask   = 1<<1,
79         Istatus = 1<<2,
80 };
81
82 static void
83 clockintr(Ureg *ureg, void *)
84 {
85         Systimers *tn;
86
87         if(m->machno != 0)
88                 panic("cpu%d: unexpected system timer interrupt", m->machno);
89         tn = (Systimers*)SYSTIMERS;
90         /* dismiss interrupt */
91         tn->cs = 1<<3;
92         timerintr(ureg, 0);
93 }
94
95 static void
96 localclockintr(Ureg *ureg, void *)
97 {
98         if(m->machno == 0)
99                 panic("cpu0: Unexpected local generic timer interrupt");
100         timerintr(ureg, 0);
101 }
102
103 void
104 clockshutdown(void)
105 {
106         Armtimer *tm;
107
108         tm = (Armtimer*)ARMTIMER;
109         tm->ctl = 0;
110 }
111
112 void
113 clockinit(void)
114 {
115         Systimers *tn;
116         Armtimer *tm;
117         ulong t0, t1, tstart, tend;
118
119         if(((cprdsc(0, CpID, CpIDfeat, 1) >> 16) & 0xF) != 0) {
120                 /* generic timer supported */
121                 cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysval, ~0);
122                 if(m->machno == 0){
123                         cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Imask);
124
125                         /* input clock is 19.2Mhz crystal */
126                         *(ulong*)(ARMLOCAL + Localctl) = 0;
127                         /* divide by (2^31/Prescaler) */
128                         *(ulong*)(ARMLOCAL + Prescaler) = (((uvlong)SystimerFreq<<31)/19200000)&~1UL;
129                 } else {
130                         cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysctl, Enable);
131                         intrenable(IRQcntpns, localclockintr, nil, 0, "clock");
132                 }
133         }
134
135         tn = (Systimers*)SYSTIMERS;
136         tstart = tn->clo;
137         do{
138                 t0 = lcycles();
139         }while(tn->clo == tstart);
140         tend = tstart + (SystimerFreq/100);
141         do{
142                 t1 = lcycles();
143         }while(tn->clo != tend);
144         t1 -= t0;
145         m->cpuhz = 100 * t1;
146         m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
147         m->cyclefreq = m->cpuhz;
148
149         if(m->machno == 0){
150                 tn->c3 = tn->clo - 1;
151                 intrenable(IRQtimer3, clockintr, nil, 0, "clock");
152
153                 tm = (Armtimer*)ARMTIMER;
154                 tm->load = 0;
155                 tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
156         }
157 }
158
159 void
160 timerset(uvlong next)
161 {
162         Systimers *tn;
163         uvlong now;
164         long period;
165
166         now = fastticks(nil);
167         period = next - now;
168         if(period < MinPeriod)
169                 period = MinPeriod;
170         else if(period > MaxPeriod)
171                 period = MaxPeriod;
172         if(m->machno)
173                 cpwrsc(0, CpTIMER, CpTIMERphys, CpTIMERphysval, period);
174         else{
175                 tn = (Systimers*)SYSTIMERS;
176                 tn->c3 = tn->clo + period;
177         }
178 }
179
180 uvlong
181 fastticks(uvlong *hz)
182 {
183         Systimers *tn;
184         ulong lo, hi;
185         uvlong now;
186
187         if(hz)
188                 *hz = SystimerFreq;
189         tn = (Systimers*)SYSTIMERS;
190         do{
191                 hi = tn->chi;
192                 lo = tn->clo;
193         }while(tn->chi != hi);
194         now = (uvlong)hi<<32 | lo;
195         return now;
196 }
197
198 ulong
199 perfticks(void)
200 {
201         Armtimer *tm;
202
203         tm = (Armtimer*)ARMTIMER;
204         return tm->count;
205 }
206
207 void
208 armtimerset(int n)
209 {
210         Armtimer *tm;
211
212         tm = (Armtimer*)ARMTIMER;
213         if(n > 0){
214                 tm->ctl |= TmrEnable|TmrIntEnable;
215                 tm->load = n;
216         }else{
217                 tm->load = 0;
218                 tm->ctl &= ~(TmrEnable|TmrIntEnable);
219                 tm->irq = 1;
220         }
221 }
222
223 ulong
224 µs(void)
225 {
226         if(SystimerFreq != 1*Mhz)
227                 return fastticks2us(fastticks(nil));
228         return ((Systimers*)SYSTIMERS)->clo;
229 }
230
231 void
232 microdelay(int n)
233 {
234         ulong now;
235
236         now = µs();
237         while(µs() - now < n);
238 }
239
240 void
241 delay(int n)
242 {
243         while(--n >= 0)
244                 microdelay(1000);
245 }