FreeBSD/Linux Kernel Cross Reference
sys/bitsy/clock.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8 #include "../port/error.h"
9
10
11 enum {
12 RTCREGS = 0x90010000, /* real time clock registers */
13 RTSR_al = 0x01, /* alarm detected */
14 RTSR_hz = 0x02, /* 1Hz tick */
15 RTSR_ale= 0x04, /* alarm interrupt enable */
16 RTSR_hze= 0x08, /* 1Hz tick enable */
17
18 Never = 0xffffffff,
19 };
20
21 typedef struct OSTimer
22 {
23 ulong osmr[4]; /* match registers */
24 volatile ulong oscr; /* counter register */
25 ulong ossr; /* status register */
26 ulong ower; /* watchdog enable register */
27 ulong oier; /* timer interrupt enable register */
28 } OSTimer;
29
30 typedef struct RTCregs
31 {
32 ulong rtar; /* alarm */
33 ulong rcnr; /* count */
34 ulong rttr; /* trim */
35 ulong dummy; /* hole */
36 ulong rtsr; /* status */
37 } RTCregs;
38
39 OSTimer *timerregs = (OSTimer*)OSTIMERREGS;
40 RTCregs *rtcregs = (RTCregs*)RTCREGS;
41 static int clockinited;
42
43 static void clockintr(Ureg*, void*);
44 static void rtcintr(Ureg*, void*);
45 static Tval when; /* scheduled time of next interrupt */
46
47 long timeradjust;
48
49 enum
50 {
51 Minfreq = ClockFreq/HZ, /* At least one interrupt per HZ (50 ms) */
52 Maxfreq = ClockFreq/10000, /* At most one interrupt every 100 µs */
53 };
54
55 ulong
56 clockpower(int on)
57 {
58 static ulong savedtime;
59
60 if (on){
61 timerregs->ossr |= 1<<0;
62 timerregs->oier = 1<<0;
63 timerregs->osmr[0] = timerregs->oscr + Minfreq;
64 if (rtcregs->rttr == 0){
65 rtcregs->rttr = 0x8000; // nominal frequency.
66 rtcregs->rcnr = 0;
67 rtcregs->rtar = 0xffffffff;
68 rtcregs->rtsr |= RTSR_ale;
69 rtcregs->rtsr |= RTSR_hze;
70 }
71 if (rtcregs->rcnr > savedtime)
72 return rtcregs->rcnr - savedtime;
73 } else
74 savedtime = rtcregs->rcnr;
75 clockinited = on;
76 return 0L;
77 }
78
79 void
80 clockinit(void)
81 {
82 ulong x;
83 ulong id;
84
85 /* map the clock registers */
86 timerregs = mapspecial(OSTIMERREGS, sizeof(OSTimer));
87 rtcregs = mapspecial(RTCREGS, sizeof(RTCregs));
88
89 /* enable interrupts on match register 0, turn off all others */
90 timerregs->ossr |= 1<<0;
91 intrenable(IRQ, IRQtimer0, clockintr, nil, "clock");
92 timerregs->oier = 1<<0;
93
94 /* figure out processor frequency */
95 x = powerregs->ppcr & 0x1f;
96 conf.hz = ClockFreq*(x*4+16);
97 conf.mhz = (conf.hz+499999)/1000000;
98
99 /* get processor type */
100 id = getcpuid();
101
102 print("%lud MHZ ARM, ver %lux/part %lux/step %lud\n", conf.mhz,
103 (id>>16)&0xff, (id>>4)&0xfff, id&0xf);
104
105 /* post interrupt 1/HZ secs from now */
106 when = timerregs->oscr + Minfreq;
107 timerregs->osmr[0] = when;
108
109 /* enable RTC interrupts and alarms */
110 intrenable(IRQ, IRQrtc, rtcintr, nil, "rtc");
111 rtcregs->rttr = 0x8000; // make rcnr 1Hz
112 rtcregs->rcnr = 0; // reset counter
113 rtcregs->rtsr |= RTSR_al;
114 rtcregs->rtsr |= RTSR_ale;
115
116 timersinit();
117
118 clockinited = 1;
119 }
120
121 /* turn 32 bit counter into a 64 bit one. since todfix calls
122 * us at least once a second and we overflow once every 1165
123 * seconds, we won't miss an overflow.
124 */
125 uvlong
126 fastticks(uvlong *hz)
127 {
128 static uvlong high;
129 static ulong last;
130 ulong x;
131
132 if(hz != nil)
133 *hz = ClockFreq;
134 x = timerregs->oscr;
135 if(x < last)
136 high += 1LL<<32;
137 last = x;
138 return high+x;
139 }
140
141 ulong
142 µs(void)
143 {
144 return fastticks2us(fastticks(nil));
145 }
146
147 void
148 timerset(Tval v)
149 {
150 ulong next, tics; /* Must be unsigned! */
151 static int count;
152
153 next = v;
154
155 /* post next interrupt: calculate # of tics from now */
156 tics = next - timerregs->oscr - Maxfreq;
157 if (tics > Minfreq){
158 timeradjust++;
159 next = timerregs->oscr + Maxfreq;
160 }
161 timerregs->osmr[0] = next;
162 }
163
164 static void
165 clockintr(Ureg *ureg, void*)
166 {
167 /* reset previous interrupt */
168 timerregs->ossr |= 1<<0;
169 when += Minfreq;
170 timerregs->osmr[0] = when; /* insurance */
171
172 timerintr(ureg, when);
173 }
174
175 void
176 rtcalarm(ulong secs)
177 {
178 vlong t;
179
180 if (t == 0){
181 iprint("RTC alarm cancelled\n");
182 rtcregs->rtsr &= ~RTSR_ale;
183 rtcregs->rtar = 0xffffffff;
184 } else {
185 t = todget(nil);
186 t = t / 1000000000ULL; // nsec to secs
187 if (secs < t)
188 return;
189 secs -= t;
190 iprint("RTC alarm set to %uld seconds from now\n", secs);
191 rtcregs->rtar = rtcregs->rcnr + secs;
192 rtcregs->rtsr|= RTSR_ale;
193 }
194 }
195
196 static void
197 rtcintr(Ureg*, void*)
198 {
199 /* reset interrupt */
200 rtcregs->rtsr&= ~RTSR_ale;
201 rtcregs->rtsr&= ~RTSR_al;
202
203 rtcregs->rtar = 0;
204 iprint("RTC alarm: %lud\n", rtcregs->rcnr);
205 }
206
207 void
208 delay(int ms)
209 {
210 ulong start;
211 int i;
212
213 if(clockinited){
214 while(ms-- > 0){
215 start = timerregs->oscr;
216 while(timerregs->oscr-start < ClockFreq/1000)
217 ;
218 }
219 } else {
220 while(ms-- > 0){
221 for(i = 0; i < 1000; i++)
222 ;
223 }
224 }
225 }
226
227 void
228 microdelay(int µs)
229 {
230 ulong start;
231 int i;
232
233 µs++;
234 if(clockinited){
235 start = timerregs->oscr;
236 while(timerregs->oscr - start < 1UL+(µs*ClockFreq)/1000000UL)
237 ;
238 } else {
239 while(µs-- > 0){
240 for(i = 0; i < 10; i++)
241 ;
242 }
243 }
244 }
245
246 /*
247 * performance measurement ticks. must be low overhead.
248 * doesn't have to count over a second.
249 */
250 ulong
251 perfticks(void)
252 {
253 return timerregs->oscr;
254 }
Cache object: 9e5ecc7ed675c381e947f0b4f696b916
|