123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- /*
- * cortex-a clocks; excludes tegra 2 SoC clocks
- *
- * cortex-a processors include private `global' and local timers
- * at soc.scu + 0x200 (global) and + 0x600 (local).
- * the global timer is a single count-up timer shared by all cores
- * but with per-cpu comparator and auto-increment registers.
- * a local count-down timer can be used as a watchdog.
- *
- * v7 arch provides a 32-bit count-up cycle counter (at about 1GHz in our case)
- * but it's unsuitable as our source of fastticks, because it stops advancing
- * when the cpu is suspended by WFI.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "arm.h"
- enum {
- Debug = 0,
- Basetickfreq = Mhz, /* soc.µs rate in Hz */
- /* the local timers seem to run at half the expected rate */
- Clockfreqbase = 250*Mhz / 2, /* private timer rate (PERIPHCLK/2) */
- Tcycles = Clockfreqbase / HZ, /* cycles per clock tick */
- MinPeriod = Tcycles / 100,
- MaxPeriod = Tcycles,
- Dogtimeout = Dogsectimeout * Clockfreqbase,
- };
- typedef struct Ltimer Ltimer;
- typedef struct Pglbtmr Pglbtmr;
- typedef struct Ploctmr Ploctmr;
- /*
- * cortex-a private-intr local timer registers. all cpus see their
- * own local timers at the same base address.
- */
- struct Ltimer {
- ulong load; /* new value + 1 */
- ulong cnt; /* counts down */
- ulong ctl;
- ulong isr;
- /* watchdog only */
- ulong wdrst;
- ulong wddis; /* wo */
- ulong _pad0[2];
- };
- struct Ploctmr {
- Ltimer loc;
- Ltimer wd;
- };
- enum {
- /* ctl bits */
- Tmrena = 1<<0, /* timer enabled */
- Wdogena = Tmrena, /* watchdog enabled */
- Xreload = 1<<1, /* reload on intr; periodic interrupts */
- Tintena = 1<<2, /* enable irq 29 at cnt==0 (30 for watchdog) */
- Wdog = 1<<3, /* watchdog, not timer, mode */
- Xsclrshift = 8,
- Xsclrmask = MASK(8),
- /* isr bits */
- Xisrclk = 1<<0, /* write to clear */
- /* wdrst bits */
- Wdrst = 1<<0,
- /* wddis values */
- Wdon = 1,
- Wdoff1 = 0x12345678, /* send these two to switch to timer mode */
- Wdoff2 = 0x87654321,
- };
- /* cortex-a private-intr globl timer registers */
- struct Pglbtmr {
- ulong cnt[2]; /* counts up; little-endian uvlong */
- ulong ctl;
- ulong isr;
- ulong cmp[2]; /* little-endian uvlong */
- ulong inc;
- };
- enum {
- /* unique ctl bits (otherwise see X* above) */
- Gcmp = 1<<1,
- // Gtintena= 1<<2, /* enable irq 27 */
- Gincr = 1<<3,
- };
- /*
- * until 5[cl] inline vlong ops, avoid them where possible,
- * they are currently slow function calls.
- */
- typedef union Vlong Vlong;
- union Vlong {
- uvlong uvl;
- struct { /* little-endian */
- ulong low;
- ulong high;
- };
- };
- static int fired;
- static int ticking[MAXMACH];
- /* no lock is needed to update our local timer. splhi keeps it tight. */
- static void
- setltimer(Ltimer *tn, ulong ticks)
- {
- int s;
- assert(ticks <= Clockfreqbase);
- s = splhi();
- tn->load = ticks - 1;
- coherence();
- tn->ctl = Tmrena | Tintena | Xreload;
- coherence();
- splx(s);
- }
- static void
- ckstuck(int cpu, long myticks, long histicks)
- {
- if (labs(histicks - myticks) > HZ) {
- // iprint("cpu%d: clock ticks %ld (vs myticks %ld cpu0 %ld); "
- // "apparently stopped\n",
- // cpu, histicks, myticks, MACHP(0)->ticks);
- if (!ticking[cpu])
- panic("cpu%d: clock not interrupting", cpu);
- }
- }
- static void
- mpclocksanity(void)
- {
- int cpu, mycpu;
- long myticks, histicks;
- if (conf.nmach <= 1 || active.exiting || navailcpus == 0)
- return;
- mycpu = m->machno;
- myticks = m->ticks;
- if (myticks == HZ)
- ticking[mycpu] = 1;
- if (myticks < 5*HZ)
- return;
- for (cpu = 0; cpu < navailcpus; cpu++) {
- if (cpu == mycpu)
- continue;
- histicks = MACHP(cpu)->ticks;
- if (myticks == 5*HZ || histicks > 1)
- ckstuck(cpu, myticks, histicks);
- }
- }
- static void
- clockintr(Ureg* ureg, void *arg)
- {
- Ltimer *wd, *tn;
- Ploctmr *lt;
- lt = (Ploctmr *)arg;
- tn = <->loc;
- tn->isr = Xisrclk;
- coherence();
- timerintr(ureg, 0);
- #ifdef watchdog_not_bloody_useless
- /* appease the dogs */
- wd = <->wd;
- if (wd->cnt == 0 &&
- (wd->ctl & (Wdog | Wdogena | Tintena)) == (Wdog | Wdogena))
- panic("cpu%d: zero watchdog count but no system reset",
- m->machno);
- wd->load = Dogtimeout - 1;
- coherence();
- #endif
- SET(wd); USED(wd);
- tegclockintr();
- mpclocksanity();
- }
- void
- clockprod(Ureg *ureg)
- {
- Ltimer *tn;
- timerintr(ureg, 0);
- tegclockintr();
- if (m->machno != 0) { /* cpu1 gets stuck */
- tn = &((Ploctmr *)soc.loctmr)->loc;
- setltimer(tn, Tcycles);
- }
- }
- static void
- clockreset(Ltimer *tn)
- {
- if (probeaddr((uintptr)tn) < 0)
- panic("no clock at %#p", tn);
- tn->ctl = 0;
- coherence();
- }
- void
- watchdogoff(Ltimer *wd)
- {
- wd->ctl &= ~Wdogena;
- coherence();
- wd->wddis = Wdoff1;
- coherence();
- wd->wddis = Wdoff2;
- coherence();
- }
- /* clear any pending watchdog intrs or causes */
- void
- wdogclrintr(Ltimer *wd)
- {
- #ifdef watchdog_not_bloody_useless
- wd->isr = Xisrclk;
- coherence();
- wd->wdrst = Wdrst;
- coherence();
- #endif
- USED(wd);
- }
- /*
- * stop clock interrupts on this cpu and disable the local watchdog timer,
- * and, if on cpu0, shutdown the shared tegra2 watchdog timer.
- */
- void
- clockshutdown(void)
- {
- Ploctmr *lt;
- lt = (Ploctmr *)soc.loctmr;
- clockreset(<->loc);
- watchdogoff(<->wd);
- tegclockshutdown();
- }
- enum {
- Instrs = 10*Mhz,
- };
- /* we assume that perfticks are microseconds */
- static long
- issue1loop(void)
- {
- register int i;
- long st;
- i = Instrs;
- st = perfticks();
- do {
- --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
- --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
- --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
- --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
- --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
- --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
- --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
- --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
- --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
- --i; --i; --i; --i; --i; --i; --i; --i; --i;
- } while(--i >= 0);
- return perfticks() - st;
- }
- static long
- issue2loop(void)
- {
- register int i, j;
- long st;
- i = Instrs / 2; /* j gets half the decrements */
- j = 0;
- st = perfticks();
- do {
- --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
- } while(--i >= 0);
- return perfticks() - st;
- }
- /* estimate instructions/s. */
- static void
- guessmips(long (*loop)(void), char *lab)
- {
- int s;
- long tcks;
- do {
- s = splhi();
- tcks = loop();
- splx(s);
- if (tcks < 0)
- iprint("again...");
- } while (tcks < 0);
- /*
- * Instrs instructions took tcks ticks @ Basetickfreq Hz.
- * round the result.
- */
- s = (((vlong)Basetickfreq * Instrs) / tcks + 500000) / 1000000;
- if (Debug)
- iprint("%ud mips (%s-issue)", s, lab);
- USED(s);
- }
- void
- wdogintr(Ureg *, void *ltmr)
- {
- #ifdef watchdog_not_bloody_useless
- Ltimer *wd;
- wd = ltmr;
- fired++;
- wdogclrintr(wd);
- #endif
- USED(ltmr);
- }
- static void
- ckcounting(Ltimer *lt)
- {
- ulong old;
- old = lt->cnt;
- if (old == lt->cnt)
- delay(1);
- if (old == lt->cnt)
- panic("cpu%d: watchdog timer not counting down", m->machno);
- }
- /* test fire with interrupt to see that it's working */
- static void
- ckwatchdog(Ltimer *wd)
- {
- #ifdef watchdog_not_bloody_useless
- int s;
- fired = 0;
- wd->load = Tcycles - 1;
- coherence();
- /* Tintena is supposed to be ignored in watchdog mode */
- wd->ctl |= Wdogena | Tintena;
- coherence();
- ckcounting(wd);
- s = spllo();
- delay(2 * 1000/HZ);
- splx(s);
- if (!fired)
- /* useless local watchdog */
- iprint("cpu%d: local watchdog failed to interrupt\n", m->machno);
- /* clean up */
- wd->ctl &= ~Wdogena;
- coherence();
- #endif
- USED(wd);
- }
- static void
- startwatchdog(void)
- {
- #ifdef watchdog_not_bloody_useless
- Ltimer *wd;
- Ploctmr *lt;
- lt = (Ploctmr *)soc.loctmr;
- wd = <->wd;
- watchdogoff(wd);
- wdogclrintr(wd);
- irqenable(Wdtmrirq, wdogintr, wd, "watchdog");
- ckwatchdog(wd);
- /* set up for normal use, causing reset */
- wd->ctl &= ~Tintena; /* reset, don't interrupt */
- coherence();
- wd->ctl |= Wdog;
- coherence();
- wd->load = Dogtimeout - 1;
- coherence();
- wd->ctl |= Wdogena;
- coherence();
- ckcounting(wd);
- #endif
- }
- static void
- clock0init(Ltimer *tn)
- {
- int s;
- ulong old, fticks;
- /*
- * calibrate fastclock
- */
- s = splhi();
- tn->load = ~0ul >> 1;
- coherence();
- tn->ctl = Tmrena;
- coherence();
- old = perfticks();
- fticks = tn->cnt;
- delay(1);
- fticks = abs(tn->cnt - fticks);
- old = perfticks() - old;
- splx(s);
- if (Debug)
- iprint("cpu%d: fastclock %ld/%ldµs = %ld fastticks/µs (MHz)\n",
- m->machno, fticks, old, (fticks + old/2 - 1) / old);
- USED(fticks, old);
- if (Debug)
- iprint("cpu%d: ", m->machno);
- guessmips(issue1loop, "single");
- if (Debug)
- iprint(", ");
- guessmips(issue2loop, "dual");
- if (Debug)
- iprint("\n");
- /*
- * m->delayloop should be the number of delay loop iterations
- * needed to consume 1 ms. 2 is instr'ns in the delay loop.
- */
- m->delayloop = m->cpuhz / (1000 * 2);
- // iprint("cpu%d: m->delayloop = %lud\n", m->machno, m->delayloop);
- tegclock0init();
- }
- /*
- * the local timer is the interrupting timer and does not
- * participate in measuring time. It is initially set to HZ.
- */
- void
- clockinit(void)
- {
- ulong old;
- Ltimer *tn;
- Ploctmr *lt;
- clockshutdown();
- /* turn my cycle counter on */
- cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31);
- /* turn all my counters on and clear my cycle counter */
- cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1);
- /* let users read my cycle counter directly */
- cpwrsc(0, CpCLD, CpCLDuser, CpCLDenapmnc, 1);
- /* verify µs counter sanity */
- tegclockinit();
- lt = (Ploctmr *)soc.loctmr;
- tn = <->loc;
- if (m->machno == 0)
- irqenable(Loctmrirq, clockintr, lt, "clock");
- else
- intcunmask(Loctmrirq);
- /*
- * verify sanity of local timer
- */
- tn->load = Clockfreqbase / 1000;
- tn->isr = Xisrclk;
- coherence();
- tn->ctl = Tmrena;
- coherence();
- old = tn->cnt;
- delay(5);
- /* m->ticks won't be incremented here because timersinit hasn't run. */
- if (tn->cnt == old)
- panic("cpu%d: clock not ticking at all", m->machno);
- else if ((long)tn->cnt > 0)
- panic("cpu%d: clock ticking slowly", m->machno);
- if (m->machno == 0)
- clock0init(tn);
- /* if pci gets stuck, maybe one of the many watchdogs will nuke us. */
- startwatchdog();
- /*
- * desynchronize the processor clocks so that they all don't
- * try to resched at the same time.
- */
- delay(m->machno*2);
- setltimer(tn, Tcycles);
- }
- /* our fastticks are at 1MHz (Basetickfreq), so the conversion is trivial. */
- ulong
- µs(void)
- {
- return fastticks2us(fastticks(nil));
- }
- /* Tval is supposed to be in fastticks units. */
- void
- timerset(Tval next)
- {
- int s;
- long offset;
- Ltimer *tn;
- tn = &((Ploctmr *)soc.loctmr)->loc;
- s = splhi();
- offset = fastticks2us(next - fastticks(nil));
- /* offset is now in µs (MHz); convert to Clockfreqbase Hz. */
- offset *= Clockfreqbase / Mhz;
- if(offset < MinPeriod)
- offset = MinPeriod;
- else if(offset > MaxPeriod)
- offset = MaxPeriod;
- setltimer(tn, offset);
- splx(s);
- }
- static ulong
- cpucycles(void) /* cpu clock rate, except when waiting for intr (unused) */
- {
- ulong v;
- /* reads 32-bit cycle counter (counting up) */
- // v = cprdsc(0, CpCLD, CpCLDcyc, 0);
- v = getcyc(); /* fast asm */
- /* keep it non-negative; prevent m->fastclock ever going to 0 */
- return v == 0? 1: v;
- }
- long
- lcycles(void)
- {
- return perfticks();
- }
- uvlong
- fastticks(uvlong *hz)
- {
- int s;
- ulong newticks;
- Vlong *fcp;
- if(hz)
- *hz = Basetickfreq;
- fcp = (Vlong *)&m->fastclock;
- /* avoid reentry on interrupt or trap, to prevent recursion */
- s = splhi();
- newticks = perfticks();
- if(newticks < fcp->low) /* low word must have wrapped */
- fcp->high++;
- fcp->low = newticks;
- splx(s);
- if (fcp->low == 0 && fcp->high == 0 && m->ticks > HZ/10)
- panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",
- m->ticks, m->fastclock);
- return m->fastclock;
- }
- void
- microdelay(int l)
- {
- for (l = l * (vlong)m->delayloop / 1000; --l >= 0; )
- ;
- }
- void
- delay(int l)
- {
- int i, d;
- d = m->delayloop;
- while(--l >= 0)
- for (i = d; --i >= 0; )
- ;
- }
|