xref: /netbsd-src/sys/arch/arm/xscale/i80321_timer.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: i80321_timer.c,v 1.19 2008/04/27 18:58:45 matt 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.19 2008/04/27 18:58:45 matt 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 static void tmr1_tc_init(void);
72 
73 static void *clock_ih;
74 
75 static uint32_t counts_per_hz;
76 
77 int	clockhandler(void *);
78 
79 static inline uint32_t
80 tmr0_read(void)
81 {
82 	uint32_t rv;
83 
84 	__asm volatile("mrc p6, 0, %0, c0, c1, 0"
85 		: "=r" (rv));
86 	return (rv);
87 }
88 
89 static inline void
90 tmr0_write(uint32_t val)
91 {
92 
93 	__asm volatile("mcr p6, 0, %0, c0, c1, 0"
94 		:
95 		: "r" (val));
96 }
97 
98 static inline uint32_t
99 tcr0_read(void)
100 {
101 	uint32_t rv;
102 
103 	__asm volatile("mrc p6, 0, %0, c2, c1, 0"
104 		: "=r" (rv));
105 	return (rv);
106 }
107 
108 static inline void
109 tcr0_write(uint32_t val)
110 {
111 
112 	__asm volatile("mcr p6, 0, %0, c2, c1, 0"
113 		:
114 		: "r" (val));
115 }
116 
117 static inline void
118 trr0_write(uint32_t val)
119 {
120 
121 	__asm volatile("mcr p6, 0, %0, c4, c1, 0"
122 		:
123 		: "r" (val));
124 }
125 
126 static inline uint32_t
127 tmr1_read(void)
128 {
129 	uint32_t rv;
130 
131 	__asm volatile("mrc p6, 0, %0, c1, c1, 0"
132 		: "=r" (rv));
133 	return (rv);
134 }
135 
136 static inline void
137 tmr1_write(uint32_t val)
138 {
139 
140 	__asm volatile("mcr p6, 0, %0, c1, c1, 0"
141 		:
142 		: "r" (val));
143 }
144 
145 static inline uint32_t
146 tcr1_read(void)
147 {
148 	uint32_t rv;
149 
150 	__asm volatile("mrc p6, 0, %0, c3, c1, 0"
151 		: "=r" (rv));
152 	return (rv);
153 }
154 
155 static inline void
156 tcr1_write(uint32_t val)
157 {
158 
159 	__asm volatile("mcr p6, 0, %0, c3, c1, 0"
160 		:
161 		: "r" (val));
162 }
163 
164 static inline void
165 trr1_write(uint32_t val)
166 {
167 
168 	__asm volatile("mcr p6, 0, %0, c5, c1, 0"
169 		:
170 		: "r" (val));
171 }
172 
173 static inline void
174 tisr_write(uint32_t val)
175 {
176 
177 	__asm volatile("mcr p6, 0, %0, c6, c1, 0"
178 		:
179 		: "r" (val));
180 }
181 
182 /*
183  * i80321_calibrate_delay:
184  *
185  *	Calibrate the delay loop.
186  */
187 void
188 i80321_calibrate_delay(void)
189 {
190 
191 	/*
192 	 * Just use hz=100 for now -- we'll adjust it, if necessary,
193 	 * in cpu_initclocks().
194 	 */
195 	counts_per_hz = COUNTS_PER_SEC / 100;
196 
197 	tmr0_write(0);			/* stop timer */
198 	tisr_write(TISR_TMR0);		/* clear interrupt */
199 	trr0_write(counts_per_hz);	/* reload value */
200 	tcr0_write(counts_per_hz);	/* current value */
201 
202 	tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
203 }
204 
205 /*
206  * cpu_initclocks:
207  *
208  *	Initialize the clock and get them going.
209  */
210 void
211 cpu_initclocks(void)
212 {
213 	u_int oldirqstate;
214 #if defined(PERFCTRS)
215 	void *pmu_ih;
216 #endif
217 
218 	if (hz < 50 || COUNTS_PER_SEC % hz) {
219 		aprint_error("Cannot get %d Hz clock; using 100 Hz\n", hz);
220 		hz = 100;
221 	}
222 
223 	/*
224 	 * We only have one timer available; stathz and profhz are
225 	 * always left as 0 (the upper-layer clock code deals with
226 	 * this situation).
227 	 */
228 	if (stathz != 0)
229 		aprint_error("Cannot get %d Hz statclock\n", stathz);
230 	stathz = 0;
231 
232 	if (profhz != 0)
233 		aprint_error("Cannot get %d Hz profclock\n", profhz);
234 	profhz = 0;
235 
236 	/* Report the clock frequency. */
237 	aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
238 
239 	oldirqstate = disable_interrupts(I32_bit);
240 
241 	/* Hook up the clock interrupt handler. */
242 	clock_ih = i80321_intr_establish(ICU_INT_TMR0, IPL_CLOCK,
243 	    clockhandler, NULL);
244 	if (clock_ih == NULL)
245 		panic("cpu_initclocks: unable to register timer interrupt");
246 
247 #if defined(PERFCTRS)
248 	pmu_ih = i80321_intr_establish(ICU_INT_PMU, IPL_HIGH,
249 	    xscale_pmc_dispatch, NULL);
250 	if (pmu_ih == NULL)
251 		panic("cpu_initclocks: unable to register timer interrupt");
252 #endif
253 
254 	/* Set up the new clock parameters. */
255 
256 	tmr0_write(0);			/* stop timer */
257 	tisr_write(TISR_TMR0);		/* clear interrupt */
258 
259 	counts_per_hz = COUNTS_PER_SEC / hz;
260 
261 	trr0_write(counts_per_hz);	/* reload value */
262 	tcr0_write(counts_per_hz);	/* current value */
263 
264 	tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
265 
266 	restore_interrupts(oldirqstate);
267 
268 	tmr1_tc_init();
269 }
270 
271 /*
272  * setstatclockrate:
273  *
274  *	Set the rate of the statistics clock.
275  *
276  *	We assume that hz is either stathz or profhz, and that neither
277  *	will change after being set by cpu_initclocks().  We could
278  *	recalculate the intervals here, but that would be a pain.
279  */
280 void
281 setstatclockrate(int newhz)
282 {
283 
284 	/*
285 	 * XXX Use TMR1?
286 	 */
287 }
288 
289 static inline uint32_t
290 tmr1_tc_get(struct timecounter *tch)
291 {
292 	return (~tcr1_read());
293 }
294 
295 void
296 tmr1_tc_init(void)
297 {
298 	static struct timecounter tmr1_tc = {
299 		.tc_get_timecount = tmr1_tc_get,
300 		.tc_frequency = COUNTS_PER_SEC,
301 		.tc_counter_mask = ~0,
302 		.tc_name = "tmr1_count",
303 		.tc_quality = 100,
304 	};
305 
306 	/* program the tc */
307 	trr1_write(~0);	/* reload value */
308 	tcr1_write(~0);	/* current value */
309 
310 	tmr1_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
311 
312 
313 	trr1_write(~0);
314 	tc_init(&tmr1_tc);
315 }
316 
317 /*
318  * delay:
319  *
320  *	Delay for at least N microseconds.
321  */
322 void
323 delay(u_int n)
324 {
325 	uint32_t cur, last, delta, usecs;
326 
327 	/*
328 	 * This works by polling the timer and counting the
329 	 * number of microseconds that go by.
330 	 */
331 	last = tcr0_read();
332 	delta = usecs = 0;
333 
334 	while (n > usecs) {
335 		cur = tcr0_read();
336 
337 		/* Check to see if the timer has wrapped around. */
338 		if (last < cur)
339 			delta += (last + (counts_per_hz - cur));
340 		else
341 			delta += (last - cur);
342 
343 		last = cur;
344 
345 		if (delta >= COUNTS_PER_USEC) {
346 			usecs += delta / COUNTS_PER_USEC;
347 			delta %= COUNTS_PER_USEC;
348 		}
349 	}
350 }
351 
352 /*
353  * clockhandler:
354  *
355  *	Handle the hardclock interrupt.
356  */
357 int
358 clockhandler(void *arg)
359 {
360 	struct clockframe *frame = arg;
361 
362 	tisr_write(TISR_TMR0);
363 
364 	hardclock(frame);
365 
366 	if (i80321_hardclock_hook != NULL)
367 		(*i80321_hardclock_hook)();
368 
369 	return (1);
370 }
371