]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm/clock.c
add raspberry pi kernel (from sources)
[plan9front.git] / sys / src / 9 / bcm / clock.c
1 /*
2  * bcm2835 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  * Use ARM timer (32 bits) for perfticks
10  * Use ARM timer to force immediate interrupt
11  * Use cycle counter for cycles()
12  */
13
14 #include "u.h"
15 #include "../port/lib.h"
16 #include "mem.h"
17 #include "dat.h"
18 #include "fns.h"
19 #include "io.h"
20
21 enum {
22         SYSTIMERS       = VIRTIO+0x3000,
23         ARMTIMER        = VIRTIO+0xB400,
24
25         SystimerFreq    = 1*Mhz,
26         MaxPeriod       = SystimerFreq / HZ,
27         MinPeriod       = SystimerFreq / (100*HZ),
28 };
29
30 typedef struct Systimers Systimers;
31 typedef struct Armtimer Armtimer;
32
33 struct Systimers {
34         u32int  cs;
35         u32int  clo;
36         u32int  chi;
37         u32int  c0;
38         u32int  c1;
39         u32int  c2;
40         u32int  c3;
41 };
42
43 struct Armtimer {
44         u32int  load;
45         u32int  val;
46         u32int  ctl;
47         u32int  irqack;
48         u32int  irq;
49         u32int  maskedirq;
50         u32int  reload;
51         u32int  predivider;
52         u32int  count;
53 };
54
55 enum {
56         CntPrescaleShift= 16,   /* freq is sys_clk/(prescale+1) */
57         CntPrescaleMask = 0xFF,
58         CntEnable       = 1<<9,
59         TmrDbgHalt      = 1<<8,
60         TmrEnable       = 1<<7,
61         TmrIntEnable    = 1<<5,
62         TmrPrescale1    = 0x00<<2,
63         TmrPrescale16   = 0x01<<2,
64         TmrPrescale256  = 0x02<<2,
65         CntWidth16      = 0<<1,
66         CntWidth32      = 1<<1,
67 };
68
69 static void
70 clockintr(Ureg *ureg, void *)
71 {
72         Systimers *tn;
73
74         tn = (Systimers*)SYSTIMERS;
75         /* dismiss interrupt */
76         tn->cs = 1<<3;
77         timerintr(ureg, 0);
78 }
79
80 void
81 clockshutdown(void)
82 {
83         Armtimer *tm;
84
85         tm = (Armtimer*)ARMTIMER;
86         tm->ctl = 0;
87 }
88
89 void
90 clockinit(void)
91 {
92         Systimers *tn;
93         Armtimer *tm;
94         u32int t0, t1, tstart, tend;
95
96         tn = (Systimers*)SYSTIMERS;
97         tm = (Armtimer*)ARMTIMER;
98         tm->load = 0;
99         tm->ctl = TmrPrescale1|CntEnable|CntWidth32;
100         coherence();
101
102         tstart = tn->clo;
103         do{
104                 t0 = lcycles();
105         }while(tn->clo == tstart);
106         tend = tstart + 10000;
107         do{
108                 t1 = lcycles();
109         }while(tn->clo != tend);
110         t1 -= t0;
111         m->cpuhz = 100 * t1;
112         m->cpumhz = (m->cpuhz + Mhz/2 - 1) / Mhz;
113         m->cyclefreq = m->cpuhz;
114
115         tn->c3 = tn->clo - 1;
116         intrenable(IRQtimer3, clockintr, nil, 0, "clock");
117 }
118
119 void
120 timerset(uvlong next)
121 {
122         Systimers *tn;
123         vlong now, period;
124
125         tn = (Systimers*)SYSTIMERS;
126         now = fastticks(nil);
127         period = next - fastticks(nil);
128         if(period < MinPeriod)
129                 next = now + MinPeriod;
130         else if(period > MaxPeriod)
131                 next = now + MaxPeriod;
132         tn->c3 = (ulong)next;
133 }
134
135 uvlong
136 fastticks(uvlong *hz)
137 {
138         Systimers *tn;
139         ulong lo, hi;
140
141         tn = (Systimers*)SYSTIMERS;
142         if(hz)
143                 *hz = SystimerFreq;
144         do{
145                 hi = tn->chi;
146                 lo = tn->clo;
147         }while(tn->chi != hi);
148         m->fastclock = (uvlong)hi<<32 | lo;
149         return m->fastclock;
150 }
151
152 ulong
153 perfticks(void)
154 {
155         Armtimer *tm;
156
157         tm = (Armtimer*)ARMTIMER;
158         return tm->count;
159 }
160
161 void
162 armtimerset(int n)
163 {
164         Armtimer *tm;
165
166         tm = (Armtimer*)ARMTIMER;
167         if(n > 0){
168                 tm->ctl |= TmrEnable|TmrIntEnable;
169                 tm->load = n;
170         }else{
171                 tm->load = 0;
172                 tm->ctl &= ~(TmrEnable|TmrIntEnable);
173                 tm->irq = 1;
174         }
175         coherence();
176 }
177
178 ulong
179 µs(void)
180 {
181         if(SystimerFreq != 1*Mhz)
182                 return fastticks2us(fastticks(nil));
183         return fastticks(nil);
184 }
185
186 void
187 microdelay(int n)
188 {
189         Systimers *tn;
190         u32int now, diff;
191
192         tn = (Systimers*)SYSTIMERS;
193         diff = n + 1;
194         now = tn->clo;
195         while(tn->clo - now < diff)
196                 ;
197 }
198
199 void
200 delay(int n)
201 {
202         while(--n >= 0)
203                 microdelay(1000);
204 }