i80321_timer.c revision 1.17 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