/*
* ar7161 clocks and timers
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
enum {
Cyccntres = 2, /* counter advances at ½ clock rate (mips 24k) */
Basetickfreq = 680*Mhz / Cyccntres, /* rb450g */
};
void (*kproftimer)(ulong);
void
silencewdog(void)
{
*Rstwdogtimer = Basetickfreq * 2 * 5; /* pet the dog */
}
void
sicwdog(void)
{
*Rstwdogtimer = Basetickfreq * 2;
*Rstwdogctl = Wdogreset; /* wake the dog */
}
void
wdogreset(void)
{
*Rstwdogtimer = Basetickfreq / 100;
*Rstwdogctl = Wdogreset; /* wake the dog */
coherence();
*Rstwdogtimer = Basetickfreq / 10000;
coherence();
}
void
stopwdog(void)
{
*Rstwdogtimer = ~0;
*Rstwdogctl = Wdognoaction; /* put the dog to sleep */
}
void
clockshutdown(void)
{
stopwdog();
}
/*
* delay for l milliseconds more or less.
*/
void
delay(int l)
{
while(l-- > 0)
microdelay(1000);
}
/*
* microseconds delay
*/
void
microdelay(int l)
{
int s;
ulong x, cyc, cnt, speed;
speed = m->speed;
if (speed == 0)
speed = Basetickfreq / Mhz * Cyccntres;
cyc = (ulong)l * (speed / Cyccntres);
s = splhi();
cnt = rdcount();
x = cnt + cyc;
if (x < cnt || x >= ~0ul - Basetickfreq) {
/* counter will wrap between now and x, or x is too near ~0 */
wrcount(0); /* somewhat drastic */
wrcompare(rdcompare() - cnt); /* match new count */
x = cyc;
}
while(rdcount() < x)
;
splx(s);
silencewdog();
}
void
clock(Ureg *ureg)
{
wrcompare(rdcount()+m->maxperiod); /* side-effect: dismiss intr */
silencewdog();
timerintr(ureg, 0);
}
enum {
Instrs = 10*Mhz,
};
static long
issue1loop(void)
{
register int i;
long st;
i = Instrs;
st = perfticks();
do {
--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
--i; --i; --i; --i; --i; --i; --i; --i; --i; --i;
--i; --i; --i; --i; --i;
/* omit 3 (--i) to account for conditional branch, nop & jump */
i -= 1+3; /* --i plus 3 omitted (--i) instructions */
} while(--i >= 0);
return perfticks() - st;
}
/* estimate instructions/s. */
static int
guessmips(long (*loop)(void), char *)
{
int s;
long cyc;
do {
s = splhi();
cyc = loop();
splx(s);
if (cyc < 0)
iprint("again...");
} while (cyc < 0);
/*
* Instrs instructions took cyc cycles @ Basetickfreq Hz.
* round the result.
*/
return (((vlong)Basetickfreq * Instrs) / cyc + Mhz/2) / Mhz;
}
void
clockinit(void)
{
int mips;
silencewdog();
/*
* calibrate fastclock
*/
mips = guessmips(issue1loop, "single");
/*
* m->delayloop should be the number of delay loop iterations
* needed to consume 1 ms, assuming 2 instr'ns in the delay loop.
*/
m->delayloop = mips*Mhz / (1000 * 2);
if(m->delayloop == 0)
m->delayloop = 1;
m->speed = mips;
m->hz = m->speed*Mhz;
m->maxperiod = Basetickfreq / HZ;
m->minperiod = Basetickfreq / (100*HZ);
wrcompare(rdcount()+m->maxperiod);
/*
* desynchronize the processor clocks so that they all don't
* try to resched at the same time.
*/
delay(m->machno*2);
syncclock();
intron(INTR7);
}
/*
* Tval is supposed to be in fastticks units.
* One fasttick unit is 1/Basetickfreq seconds.
*/
void
timerset(Tval next)
{
int x;
long period;
if(next == 0)
return;
x = splhi(); /* don't let us get scheduled */
period = next - fastticks(nil);
if(period > m->maxperiod - m->minperiod)
period = m->maxperiod;
else if(period < m->minperiod)
period = m->minperiod;
wrcompare(rdcount()+period);
silencewdog();
splx(x);
}
/*
* The rewriting of compare in this routine shouldn't be necessary.
* However, we lose clock interrupts if I don't, either a chip bug
* or one of ours -- presotto
*/
uvlong
fastticks(uvlong *hz)
{
int x;
ulong delta, count;
if(hz)
*hz = Basetickfreq;
/* avoid reentry on interrupt or trap, to prevent recursion */
x = splhi();
count = rdcount();
if(rdcompare() - count > m->maxperiod)
wrcompare(count+m->maxperiod);
silencewdog();
if (count < m->lastcount) /* wrapped around? */
delta = count + ((1ull<<32) - m->lastcount);
else
delta = count - m->lastcount;
m->lastcount = count;
m->fastticks += delta;
splx(x);
return m->fastticks;
}
ulong
µs(void)
{
return fastticks2us(fastticks(nil));
}
/*
* performance measurement ticks. must be low overhead.
* doesn't have to count over a second.
*/
ulong
perfticks(void)
{
return rdcount();
}
long
lcycles(void)
{
return perfticks();
}
/* should use vlong hw counters ideally; lcycles is inadequate */
void
cycles(uvlong *cycp)
{
*cycp = fastticks(nil);
}
Lock mpsynclock;
/*
* synchronize all clocks with processor 0
*/
void
syncclock(void)
{
uvlong x;
if(m->machno == 0){
m->lastcount = rdcount();
m->fastticks = 0;
m->ticks = 0;
wrcompare(rdcount()+m->maxperiod);
} else {
/* wait for processor 0's soft clock to change and then sync ours */
lock(&mpsynclock);
x = MACHP(0)->fastticks;
while(MACHP(0)->fastticks == x)
;
m->lastcount = rdcount();
m->fastticks = MACHP(0)->fastticks;
m->ticks = MACHP(0)->ticks;
wrcompare(rdcount()+m->maxperiod);
unlock(&mpsynclock);
}
}
|