xref: /netbsd-src/sys/arch/arm/xscale/i80321_timer.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: i80321_timer.c,v 1.17 2007/12/03 15:33:20 ad Exp $	*/
2 
3 /*
4  * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed for the NetBSD Project by
20  *	Wasabi Systems, Inc.
21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22  *    or promote products derived from this software without specific prior
23  *    written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 /*
39  * Timer/clock support for the Intel i80321 I/O processor.
40  */
41 
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: i80321_timer.c,v 1.17 2007/12/03 15:33:20 ad Exp $");
44 
45 #include "opt_perfctrs.h"
46 #include "opt_i80321.h"
47 
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/kernel.h>
51 #include <sys/time.h>
52 #include <sys/timetc.h>
53 
54 #include <dev/clock_subr.h>
55 
56 #include <machine/bus.h>
57 #include <arm/cpufunc.h>
58 
59 #include <arm/xscale/i80321reg.h>
60 #include <arm/xscale/i80321var.h>
61 
62 #include <arm/xscale/xscalevar.h>
63 
64 void	(*i80321_hardclock_hook)(void);
65 
66 #ifndef COUNTS_PER_SEC
67 #define	COUNTS_PER_SEC		200000000	/* 200MHz */
68 #endif
69 #define	COUNTS_PER_USEC		(COUNTS_PER_SEC / 1000000)
70 
71 #ifdef __HAVE_TIMECOUNTER
72 static void tmr1_tc_init(void);
73 #endif
74 
75 static void *clock_ih;
76 
77 static uint32_t counts_per_hz;
78 
79 int	clockhandler(void *);
80 
81 static inline uint32_t
82 tmr0_read(void)
83 {
84 	uint32_t rv;
85 
86 	__asm volatile("mrc p6, 0, %0, c0, c1, 0"
87 		: "=r" (rv));
88 	return (rv);
89 }
90 
91 static inline void
92 tmr0_write(uint32_t val)
93 {
94 
95 	__asm volatile("mcr p6, 0, %0, c0, c1, 0"
96 		:
97 		: "r" (val));
98 }
99 
100 static inline uint32_t
101 tcr0_read(void)
102 {
103 	uint32_t rv;
104 
105 	__asm volatile("mrc p6, 0, %0, c2, c1, 0"
106 		: "=r" (rv));
107 	return (rv);
108 }
109 
110 static inline void
111 tcr0_write(uint32_t val)
112 {
113 
114 	__asm volatile("mcr p6, 0, %0, c2, c1, 0"
115 		:
116 		: "r" (val));
117 }
118 
119 static inline void
120 trr0_write(uint32_t val)
121 {
122 
123 	__asm volatile("mcr p6, 0, %0, c4, c1, 0"
124 		:
125 		: "r" (val));
126 }
127 
128 #ifdef __HAVE_TIMECOUNTER
129 
130 static inline uint32_t
131 tmr1_read(void)
132 {
133 	uint32_t rv;
134 
135 	__asm volatile("mrc p6, 0, %0, c1, c1, 0"
136 		: "=r" (rv));
137 	return (rv);
138 }
139 
140 static inline void
141 tmr1_write(uint32_t val)
142 {
143 
144 	__asm volatile("mcr p6, 0, %0, c1, c1, 0"
145 		:
146 		: "r" (val));
147 }
148 
149 static inline uint32_t
150 tcr1_read(void)
151 {
152 	uint32_t rv;
153 
154 	__asm volatile("mrc p6, 0, %0, c3, c1, 0"
155 		: "=r" (rv));
156 	return (rv);
157 }
158 
159 static inline void
160 tcr1_write(uint32_t val)
161 {
162 
163 	__asm volatile("mcr p6, 0, %0, c3, c1, 0"
164 		:
165 		: "r" (val));
166 }
167 
168 static inline void
169 trr1_write(uint32_t val)
170 {
171 
172 	__asm volatile("mcr p6, 0, %0, c5, c1, 0"
173 		:
174 		: "r" (val));
175 }
176 
177 #endif /* __HAVE_TIMECOUNTER */
178 
179 static inline void
180 tisr_write(uint32_t val)
181 {
182 
183 	__asm volatile("mcr p6, 0, %0, c6, c1, 0"
184 		:
185 		: "r" (val));
186 }
187 
188 /*
189  * i80321_calibrate_delay:
190  *
191  *	Calibrate the delay loop.
192  */
193 void
194 i80321_calibrate_delay(void)
195 {
196 
197 	/*
198 	 * Just use hz=100 for now -- we'll adjust it, if necessary,
199 	 * in cpu_initclocks().
200 	 */
201 	counts_per_hz = COUNTS_PER_SEC / 100;
202 
203 	tmr0_write(0);			/* stop timer */
204 	tisr_write(TISR_TMR0);		/* clear interrupt */
205 	trr0_write(counts_per_hz);	/* reload value */
206 	tcr0_write(counts_per_hz);	/* current value */
207 
208 	tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
209 }
210 
211 /*
212  * cpu_initclocks:
213  *
214  *	Initialize the clock and get them going.
215  */
216 void
217 cpu_initclocks(void)
218 {
219 	u_int oldirqstate;
220 #if defined(PERFCTRS)
221 	void *pmu_ih;
222 #endif
223 
224 	if (hz < 50 || COUNTS_PER_SEC % hz) {
225 		aprint_error("Cannot get %d Hz clock; using 100 Hz\n", hz);
226 		hz = 100;
227 	}
228 #ifndef __HAVE_TIMECOUNTER
229 	tick = 1000000 / hz;	/* number of microseconds between interrupts */
230 	tickfix = 1000000 - (hz * tick);
231 	if (tickfix) {
232 		int ftp;
233 
234 		ftp = min(ffs(tickfix), ffs(hz));
235 		tickfix >>= (ftp - 1);
236 		tickfixinterval = hz >> (ftp - 1);
237 	}
238 #endif
239 
240 	/*
241 	 * We only have one timer available; stathz and profhz are
242 	 * always left as 0 (the upper-layer clock code deals with
243 	 * this situation).
244 	 */
245 	if (stathz != 0)
246 		aprint_error("Cannot get %d Hz statclock\n", stathz);
247 	stathz = 0;
248 
249 	if (profhz != 0)
250 		aprint_error("Cannot get %d Hz profclock\n", profhz);
251 	profhz = 0;
252 
253 	/* Report the clock frequency. */
254 	aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
255 
256 	oldirqstate = disable_interrupts(I32_bit);
257 
258 	/* Hook up the clock interrupt handler. */
259 	clock_ih = i80321_intr_establish(ICU_INT_TMR0, IPL_CLOCK,
260 	    clockhandler, NULL);
261 	if (clock_ih == NULL)
262 		panic("cpu_initclocks: unable to register timer interrupt");
263 
264 #if defined(PERFCTRS)
265 	pmu_ih = i80321_intr_establish(ICU_INT_PMU, IPL_HIGH,
266 	    xscale_pmc_dispatch, NULL);
267 	if (pmu_ih == NULL)
268 		panic("cpu_initclocks: unable to register timer interrupt");
269 #endif
270 
271 	/* Set up the new clock parameters. */
272 
273 	tmr0_write(0);			/* stop timer */
274 	tisr_write(TISR_TMR0);		/* clear interrupt */
275 
276 	counts_per_hz = COUNTS_PER_SEC / hz;
277 
278 	trr0_write(counts_per_hz);	/* reload value */
279 	tcr0_write(counts_per_hz);	/* current value */
280 
281 	tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
282 
283 	restore_interrupts(oldirqstate);
284 
285 #ifdef	__HAVE_TIMECOUNTER
286 	tmr1_tc_init();
287 #endif
288 }
289 
290 /*
291  * setstatclockrate:
292  *
293  *	Set the rate of the statistics clock.
294  *
295  *	We assume that hz is either stathz or profhz, and that neither
296  *	will change after being set by cpu_initclocks().  We could
297  *	recalculate the intervals here, but that would be a pain.
298  */
299 void
300 setstatclockrate(int newhz)
301 {
302 
303 	/*
304 	 * XXX Use TMR1?
305 	 */
306 }
307 
308 #ifndef __HAVE_TIMECOUNTER
309 
310 /*
311  * microtime:
312  *
313  *	Fill in the specified timeval struct with the current time
314  *	accurate to the microsecond.
315  */
316 void
317 microtime(struct timeval *tvp)
318 {
319 	static struct timeval lasttv;
320 	u_int oldirqstate;
321 	uint32_t counts;
322 
323 	oldirqstate = disable_interrupts(I32_bit);
324 
325 	counts = counts_per_hz - tcr0_read();
326 
327 	/* Fill in the timeval struct. */
328 	*tvp = time;
329 	tvp->tv_usec += (counts / COUNTS_PER_USEC);
330 
331 	/* Make sure microseconds doesn't overflow. */
332 	while (tvp->tv_usec >= 1000000) {
333 		tvp->tv_usec -= 1000000;
334 		tvp->tv_sec++;
335 	}
336 
337 	/* Make sure the time has advanced. */
338 	if (tvp->tv_sec == lasttv.tv_sec &&
339 	    tvp->tv_usec <= lasttv.tv_usec) {
340 		tvp->tv_usec = lasttv.tv_usec + 1;
341 		if (tvp->tv_usec >= 1000000) {
342 			tvp->tv_usec -= 1000000;
343 			tvp->tv_sec++;
344 		}
345 	}
346 
347 	lasttv = *tvp;
348 
349 	restore_interrupts(oldirqstate);
350 }
351 
352 
353 #else
354 
355 static inline uint32_t
356 tmr1_tc_get(struct timecounter *tch)
357 {
358 	return (~tcr1_read());
359 }
360 
361 void
362 tmr1_tc_init(void)
363 {
364 	static struct timecounter tmr1_tc = {
365 		.tc_get_timecount = tmr1_tc_get,
366 		.tc_frequency = COUNTS_PER_SEC,
367 		.tc_counter_mask = ~0,
368 		.tc_name = "tmr1_count",
369 		.tc_quality = 100,
370 	};
371 
372 	/* program the tc */
373 	trr1_write(~0);	/* reload value */
374 	tcr1_write(~0);	/* current value */
375 
376 	tmr1_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
377 
378 
379 	trr1_write(~0);
380 	tc_init(&tmr1_tc);
381 }
382 #endif
383 
384 /*
385  * delay:
386  *
387  *	Delay for at least N microseconds.
388  */
389 void
390 delay(u_int n)
391 {
392 	uint32_t cur, last, delta, usecs;
393 
394 	/*
395 	 * This works by polling the timer and counting the
396 	 * number of microseconds that go by.
397 	 */
398 	last = tcr0_read();
399 	delta = usecs = 0;
400 
401 	while (n > usecs) {
402 		cur = tcr0_read();
403 
404 		/* Check to see if the timer has wrapped around. */
405 		if (last < cur)
406 			delta += (last + (counts_per_hz - cur));
407 		else
408 			delta += (last - cur);
409 
410 		last = cur;
411 
412 		if (delta >= COUNTS_PER_USEC) {
413 			usecs += delta / COUNTS_PER_USEC;
414 			delta %= COUNTS_PER_USEC;
415 		}
416 	}
417 }
418 
419 /*
420  * clockhandler:
421  *
422  *	Handle the hardclock interrupt.
423  */
424 int
425 clockhandler(void *arg)
426 {
427 	struct clockframe *frame = arg;
428 
429 	tisr_write(TISR_TMR0);
430 
431 	hardclock(frame);
432 
433 	if (i80321_hardclock_hook != NULL)
434 		(*i80321_hardclock_hook)();
435 
436 	return (1);
437 }
438