Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

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