]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/omap/clock.c
kernel: dont use atomic increment for Proc.nlocks, maintain Lock.m for lock(), use...
[plan9front.git] / sys / src / 9 / omap / clock.c
1 /*
2  * omap3530 clocks
3  *
4  * timers count up to zero.
5  *
6  * the source clock signals for the timers are sometimes selectable.  for
7  * WDTIMER[23] and GPTIMER12, it's always the 32kHz clock.  for the
8  * others, it can be the 32kHz clock or the system clock.  we use only
9  * WDTIMER2 and GPTIMER[12], and configure GPTIMER[12] in archomap.c to
10  * use the 32kHZ clock.  WDTIMER1 is not accessible to us on GP
11  * (general-purpose) omaps.
12  */
13 #include "u.h"
14 #include "../port/lib.h"
15 #include "mem.h"
16 #include "dat.h"
17 #include "fns.h"
18 #include "arm.h"
19
20 enum {
21         Debug           = 0,
22
23         Tn0             = PHYSTIMER1,
24         Tn1             = PHYSTIMER2,
25
26         /* irq 36 is watchdog timer module 3 overflow */
27         Tn0irq          = 37,                   /* base IRQ for all timers */
28
29         Freebase        = 1,                    /* base of free-running timer */
30
31         /*
32          * clock is 32K (32,768) Hz, so one tick is 30.517µs,
33          * so 327.68 ticks is 10ms, 32.768 ticks is 1ms.
34          */
35         Clockfreqbase   = 32 * 1024,            /* base rate in Hz */
36         Tcycles         = Clockfreqbase / HZ,   /* cycles per clock tick */
37
38         MinPeriod       = (Tcycles / 100 < 2? 2: Tcycles / 100),
39         MaxPeriod       = Tcycles,
40
41         Dogtimeout      = 20 * Clockfreqbase,   /* was 4 s.; must be ≤ 21 s. */
42 };
43
44 enum {
45         /* ticpcfg bits */
46         Noidle          = 1<<3,
47         Softreset       = 1<<1,
48
49         /* tistat bits */
50         Resetdone       = 1<<0,
51
52         /* tisr/tier bits */
53         Ovf_it          = 1<<1,         /* gp: overflow intr */
54         Mat_it          = 1<<0,         /* gp: match intr */
55         Wdovf_it        = 1<<0,         /* wdog: overflow intr */
56
57         /* tclr bits */
58         Ar              = 1<<1,         /* gp only: autoreload mode overflow */
59         St              = 1<<0,         /* gp only: start the timer */
60 };
61
62 /* omap35x timer registers */
63 typedef struct Timerregs Timerregs;
64 struct Timerregs {
65         /* common to all timers, gp and watchdog */
66         uchar   pad0[0x10];
67         ulong   ticpcfg;
68         ulong   tistat;         /* ro: low bit: reset done */
69         ulong   tisr;
70         ulong   tier;
71         ulong   twer;
72         ulong   tclr;
73         ulong   tcrr;           /* counter: cycles to zero */
74         ulong   tldr;
75         ulong   ttgr;           /* trigger */
76         ulong   twps;           /* ro: write posted pending */
77
78         /* gp timers only, unused by us */
79         ulong   tmar;           /* value to compare with counter */
80         ulong   tcar1;          /* ro */
81         ulong   tsicr;
82         ulong   tcar2;          /* ro */
83         union {
84                 ulong   tpir;   /* gp: 1 ms tick generation: +ve */
85                 ulong   wspr;   /* wdog: start/stop control */
86         };
87         ulong   tnir;           /* 1 ms tick generation: -ve */
88         ulong   tcvr;           /* 1 ms tick generation: next counter value */
89         ulong   tocr;           /* intr mask for n ticks */
90         ulong   towr;
91 };
92
93 static int ticks; /* for sanity checking; m->ticks doesn't always get called */
94 static Lock clklck;
95
96 static ulong    rdcycles(void), rdbaseticks(void);
97
98 /* write a watchdog timer's start/stop register */
99 static void
100 wdogwrss(Timerregs *tn, ulong val)
101 {
102         while (tn->twps & (1 << 4))     /* pending write to start/stop reg? */
103                 ;
104         tn->wspr = val;
105         coherence();
106         while (tn->twps & (1 << 4))     /* pending write to start/stop reg? */
107                 ;
108 }
109
110 static void
111 resetwait(Timerregs *tn)
112 {
113         long bound;
114
115         for (bound = 400*Mhz; !(tn->tistat & Resetdone) && bound > 0; bound--)
116                 ;
117         if (bound <= 0)
118                 iprint("clock reset didn't complete\n");
119 }
120
121
122 static void
123 wdogoff(Timerregs *tn)
124 {
125         resetwait(tn);
126
127         wdogwrss(tn, 0xaaaa);           /* magic off sequence */
128         wdogwrss(tn, 0x5555);
129
130         tn->tldr = 1;
131         coherence();
132         tn->tcrr = 1;                   /* paranoia */
133         coherence();
134 }
135
136 static void wdogassure(void);
137
138 static void
139 wdogon(Timerregs *tn)
140 {
141         static int beenhere;
142
143         resetwait(tn);
144         tn->tldr = -Dogtimeout;
145         tn->tcrr = -Dogtimeout;
146         coherence();
147         wdogwrss(tn, 0xbbbb);           /* magic on sequence */
148         wdogwrss(tn, 0x4444);           /* magic on sequence */
149
150         if (!beenhere) {
151                 beenhere = 1;
152                 /* touching the dog is not quick, so do it infrequently */
153                 addclock0link(wdogassure, HZ);
154         }
155 }
156
157 static void
158 wdogassure(void)                /* reset the watch dog's counter */
159 {
160         Timerregs *tn;
161
162         tn = (Timerregs *)PHYSWDOG;
163         wdogoff(tn);
164
165         tn->tcrr = -Dogtimeout;
166         coherence();
167
168         wdogon(tn);
169 }
170
171 static void
172 clockintr(Ureg* ureg, void *arg)
173 {
174         Timerregs *tn;
175         static int nesting;
176
177         ticks++;
178         coherence();
179
180         if (nesting == 0) {     /* if the clock interrupted itself, bail out */
181                 ++nesting;
182                 timerintr(ureg, 0);
183                 --nesting;
184         }
185
186         tn = arg;
187         tn->tisr = Ovf_it;                      /* dismiss the interrupt */
188         coherence();
189 }
190
191 static void
192 clockreset(Timerregs *tn)
193 {
194         if (probeaddr((uintptr)&tn->ticpcfg) < 0)
195                 panic("no clock at %#p", tn);
196         tn->ticpcfg = Softreset | Noidle;
197         coherence();
198         resetwait(tn);
199         tn->tier = tn->tclr = 0;
200         coherence();
201 }
202
203 /* stop clock interrupts and disable the watchdog timer */
204 void
205 clockshutdown(void)
206 {
207         clockreset((Timerregs *)PHYSWDT2);
208         wdogoff((Timerregs *)PHYSWDT2);
209         clockreset((Timerregs *)PHYSWDT3);
210         wdogoff((Timerregs *)PHYSWDT3);
211
212         clockreset((Timerregs *)Tn0);
213         clockreset((Timerregs *)Tn1);
214 }
215
216 enum {
217         Instrs          = 10*Mhz,
218 };
219
220 static long
221 issue1loop(void)
222 {
223         register int i;
224         long st;
225
226         st = rdbaseticks();
227         i = Instrs;
228         do {
229                 --i; --i; --i; --i; --i;
230                 --i; --i; --i; --i;
231         } while(--i >= 0);
232         return rdbaseticks() - st;
233 }
234
235 static long
236 issue2loop(void)
237 {
238         register int i, j;
239         long st;
240
241         st = rdbaseticks();
242         i = Instrs / 2;
243         j = 0;
244         do {
245                 --i; --j; --i; --j;
246                 --i; --j; --i; --j;
247                 --j;
248         } while(--i >= 0);
249         return rdbaseticks() - st;
250 }
251
252 /* estimate instructions/s. using 32kHz clock */
253 static void
254 guessmips(long (*loop)(void), char *lab)
255 {
256         int s;
257         long tcks;
258
259         do {
260                 s = splhi();
261                 tcks = loop();
262                 splx(s);
263                 if (tcks < 0)
264                         iprint("again...");
265         } while (tcks < 0);
266         /*
267          * Instrs instructions took tcks ticks @ Clockfreqbase Hz.
268          */
269         s = ((vlong)Clockfreqbase * Instrs) / tcks / 1000000;
270         if (Debug)
271                 iprint("%ud mips (%s-issue)", s, lab);
272         USED(s);
273 }
274
275 void
276 clockinit(void)
277 {
278         int i, s;
279         Timerregs *tn;
280
281         clockshutdown();
282
283         /* turn cycle counter on */
284         cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31);
285
286         /* turn all counters on and clear the cycle counter */
287         cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1);
288
289         /* let users read the cycle counter directly */
290         cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1);
291
292         ilock(&clklck);
293         m->fastclock = 1;
294         m->ticks = ticks = 0;
295
296         /*
297          * T0 is a freerunning timer (cycle counter); it wraps,
298          * automatically reloads, and does not dispatch interrupts.
299          */
300         tn = (Timerregs *)Tn0;
301         tn->tcrr = Freebase;                    /* count up to 0 */
302         tn->tldr = Freebase;
303         coherence();
304         tn->tclr = Ar | St;
305         iunlock(&clklck);
306
307         /*
308          * T1 is the interrupting timer and does not participate
309          * in measuring time.  It is initially set to HZ.
310          */
311         tn = (Timerregs *)Tn1;
312         irqenable(Tn0irq+1, clockintr, tn, "clock");
313         ilock(&clklck);
314         tn->tcrr = -Tcycles;                    /* approx.; count up to 0 */
315         tn->tldr = -Tcycles;
316         coherence();
317         tn->tclr = Ar | St;
318         coherence();
319         tn->tier = Ovf_it;
320         coherence();
321         iunlock(&clklck);
322
323         /*
324          * verify sanity of timer1
325          */
326         s = spllo();                    /* risky */
327         for (i = 0; i < 5 && ticks == 0; i++) {
328                 delay(10);
329                 cachedwbinvse(&ticks, sizeof ticks);
330         }
331         splx(s);
332         if (ticks == 0) {
333                 if (tn->tcrr == 0)
334                         panic("clock not interrupting");
335                 else if (tn->tcrr == tn->tldr)
336                         panic("clock not ticking at all");
337 #ifdef PARANOID
338                 else
339                         panic("clock running very slowly");
340 #endif
341         }
342
343         guessmips(issue1loop, "single");
344         if (Debug)
345                 iprint(", ");
346         guessmips(issue2loop, "dual");
347         if (Debug)
348                 iprint("\n");
349
350         /*
351          * m->delayloop should be the number of delay loop iterations
352          * needed to consume 1 ms.  2 is min. instructions in the delay loop.
353          */
354         m->delayloop = m->cpuhz / (1000 * 2);
355 //      iprint("m->delayloop = %lud\n", m->delayloop);
356
357         /*
358          *  desynchronize the processor clocks so that they all don't
359          *  try to resched at the same time.
360          */
361         delay(m->machno*2);
362 }
363
364 void
365 watchdoginit(void)
366 {
367         wdogassure();
368 }
369
370 ulong
371 µs(void)
372 {
373         return fastticks2us(fastticks(nil));
374 }
375
376 void
377 timerset(Tval next)
378 {
379         long offset;
380         Timerregs *tn = (Timerregs *)Tn1;
381         static Lock setlck;
382
383         ilock(&setlck);
384         offset = next - fastticks(nil);
385         if(offset < MinPeriod)
386                 offset = MinPeriod;
387         else if(offset > MaxPeriod)
388                 offset = MaxPeriod;
389         tn->tcrr = -offset;
390         coherence();
391         iunlock(&setlck);
392 }
393
394 static ulong
395 rdcycles(void)
396 {
397         ulong v;
398
399         /* reads 32-bit cycle counter (counting up) */
400         v = cprdsc(0, CpCLD, CpCLDcyc, 0);
401         /* keep it positive; prevent m->fastclock ever going to 0 */
402         return v == 0? 1: v;
403 }
404
405 static ulong
406 rdbaseticks(void)
407 {
408         ulong v;
409
410         v = ((Timerregs *)Tn0)->tcrr;           /* tcrr should be counting up */
411         /* keep it positive; prevent m->fastclock ever going to 0 */
412         return v == 0? 1: v;
413 }
414
415 ulong
416 perfticks(void)
417 {
418         return rdcycles();
419 }
420
421 long
422 lcycles(void)
423 {
424         return perfticks();
425 }
426
427 /*
428  * until 5[cal] inline vlong ops, avoid them where possible,
429  * they are currently slow function calls.
430  */
431 typedef union Counter Counter;
432 union Counter {
433         uvlong  uvl;
434         struct {                        /* little-endian */
435                 ulong   low;
436                 ulong   high;
437         };
438 };
439
440 enum {
441         Fastvlongops    = 0,
442 };
443
444 uvlong
445 fastticks(uvlong *hz)
446 {
447         Counter now, sclnow;
448
449         if(hz)
450                 *hz = m->cpuhz;
451         ilock(&clklck);
452         if (m->ticks > HZ/10 && m->fastclock == 0)
453                 panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",
454                         m->ticks, m->fastclock);
455
456         now.uvl = m->fastclock;
457         now.low = rdcycles();
458         if(now.uvl < m->fastclock)      /* low bits must have wrapped */
459                 now.high++;
460         m->fastclock = now.uvl;
461         coherence();
462
463         sclnow.uvl = now.uvl;
464         iunlock(&clklck);
465         return sclnow.uvl;
466 }
467
468 void
469 microdelay(int l)
470 {
471         int i;
472
473         l = l * (vlong)m->delayloop / 1000;
474         if(l <= 0)
475                 l = 1;
476         for(i = 0; i < l; i++)
477                 ;
478 }
479
480 void
481 delay(int l)
482 {
483         ulong i, j;
484
485         j = m->delayloop;
486         while(l-- > 0)
487                 for(i=0; i < j; i++)
488                         ;
489 }