/* * kirkwood clock */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #define TIMERREG ((TimerReg*)AddrTimer) enum { Tcycles = CLOCKFREQ / HZ, /* cycles per clock tick */ /* timer ctl bits */ Tmr0enable = 1<<0, Tmr0periodic = 1<<1, Tmr1enable = 1<<2, Tmr1periodic = 1<<3, TmrWDenable = 1<<4, TmrWDperiodic = 1<<5, }; typedef struct TimerReg TimerReg; struct TimerReg { ulong ctl; ulong pad[3]; ulong reload0; ulong timer0; ulong reload1; ulong timer1; ulong reloadwd; ulong timerwd; }; static void clockintr(Ureg *ureg, void*) { TIMERREG->timerwd = CLOCKFREQ; /* reassure the watchdog */ m->fastclock++; coherence(); timerintr(ureg, 0); intrclear(Irqbridge, IRQcputimer0); } /* stop clock interrupts and disable the watchdog timer */ void clockshutdown(void) { TIMERREG->ctl = 0; coherence(); } void clockinit(void) { int s; long cyc; TimerReg *tmr = TIMERREG; clockshutdown(); intrenable(Irqbridge, IRQcputimer0, clockintr, nil, "clock"); s = spllo(); /* risky */ /* take any deferred clock (& other) interrupts here */ splx(s); /* adjust m->bootdelay, used by delay()? */ m->ticks = 0; m->fastclock = 0; tmr->timer0 = Tcycles; tmr->ctl = Tmr0enable; /* just once */ coherence(); s = spllo(); /* risky */ /* one iteration seems to take about 40 ns. */ for (cyc = Tcycles; cyc > 0 && m->fastclock == 0; cyc--) ; splx(s); if (m->fastclock == 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"); } clockshutdown(); tmr->timer0 = Tcycles; tmr->reload0 = Tcycles; tmr->timerwd = CLOCKFREQ; coherence(); tmr->ctl = Tmr0enable | Tmr0periodic | TmrWDenable; CPUCSREG->rstout |= RstoutWatchdog; coherence(); } void timerset(uvlong next) { #ifdef FANCYTIMERS Tn *tn; Tval offset; ilock(&timers.tn1lock); tn = (Tn*)Tn1; tn->cr = Tm; offset = next + tn->cv; if(offset < timers.tn1minperiod) offset = timers.tn1minperiod; else if(offset > timers.tn1maxperiod) offset = timers.tn1maxperiod; tn->lc = offset; tn->cr = Tm|Te; iunlock(&timers.tn1lock); #else USED(next); #endif } /* * shift by 8 to provide enough resolution that dropping the tick rate * won't mess up TOD calculation and cause infrequent clock interrupts. */ uvlong fastticks(uvlong *hz) { if(hz) *hz = HZ << 8; return m->fastclock << 8; } 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++) ; } ulong perfticks(void) { // return ((Tn*)Tn0)->cv; // TODO: FANCYTIMERS return (ulong)fastticks(nil); }