1154abd99SDavid du Colombier /*
2b2495906SDavid du Colombier * kirkwood clocks
30d0cdc9eSDavid du Colombier *
40d0cdc9eSDavid du Colombier * timers count down to zero.
5154abd99SDavid du Colombier */
6154abd99SDavid du Colombier #include "u.h"
7154abd99SDavid du Colombier #include "../port/lib.h"
8154abd99SDavid du Colombier #include "mem.h"
9154abd99SDavid du Colombier #include "dat.h"
10154abd99SDavid du Colombier #include "fns.h"
11154abd99SDavid du Colombier #include "io.h"
12154abd99SDavid du Colombier
13154abd99SDavid du Colombier #include "ureg.h"
14154abd99SDavid du Colombier
15154abd99SDavid du Colombier enum {
16154abd99SDavid du Colombier Tcycles = CLOCKFREQ / HZ, /* cycles per clock tick */
174b9994c9SDavid du Colombier Dogperiod = 15 * CLOCKFREQ, /* at most 21 s.; must fit in ulong */
180d0cdc9eSDavid du Colombier MaxPeriod = Tcycles,
190d0cdc9eSDavid du Colombier MinPeriod = MaxPeriod / 100,
2056713243SDavid du Colombier
2156713243SDavid du Colombier /* timer ctl bits */
2256713243SDavid du Colombier Tmr0enable = 1<<0,
2380f6c381SDavid du Colombier Tmr0reload = 1<<1, /* at 0 count, load timer0 from reload0 */
2456713243SDavid du Colombier Tmr1enable = 1<<2,
2580f6c381SDavid du Colombier Tmr1reload = 1<<3, /* at 0 count, load timer1 from reload1 */
2656713243SDavid du Colombier TmrWDenable = 1<<4,
2780f6c381SDavid du Colombier TmrWDreload = 1<<5,
2856713243SDavid du Colombier };
2956713243SDavid du Colombier
3056713243SDavid du Colombier typedef struct TimerReg TimerReg;
3156713243SDavid du Colombier struct TimerReg
3256713243SDavid du Colombier {
3356713243SDavid du Colombier ulong ctl;
3456713243SDavid du Colombier ulong pad[3];
3556713243SDavid du Colombier ulong reload0;
360d0cdc9eSDavid du Colombier ulong timer0; /* cycles until zero */
3756713243SDavid du Colombier ulong reload1;
380d0cdc9eSDavid du Colombier ulong timer1; /* cycles until zero */
3956713243SDavid du Colombier ulong reloadwd;
4056713243SDavid du Colombier ulong timerwd;
41154abd99SDavid du Colombier };
42154abd99SDavid du Colombier
4380f6c381SDavid du Colombier static int ticks; /* for sanity checking; m->ticks doesn't always get updated */
440d0cdc9eSDavid du Colombier
45154abd99SDavid du Colombier static void
clockintr(Ureg * ureg,void * arg)460d0cdc9eSDavid du Colombier clockintr(Ureg *ureg, void *arg)
47154abd99SDavid du Colombier {
480d0cdc9eSDavid du Colombier TimerReg *tmr = arg;
4980f6c381SDavid du Colombier static int nesting;
500d0cdc9eSDavid du Colombier
51003c0964SDavid du Colombier tmr->timerwd = Dogperiod; /* reassure the watchdog */
520d0cdc9eSDavid du Colombier ticks++;
53304cb050SDavid du Colombier coherence();
5480f6c381SDavid du Colombier
5580f6c381SDavid du Colombier if (nesting == 0) { /* if the clock interrupted itself, bail out */
5680f6c381SDavid du Colombier ++nesting;
57154abd99SDavid du Colombier timerintr(ureg, 0);
5880f6c381SDavid du Colombier --nesting;
5980f6c381SDavid du Colombier }
6080f6c381SDavid du Colombier
61154abd99SDavid du Colombier intrclear(Irqbridge, IRQcputimer0);
62154abd99SDavid du Colombier }
63154abd99SDavid du Colombier
6456713243SDavid du Colombier /* stop clock interrupts and disable the watchdog timer */
6556713243SDavid du Colombier void
clockshutdown(void)6656713243SDavid du Colombier clockshutdown(void)
6756713243SDavid du Colombier {
68*7365b686SDavid du Colombier TimerReg *tmr = (TimerReg *)soc.clock;
69*7365b686SDavid du Colombier
70*7365b686SDavid du Colombier tmr->ctl = 0;
7156713243SDavid du Colombier coherence();
7256713243SDavid du Colombier }
7356713243SDavid du Colombier
74154abd99SDavid du Colombier void
clockinit(void)75154abd99SDavid du Colombier clockinit(void)
76154abd99SDavid du Colombier {
770d0cdc9eSDavid du Colombier int i, s;
78*7365b686SDavid du Colombier CpucsReg *cpu = (CpucsReg *)soc.cpu;
79*7365b686SDavid du Colombier TimerReg *tmr = (TimerReg *)soc.clock;
80154abd99SDavid du Colombier
8156713243SDavid du Colombier clockshutdown();
82154abd99SDavid du Colombier
830d0cdc9eSDavid du Colombier /*
840d0cdc9eSDavid du Colombier * verify sanity of timer0
850d0cdc9eSDavid du Colombier */
860d0cdc9eSDavid du Colombier
8780f6c381SDavid du Colombier intrenable(Irqbridge, IRQcputimer0, clockintr, tmr, "clock0");
88154abd99SDavid du Colombier s = spllo(); /* risky */
89154abd99SDavid du Colombier /* take any deferred clock (& other) interrupts here */
90154abd99SDavid du Colombier splx(s);
91154abd99SDavid du Colombier
92154abd99SDavid du Colombier /* adjust m->bootdelay, used by delay()? */
930d0cdc9eSDavid du Colombier m->ticks = ticks = 0;
94154abd99SDavid du Colombier m->fastclock = 0;
95154abd99SDavid du Colombier
960d0cdc9eSDavid du Colombier tmr->timer0 = 1;
97154abd99SDavid du Colombier tmr->ctl = Tmr0enable; /* just once */
985e27dea9SDavid du Colombier coherence();
99154abd99SDavid du Colombier
100154abd99SDavid du Colombier s = spllo(); /* risky */
1010d0cdc9eSDavid du Colombier for (i = 0; i < 10 && ticks == 0; i++) {
1020d0cdc9eSDavid du Colombier delay(1);
1030d0cdc9eSDavid du Colombier coherence();
1040d0cdc9eSDavid du Colombier }
105154abd99SDavid du Colombier splx(s);
1060d0cdc9eSDavid du Colombier if (ticks == 0) {
107154abd99SDavid du Colombier serialputc('?');
108154abd99SDavid du Colombier if (tmr->timer0 == 0)
109154abd99SDavid du Colombier panic("clock not interrupting");
110154abd99SDavid du Colombier else if (tmr->timer0 == tmr->reload0)
111154abd99SDavid du Colombier panic("clock not ticking");
112154abd99SDavid du Colombier else
113154abd99SDavid du Colombier panic("clock running very slowly");
114154abd99SDavid du Colombier }
115154abd99SDavid du Colombier
1160d0cdc9eSDavid du Colombier /*
1170d0cdc9eSDavid du Colombier * configure all timers
1180d0cdc9eSDavid du Colombier */
11956713243SDavid du Colombier clockshutdown();
1200d0cdc9eSDavid du Colombier tmr->reload0 = tmr->timer0 = Tcycles; /* tick clock */
1210d0cdc9eSDavid du Colombier tmr->reload1 = tmr->timer1 = ~0; /* cycle clock */
122003c0964SDavid du Colombier tmr->timerwd = Dogperiod; /* watch dog timer */
123304cb050SDavid du Colombier coherence();
12480f6c381SDavid du Colombier tmr->ctl = Tmr0enable | Tmr0reload | Tmr1enable | Tmr1reload |
12580f6c381SDavid du Colombier TmrWDenable;
126a7a38e3eSDavid du Colombier cpu->rstout |= RstoutWatchdog;
1275e27dea9SDavid du Colombier coherence();
128154abd99SDavid du Colombier }
129154abd99SDavid du Colombier
130154abd99SDavid du Colombier void
timerset(Tval next)131b2495906SDavid du Colombier timerset(Tval next)
132154abd99SDavid du Colombier {
133b2495906SDavid du Colombier int offset;
134*7365b686SDavid du Colombier TimerReg *tmr = (TimerReg *)soc.clock;
135154abd99SDavid du Colombier
136b2495906SDavid du Colombier offset = next - fastticks(nil);
137b2495906SDavid du Colombier if(offset < MinPeriod)
138b2495906SDavid du Colombier offset = MinPeriod;
139b2495906SDavid du Colombier else if(offset > MaxPeriod)
140b2495906SDavid du Colombier offset = MaxPeriod;
141b2495906SDavid du Colombier tmr->timer0 = offset;
142b2495906SDavid du Colombier coherence();
143154abd99SDavid du Colombier }
144154abd99SDavid du Colombier
145154abd99SDavid du Colombier uvlong
fastticks(uvlong * hz)146154abd99SDavid du Colombier fastticks(uvlong *hz)
147154abd99SDavid du Colombier {
148b2495906SDavid du Colombier uvlong now;
149b2495906SDavid du Colombier int s;
150b2495906SDavid du Colombier
151154abd99SDavid du Colombier if(hz)
152b2495906SDavid du Colombier *hz = CLOCKFREQ;
153b2495906SDavid du Colombier s = splhi();
154b2495906SDavid du Colombier /* zero low ulong of fastclock */
1550d0cdc9eSDavid du Colombier now = (m->fastclock & ~(uvlong)~0ul) | perfticks();
1560d0cdc9eSDavid du Colombier if(now < m->fastclock) /* low bits must have wrapped */
157b2495906SDavid du Colombier now += 1ll << 32;
158b2495906SDavid du Colombier m->fastclock = now;
159b2495906SDavid du Colombier splx(s);
160b2495906SDavid du Colombier return now;
161154abd99SDavid du Colombier }
162154abd99SDavid du Colombier
163154abd99SDavid du Colombier ulong
perfticks(void)1640d0cdc9eSDavid du Colombier perfticks(void)
1650d0cdc9eSDavid du Colombier {
166*7365b686SDavid du Colombier TimerReg *tmr = (TimerReg *)soc.clock;
167*7365b686SDavid du Colombier
168*7365b686SDavid du Colombier return ~tmr->timer1;
1690d0cdc9eSDavid du Colombier }
1700d0cdc9eSDavid du Colombier
171ab6ce076SDavid du Colombier long
lcycles(void)172ab6ce076SDavid du Colombier lcycles(void)
173ab6ce076SDavid du Colombier {
174ab6ce076SDavid du Colombier return perfticks();
175ab6ce076SDavid du Colombier }
176ab6ce076SDavid du Colombier
1770d0cdc9eSDavid du Colombier ulong
s(void)178154abd99SDavid du Colombier µs(void)
179154abd99SDavid du Colombier {
180154abd99SDavid du Colombier return fastticks2us(fastticks(nil));
181154abd99SDavid du Colombier }
182154abd99SDavid du Colombier
183154abd99SDavid du Colombier void
microdelay(int l)184154abd99SDavid du Colombier microdelay(int l)
185154abd99SDavid du Colombier {
186154abd99SDavid du Colombier int i;
187154abd99SDavid du Colombier
188154abd99SDavid du Colombier l *= m->delayloop;
189154abd99SDavid du Colombier l /= 1000;
190154abd99SDavid du Colombier if(l <= 0)
191154abd99SDavid du Colombier l = 1;
192154abd99SDavid du Colombier for(i = 0; i < l; i++)
193154abd99SDavid du Colombier ;
194154abd99SDavid du Colombier }
195154abd99SDavid du Colombier
196154abd99SDavid du Colombier void
delay(int l)197154abd99SDavid du Colombier delay(int l)
198154abd99SDavid du Colombier {
199154abd99SDavid du Colombier ulong i, j;
200154abd99SDavid du Colombier
201154abd99SDavid du Colombier j = m->delayloop;
202154abd99SDavid du Colombier while(l-- > 0)
203154abd99SDavid du Colombier for(i=0; i < j; i++)
204154abd99SDavid du Colombier ;
205154abd99SDavid du Colombier }
206