13de6a9c0SDavid du Colombier /*
23de6a9c0SDavid du Colombier * cortex-a clocks; excludes tegra 2 SoC clocks
33de6a9c0SDavid du Colombier *
43de6a9c0SDavid du Colombier * cortex-a processors include private `global' and local timers
53de6a9c0SDavid du Colombier * at soc.scu + 0x200 (global) and + 0x600 (local).
63de6a9c0SDavid du Colombier * the global timer is a single count-up timer shared by all cores
73de6a9c0SDavid du Colombier * but with per-cpu comparator and auto-increment registers.
83de6a9c0SDavid du Colombier * a local count-down timer can be used as a watchdog.
93de6a9c0SDavid du Colombier *
103de6a9c0SDavid du Colombier * v7 arch provides a 32-bit count-up cycle counter (at about 1GHz in our case)
113de6a9c0SDavid du Colombier * but it's unsuitable as our source of fastticks, because it stops advancing
123de6a9c0SDavid du Colombier * when the cpu is suspended by WFI.
133de6a9c0SDavid du Colombier */
143de6a9c0SDavid du Colombier #include "u.h"
153de6a9c0SDavid du Colombier #include "../port/lib.h"
163de6a9c0SDavid du Colombier #include "mem.h"
173de6a9c0SDavid du Colombier #include "dat.h"
183de6a9c0SDavid du Colombier #include "fns.h"
193de6a9c0SDavid du Colombier #include "arm.h"
203de6a9c0SDavid du Colombier
213de6a9c0SDavid du Colombier enum {
223de6a9c0SDavid du Colombier Debug = 0,
233de6a9c0SDavid du Colombier
243de6a9c0SDavid du Colombier Basetickfreq = Mhz, /* soc.µs rate in Hz */
253de6a9c0SDavid du Colombier /* the local timers seem to run at half the expected rate */
263de6a9c0SDavid du Colombier Clockfreqbase = 250*Mhz / 2, /* private timer rate (PERIPHCLK/2) */
273de6a9c0SDavid du Colombier Tcycles = Clockfreqbase / HZ, /* cycles per clock tick */
283de6a9c0SDavid du Colombier
293de6a9c0SDavid du Colombier MinPeriod = Tcycles / 100,
303de6a9c0SDavid du Colombier MaxPeriod = Tcycles,
313de6a9c0SDavid du Colombier
323de6a9c0SDavid du Colombier Dogtimeout = Dogsectimeout * Clockfreqbase,
333de6a9c0SDavid du Colombier };
343de6a9c0SDavid du Colombier
353de6a9c0SDavid du Colombier typedef struct Ltimer Ltimer;
363de6a9c0SDavid du Colombier typedef struct Pglbtmr Pglbtmr;
373de6a9c0SDavid du Colombier typedef struct Ploctmr Ploctmr;
383de6a9c0SDavid du Colombier
393de6a9c0SDavid du Colombier /*
403de6a9c0SDavid du Colombier * cortex-a private-intr local timer registers. all cpus see their
413de6a9c0SDavid du Colombier * own local timers at the same base address.
423de6a9c0SDavid du Colombier */
433de6a9c0SDavid du Colombier struct Ltimer {
443de6a9c0SDavid du Colombier ulong load; /* new value + 1 */
453de6a9c0SDavid du Colombier ulong cnt; /* counts down */
463de6a9c0SDavid du Colombier ulong ctl;
473de6a9c0SDavid du Colombier ulong isr;
483de6a9c0SDavid du Colombier
493de6a9c0SDavid du Colombier /* watchdog only */
503de6a9c0SDavid du Colombier ulong wdrst;
513de6a9c0SDavid du Colombier ulong wddis; /* wo */
523de6a9c0SDavid du Colombier
533de6a9c0SDavid du Colombier ulong _pad0[2];
543de6a9c0SDavid du Colombier };
553de6a9c0SDavid du Colombier struct Ploctmr {
563de6a9c0SDavid du Colombier Ltimer loc;
573de6a9c0SDavid du Colombier Ltimer wd;
583de6a9c0SDavid du Colombier };
593de6a9c0SDavid du Colombier
603de6a9c0SDavid du Colombier enum {
613de6a9c0SDavid du Colombier /* ctl bits */
623de6a9c0SDavid du Colombier Tmrena = 1<<0, /* timer enabled */
633de6a9c0SDavid du Colombier Wdogena = Tmrena, /* watchdog enabled */
643de6a9c0SDavid du Colombier Xreload = 1<<1, /* reload on intr; periodic interrupts */
653de6a9c0SDavid du Colombier Tintena = 1<<2, /* enable irq 29 at cnt==0 (30 for watchdog) */
663de6a9c0SDavid du Colombier Wdog = 1<<3, /* watchdog, not timer, mode */
673de6a9c0SDavid du Colombier Xsclrshift = 8,
683de6a9c0SDavid du Colombier Xsclrmask = MASK(8),
693de6a9c0SDavid du Colombier
703de6a9c0SDavid du Colombier /* isr bits */
713de6a9c0SDavid du Colombier Xisrclk = 1<<0, /* write to clear */
723de6a9c0SDavid du Colombier
733de6a9c0SDavid du Colombier /* wdrst bits */
743de6a9c0SDavid du Colombier Wdrst = 1<<0,
753de6a9c0SDavid du Colombier
763de6a9c0SDavid du Colombier /* wddis values */
773de6a9c0SDavid du Colombier Wdon = 1,
783de6a9c0SDavid du Colombier Wdoff1 = 0x12345678, /* send these two to switch to timer mode */
793de6a9c0SDavid du Colombier Wdoff2 = 0x87654321,
803de6a9c0SDavid du Colombier };
813de6a9c0SDavid du Colombier
823de6a9c0SDavid du Colombier /* cortex-a private-intr globl timer registers */
833de6a9c0SDavid du Colombier struct Pglbtmr {
843de6a9c0SDavid du Colombier ulong cnt[2]; /* counts up; little-endian uvlong */
853de6a9c0SDavid du Colombier ulong ctl;
863de6a9c0SDavid du Colombier ulong isr;
873de6a9c0SDavid du Colombier ulong cmp[2]; /* little-endian uvlong */
883de6a9c0SDavid du Colombier ulong inc;
893de6a9c0SDavid du Colombier };
903de6a9c0SDavid du Colombier
913de6a9c0SDavid du Colombier enum {
923de6a9c0SDavid du Colombier /* unique ctl bits (otherwise see X* above) */
933de6a9c0SDavid du Colombier Gcmp = 1<<1,
943de6a9c0SDavid du Colombier // Gtintena= 1<<2, /* enable irq 27 */
953de6a9c0SDavid du Colombier Gincr = 1<<3,
963de6a9c0SDavid du Colombier };
973de6a9c0SDavid du Colombier
983de6a9c0SDavid du Colombier /*
99*35ef7fcaSDavid du Colombier * until 5[cl] inline vlong ops, avoid them where possible,
1003de6a9c0SDavid du Colombier * they are currently slow function calls.
1013de6a9c0SDavid du Colombier */
102*35ef7fcaSDavid du Colombier typedef union Vlong Vlong;
103*35ef7fcaSDavid du Colombier union Vlong {
1043de6a9c0SDavid du Colombier uvlong uvl;
1053de6a9c0SDavid du Colombier struct { /* little-endian */
1063de6a9c0SDavid du Colombier ulong low;
1073de6a9c0SDavid du Colombier ulong high;
1083de6a9c0SDavid du Colombier };
1093de6a9c0SDavid du Colombier };
1103de6a9c0SDavid du Colombier
1113de6a9c0SDavid du Colombier static int fired;
1123de6a9c0SDavid du Colombier static int ticking[MAXMACH];
1133de6a9c0SDavid du Colombier
1143de6a9c0SDavid du Colombier /* no lock is needed to update our local timer. splhi keeps it tight. */
1153de6a9c0SDavid du Colombier static void
setltimer(Ltimer * tn,ulong ticks)1163de6a9c0SDavid du Colombier setltimer(Ltimer *tn, ulong ticks)
1173de6a9c0SDavid du Colombier {
1183de6a9c0SDavid du Colombier int s;
1193de6a9c0SDavid du Colombier
1203de6a9c0SDavid du Colombier assert(ticks <= Clockfreqbase);
1213de6a9c0SDavid du Colombier s = splhi();
1223de6a9c0SDavid du Colombier tn->load = ticks - 1;
1233de6a9c0SDavid du Colombier coherence();
1243de6a9c0SDavid du Colombier tn->ctl = Tmrena | Tintena | Xreload;
1253de6a9c0SDavid du Colombier coherence();
1263de6a9c0SDavid du Colombier splx(s);
1273de6a9c0SDavid du Colombier }
1283de6a9c0SDavid du Colombier
1293de6a9c0SDavid du Colombier static void
ckstuck(int cpu,long myticks,long histicks)1303de6a9c0SDavid du Colombier ckstuck(int cpu, long myticks, long histicks)
1313de6a9c0SDavid du Colombier {
1323de6a9c0SDavid du Colombier if (labs(histicks - myticks) > HZ) {
1333de6a9c0SDavid du Colombier // iprint("cpu%d: clock ticks %ld (vs myticks %ld cpu0 %ld); "
1343de6a9c0SDavid du Colombier // "apparently stopped\n",
1353de6a9c0SDavid du Colombier // cpu, histicks, myticks, MACHP(0)->ticks);
1363de6a9c0SDavid du Colombier if (!ticking[cpu])
1373de6a9c0SDavid du Colombier panic("cpu%d: clock not interrupting", cpu);
1383de6a9c0SDavid du Colombier }
1393de6a9c0SDavid du Colombier }
1403de6a9c0SDavid du Colombier
1413de6a9c0SDavid du Colombier static void
mpclocksanity(void)1423de6a9c0SDavid du Colombier mpclocksanity(void)
1433de6a9c0SDavid du Colombier {
1443de6a9c0SDavid du Colombier int cpu, mycpu;
1453de6a9c0SDavid du Colombier long myticks, histicks;
1463de6a9c0SDavid du Colombier
1473de6a9c0SDavid du Colombier if (conf.nmach <= 1 || active.exiting || navailcpus == 0)
1483de6a9c0SDavid du Colombier return;
1493de6a9c0SDavid du Colombier
1503de6a9c0SDavid du Colombier mycpu = m->machno;
1513de6a9c0SDavid du Colombier myticks = m->ticks;
1523de6a9c0SDavid du Colombier if (myticks == HZ)
1533de6a9c0SDavid du Colombier ticking[mycpu] = 1;
1543de6a9c0SDavid du Colombier
1553de6a9c0SDavid du Colombier if (myticks < 5*HZ)
1563de6a9c0SDavid du Colombier return;
1573de6a9c0SDavid du Colombier
1583de6a9c0SDavid du Colombier for (cpu = 0; cpu < navailcpus; cpu++) {
1593de6a9c0SDavid du Colombier if (cpu == mycpu)
1603de6a9c0SDavid du Colombier continue;
1613de6a9c0SDavid du Colombier histicks = MACHP(cpu)->ticks;
1623de6a9c0SDavid du Colombier if (myticks == 5*HZ || histicks > 1)
1633de6a9c0SDavid du Colombier ckstuck(cpu, myticks, histicks);
1643de6a9c0SDavid du Colombier }
1653de6a9c0SDavid du Colombier }
1663de6a9c0SDavid du Colombier
1673de6a9c0SDavid du Colombier static void
clockintr(Ureg * ureg,void * arg)1683de6a9c0SDavid du Colombier clockintr(Ureg* ureg, void *arg)
1693de6a9c0SDavid du Colombier {
1703de6a9c0SDavid du Colombier Ltimer *wd, *tn;
1713de6a9c0SDavid du Colombier Ploctmr *lt;
1723de6a9c0SDavid du Colombier
1733de6a9c0SDavid du Colombier lt = (Ploctmr *)arg;
1743de6a9c0SDavid du Colombier tn = <->loc;
1753de6a9c0SDavid du Colombier tn->isr = Xisrclk;
1763de6a9c0SDavid du Colombier coherence();
1773de6a9c0SDavid du Colombier
1783de6a9c0SDavid du Colombier timerintr(ureg, 0);
1793de6a9c0SDavid du Colombier
1803de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
1813de6a9c0SDavid du Colombier /* appease the dogs */
1823de6a9c0SDavid du Colombier wd = <->wd;
1833de6a9c0SDavid du Colombier if (wd->cnt == 0 &&
1843de6a9c0SDavid du Colombier (wd->ctl & (Wdog | Wdogena | Tintena)) == (Wdog | Wdogena))
1853de6a9c0SDavid du Colombier panic("cpu%d: zero watchdog count but no system reset",
1863de6a9c0SDavid du Colombier m->machno);
1873de6a9c0SDavid du Colombier wd->load = Dogtimeout - 1;
1883de6a9c0SDavid du Colombier coherence();
1893de6a9c0SDavid du Colombier #endif
1903de6a9c0SDavid du Colombier SET(wd); USED(wd);
1913de6a9c0SDavid du Colombier tegclockintr();
1923de6a9c0SDavid du Colombier
1933de6a9c0SDavid du Colombier mpclocksanity();
1943de6a9c0SDavid du Colombier }
1953de6a9c0SDavid du Colombier
1963de6a9c0SDavid du Colombier void
clockprod(Ureg * ureg)1973de6a9c0SDavid du Colombier clockprod(Ureg *ureg)
1983de6a9c0SDavid du Colombier {
1993de6a9c0SDavid du Colombier Ltimer *tn;
2003de6a9c0SDavid du Colombier
2013de6a9c0SDavid du Colombier timerintr(ureg, 0);
2023de6a9c0SDavid du Colombier tegclockintr();
2033de6a9c0SDavid du Colombier if (m->machno != 0) { /* cpu1 gets stuck */
2043de6a9c0SDavid du Colombier tn = &((Ploctmr *)soc.loctmr)->loc;
2053de6a9c0SDavid du Colombier setltimer(tn, Tcycles);
2063de6a9c0SDavid du Colombier }
2073de6a9c0SDavid du Colombier }
2083de6a9c0SDavid du Colombier
2093de6a9c0SDavid du Colombier static void
clockreset(Ltimer * tn)2103de6a9c0SDavid du Colombier clockreset(Ltimer *tn)
2113de6a9c0SDavid du Colombier {
2123de6a9c0SDavid du Colombier if (probeaddr((uintptr)tn) < 0)
2133de6a9c0SDavid du Colombier panic("no clock at %#p", tn);
2143de6a9c0SDavid du Colombier tn->ctl = 0;
2153de6a9c0SDavid du Colombier coherence();
2163de6a9c0SDavid du Colombier }
2173de6a9c0SDavid du Colombier
2183de6a9c0SDavid du Colombier void
watchdogoff(Ltimer * wd)2193de6a9c0SDavid du Colombier watchdogoff(Ltimer *wd)
2203de6a9c0SDavid du Colombier {
2213de6a9c0SDavid du Colombier wd->ctl &= ~Wdogena;
2223de6a9c0SDavid du Colombier coherence();
2233de6a9c0SDavid du Colombier wd->wddis = Wdoff1;
2243de6a9c0SDavid du Colombier coherence();
2253de6a9c0SDavid du Colombier wd->wddis = Wdoff2;
2263de6a9c0SDavid du Colombier coherence();
2273de6a9c0SDavid du Colombier }
2283de6a9c0SDavid du Colombier
2293de6a9c0SDavid du Colombier /* clear any pending watchdog intrs or causes */
2303de6a9c0SDavid du Colombier void
wdogclrintr(Ltimer * wd)2313de6a9c0SDavid du Colombier wdogclrintr(Ltimer *wd)
2323de6a9c0SDavid du Colombier {
2333de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
2343de6a9c0SDavid du Colombier wd->isr = Xisrclk;
2353de6a9c0SDavid du Colombier coherence();
2363de6a9c0SDavid du Colombier wd->wdrst = Wdrst;
2373de6a9c0SDavid du Colombier coherence();
2383de6a9c0SDavid du Colombier #endif
2393de6a9c0SDavid du Colombier USED(wd);
2403de6a9c0SDavid du Colombier }
2413de6a9c0SDavid du Colombier
2423de6a9c0SDavid du Colombier /*
2433de6a9c0SDavid du Colombier * stop clock interrupts on this cpu and disable the local watchdog timer,
2443de6a9c0SDavid du Colombier * and, if on cpu0, shutdown the shared tegra2 watchdog timer.
2453de6a9c0SDavid du Colombier */
2463de6a9c0SDavid du Colombier void
clockshutdown(void)2473de6a9c0SDavid du Colombier clockshutdown(void)
2483de6a9c0SDavid du Colombier {
2493de6a9c0SDavid du Colombier Ploctmr *lt;
2503de6a9c0SDavid du Colombier
2513de6a9c0SDavid du Colombier lt = (Ploctmr *)soc.loctmr;
2523de6a9c0SDavid du Colombier clockreset(<->loc);
2533de6a9c0SDavid du Colombier watchdogoff(<->wd);
2543de6a9c0SDavid du Colombier
2553de6a9c0SDavid du Colombier tegclockshutdown();
2563de6a9c0SDavid du Colombier }
2573de6a9c0SDavid du Colombier
2583de6a9c0SDavid du Colombier enum {
2593de6a9c0SDavid du Colombier Instrs = 10*Mhz,
2603de6a9c0SDavid du Colombier };
2613de6a9c0SDavid du Colombier
2623de6a9c0SDavid du Colombier /* we assume that perfticks are microseconds */
2633de6a9c0SDavid du Colombier static long
issue1loop(void)2643de6a9c0SDavid du Colombier issue1loop(void)
2653de6a9c0SDavid du Colombier {
2663de6a9c0SDavid du Colombier register int i;
2673de6a9c0SDavid du Colombier long st;
2683de6a9c0SDavid du Colombier
2693de6a9c0SDavid du Colombier i = Instrs;
2703de6a9c0SDavid du Colombier st = perfticks();
2713de6a9c0SDavid du Colombier do {
2723de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2733de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2743de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2753de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2763de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2773de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2783de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2793de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2803de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
2813de6a9c0SDavid du Colombier --i; --i; --i; --i; --i; --i; --i; --i; --i;
2823de6a9c0SDavid du Colombier } while(--i >= 0);
2833de6a9c0SDavid du Colombier return perfticks() - st;
2843de6a9c0SDavid du Colombier }
2853de6a9c0SDavid du Colombier
2863de6a9c0SDavid du Colombier static long
issue2loop(void)2873de6a9c0SDavid du Colombier issue2loop(void)
2883de6a9c0SDavid du Colombier {
2893de6a9c0SDavid du Colombier register int i, j;
2903de6a9c0SDavid du Colombier long st;
2913de6a9c0SDavid du Colombier
2923de6a9c0SDavid du Colombier i = Instrs / 2; /* j gets half the decrements */
2933de6a9c0SDavid du Colombier j = 0;
2943de6a9c0SDavid du Colombier st = perfticks();
2953de6a9c0SDavid du Colombier do {
2963de6a9c0SDavid du Colombier --j; --i; --j; --i; --j; --i; --j; --i; --j;
2973de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
2983de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
2993de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3003de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3013de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3023de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3033de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3043de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3053de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3063de6a9c0SDavid du Colombier
3073de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3083de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3093de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3103de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3113de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3123de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3133de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3143de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3153de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3163de6a9c0SDavid du Colombier --i; --j; --i; --j; --i; --j; --i; --j; --i; --j;
3173de6a9c0SDavid du Colombier } while(--i >= 0);
3183de6a9c0SDavid du Colombier return perfticks() - st;
3193de6a9c0SDavid du Colombier }
3203de6a9c0SDavid du Colombier
3213de6a9c0SDavid du Colombier /* estimate instructions/s. */
3223de6a9c0SDavid du Colombier static void
guessmips(long (* loop)(void),char * lab)3233de6a9c0SDavid du Colombier guessmips(long (*loop)(void), char *lab)
3243de6a9c0SDavid du Colombier {
3253de6a9c0SDavid du Colombier int s;
3263de6a9c0SDavid du Colombier long tcks;
3273de6a9c0SDavid du Colombier
3283de6a9c0SDavid du Colombier do {
3293de6a9c0SDavid du Colombier s = splhi();
3303de6a9c0SDavid du Colombier tcks = loop();
3313de6a9c0SDavid du Colombier splx(s);
3323de6a9c0SDavid du Colombier if (tcks < 0)
3333de6a9c0SDavid du Colombier iprint("again...");
3343de6a9c0SDavid du Colombier } while (tcks < 0);
3353de6a9c0SDavid du Colombier /*
3363de6a9c0SDavid du Colombier * Instrs instructions took tcks ticks @ Basetickfreq Hz.
3373de6a9c0SDavid du Colombier * round the result.
3383de6a9c0SDavid du Colombier */
3393de6a9c0SDavid du Colombier s = (((vlong)Basetickfreq * Instrs) / tcks + 500000) / 1000000;
3403de6a9c0SDavid du Colombier if (Debug)
3413de6a9c0SDavid du Colombier iprint("%ud mips (%s-issue)", s, lab);
3423de6a9c0SDavid du Colombier USED(s);
3433de6a9c0SDavid du Colombier }
3443de6a9c0SDavid du Colombier
3453de6a9c0SDavid du Colombier void
wdogintr(Ureg *,void * ltmr)3463de6a9c0SDavid du Colombier wdogintr(Ureg *, void *ltmr)
3473de6a9c0SDavid du Colombier {
3483de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
3493de6a9c0SDavid du Colombier Ltimer *wd;
3503de6a9c0SDavid du Colombier
3513de6a9c0SDavid du Colombier wd = ltmr;
3523de6a9c0SDavid du Colombier fired++;
3533de6a9c0SDavid du Colombier wdogclrintr(wd);
3543de6a9c0SDavid du Colombier #endif
3553de6a9c0SDavid du Colombier USED(ltmr);
3563de6a9c0SDavid du Colombier }
3573de6a9c0SDavid du Colombier
3583de6a9c0SDavid du Colombier static void
ckcounting(Ltimer * lt)3593de6a9c0SDavid du Colombier ckcounting(Ltimer *lt)
3603de6a9c0SDavid du Colombier {
3613de6a9c0SDavid du Colombier ulong old;
3623de6a9c0SDavid du Colombier
3633de6a9c0SDavid du Colombier old = lt->cnt;
3643de6a9c0SDavid du Colombier if (old == lt->cnt)
3653de6a9c0SDavid du Colombier delay(1);
3663de6a9c0SDavid du Colombier if (old == lt->cnt)
3673de6a9c0SDavid du Colombier panic("cpu%d: watchdog timer not counting down", m->machno);
3683de6a9c0SDavid du Colombier }
3693de6a9c0SDavid du Colombier
3703de6a9c0SDavid du Colombier /* test fire with interrupt to see that it's working */
3713de6a9c0SDavid du Colombier static void
ckwatchdog(Ltimer * wd)3723de6a9c0SDavid du Colombier ckwatchdog(Ltimer *wd)
3733de6a9c0SDavid du Colombier {
3743de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
3753de6a9c0SDavid du Colombier int s;
3763de6a9c0SDavid du Colombier
3773de6a9c0SDavid du Colombier fired = 0;
3783de6a9c0SDavid du Colombier wd->load = Tcycles - 1;
3793de6a9c0SDavid du Colombier coherence();
3803de6a9c0SDavid du Colombier /* Tintena is supposed to be ignored in watchdog mode */
3813de6a9c0SDavid du Colombier wd->ctl |= Wdogena | Tintena;
3823de6a9c0SDavid du Colombier coherence();
3833de6a9c0SDavid du Colombier
3843de6a9c0SDavid du Colombier ckcounting(wd);
3853de6a9c0SDavid du Colombier
3863de6a9c0SDavid du Colombier s = spllo();
3873de6a9c0SDavid du Colombier delay(2 * 1000/HZ);
3883de6a9c0SDavid du Colombier splx(s);
3893de6a9c0SDavid du Colombier if (!fired)
3903de6a9c0SDavid du Colombier /* useless local watchdog */
3913de6a9c0SDavid du Colombier iprint("cpu%d: local watchdog failed to interrupt\n", m->machno);
3923de6a9c0SDavid du Colombier /* clean up */
3933de6a9c0SDavid du Colombier wd->ctl &= ~Wdogena;
3943de6a9c0SDavid du Colombier coherence();
3953de6a9c0SDavid du Colombier #endif
3963de6a9c0SDavid du Colombier USED(wd);
3973de6a9c0SDavid du Colombier }
3983de6a9c0SDavid du Colombier
3993de6a9c0SDavid du Colombier static void
startwatchdog(void)4003de6a9c0SDavid du Colombier startwatchdog(void)
4013de6a9c0SDavid du Colombier {
4023de6a9c0SDavid du Colombier #ifdef watchdog_not_bloody_useless
4033de6a9c0SDavid du Colombier Ltimer *wd;
4043de6a9c0SDavid du Colombier Ploctmr *lt;
4053de6a9c0SDavid du Colombier
4063de6a9c0SDavid du Colombier lt = (Ploctmr *)soc.loctmr;
4073de6a9c0SDavid du Colombier wd = <->wd;
4083de6a9c0SDavid du Colombier watchdogoff(wd);
4093de6a9c0SDavid du Colombier wdogclrintr(wd);
4103de6a9c0SDavid du Colombier irqenable(Wdtmrirq, wdogintr, wd, "watchdog");
4113de6a9c0SDavid du Colombier
4123de6a9c0SDavid du Colombier ckwatchdog(wd);
4133de6a9c0SDavid du Colombier
4143de6a9c0SDavid du Colombier /* set up for normal use, causing reset */
4153de6a9c0SDavid du Colombier wd->ctl &= ~Tintena; /* reset, don't interrupt */
4163de6a9c0SDavid du Colombier coherence();
4173de6a9c0SDavid du Colombier wd->ctl |= Wdog;
4183de6a9c0SDavid du Colombier coherence();
4193de6a9c0SDavid du Colombier wd->load = Dogtimeout - 1;
4203de6a9c0SDavid du Colombier coherence();
4213de6a9c0SDavid du Colombier wd->ctl |= Wdogena;
4223de6a9c0SDavid du Colombier coherence();
4233de6a9c0SDavid du Colombier
4243de6a9c0SDavid du Colombier ckcounting(wd);
4253de6a9c0SDavid du Colombier #endif
4263de6a9c0SDavid du Colombier }
4273de6a9c0SDavid du Colombier
4283de6a9c0SDavid du Colombier static void
clock0init(Ltimer * tn)4293de6a9c0SDavid du Colombier clock0init(Ltimer *tn)
4303de6a9c0SDavid du Colombier {
4313de6a9c0SDavid du Colombier int s;
4323de6a9c0SDavid du Colombier ulong old, fticks;
4333de6a9c0SDavid du Colombier
4343de6a9c0SDavid du Colombier /*
4353de6a9c0SDavid du Colombier * calibrate fastclock
4363de6a9c0SDavid du Colombier */
4373de6a9c0SDavid du Colombier s = splhi();
4383de6a9c0SDavid du Colombier tn->load = ~0ul >> 1;
4393de6a9c0SDavid du Colombier coherence();
4403de6a9c0SDavid du Colombier tn->ctl = Tmrena;
4413de6a9c0SDavid du Colombier coherence();
4423de6a9c0SDavid du Colombier
4433de6a9c0SDavid du Colombier old = perfticks();
4443de6a9c0SDavid du Colombier fticks = tn->cnt;
4453de6a9c0SDavid du Colombier delay(1);
4463de6a9c0SDavid du Colombier fticks = abs(tn->cnt - fticks);
4473de6a9c0SDavid du Colombier old = perfticks() - old;
4483de6a9c0SDavid du Colombier splx(s);
4493de6a9c0SDavid du Colombier if (Debug)
4503de6a9c0SDavid du Colombier iprint("cpu%d: fastclock %ld/%ldµs = %ld fastticks/µs (MHz)\n",
4513de6a9c0SDavid du Colombier m->machno, fticks, old, (fticks + old/2 - 1) / old);
4523de6a9c0SDavid du Colombier USED(fticks, old);
4533de6a9c0SDavid du Colombier
4543de6a9c0SDavid du Colombier if (Debug)
4553de6a9c0SDavid du Colombier iprint("cpu%d: ", m->machno);
4563de6a9c0SDavid du Colombier guessmips(issue1loop, "single");
4573de6a9c0SDavid du Colombier if (Debug)
4583de6a9c0SDavid du Colombier iprint(", ");
4593de6a9c0SDavid du Colombier guessmips(issue2loop, "dual");
4603de6a9c0SDavid du Colombier if (Debug)
4613de6a9c0SDavid du Colombier iprint("\n");
4623de6a9c0SDavid du Colombier
4633de6a9c0SDavid du Colombier /*
4643de6a9c0SDavid du Colombier * m->delayloop should be the number of delay loop iterations
4653de6a9c0SDavid du Colombier * needed to consume 1 ms. 2 is instr'ns in the delay loop.
4663de6a9c0SDavid du Colombier */
4673de6a9c0SDavid du Colombier m->delayloop = m->cpuhz / (1000 * 2);
4683de6a9c0SDavid du Colombier // iprint("cpu%d: m->delayloop = %lud\n", m->machno, m->delayloop);
4693de6a9c0SDavid du Colombier
4703de6a9c0SDavid du Colombier tegclock0init();
4713de6a9c0SDavid du Colombier }
4723de6a9c0SDavid du Colombier
4733de6a9c0SDavid du Colombier /*
4743de6a9c0SDavid du Colombier * the local timer is the interrupting timer and does not
4753de6a9c0SDavid du Colombier * participate in measuring time. It is initially set to HZ.
4763de6a9c0SDavid du Colombier */
4773de6a9c0SDavid du Colombier void
clockinit(void)4783de6a9c0SDavid du Colombier clockinit(void)
4793de6a9c0SDavid du Colombier {
4803de6a9c0SDavid du Colombier ulong old;
4813de6a9c0SDavid du Colombier Ltimer *tn;
4823de6a9c0SDavid du Colombier Ploctmr *lt;
4833de6a9c0SDavid du Colombier
4843de6a9c0SDavid du Colombier clockshutdown();
4853de6a9c0SDavid du Colombier
4863de6a9c0SDavid du Colombier /* turn my cycle counter on */
4873de6a9c0SDavid du Colombier cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31);
4883de6a9c0SDavid du Colombier
4893de6a9c0SDavid du Colombier /* turn all my counters on and clear my cycle counter */
4903de6a9c0SDavid du Colombier cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1);
4913de6a9c0SDavid du Colombier
4923de6a9c0SDavid du Colombier /* let users read my cycle counter directly */
4933de6a9c0SDavid du Colombier cpwrsc(0, CpCLD, CpCLDuser, CpCLDenapmnc, 1);
4943de6a9c0SDavid du Colombier
4953de6a9c0SDavid du Colombier /* verify µs counter sanity */
4963de6a9c0SDavid du Colombier tegclockinit();
4973de6a9c0SDavid du Colombier
4983de6a9c0SDavid du Colombier lt = (Ploctmr *)soc.loctmr;
4993de6a9c0SDavid du Colombier tn = <->loc;
5003de6a9c0SDavid du Colombier if (m->machno == 0)
5013de6a9c0SDavid du Colombier irqenable(Loctmrirq, clockintr, lt, "clock");
5023de6a9c0SDavid du Colombier else
5033de6a9c0SDavid du Colombier intcunmask(Loctmrirq);
5043de6a9c0SDavid du Colombier
5053de6a9c0SDavid du Colombier /*
5063de6a9c0SDavid du Colombier * verify sanity of local timer
5073de6a9c0SDavid du Colombier */
5083de6a9c0SDavid du Colombier tn->load = Clockfreqbase / 1000;
5093de6a9c0SDavid du Colombier tn->isr = Xisrclk;
5103de6a9c0SDavid du Colombier coherence();
5113de6a9c0SDavid du Colombier tn->ctl = Tmrena;
5123de6a9c0SDavid du Colombier coherence();
5133de6a9c0SDavid du Colombier
5143de6a9c0SDavid du Colombier old = tn->cnt;
5153de6a9c0SDavid du Colombier delay(5);
5163de6a9c0SDavid du Colombier /* m->ticks won't be incremented here because timersinit hasn't run. */
5173de6a9c0SDavid du Colombier if (tn->cnt == old)
5183de6a9c0SDavid du Colombier panic("cpu%d: clock not ticking at all", m->machno);
5193de6a9c0SDavid du Colombier else if ((long)tn->cnt > 0)
5203de6a9c0SDavid du Colombier panic("cpu%d: clock ticking slowly", m->machno);
5213de6a9c0SDavid du Colombier
5223de6a9c0SDavid du Colombier if (m->machno == 0)
5233de6a9c0SDavid du Colombier clock0init(tn);
5243de6a9c0SDavid du Colombier
5253de6a9c0SDavid du Colombier /* if pci gets stuck, maybe one of the many watchdogs will nuke us. */
5263de6a9c0SDavid du Colombier startwatchdog();
5273de6a9c0SDavid du Colombier
5283de6a9c0SDavid du Colombier /*
5293de6a9c0SDavid du Colombier * desynchronize the processor clocks so that they all don't
5303de6a9c0SDavid du Colombier * try to resched at the same time.
5313de6a9c0SDavid du Colombier */
5323de6a9c0SDavid du Colombier delay(m->machno*2);
5333de6a9c0SDavid du Colombier setltimer(tn, Tcycles);
5343de6a9c0SDavid du Colombier }
5353de6a9c0SDavid du Colombier
5363de6a9c0SDavid du Colombier /* our fastticks are at 1MHz (Basetickfreq), so the conversion is trivial. */
5373de6a9c0SDavid du Colombier ulong
s(void)5383de6a9c0SDavid du Colombier µs(void)
5393de6a9c0SDavid du Colombier {
5403de6a9c0SDavid du Colombier return fastticks2us(fastticks(nil));
5413de6a9c0SDavid du Colombier }
5423de6a9c0SDavid du Colombier
5433de6a9c0SDavid du Colombier /* Tval is supposed to be in fastticks units. */
5443de6a9c0SDavid du Colombier void
timerset(Tval next)5453de6a9c0SDavid du Colombier timerset(Tval next)
5463de6a9c0SDavid du Colombier {
5473de6a9c0SDavid du Colombier int s;
5483de6a9c0SDavid du Colombier long offset;
5493de6a9c0SDavid du Colombier Ltimer *tn;
5503de6a9c0SDavid du Colombier
5513de6a9c0SDavid du Colombier tn = &((Ploctmr *)soc.loctmr)->loc;
5523de6a9c0SDavid du Colombier s = splhi();
5533de6a9c0SDavid du Colombier offset = fastticks2us(next - fastticks(nil));
5543de6a9c0SDavid du Colombier /* offset is now in µs (MHz); convert to Clockfreqbase Hz. */
5553de6a9c0SDavid du Colombier offset *= Clockfreqbase / Mhz;
5563de6a9c0SDavid du Colombier if(offset < MinPeriod)
5573de6a9c0SDavid du Colombier offset = MinPeriod;
5583de6a9c0SDavid du Colombier else if(offset > MaxPeriod)
5593de6a9c0SDavid du Colombier offset = MaxPeriod;
5603de6a9c0SDavid du Colombier
5613de6a9c0SDavid du Colombier setltimer(tn, offset);
5623de6a9c0SDavid du Colombier splx(s);
5633de6a9c0SDavid du Colombier }
5643de6a9c0SDavid du Colombier
5653de6a9c0SDavid du Colombier static ulong
cpucycles(void)5663de6a9c0SDavid du Colombier cpucycles(void) /* cpu clock rate, except when waiting for intr (unused) */
5673de6a9c0SDavid du Colombier {
5683de6a9c0SDavid du Colombier ulong v;
5693de6a9c0SDavid du Colombier
5703de6a9c0SDavid du Colombier /* reads 32-bit cycle counter (counting up) */
5713de6a9c0SDavid du Colombier // v = cprdsc(0, CpCLD, CpCLDcyc, 0);
5723de6a9c0SDavid du Colombier v = getcyc(); /* fast asm */
5733de6a9c0SDavid du Colombier /* keep it non-negative; prevent m->fastclock ever going to 0 */
5743de6a9c0SDavid du Colombier return v == 0? 1: v;
5753de6a9c0SDavid du Colombier }
5763de6a9c0SDavid du Colombier
5773de6a9c0SDavid du Colombier long
lcycles(void)5783de6a9c0SDavid du Colombier lcycles(void)
5793de6a9c0SDavid du Colombier {
5803de6a9c0SDavid du Colombier return perfticks();
5813de6a9c0SDavid du Colombier }
5823de6a9c0SDavid du Colombier
5833de6a9c0SDavid du Colombier uvlong
fastticks(uvlong * hz)5843de6a9c0SDavid du Colombier fastticks(uvlong *hz)
5853de6a9c0SDavid du Colombier {
586*35ef7fcaSDavid du Colombier int s;
587*35ef7fcaSDavid du Colombier ulong newticks;
588*35ef7fcaSDavid du Colombier Vlong *fcp;
5893de6a9c0SDavid du Colombier
5903de6a9c0SDavid du Colombier if(hz)
5913de6a9c0SDavid du Colombier *hz = Basetickfreq;
5923de6a9c0SDavid du Colombier
593*35ef7fcaSDavid du Colombier fcp = (Vlong *)&m->fastclock;
594*35ef7fcaSDavid du Colombier /* avoid reentry on interrupt or trap, to prevent recursion */
595*35ef7fcaSDavid du Colombier s = splhi();
596*35ef7fcaSDavid du Colombier newticks = perfticks();
597*35ef7fcaSDavid du Colombier if(newticks < fcp->low) /* low word must have wrapped */
598*35ef7fcaSDavid du Colombier fcp->high++;
599*35ef7fcaSDavid du Colombier fcp->low = newticks;
600*35ef7fcaSDavid du Colombier splx(s);
601*35ef7fcaSDavid du Colombier
602*35ef7fcaSDavid du Colombier if (fcp->low == 0 && fcp->high == 0 && m->ticks > HZ/10)
603*35ef7fcaSDavid du Colombier panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",
604*35ef7fcaSDavid du Colombier m->ticks, m->fastclock);
605*35ef7fcaSDavid du Colombier return m->fastclock;
6063de6a9c0SDavid du Colombier }
6073de6a9c0SDavid du Colombier
6083de6a9c0SDavid du Colombier void
microdelay(int l)6093de6a9c0SDavid du Colombier microdelay(int l)
6103de6a9c0SDavid du Colombier {
6113de6a9c0SDavid du Colombier for (l = l * (vlong)m->delayloop / 1000; --l >= 0; )
6123de6a9c0SDavid du Colombier ;
6133de6a9c0SDavid du Colombier }
6143de6a9c0SDavid du Colombier
6153de6a9c0SDavid du Colombier void
delay(int l)6163de6a9c0SDavid du Colombier delay(int l)
6173de6a9c0SDavid du Colombier {
6183de6a9c0SDavid du Colombier int i, d;
6193de6a9c0SDavid du Colombier
6203de6a9c0SDavid du Colombier d = m->delayloop;
6213de6a9c0SDavid du Colombier while(--l >= 0)
6223de6a9c0SDavid du Colombier for (i = d; --i >= 0; )
6233de6a9c0SDavid du Colombier ;
6243de6a9c0SDavid du Colombier }
625