2 * cortex-a clocks; excludes tegra 2 SoC clocks
4 * cortex-a processors include private `global' and local timers
5 * at soc.scu + 0x200 (global) and + 0x600 (local).
6 * the global timer is a single count-up timer shared by all cores
7 * but with per-cpu comparator and auto-increment registers.
8 * a local count-down timer can be used as a watchdog.
10 * v7 arch provides a 32-bit count-up cycle counter (at about 1GHz in our case)
11 * but it's unsuitable as our source of fastticks, because it stops advancing
12 * when the cpu is suspended by WFI.
15 #include "../port/lib.h"
24 Basetickfreq = Mhz, /* soc.µs rate in Hz */
25 /* the local timers seem to run at half the expected rate */
26 Clockfreqbase = 250*Mhz / 2, /* private timer rate (PERIPHCLK/2) */
27 Tcycles = Clockfreqbase / HZ, /* cycles per clock tick */
29 MinPeriod = Tcycles / 100,
32 Dogtimeout = Dogsectimeout * Clockfreqbase,
35 typedef struct Ltimer Ltimer;
36 typedef struct Pglbtmr Pglbtmr;
37 typedef struct Ploctmr Ploctmr;
40 * cortex-a private-intr local timer registers. all cpus see their
41 * own local timers at the same base address.
44 ulong load; /* new value + 1 */
45 ulong cnt; /* counts down */
62 Tmrena = 1<<0, /* timer enabled */
63 Wdogena = Tmrena, /* watchdog enabled */
64 Xreload = 1<<1, /* reload on intr; periodic interrupts */
65 Tintena = 1<<2, /* enable irq 29 at cnt==0 (30 for watchdog) */
66 Wdog = 1<<3, /* watchdog, not timer, mode */
71 Xisrclk = 1<<0, /* write to clear */
78 Wdoff1 = 0x12345678, /* send these two to switch to timer mode */
82 /* cortex-a private-intr globl timer registers */
84 ulong cnt[2]; /* counts up; little-endian uvlong */
87 ulong cmp[2]; /* little-endian uvlong */
92 /* unique ctl bits (otherwise see X* above) */
94 // Gtintena= 1<<2, /* enable irq 27 */
99 * until 5[cl] inline vlong ops, avoid them where possible,
100 * they are currently slow function calls.
102 typedef union Vlong Vlong;
105 struct { /* little-endian */
112 static int ticking[MAXMACH];
114 /* no lock is needed to update our local timer. splhi keeps it tight. */
116 setltimer(Ltimer *tn, ulong ticks)
120 assert(ticks <= Clockfreqbase);
122 tn->load = ticks - 1;
124 tn->ctl = Tmrena | Tintena | Xreload;
130 ckstuck(int cpu, long myticks, long histicks)
132 if (labs(histicks - myticks) > HZ) {
133 // iprint("cpu%d: clock ticks %ld (vs myticks %ld cpu0 %ld); "
134 // "apparently stopped\n",
135 // cpu, histicks, myticks, MACHP(0)->ticks);
137 panic("cpu%d: clock not interrupting", cpu);
145 long myticks, histicks;
147 if (conf.nmach <= 1 || active.exiting || navailcpus == 0)
158 for (cpu = 0; cpu < navailcpus; cpu++) {
161 histicks = MACHP(cpu)->ticks;
162 if (myticks == 5*HZ || histicks > 1)
163 ckstuck(cpu, myticks, histicks);
168 clockintr(Ureg* ureg, void *arg)
180 #ifdef watchdog_not_bloody_useless
181 /* appease the dogs */
184 (wd->ctl & (Wdog | Wdogena | Tintena)) == (Wdog | Wdogena))
185 panic("cpu%d: zero watchdog count but no system reset",
187 wd->load = Dogtimeout - 1;
197 clockprod(Ureg *ureg)
203 if (m->machno != 0) { /* cpu1 gets stuck */
204 tn = &((Ploctmr *)soc.loctmr)->loc;
205 setltimer(tn, Tcycles);
210 clockreset(Ltimer *tn)
212 if (probeaddr((uintptr)tn) < 0)
213 panic("no clock at %#p", tn);
219 watchdogoff(Ltimer *wd)
229 /* clear any pending watchdog intrs or causes */
231 wdogclrintr(Ltimer *wd)
233 #ifdef watchdog_not_bloody_useless
243 * stop clock interrupts on this cpu and disable the local watchdog timer,
244 * and, if on cpu0, shutdown the shared tegra2 watchdog timer.
251 lt = (Ploctmr *)soc.loctmr;
252 clockreset(<->loc);
253 watchdogoff(<->wd);
262 /* we assume that perfticks are microseconds */
272 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
273 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
274 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
275 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
276 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
277 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
278 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
279 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
280 --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
281 --i; --i; --i; --i; --i; --i; --i; --i; --i;
283 return perfticks() - st;
292 i = Instrs / 2; /* j gets half the decrements */
296 --j; --i; --j; --i; --j; --i; --j; --i; --j;
297 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
298 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
299 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
300 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
301 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
302 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
303 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
304 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
305 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
307 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
308 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
309 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
310 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
311 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
312 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
313 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
314 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
315 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
316 --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
318 return perfticks() - st;
321 /* estimate instructions/s. */
323 guessmips(long (*loop)(void), char *lab)
336 * Instrs instructions took tcks ticks @ Basetickfreq Hz.
339 s = (((vlong)Basetickfreq * Instrs) / tcks + 500000) / 1000000;
341 iprint("%ud mips (%s-issue)", s, lab);
346 wdogintr(Ureg *, void *ltmr)
348 #ifdef watchdog_not_bloody_useless
359 ckcounting(Ltimer *lt)
367 panic("cpu%d: watchdog timer not counting down", m->machno);
370 /* test fire with interrupt to see that it's working */
372 ckwatchdog(Ltimer *wd)
374 #ifdef watchdog_not_bloody_useless
378 wd->load = Tcycles - 1;
380 /* Tintena is supposed to be ignored in watchdog mode */
381 wd->ctl |= Wdogena | Tintena;
390 /* useless local watchdog */
391 iprint("cpu%d: local watchdog failed to interrupt\n", m->machno);
402 #ifdef watchdog_not_bloody_useless
406 lt = (Ploctmr *)soc.loctmr;
410 irqenable(Wdtmrirq, wdogintr, wd, "watchdog");
414 /* set up for normal use, causing reset */
415 wd->ctl &= ~Tintena; /* reset, don't interrupt */
419 wd->load = Dogtimeout - 1;
429 clock0init(Ltimer *tn)
435 * calibrate fastclock
438 tn->load = ~0ul >> 1;
446 fticks = abs(tn->cnt - fticks);
447 old = perfticks() - old;
450 iprint("cpu%d: fastclock %ld/%ldµs = %ld fastticks/µs (MHz)\n",
451 m->machno, fticks, old, (fticks + old/2 - 1) / old);
455 iprint("cpu%d: ", m->machno);
456 guessmips(issue1loop, "single");
459 guessmips(issue2loop, "dual");
464 * m->delayloop should be the number of delay loop iterations
465 * needed to consume 1 ms. 2 is instr'ns in the delay loop.
467 m->delayloop = m->cpuhz / (1000 * 2);
468 // iprint("cpu%d: m->delayloop = %lud\n", m->machno, m->delayloop);
474 * the local timer is the interrupting timer and does not
475 * participate in measuring time. It is initially set to HZ.
486 /* turn my cycle counter on */
487 cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31);
489 /* turn all my counters on and clear my cycle counter */
490 cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1);
492 /* let users read my cycle counter directly */
493 cpwrsc(0, CpCLD, CpCLDuser, CpCLDenapmnc, 1);
495 /* verify µs counter sanity */
498 lt = (Ploctmr *)soc.loctmr;
501 irqenable(Loctmrirq, clockintr, lt, "clock");
503 intcunmask(Loctmrirq);
506 * verify sanity of local timer
508 tn->load = Clockfreqbase / 1000;
516 /* m->ticks won't be incremented here because timersinit hasn't run. */
518 panic("cpu%d: clock not ticking at all", m->machno);
519 else if ((long)tn->cnt > 0)
520 panic("cpu%d: clock ticking slowly", m->machno);
525 /* if pci gets stuck, maybe one of the many watchdogs will nuke us. */
529 * desynchronize the processor clocks so that they all don't
530 * try to resched at the same time.
533 setltimer(tn, Tcycles);
536 /* our fastticks are at 1MHz (Basetickfreq), so the conversion is trivial. */
540 return fastticks2us(fastticks(nil));
543 /* Tval is supposed to be in fastticks units. */
551 tn = &((Ploctmr *)soc.loctmr)->loc;
553 offset = fastticks2us(next - fastticks(nil));
554 /* offset is now in µs (MHz); convert to Clockfreqbase Hz. */
555 offset *= Clockfreqbase / Mhz;
556 if(offset < MinPeriod)
558 else if(offset > MaxPeriod)
561 setltimer(tn, offset);
566 cpucycles(void) /* cpu clock rate, except when waiting for intr (unused) */
570 /* reads 32-bit cycle counter (counting up) */
571 // v = cprdsc(0, CpCLD, CpCLDcyc, 0);
572 v = getcyc(); /* fast asm */
573 /* keep it non-negative; prevent m->fastclock ever going to 0 */
584 fastticks(uvlong *hz)
593 fcp = (Vlong *)&m->fastclock;
594 /* avoid reentry on interrupt or trap, to prevent recursion */
596 newticks = perfticks();
597 if(newticks < fcp->low) /* low word must have wrapped */
602 if (fcp->low == 0 && fcp->high == 0 && m->ticks > HZ/10)
603 panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",
604 m->ticks, m->fastclock);
611 for (l = l * (vlong)m->delayloop / 1000; --l >= 0; )
622 for (i = d; --i >= 0; )