Sync usage with man page.
[netbsd-mini2440.git] / sys / arch / arm / omap / omap_mputmr.c
blobde51459932d8016d6be5c3a056efa1e17f52a9b0
1 /* $NetBSD: omap_mputmr.c,v 1.4 2008/11/21 17:13:07 matt Exp $ */
3 /*
4 * Based on i80321_timer.c and arch/arm/sa11x0/sa11x0_ost.c
6 * Copyright (c) 1997 Mark Brinicombe.
7 * Copyright (c) 1997 Causality Limited.
8 * All rights reserved.
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by IWAMOTO Toshihiro and Ichiro FUKUHARA.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the NetBSD
24 * Foundation, Inc. and its contributors.
25 * 4. Neither the name of The NetBSD Foundation nor the names of its
26 * contributors may be used to endorse or promote products derived
27 * from this software without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 * POSSIBILITY OF SUCH DAMAGE.
41 * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
42 * All rights reserved.
44 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
46 * Redistribution and use in source and binary forms, with or without
47 * modification, are permitted provided that the following conditions
48 * are met:
49 * 1. Redistributions of source code must retain the above copyright
50 * notice, this list of conditions and the following disclaimer.
51 * 2. Redistributions in binary form must reproduce the above copyright
52 * notice, this list of conditions and the following disclaimer in the
53 * documentation and/or other materials provided with the distribution.
54 * 3. All advertising materials mentioning features or use of this software
55 * must display the following acknowledgement:
56 * This product includes software developed for the NetBSD Project by
57 * Wasabi Systems, Inc.
58 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
59 * or promote products derived from this software without specific prior
60 * written permission.
62 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
63 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
64 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
65 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
66 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
67 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
68 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
69 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
70 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
71 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
72 * POSSIBILITY OF SUCH DAMAGE.
75 #include <sys/cdefs.h>
76 __KERNEL_RCSID(0, "$NetBSD: omap_mputmr.c,v 1.4 2008/11/21 17:13:07 matt Exp $");
78 #include <sys/types.h>
79 #include <sys/param.h>
80 #include <sys/systm.h>
81 #include <sys/kernel.h>
82 #include <sys/time.h>
83 #include <sys/timetc.h>
84 #include <sys/device.h>
86 #include <dev/clock_subr.h>
88 #include <machine/bus.h>
89 #include <machine/intr.h>
91 #include <arm/omap/omap_reg.h>
92 #include <arm/omap/omap_tipb.h>
94 #include "opt_omap.h"
96 static int omapmputmr_match(device_t, cfdata_t, void *);
97 static void omapmputmr_attach(device_t, device_t, void *);
99 static int clockintr(void *);
100 static int statintr(void *);
101 void rtcinit(void);
103 typedef struct timer_factors {
104 uint32_t ptv;
105 uint32_t reload;
106 uint32_t counts_per_usec;
107 } timer_factors;
108 static void calc_timer_factors(int, timer_factors*);
110 struct omapmputmr_softc {
111 device_t sc_dev;
112 bus_space_tag_t sc_iot;
113 bus_space_handle_t sc_ioh;
114 int sc_intr;
117 static uint32_t counts_per_usec, counts_per_hz;
118 static uint32_t hardref;
119 static struct omapmputmr_softc *clock_sc = NULL;
120 static struct omapmputmr_softc *stat_sc = NULL;
121 static struct omapmputmr_softc *ref_sc = NULL;
123 #ifndef STATHZ
124 #define STATHZ 64
125 #endif
127 #ifndef OMAP_MPU_TIMER_CLOCK_FREQ
128 #error Specify the timer frequency in Hz with the OMAP_MPU_TIMER_CLOCK_FREQ option.
129 #endif
131 /* Encapsulate the device knowledge within this source. */
132 /* Register offsets and values */
133 #define MPU_CNTL_TIMER 0x00
134 #define MPU_FREE (1<<6)
135 #define MPU_CLOCK_ENABLE (1<<5)
136 #define MPU_PTV_SHIFT 2
137 #define MPU_AR (1<<1)
138 #define MPU_ST (1<<0)
139 #define MPU_LOAD_TIMER 0x04
140 #define MPU_READ_TIMER 0x08
142 CFATTACH_DECL_NEW(omapmputmr, sizeof(struct omapmputmr_softc),
143 omapmputmr_match, omapmputmr_attach, NULL, NULL);
145 static int
146 omapmputmr_match(device_t parent, cfdata_t match, void *aux)
148 struct tipb_attach_args *tipb = aux;
150 if (tipb->tipb_addr == -1 || tipb->tipb_intr == -1)
151 panic("omapmputmr must have addr and intr specified in config.");
153 if (tipb->tipb_size == 0)
154 tipb->tipb_size = 256; /* Per the OMAP TRM. */
156 /* We implicitly trust the config file. */
157 return (1);
160 void
161 omapmputmr_attach(device_t parent, device_t self, void *aux)
163 struct omapmputmr_softc *sc = device_private(self);
164 struct tipb_attach_args *tipb = aux;
165 int ints_per_sec;
167 sc->sc_iot = tipb->tipb_iot;
168 sc->sc_intr = tipb->tipb_intr;
170 if (bus_space_map(tipb->tipb_iot, tipb->tipb_addr, tipb->tipb_size, 0,
171 &sc->sc_ioh))
172 panic("%s: Cannot map registers", device_xname(self));
174 switch (device_unit(self)) {
175 case 0:
176 clock_sc = sc;
177 ints_per_sec = hz;
178 break;
179 case 1:
180 stat_sc = sc;
181 ints_per_sec = profhz = stathz = STATHZ;
182 break;
183 case 2:
184 ref_sc = sc;
185 ints_per_sec = hz; /* Same rate as clock */
186 break;
187 default:
188 ints_per_sec = hz; /* Better value? */
189 break;
192 aprint_normal(": OMAP MPU Timer\n");
193 aprint_naive("\n");
195 /* Stop the timer from counting, but keep the timer module working. */
196 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MPU_CNTL_TIMER,
197 MPU_CLOCK_ENABLE);
199 timer_factors tf;
200 calc_timer_factors(ints_per_sec, &tf);
202 switch (device_unit(self)) {
203 case 0:
204 counts_per_hz = tf.reload + 1;
205 counts_per_usec = tf.counts_per_usec;
206 break;
207 case 2:
210 * The microtime reference clock for all practical purposes
211 * just wraps around as an unsigned int.
214 tf.reload = 0xffffffff;
215 break;
217 default:
218 break;
221 /* Set the reload value. */
222 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MPU_LOAD_TIMER, tf.reload);
223 /* Set the PTV and the other required bits and pieces. */
224 bus_space_write_4(sc->sc_iot, sc->sc_ioh, MPU_CNTL_TIMER,
225 ( MPU_CLOCK_ENABLE
226 | (tf.ptv << MPU_PTV_SHIFT)
227 | MPU_AR
228 | MPU_ST));
229 /* The clock is now running, but is not generating interrupts. */
232 static int
233 clockintr(void *arg)
235 struct clockframe *frame = arg;
236 unsigned int newref;
237 int ticks, i, oldirqstate;
239 oldirqstate = disable_interrupts(I32_bit);
240 newref = bus_space_read_4(ref_sc->sc_iot, ref_sc->sc_ioh,
241 MPU_READ_TIMER);
242 ticks = hardref ? (hardref - newref) / counts_per_hz : 1;
243 hardref = newref;
244 restore_interrupts(oldirqstate);
246 if (ticks == 0)
247 ticks = 1;
249 #ifdef DEBUG
250 if (ticks > 1)
251 printf("Missed %d ticks.\n", ticks-1);
252 #endif
255 for (i = 0; i < ticks; i++)
256 hardclock(frame);
258 if (ticks > 1) {
259 newref = bus_space_read_4(ref_sc->sc_iot, ref_sc->sc_ioh,
260 MPU_READ_TIMER);
262 if ((hardref - newref) / counts_per_hz)
263 hardclock(frame);
266 return(1);
269 static int
270 statintr(void *arg)
272 struct clockframe *frame = arg;
274 statclock(frame);
276 return(1);
280 void
281 setstatclockrate(int schz)
283 /* Stop the timer from counting, but keep the timer module working. */
284 bus_space_write_4(stat_sc->sc_iot, stat_sc->sc_ioh, MPU_CNTL_TIMER,
285 MPU_CLOCK_ENABLE);
287 timer_factors tf;
288 calc_timer_factors(schz, &tf);
290 /* Set the reload value. */
291 bus_space_write_4(stat_sc->sc_iot, stat_sc->sc_ioh, MPU_LOAD_TIMER,
292 tf.reload);
293 /* Set the PTV and the other required bits and pieces. */
294 bus_space_write_4(stat_sc->sc_iot, stat_sc->sc_ioh, MPU_CNTL_TIMER,
295 ( MPU_CLOCK_ENABLE
296 | (tf.ptv << MPU_PTV_SHIFT)
297 | MPU_AR
298 | MPU_ST));
301 static u_int
302 mpu_get_timecount(struct timecounter *tc)
304 uint32_t counter;
305 int oldirqstate;
307 oldirqstate = disable_interrupts(I32_bit);
308 counter = bus_space_read_4(ref_sc->sc_iot, ref_sc->sc_ioh,
309 MPU_READ_TIMER);
310 restore_interrupts(oldirqstate);
312 return counter;
315 static struct timecounter mpu_timecounter = {
316 mpu_get_timecount,
317 NULL,
318 0xffffffff,
320 "mpu",
321 100,
322 NULL,
323 NULL,
326 void
327 cpu_initclocks(void)
329 if (clock_sc == NULL)
330 panic("Clock timer was not configured.");
331 if (stat_sc == NULL)
332 panic("Statistics timer was not configured.");
333 if (ref_sc == NULL)
334 panic("Microtime reference timer was not configured.");
337 * We already have the timers running, but not generating interrupts.
338 * In addition, we've set stathz and profhz.
340 printf("clock: hz=%d stathz=%d\n", hz, stathz);
343 * The "cookie" parameter must be zero to pass the interrupt frame
344 * through to hardclock() and statclock().
347 omap_intr_establish(clock_sc->sc_intr, IPL_CLOCK,
348 device_xname(clock_sc->sc_dev), clockintr, 0);
349 omap_intr_establish(stat_sc->sc_intr, IPL_HIGH,
350 device_xname(stat_sc->sc_dev), statintr, 0);
352 tc_init(&mpu_timecounter);
355 void
356 delay(u_int n)
358 uint32_t cur, last, delta, usecs;
360 if (clock_sc == NULL)
361 panic("The timer must be initialized sooner.");
364 * This works by polling the timer and counting the
365 * number of microseconds that go by.
367 last = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
368 MPU_READ_TIMER);
369 delta = usecs = 0;
371 while (n > usecs) {
372 cur = bus_space_read_4(clock_sc->sc_iot, clock_sc->sc_ioh,
373 MPU_READ_TIMER);
375 /* Check to see if the timer has wrapped around. */
376 if (last < cur)
377 delta += (last + (counts_per_hz - cur));
378 else
379 delta += (last - cur);
381 last = cur;
383 if (delta >= counts_per_usec) {
384 usecs += delta / counts_per_usec;
385 delta %= counts_per_usec;
390 static void
391 calc_timer_factors(int ints_per_sec, timer_factors *tf)
394 * From the OMAP Technical Reference Manual:
395 * T(MPU_Interrupt) = T(MPU_ref_clk) * (MPU_LOAD_TIMER+1) * 2**(PTV+1)
397 * T(MPU_ref_clk) is 1/OMAP_MPU_TIMER_CLOCK_FREQ and we want
398 * T(MPU_Interrupt) to be 1/ints_per_sec. Rewriting the equation:
400 * 1 (MPU_LOAD_TIMER+1) * 2**(PTV+1)
401 * ------------ = -------------------------------
402 * ints_per_sec OMAP_MPU_TIMER_CLOCK_FREQ
404 * or
406 * OMAP_MPU_TIMER_CLOCK_FREQ
407 * ------------------------- = (MPU_LOAD_TIMER+1) * 2**(PTV+1)
408 * ints_per_sec
410 * or
412 * OMAP_MPU_TIMER_CLOCK_FREQ
413 * ------------------------- = (MPU_LOAD_TIMER+1)
414 * ints_per_sec * 2**(PTV+1)
417 * To save that last smidgen of power, find the largest prescaler that
418 * will give us a reload value that doesn't have any error. However,
419 * to keep delay() accurate, it is desireable to have the number of
420 * counts per us be non-fractional.
422 * us_incs = OMAP_MPU_TIMER_CLOCK_FREQ / 2**(PTV+1) / 1,000,000
425 /* The PTV can range from 7 to 0. */
426 tf->ptv = 7;
427 for (;;) {
428 static const uint32_t us_per_sec = 1000000;
429 uint32_t ptv_power = 1 << (tf->ptv + 1);
430 uint32_t count_freq = OMAP_MPU_TIMER_CLOCK_FREQ / ptv_power;
432 tf->reload = count_freq / ints_per_sec;
433 tf->counts_per_usec = count_freq / us_per_sec;
435 if ((tf->reload * ptv_power * ints_per_sec
436 == OMAP_MPU_TIMER_CLOCK_FREQ)
437 && (tf->counts_per_usec * ptv_power * us_per_sec
438 == OMAP_MPU_TIMER_CLOCK_FREQ))
439 { /* Exact match. Life is good. */
440 /* Currently reload is MPU_LOAD_TIMER+1. Fix it. */
441 tf->reload--;
442 return;
444 if (tf->ptv == 0) {
446 * Not exact, but we're out of options. Leave the
447 * reload at being one too large and bump the counts
448 * per microsecond up one to make sure that we run a
449 * bit slow rather than too fast.
451 tf->counts_per_usec++;
452 return;
454 tf->ptv--;