xref: /plan9/sys/src/9/kw/clock.c (revision 7365b686ae7154552580a79fd89a0ba5dc9297c4)
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