summaryrefslogtreecommitdiff
path: root/sys/src/9/omap/clock.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/omap/clock.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/omap/clock.c')
-rwxr-xr-xsys/src/9/omap/clock.c489
1 files changed, 489 insertions, 0 deletions
diff --git a/sys/src/9/omap/clock.c b/sys/src/9/omap/clock.c
new file mode 100755
index 000000000..eaab1e270
--- /dev/null
+++ b/sys/src/9/omap/clock.c
@@ -0,0 +1,489 @@
+/*
+ * omap3530 clocks
+ *
+ * timers count up to zero.
+ *
+ * the source clock signals for the timers are sometimes selectable. for
+ * WDTIMER[23] and GPTIMER12, it's always the 32kHz clock. for the
+ * others, it can be the 32kHz clock or the system clock. we use only
+ * WDTIMER2 and GPTIMER[12], and configure GPTIMER[12] in archomap.c to
+ * use the 32kHZ clock. WDTIMER1 is not accessible to us on GP
+ * (general-purpose) omaps.
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "arm.h"
+
+enum {
+ Debug = 0,
+
+ Tn0 = PHYSTIMER1,
+ Tn1 = PHYSTIMER2,
+
+ /* irq 36 is watchdog timer module 3 overflow */
+ Tn0irq = 37, /* base IRQ for all timers */
+
+ Freebase = 1, /* base of free-running timer */
+
+ /*
+ * clock is 32K (32,768) Hz, so one tick is 30.517µs,
+ * so 327.68 ticks is 10ms, 32.768 ticks is 1ms.
+ */
+ Clockfreqbase = 32 * 1024, /* base rate in Hz */
+ Tcycles = Clockfreqbase / HZ, /* cycles per clock tick */
+
+ MinPeriod = (Tcycles / 100 < 2? 2: Tcycles / 100),
+ MaxPeriod = Tcycles,
+
+ Dogtimeout = 20 * Clockfreqbase, /* was 4 s.; must be ≤ 21 s. */
+};
+
+enum {
+ /* ticpcfg bits */
+ Noidle = 1<<3,
+ Softreset = 1<<1,
+
+ /* tistat bits */
+ Resetdone = 1<<0,
+
+ /* tisr/tier bits */
+ Ovf_it = 1<<1, /* gp: overflow intr */
+ Mat_it = 1<<0, /* gp: match intr */
+ Wdovf_it = 1<<0, /* wdog: overflow intr */
+
+ /* tclr bits */
+ Ar = 1<<1, /* gp only: autoreload mode overflow */
+ St = 1<<0, /* gp only: start the timer */
+};
+
+/* omap35x timer registers */
+typedef struct Timerregs Timerregs;
+struct Timerregs {
+ /* common to all timers, gp and watchdog */
+ uchar pad0[0x10];
+ ulong ticpcfg;
+ ulong tistat; /* ro: low bit: reset done */
+ ulong tisr;
+ ulong tier;
+ ulong twer;
+ ulong tclr;
+ ulong tcrr; /* counter: cycles to zero */
+ ulong tldr;
+ ulong ttgr; /* trigger */
+ ulong twps; /* ro: write posted pending */
+
+ /* gp timers only, unused by us */
+ ulong tmar; /* value to compare with counter */
+ ulong tcar1; /* ro */
+ ulong tsicr;
+ ulong tcar2; /* ro */
+ union {
+ ulong tpir; /* gp: 1 ms tick generation: +ve */
+ ulong wspr; /* wdog: start/stop control */
+ };
+ ulong tnir; /* 1 ms tick generation: -ve */
+ ulong tcvr; /* 1 ms tick generation: next counter value */
+ ulong tocr; /* intr mask for n ticks */
+ ulong towr;
+};
+
+static int ticks; /* for sanity checking; m->ticks doesn't always get called */
+static Lock clklck;
+
+static ulong rdcycles(void), rdbaseticks(void);
+
+/* write a watchdog timer's start/stop register */
+static void
+wdogwrss(Timerregs *tn, ulong val)
+{
+ while (tn->twps & (1 << 4)) /* pending write to start/stop reg? */
+ ;
+ tn->wspr = val;
+ coherence();
+ while (tn->twps & (1 << 4)) /* pending write to start/stop reg? */
+ ;
+}
+
+static void
+resetwait(Timerregs *tn)
+{
+ long bound;
+
+ for (bound = 400*Mhz; !(tn->tistat & Resetdone) && bound > 0; bound--)
+ ;
+ if (bound <= 0)
+ iprint("clock reset didn't complete\n");
+}
+
+
+static void
+wdogoff(Timerregs *tn)
+{
+ resetwait(tn);
+
+ wdogwrss(tn, 0xaaaa); /* magic off sequence */
+ wdogwrss(tn, 0x5555);
+
+ tn->tldr = 1;
+ coherence();
+ tn->tcrr = 1; /* paranoia */
+ coherence();
+}
+
+static void wdogassure(void);
+
+static void
+wdogon(Timerregs *tn)
+{
+ static int beenhere;
+
+ resetwait(tn);
+ tn->tldr = -Dogtimeout;
+ tn->tcrr = -Dogtimeout;
+ coherence();
+ wdogwrss(tn, 0xbbbb); /* magic on sequence */
+ wdogwrss(tn, 0x4444); /* magic on sequence */
+
+ if (!beenhere) {
+ beenhere = 1;
+ /* touching the dog is not quick, so do it infrequently */
+ addclock0link(wdogassure, HZ);
+ }
+}
+
+static void
+wdogassure(void) /* reset the watch dog's counter */
+{
+ Timerregs *tn;
+
+ tn = (Timerregs *)PHYSWDOG;
+ wdogoff(tn);
+
+ tn->tcrr = -Dogtimeout;
+ coherence();
+
+ wdogon(tn);
+}
+
+static void
+clockintr(Ureg* ureg, void *arg)
+{
+ Timerregs *tn;
+ static int nesting;
+
+ ticks++;
+ coherence();
+
+ if (nesting == 0) { /* if the clock interrupted itself, bail out */
+ ++nesting;
+ timerintr(ureg, 0);
+ --nesting;
+ }
+
+ tn = arg;
+ tn->tisr = Ovf_it; /* dismiss the interrupt */
+ coherence();
+}
+
+static void
+clockreset(Timerregs *tn)
+{
+ if (probeaddr((uintptr)&tn->ticpcfg) < 0)
+ panic("no clock at %#p", tn);
+ tn->ticpcfg = Softreset | Noidle;
+ coherence();
+ resetwait(tn);
+ tn->tier = tn->tclr = 0;
+ coherence();
+}
+
+/* stop clock interrupts and disable the watchdog timer */
+void
+clockshutdown(void)
+{
+ clockreset((Timerregs *)PHYSWDT2);
+ wdogoff((Timerregs *)PHYSWDT2);
+ clockreset((Timerregs *)PHYSWDT3);
+ wdogoff((Timerregs *)PHYSWDT3);
+
+ clockreset((Timerregs *)Tn0);
+ clockreset((Timerregs *)Tn1);
+}
+
+enum {
+ Instrs = 10*Mhz,
+};
+
+static long
+issue1loop(void)
+{
+ register int i;
+ long st;
+
+ st = rdbaseticks();
+ i = Instrs;
+ do {
+ --i; --i; --i; --i; --i;
+ --i; --i; --i; --i;
+ } while(--i >= 0);
+ return rdbaseticks() - st;
+}
+
+static long
+issue2loop(void)
+{
+ register int i, j;
+ long st;
+
+ st = rdbaseticks();
+ i = Instrs / 2;
+ j = 0;
+ do {
+ --i; --j; --i; --j;
+ --i; --j; --i; --j;
+ --j;
+ } while(--i >= 0);
+ return rdbaseticks() - st;
+}
+
+/* estimate instructions/s. using 32kHz clock */
+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 @ Clockfreqbase Hz.
+ */
+ s = ((vlong)Clockfreqbase * Instrs) / tcks / 1000000;
+ if (Debug)
+ iprint("%ud mips (%s-issue)", s, lab);
+ USED(s);
+}
+
+void
+clockinit(void)
+{
+ int i, s;
+ Timerregs *tn;
+
+ clockshutdown();
+
+ /* turn cycle counter on */
+ cpwrsc(0, CpCLD, CpCLDena, CpCLDenacyc, 1<<31);
+
+ /* turn all counters on and clear the cycle counter */
+ cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1<<2 | 1);
+
+ /* let users read the cycle counter directly */
+ cpwrsc(0, CpCLD, CpCLDena, CpCLDenapmnc, 1);
+
+ ilock(&clklck);
+ m->fastclock = 1;
+ m->ticks = ticks = 0;
+
+ /*
+ * T0 is a freerunning timer (cycle counter); it wraps,
+ * automatically reloads, and does not dispatch interrupts.
+ */
+ tn = (Timerregs *)Tn0;
+ tn->tcrr = Freebase; /* count up to 0 */
+ tn->tldr = Freebase;
+ coherence();
+ tn->tclr = Ar | St;
+ iunlock(&clklck);
+
+ /*
+ * T1 is the interrupting timer and does not participate
+ * in measuring time. It is initially set to HZ.
+ */
+ tn = (Timerregs *)Tn1;
+ irqenable(Tn0irq+1, clockintr, tn, "clock");
+ ilock(&clklck);
+ tn->tcrr = -Tcycles; /* approx.; count up to 0 */
+ tn->tldr = -Tcycles;
+ coherence();
+ tn->tclr = Ar | St;
+ coherence();
+ tn->tier = Ovf_it;
+ coherence();
+ iunlock(&clklck);
+
+ /*
+ * verify sanity of timer1
+ */
+ s = spllo(); /* risky */
+ for (i = 0; i < 5 && ticks == 0; i++) {
+ delay(10);
+ cachedwbinvse(&ticks, sizeof ticks);
+ }
+ splx(s);
+ if (ticks == 0) {
+ if (tn->tcrr == 0)
+ panic("clock not interrupting");
+ else if (tn->tcrr == tn->tldr)
+ panic("clock not ticking at all");
+#ifdef PARANOID
+ else
+ panic("clock running very slowly");
+#endif
+ }
+
+ 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 min. instructions in the delay loop.
+ */
+ m->delayloop = m->cpuhz / (1000 * 2);
+// iprint("m->delayloop = %lud\n", m->delayloop);
+
+ /*
+ * desynchronize the processor clocks so that they all don't
+ * try to resched at the same time.
+ */
+ delay(m->machno*2);
+}
+
+void
+watchdoginit(void)
+{
+ wdogassure();
+}
+
+ulong
+µs(void)
+{
+ return fastticks2us(fastticks(nil));
+}
+
+void
+timerset(Tval next)
+{
+ long offset;
+ Timerregs *tn = (Timerregs *)Tn1;
+ static Lock setlck;
+
+ ilock(&setlck);
+ offset = next - fastticks(nil);
+ if(offset < MinPeriod)
+ offset = MinPeriod;
+ else if(offset > MaxPeriod)
+ offset = MaxPeriod;
+ tn->tcrr = -offset;
+ coherence();
+ iunlock(&setlck);
+}
+
+static ulong
+rdcycles(void)
+{
+ ulong v;
+
+ /* reads 32-bit cycle counter (counting up) */
+ v = cprdsc(0, CpCLD, CpCLDcyc, 0);
+ /* keep it positive; prevent m->fastclock ever going to 0 */
+ return v == 0? 1: v;
+}
+
+static ulong
+rdbaseticks(void)
+{
+ ulong v;
+
+ v = ((Timerregs *)Tn0)->tcrr; /* tcrr should be counting up */
+ /* keep it positive; prevent m->fastclock ever going to 0 */
+ return v == 0? 1: v;
+}
+
+ulong
+perfticks(void)
+{
+ return rdcycles();
+}
+
+long
+lcycles(void)
+{
+ return perfticks();
+}
+
+/*
+ * until 5[cal] inline vlong ops, avoid them where possible,
+ * they are currently slow function calls.
+ */
+typedef union Counter Counter;
+union Counter {
+ uvlong uvl;
+ struct { /* little-endian */
+ ulong low;
+ ulong high;
+ };
+};
+
+enum {
+ Fastvlongops = 0,
+};
+
+uvlong
+fastticks(uvlong *hz)
+{
+ Counter now, sclnow;
+
+ if(hz)
+ *hz = m->cpuhz;
+ ilock(&clklck);
+ if (m->ticks > HZ/10 && m->fastclock == 0)
+ panic("fastticks: zero m->fastclock; ticks %lud fastclock %#llux",
+ m->ticks, m->fastclock);
+
+ now.uvl = m->fastclock;
+ now.low = rdcycles();
+ if(now.uvl < m->fastclock) /* low bits must have wrapped */
+ now.high++;
+ m->fastclock = now.uvl;
+ coherence();
+
+ sclnow.uvl = now.uvl;
+ iunlock(&clklck);
+ return sclnow.uvl;
+}
+
+void
+microdelay(int l)
+{
+ int i;
+
+ l = l * (vlong)m->delayloop / 1000;
+ if(l <= 0)
+ l = 1;
+ for(i = 0; i < l; i++)
+ ;
+}
+
+void
+delay(int l)
+{
+ ulong i, j;
+
+ j = m->delayloop;
+ while(l-- > 0)
+ for(i=0; i < j; i++)
+ ;
+}