/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * 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 voidwdogwrss(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 voidresetwait(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 voidwdogoff(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 voidwdogon(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 voidwdogassure(void) /* reset the watch dog's counter */{ Timerregs *tn; tn = (Timerregs *)PHYSWDOG; wdogoff(tn); tn->tcrr = -Dogtimeout; coherence(); wdogon(tn);} static voidclockintr(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 voidclockreset(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 */voidclockshutdown(void){ clockreset((Timerregs *)PHYSWDT2); wdogoff((Timerregs *)PHYSWDT2); clockreset((Timerregs *)PHYSWDT3); wdogoff((Timerregs *)PHYSWDT3); clockreset((Timerregs *)Tn0); clockreset((Timerregs *)Tn1);} enum { Instrs = 10*Mhz,}; static longissue1loop(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 longissue2loop(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 voidguessmips(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);} voidclockinit(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);} voidwatchdoginit(void){ wdogassure();} ulongµs(void){ return fastticks2us(fastticks(nil));} voidtimerset(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 ulongrdcycles(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 ulongrdbaseticks(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;} ulongperfticks(void){ return rdcycles();} longlcycles(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,}; uvlongfastticks(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;} voidmicrodelay(int l){ int i; l = l * (vlong)m->delayloop / 1000; if(l <= 0) l = 1; for(i = 0; i < l; i++) ;} voiddelay(int l){ ulong i, j; j = m->delayloop; while(l-- > 0) for(i=0; i < j; i++) ;}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* 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++)
;
}