Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * kirkwood clocks
 *
 * timers count down to zero.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"

#include "ureg.h"

enum {
        Tcycles         = CLOCKFREQ / HZ,       /* cycles per clock tick */
        Dogperiod       = 15 * CLOCKFREQ, /* at most 21 s.; must fit in ulong */
        MaxPeriod       = Tcycles,
        MinPeriod       = MaxPeriod / 100,

        /* timer ctl bits */
        Tmr0enable      = 1<<0,
        Tmr0reload      = 1<<1, /* at 0 count, load timer0 from reload0 */
        Tmr1enable      = 1<<2,
        Tmr1reload      = 1<<3, /* at 0 count, load timer1 from reload1 */
        TmrWDenable     = 1<<4,
        TmrWDreload     = 1<<5,
};

typedef struct TimerReg TimerReg;
struct TimerReg
{
        ulong   ctl;
        ulong   pad[3];
        ulong   reload0;
        ulong   timer0;                 /* cycles until zero */
        ulong   reload1;
        ulong   timer1;                 /* cycles until zero */
        ulong   reloadwd;
        ulong   timerwd;
};

static int ticks; /* for sanity checking; m->ticks doesn't always get updated */

static void
clockintr(Ureg *ureg, void *arg)
{
        TimerReg *tmr = arg;
        static int nesting;

        tmr->timerwd = Dogperiod;               /* reassure the watchdog */
        ticks++;
        coherence();

        if (nesting == 0) {     /* if the clock interrupted itself, bail out */
                ++nesting;
                timerintr(ureg, 0);
                --nesting;
        }

        intrclear(Irqbridge, IRQcputimer0);
}

/* stop clock interrupts and disable the watchdog timer */
void
clockshutdown(void)
{
        TimerReg *tmr = (TimerReg *)soc.clock;

        tmr->ctl = 0;
        coherence();
}

void
clockinit(void)
{
        int i, s;
        CpucsReg *cpu = (CpucsReg *)soc.cpu;
        TimerReg *tmr = (TimerReg *)soc.clock;

        clockshutdown();

        /*
         * verify sanity of timer0
         */

        intrenable(Irqbridge, IRQcputimer0, clockintr, tmr, "clock0");
        s = spllo();                    /* risky */
        /* take any deferred clock (& other) interrupts here */
        splx(s);

        /* adjust m->bootdelay, used by delay()? */
        m->ticks = ticks = 0;
        m->fastclock = 0;

        tmr->timer0 = 1;
        tmr->ctl = Tmr0enable;          /* just once */
        coherence();

        s = spllo();                    /* risky */
        for (i = 0; i < 10 && ticks == 0; i++) {
                delay(1);
                coherence();
        }
        splx(s);
        if (ticks == 0) {
                serialputc('?');
                if (tmr->timer0 == 0)
                        panic("clock not interrupting");
                else if (tmr->timer0 == tmr->reload0)
                        panic("clock not ticking");
                else
                        panic("clock running very slowly");
        }

        /*
         * configure all timers
         */
        clockshutdown();
        tmr->reload0 = tmr->timer0 = Tcycles;   /* tick clock */
        tmr->reload1 = tmr->timer1 = ~0;        /* cycle clock */
        tmr->timerwd = Dogperiod;               /* watch dog timer */
        coherence();
        tmr->ctl = Tmr0enable | Tmr0reload | Tmr1enable | Tmr1reload |
                TmrWDenable;
        cpu->rstout |= RstoutWatchdog;
        coherence();
}

void
timerset(Tval next)
{
        int offset;
        TimerReg *tmr = (TimerReg *)soc.clock;

        offset = next - fastticks(nil);
        if(offset < MinPeriod)
                offset = MinPeriod;
        else if(offset > MaxPeriod)
                offset = MaxPeriod;
        tmr->timer0 = offset;
        coherence();
}

uvlong
fastticks(uvlong *hz)
{
        uvlong now;
        int s;

        if(hz)
                *hz = CLOCKFREQ;
        s = splhi();
        /* zero low ulong of fastclock */
        now = (m->fastclock & ~(uvlong)~0ul) | perfticks();
        if(now < m->fastclock)          /* low bits must have wrapped */
                now += 1ll << 32;
        m->fastclock = now;
        splx(s);
        return now;
}

ulong
perfticks(void)
{
        TimerReg *tmr = (TimerReg *)soc.clock;

        return ~tmr->timer1;
}

long
lcycles(void)
{
        return perfticks();
}

ulong
µs(void)
{
        return fastticks2us(fastticks(nil));
}

void
microdelay(int l)
{
        int i;

        l *= m->delayloop;
        l /= 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++)
                        ;
}