]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bitsy/clock.c
merge
[plan9front.git] / sys / src / 9 / 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 }