xref: /plan9/sys/src/9/kw/clock.c (revision 7365b686ae7154552580a79fd89a0ba5dc9297c4)
1 /*
2  * kirkwood clocks
3  *
4  * timers count down to zero.
5  */
6 #include "u.h"
7 #include "../port/lib.h"
8 #include "mem.h"
9 #include "dat.h"
10 #include "fns.h"
11 #include "io.h"
12 
13 #include "ureg.h"
14 
15 enum {
16 	Tcycles		= CLOCKFREQ / HZ,	/* cycles per clock tick */
17 	Dogperiod	= 15 * CLOCKFREQ, /* at most 21 s.; must fit in ulong */
18 	MaxPeriod	= Tcycles,
19 	MinPeriod	= MaxPeriod / 100,
20 
21 	/* timer ctl bits */
22 	Tmr0enable	= 1<<0,
23 	Tmr0reload	= 1<<1,	/* at 0 count, load timer0 from reload0 */
24 	Tmr1enable	= 1<<2,
25 	Tmr1reload	= 1<<3,	/* at 0 count, load timer1 from reload1 */
26 	TmrWDenable	= 1<<4,
27 	TmrWDreload	= 1<<5,
28 };
29 
30 typedef struct TimerReg TimerReg;
31 struct TimerReg
32 {
33 	ulong	ctl;
34 	ulong	pad[3];
35 	ulong	reload0;
36 	ulong	timer0;			/* cycles until zero */
37 	ulong	reload1;
38 	ulong	timer1;			/* cycles until zero */
39 	ulong	reloadwd;
40 	ulong	timerwd;
41 };
42 
43 static int ticks; /* for sanity checking; m->ticks doesn't always get updated */
44 
45 static void
clockintr(Ureg * ureg,void * arg)46 clockintr(Ureg *ureg, void *arg)
47 {
48 	TimerReg *tmr = arg;
49 	static int nesting;
50 
51 	tmr->timerwd = Dogperiod;		/* reassure the watchdog */
52 	ticks++;
53 	coherence();
54 
55 	if (nesting == 0) {	/* if the clock interrupted itself, bail out */
56 		++nesting;
57 		timerintr(ureg, 0);
58 		--nesting;
59 	}
60 
61 	intrclear(Irqbridge, IRQcputimer0);
62 }
63 
64 /* stop clock interrupts and disable the watchdog timer */
65 void
clockshutdown(void)66 clockshutdown(void)
67 {
68 	TimerReg *tmr = (TimerReg *)soc.clock;
69 
70 	tmr->ctl = 0;
71 	coherence();
72 }
73 
74 void
clockinit(void)75 clockinit(void)
76 {
77 	int i, s;
78 	CpucsReg *cpu = (CpucsReg *)soc.cpu;
79 	TimerReg *tmr = (TimerReg *)soc.clock;
80 
81 	clockshutdown();
82 
83 	/*
84 	 * verify sanity of timer0
85 	 */
86 
87 	intrenable(Irqbridge, IRQcputimer0, clockintr, tmr, "clock0");
88 	s = spllo();			/* risky */
89 	/* take any deferred clock (& other) interrupts here */
90 	splx(s);
91 
92 	/* adjust m->bootdelay, used by delay()? */
93 	m->ticks = ticks = 0;
94 	m->fastclock = 0;
95 
96 	tmr->timer0 = 1;
97 	tmr->ctl = Tmr0enable;		/* just once */
98 	coherence();
99 
100 	s = spllo();			/* risky */
101 	for (i = 0; i < 10 && ticks == 0; i++) {
102 		delay(1);
103 		coherence();
104 	}
105 	splx(s);
106 	if (ticks == 0) {
107 		serialputc('?');
108 		if (tmr->timer0 == 0)
109 			panic("clock not interrupting");
110 		else if (tmr->timer0 == tmr->reload0)
111 			panic("clock not ticking");
112 		else
113 			panic("clock running very slowly");
114 	}
115 
116 	/*
117 	 * configure all timers
118 	 */
119 	clockshutdown();
120 	tmr->reload0 = tmr->timer0 = Tcycles;	/* tick clock */
121 	tmr->reload1 = tmr->timer1 = ~0;	/* cycle clock */
122 	tmr->timerwd = Dogperiod;		/* watch dog timer */
123 	coherence();
124 	tmr->ctl = Tmr0enable | Tmr0reload | Tmr1enable | Tmr1reload |
125 		TmrWDenable;
126 	cpu->rstout |= RstoutWatchdog;
127 	coherence();
128 }
129 
130 void
timerset(Tval next)131 timerset(Tval next)
132 {
133 	int offset;
134 	TimerReg *tmr = (TimerReg *)soc.clock;
135 
136 	offset = next - fastticks(nil);
137 	if(offset < MinPeriod)
138 		offset = MinPeriod;
139 	else if(offset > MaxPeriod)
140 		offset = MaxPeriod;
141 	tmr->timer0 = offset;
142 	coherence();
143 }
144 
145 uvlong
fastticks(uvlong * hz)146 fastticks(uvlong *hz)
147 {
148 	uvlong now;
149 	int s;
150 
151 	if(hz)
152 		*hz = CLOCKFREQ;
153 	s = splhi();
154 	/* zero low ulong of fastclock */
155 	now = (m->fastclock & ~(uvlong)~0ul) | perfticks();
156 	if(now < m->fastclock)		/* low bits must have wrapped */
157 		now += 1ll << 32;
158 	m->fastclock = now;
159 	splx(s);
160 	return now;
161 }
162 
163 ulong
perfticks(void)164 perfticks(void)
165 {
166 	TimerReg *tmr = (TimerReg *)soc.clock;
167 
168 	return ~tmr->timer1;
169 }
170 
171 long
lcycles(void)172 lcycles(void)
173 {
174 	return perfticks();
175 }
176 
177 ulong
s(void)178 µs(void)
179 {
180 	return fastticks2us(fastticks(nil));
181 }
182 
183 void
microdelay(int l)184 microdelay(int l)
185 {
186 	int i;
187 
188 	l *= m->delayloop;
189 	l /= 1000;
190 	if(l <= 0)
191 		l = 1;
192 	for(i = 0; i < l; i++)
193 		;
194 }
195 
196 void
delay(int l)197 delay(int l)
198 {
199 	ulong i, j;
200 
201 	j = m->delayloop;
202 	while(l-- > 0)
203 		for(i=0; i < j; i++)
204 			;
205 }
206