xref: /netbsd/sys/arch/evbarm/ifpga/ifpga_clock.c (revision bf9ec67e)
1 /*	$NetBSD: ifpga_clock.c,v 1.3 2002/01/30 03:59:41 thorpej Exp $ */
2 
3 /*
4  * Copyright (c) 2001 ARM Ltd
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the company may not be used to endorse or promote
16  *    products derived from this software without specific prior written
17  *    permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * The IFPGA has three timers.  Timer 0 is clocked by the system bus clock,
34  * while timers 1 and 2 are clocked at 24MHz.  To keep things simple here,
35  * we use timers 1 and 2 only.  All three timers are 16-bit counters that
36  * are programmable in either periodic mode or in one-shot mode.
37  */
38 
39 /* Include header files */
40 
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/time.h>
46 #include <sys/device.h>
47 
48 #include <arm/cpufunc.h>
49 #include <machine/intr.h>
50 #include <evbarm/ifpga/irqhandler.h>	/* XXX XXX XXX */
51 
52 #include <evbarm/ifpga/ifpgavar.h>
53 #include <evbarm/ifpga/ifpgamem.h>
54 #include <evbarm/ifpga/ifpgareg.h>
55 
56 /*
57  * Statistics clock interval and variance, in usec.  Variance must be a
58  * power of two.  Since this gives us an even number, not an odd number,
59  * we discard one case and compensate.  That is, a variance of 1024 would
60  * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
61  * This is symmetric about the point 512, or statvar/2, and thus averages
62  * to that value (assuming uniform random numbers).
63  */
64 static int statvar = 1024 / 4;	/* {stat,prof}clock variance */
65 static int statmin;		/* statclock interval - variance/2 */
66 static int profmin;		/* profclock interval - variance/2 */
67 static int timer2min;		/* current, from above choices */
68 static int statprev;		/* previous value in stat timer */
69 
70 #define TIMER_1_CLEAR (IFPGA_TIMER1_BASE + TIMERx_CLR)
71 #define TIMER_1_LOAD  (IFPGA_TIMER1_BASE + TIMERx_LOAD)
72 #define TIMER_1_VALUE (IFPGA_TIMER1_BASE + TIMERx_VALUE)
73 #define TIMER_1_CTRL  (IFPGA_TIMER1_BASE + TIMERx_CTRL)
74 
75 #define TIMER_2_CLEAR (IFPGA_TIMER2_BASE + TIMERx_CLR)
76 #define TIMER_2_LOAD  (IFPGA_TIMER2_BASE + TIMERx_LOAD)
77 #define TIMER_2_VALUE (IFPGA_TIMER2_BASE + TIMERx_VALUE)
78 #define TIMER_2_CTRL  (IFPGA_TIMER2_BASE + TIMERx_CTRL)
79 
80 #define COUNTS_PER_SEC (IFPGA_TIMER1_FREQ / 16)
81 
82 extern struct ifpga_softc *clock_sc;
83 
84 static int clock_started = 0;
85 
86 static int load_timer(int, int);
87 
88 static __inline u_int
89 getclock(void)
90 {
91 	return bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_tmr_ioh,
92 	    TIMER_1_VALUE);
93 }
94 
95 static __inline u_int
96 getstatclock(void)
97 {
98 	return bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_tmr_ioh,
99 	    TIMER_2_VALUE);
100 }
101 
102 /*
103  * int clockhandler(struct clockframe *frame)
104  *
105  * Function called by timer 1 interrupts.
106  * This just clears the interrupt condition and calls hardclock().
107  */
108 
109 static int
110 clockhandler(void *fr)
111 {
112 	struct clockframe *frame = (struct clockframe *)fr;
113 
114 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_tmr_ioh,
115 	    TIMER_1_CLEAR, 0);
116 	hardclock(frame);
117 	return 0;	/* Pass the interrupt on down the chain */
118 }
119 
120 
121 /*
122  * int statclockhandler(struct clockframe *frame)
123  *
124  * Function called by timer 2 interrupts.
125  * Add some random jitter to the clock, and then call statclock().
126  */
127 
128 static int
129 statclockhandler(void *fr)
130 {
131 	struct clockframe *frame = (struct clockframe *) fr;
132 	int newint, r, var;
133 
134 	var = statvar;
135 	do {
136 		r = random() & (var - 1);
137 	} while (r == 0);
138 	newint = timer2min + r;
139 
140 	if (newint & ~0x0000ffff)
141 		panic("statclockhandler: statclock variance too large");
142 
143 	/*
144 	 * The timer was automatically reloaded with the previous latch
145 	 * value at the time of the interrupts.  Compensate now for the
146 	 * amount of time that has run off since then, plus one tick
147 	 * roundoff.  This should keep us closer to the mean.
148 	 */
149 
150 	r = (statprev - getstatclock() + 1);
151 	if (r < newint) {
152 		newint -= r;
153 		r = 0;
154 	}
155 	else
156 		printf("statclockhandler: Statclock overrun\n");
157 
158 	statprev = load_timer(IFPGA_TIMER2_BASE, newint);
159 	statclock(frame);
160 	if (r)
161 		/*
162 		 * We've completely overrun the previous interval,
163 		 * make sure we report the correct number of ticks.
164 		 */
165 		statclock(frame);
166 
167 	return 0;	/* Pass the interrupt on down the chain */
168 }
169 
170 static int
171 load_timer(int base, int intvl)
172 {
173 	int control;
174 
175 	if (intvl & ~0x0000ffff)
176 		panic("clock: Invalid interval\n");
177 
178 	control = (TIMERx_CTRL_ENABLE | TIMERx_CTRL_MODE_PERIODIC |
179 	    TIMERx_CTRL_PRESCALE_DIV16);
180 
181 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_tmr_ioh,
182 	    base + TIMERx_LOAD, intvl);
183 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_tmr_ioh,
184 	    base + TIMERx_CTRL, control);
185 	bus_space_write_4(clock_sc->sc_iot, clock_sc->sc_tmr_ioh,
186 	    base + TIMERx_CLR, 0);
187 	return intvl;
188 }
189 
190 /*
191  * void setstatclockrate(int hz)
192  *
193  * We assume that hz is either stathz or profhz, and that neither will
194  * change after being set by cpu_initclocks().  We could recalculate the
195  * intervals here, but that would be a pain.
196  */
197 
198 void
199 setstatclockrate(int hz)
200 {
201 	if (hz == stathz)
202 		timer2min = statmin;
203 	else
204 		timer2min = profmin;
205 }
206 
207 /*
208  * void cpu_initclocks(void)
209  *
210  * Initialise the clocks.
211  */
212 
213 void
214 cpu_initclocks()
215 {
216 	int intvl;
217 	int statint;
218 	int profint;
219 	int minint;
220 
221 	if (hz < 50 || COUNTS_PER_SEC % hz) {
222 		printf("cannot get %d Hz clock; using 100 Hz\n", hz);
223 		hz = 100;
224 		tick = 1000000 / hz;
225 	}
226 
227 	if (stathz == 0)
228 		stathz = hz;
229 	else if (stathz < 50 || COUNTS_PER_SEC % stathz) {
230 		printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
231 		stathz = 100;
232 	}
233 
234 	if (profhz == 0)
235 		profhz = stathz * 5;
236 	else if (profhz < stathz || COUNTS_PER_SEC % profhz) {
237 		printf("cannot get %d Hz profclock; using %d Hz\n", profhz,
238 		    stathz);
239 		profhz = stathz;
240 	}
241 
242 	intvl = COUNTS_PER_SEC / hz;
243 	statint = COUNTS_PER_SEC / stathz;
244 	profint = COUNTS_PER_SEC / profhz;
245 	minint = statint / 2 + 100;
246 	while (statvar > minint)
247 		statvar >>= 1;
248 
249 	/* Adjust interval counts, per note above.  */
250 	intvl--;
251 	statint--;
252 	profint--;
253 
254 	/* Calculate the base reload values.  */
255 	statmin = statint - (statvar >> 1);
256 	profmin = profint - (statvar >> 1);
257 	timer2min = statmin;
258 	statprev = statint;
259 
260 	/* Report the clock frequencies */
261 	printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
262 
263 	/* Setup timer 1 and claim interrupt */
264 	clock_sc->sc_clockintr = intr_claim(IFPGA_TIMER1_IRQ, IPL_CLOCK,
265 	    "tmr1 hard clk", clockhandler, 0);
266 	if (clock_sc->sc_clockintr == NULL)
267 		panic("%s: Cannot install timer 1 interrupt handler\n",
268 		    clock_sc->sc_dev.dv_xname);
269 
270 	clock_sc->sc_clock_count
271 	    = load_timer(IFPGA_TIMER1_BASE, intvl);
272 
273 	/*
274 	 * Use ticks per 256us for accuracy since ticks per us is often
275 	 * fractional e.g. @ 66MHz
276 	 */
277 	clock_sc->sc_clock_ticks_per_256us =
278 	    ((((clock_sc->sc_clock_count * hz) / 1000) * 256) / 1000);
279 
280 	clock_started = 1;
281 
282 	/* Set up timer 2 as statclk/profclk. */
283 	clock_sc->sc_statclockintr = intr_claim(IFPGA_TIMER2_IRQ,
284 	    IPL_STATCLOCK, "tmr2 stat clk", statclockhandler, 0);
285 	if (clock_sc->sc_statclockintr == NULL)
286 		panic("%s: Cannot install timer 2 interrupt handler\n",
287 		    clock_sc->sc_dev.dv_xname);
288 	load_timer(IFPGA_TIMER2_BASE, statint);
289 }
290 
291 
292 /*
293  * void microtime(struct timeval *tvp)
294  *
295  * Fill in the specified timeval struct with the current time
296  * accurate to the microsecond.
297  */
298 
299 void
300 microtime(struct timeval *tvp)
301 {
302 	int s;
303 	int tm;
304 	int deltatm;
305 	static struct timeval oldtv;
306 
307 	if (clock_sc == NULL || clock_sc->sc_clock_count == 0)
308 		return;
309 
310 	s = splhigh();
311 
312 	tm = getclock();
313 
314 	deltatm = clock_sc->sc_clock_count - tm;
315 
316 #ifdef DIAGNOSTIC
317 	if (deltatm < 0)
318 		panic("opps deltatm < 0 tm=%d deltatm=%d\n", tm, deltatm);
319 #endif
320 
321 	/* Fill in the timeval struct */
322 	*tvp = time;
323 	tvp->tv_usec += ((deltatm << 8) / clock_sc->sc_clock_ticks_per_256us);
324 
325 	/* Make sure the micro seconds don't overflow. */
326 	while (tvp->tv_usec >= 1000000) {
327 		tvp->tv_usec -= 1000000;
328 		++tvp->tv_sec;
329 	}
330 
331 	/* Make sure the time has advanced. */
332 	if (tvp->tv_sec == oldtv.tv_sec &&
333 	    tvp->tv_usec <= oldtv.tv_usec) {
334 		tvp->tv_usec = oldtv.tv_usec + 1;
335 		if (tvp->tv_usec >= 1000000) {
336 			tvp->tv_usec -= 1000000;
337 			++tvp->tv_sec;
338 		}
339 	}
340 
341 	oldtv = *tvp;
342 	(void)splx(s);
343 }
344 
345 /*
346  * Estimated loop for n microseconds
347  */
348 
349 /* Need to re-write this to use the timers */
350 
351 /* One day soon I will actually do this */
352 
353 int delaycount = 50;
354 
355 void
356 delay(u_int n)
357 {
358 	if (clock_started) {
359 		u_int starttime;
360 		u_int curtime;
361 
362 		starttime = getclock();
363 
364 		n *= IFPGA_TIMER1_FREQ / 1000000;
365 
366 		do {
367 			curtime = getclock();
368 		} while (n > (curtime - starttime));
369 	} else {
370 		volatile u_int i;
371 
372 		if (n == 0) return;
373 		while (n-- > 0) {
374 			/* XXX - Seriously gross hack */
375 			if (cputype == CPU_ID_SA110)
376 				for (i = delaycount; --i;)
377 					;
378 			else
379 				for (i = 8; --i;)
380 					;
381 		}
382 	}
383 }
384