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