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