/*
* 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 */
/*
* 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 */
/*
* 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;
/*
* 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;
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);
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);