Subversion Repositories planix.SVN

Rev

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

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "msaturn.h"

enum {
        Timer_ctrl = Saturn + 0x0106,
        Timer0_load = Saturn + 0x0200,
        Timer0_cnt = Saturn + 0x0204,
        Timer1_load = Saturn + 0x0300,
        Timer1_cnt = Saturn + 0x0304,
        
        T0_event = RBIT(13, ushort),
        T0_ie = RBIT(14, ushort),
        T0_cen = RBIT(15, ushort),
        T1_event = RBIT(5, ushort),
        T1_ie = RBIT(6, ushort),
        T1_cen = RBIT(7, ushort),
};

static ulong ticks;
static Lock tlock;
static ushort timer_ctl;

ulong multiplier;

ulong
µs(void)
{
        uvlong x;

        if(multiplier == 0){
                multiplier = (uvlong)(1000000LL << 16) / m->cyclefreq;
                print("µs: multiplier %ld, cyclefreq %lld, shifter %d\n", multiplier, m->cyclefreq, 16);
        }
        cycles(&x);
        return (x*multiplier) >> 16;
}

void
saturntimerintr(Ureg *u, void*)
{
        ushort ctl = *(ushort*)Timer_ctrl, v = 0;

        if(ctl&T1_event){
                v = T1_event;
                ticks++;
        }
                
        *(ushort*)Timer_ctrl = timer_ctl|T0_event|v;
        intack();
        timerintr(u, 0);
}

void
timerinit(void)
{
        *(ushort*)Timer_ctrl = 0;
        *(ulong*)Timer0_load = m->bushz  / HZ;
        *(ulong*)Timer0_cnt = m->bushz / HZ;
        *(ulong*)Timer1_load = m->bushz;
        *(ulong*)Timer1_cnt = m->bushz;

        intrenable(Vectimer0, saturntimerintr, nil, "timer");

        timer_ctl = T0_cen|T0_ie|T1_cen;
        *(ushort*)Timer_ctrl = timer_ctl;
}

uvlong
fastticks(uvlong *hz)
{
        assert(*(ushort*)Timer_ctrl&T1_cen);
        if(*(ushort*)Timer_ctrl&T1_event){
                *(ushort*)Timer_ctrl = timer_ctl|T1_event;
                ticks++;
        }
                
        if (hz)
                *hz = m->bushz;

        return (uvlong)ticks*m->bushz+(uvlong)(m->bushz-*(ulong*)Timer1_cnt);
}

void
timerset(Tval next)
{
        ulong offset;
        uvlong now;

        ilock(&tlock);
        *(ushort*)Timer_ctrl = T1_cen;

        now = fastticks(nil);
        offset = next - now;
        if((long)offset < 10000)
                offset = 10000;
        else if(offset > m->bushz)
                offset = m->bushz;

        *(ulong*)Timer0_cnt = offset;
        *(ushort*)Timer_ctrl = timer_ctl;
        assert(*(ushort*)Timer_ctrl & T1_cen);
        iunlock(&tlock);
}