ref: 2657773699da675d436705002230f2d25142f570
dir: /sys/src/9/omap/clock.c/
/* * 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++) ; }