1 /* $NetBSD: omap_mputmr.c,v 1.4 2008/11/21 17:13:07 matt Exp $ */
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.
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
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
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
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>
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>
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 *);
103 typedef struct timer_factors
{
106 uint32_t counts_per_usec
;
108 static void calc_timer_factors(int, timer_factors
*);
110 struct omapmputmr_softc
{
112 bus_space_tag_t sc_iot
;
113 bus_space_handle_t sc_ioh
;
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
;
127 #ifndef OMAP_MPU_TIMER_CLOCK_FREQ
128 #error Specify the timer frequency in Hz with the OMAP_MPU_TIMER_CLOCK_FREQ option.
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
);
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. */
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
;
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,
172 panic("%s: Cannot map registers", device_xname(self
));
174 switch (device_unit(self
)) {
181 ints_per_sec
= profhz
= stathz
= STATHZ
;
185 ints_per_sec
= hz
; /* Same rate as clock */
188 ints_per_sec
= hz
; /* Better value? */
192 aprint_normal(": OMAP MPU Timer\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
,
200 calc_timer_factors(ints_per_sec
, &tf
);
202 switch (device_unit(self
)) {
204 counts_per_hz
= tf
.reload
+ 1;
205 counts_per_usec
= tf
.counts_per_usec
;
210 * The microtime reference clock for all practical purposes
211 * just wraps around as an unsigned int.
214 tf
.reload
= 0xffffffff;
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
,
226 | (tf
.ptv
<< MPU_PTV_SHIFT
)
229 /* The clock is now running, but is not generating interrupts. */
235 struct clockframe
*frame
= arg
;
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
,
242 ticks
= hardref
? (hardref
- newref
) / counts_per_hz
: 1;
244 restore_interrupts(oldirqstate
);
251 printf("Missed %d ticks.\n", ticks
-1);
255 for (i
= 0; i
< ticks
; i
++)
259 newref
= bus_space_read_4(ref_sc
->sc_iot
, ref_sc
->sc_ioh
,
262 if ((hardref
- newref
) / counts_per_hz
)
272 struct clockframe
*frame
= arg
;
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
,
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
,
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
,
296 | (tf
.ptv
<< MPU_PTV_SHIFT
)
302 mpu_get_timecount(struct timecounter
*tc
)
307 oldirqstate
= disable_interrupts(I32_bit
);
308 counter
= bus_space_read_4(ref_sc
->sc_iot
, ref_sc
->sc_ioh
,
310 restore_interrupts(oldirqstate
);
315 static struct timecounter mpu_timecounter
= {
329 if (clock_sc
== NULL
)
330 panic("Clock timer was not configured.");
332 panic("Statistics timer was not configured.");
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
);
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
,
372 cur
= bus_space_read_4(clock_sc
->sc_iot
, clock_sc
->sc_ioh
,
375 /* Check to see if the timer has wrapped around. */
377 delta
+= (last
+ (counts_per_hz
- cur
));
379 delta
+= (last
- cur
);
383 if (delta
>= counts_per_usec
) {
384 usecs
+= delta
/ counts_per_usec
;
385 delta
%= counts_per_usec
;
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
406 * OMAP_MPU_TIMER_CLOCK_FREQ
407 * ------------------------- = (MPU_LOAD_TIMER+1) * 2**(PTV+1)
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. */
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. */
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
++;