2 * linux/arch/arm/mach-omap/time.c
6 * Copyright (C) 2004 Nokia Corporation
7 * Partial timer rewrite and additional VST timer support by
8 * Tony Lindgen <tony@atomide.com> and
9 * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
11 * MPU timer code based on the older MPU timer code for OMAP
12 * Copyright (C) 2000 RidgeRun, Inc.
13 * Author: Greg Lonnon <glonnon@ridgerun.com>
15 * This program is free software; you can redistribute it and/or modify it
16 * under the terms of the GNU General Public License as published by the
17 * Free Software Foundation; either version 2 of the License, or (at your
18 * option) any later version.
20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
21 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
23 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * You should have received a copy of the GNU General Public License along
32 * with this program; if not, write to the Free Software Foundation, Inc.,
33 * 675 Mass Ave, Cambridge, MA 02139, USA.
36 #include <linux/config.h>
37 #include <linux/kernel.h>
38 #include <linux/init.h>
39 #include <linux/delay.h>
40 #include <linux/interrupt.h>
41 #include <linux/sched.h>
42 #include <linux/spinlock.h>
44 #include <asm/system.h>
45 #include <asm/hardware.h>
49 #include <asm/mach/irq.h>
50 #include <asm/mach/time.h>
52 struct sys_timer omap_timer;
55 * ---------------------------------------------------------------------------
57 * ---------------------------------------------------------------------------
59 #define OMAP_MPU_TIMER1_BASE (0xfffec500)
60 #define OMAP_MPU_TIMER2_BASE (0xfffec600)
61 #define OMAP_MPU_TIMER3_BASE (0xfffec700)
62 #define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE
63 #define OMAP_MPU_TIMER_OFFSET 0x100
65 #define MPU_TIMER_FREE (1 << 6)
66 #define MPU_TIMER_CLOCK_ENABLE (1 << 5)
67 #define MPU_TIMER_AR (1 << 1)
68 #define MPU_TIMER_ST (1 << 0)
71 * MPU_TICKS_PER_SEC must be an even number, otherwise machinecycles_to_usecs
72 * will break. On P2, the timer count rate is 6.5 MHz after programming PTV
73 * with 0. This divides the 13MHz input by 2, and is undocumented.
75 #ifdef CONFIG_MACH_OMAP_PERSEUS2
76 /* REVISIT: This ifdef construct should be replaced by a query to clock
77 * framework to see if timer base frequency is 12.0, 13.0 or 19.2 MHz.
79 #define MPU_TICKS_PER_SEC (13000000 / 2)
81 #define MPU_TICKS_PER_SEC (12000000 / 2)
84 #define MPU_TIMER_TICK_PERIOD ((MPU_TICKS_PER_SEC / HZ) - 1)
87 u32 cntl; /* CNTL_TIMER, R/W */
88 u32 load_tim; /* LOAD_TIM, W */
89 u32 read_tim; /* READ_TIM, R */
90 } omap_mpu_timer_regs_t;
92 #define omap_mpu_timer_base(n) \
93 ((volatile omap_mpu_timer_regs_t*)IO_ADDRESS(OMAP_MPU_TIMER_BASE + \
94 (n)*OMAP_MPU_TIMER_OFFSET))
96 static inline unsigned long omap_mpu_timer_read(int nr)
98 volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
99 return timer->read_tim;
102 static inline void omap_mpu_timer_start(int nr, unsigned long load_val)
104 volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
106 timer->cntl = MPU_TIMER_CLOCK_ENABLE;
108 timer->load_tim = load_val;
110 timer->cntl = (MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_AR | MPU_TIMER_ST);
113 unsigned long omap_mpu_timer_ticks_to_usecs(unsigned long nr_ticks)
115 /* Round up to nearest usec */
116 return ((nr_ticks * 1000) / (MPU_TICKS_PER_SEC / 2 / 1000) + 1) >> 1;
120 * Last processed system timer interrupt
122 static unsigned long omap_mpu_timer_last = 0;
125 * Returns elapsed usecs since last system timer interrupt
127 static unsigned long omap_mpu_timer_gettimeoffset(void)
129 unsigned long now = 0 - omap_mpu_timer_read(0);
130 unsigned long elapsed = now - omap_mpu_timer_last;
132 return omap_mpu_timer_ticks_to_usecs(elapsed);
136 * Elapsed time between interrupts is calculated using timer0.
137 * Latency during the interrupt is calculated using timer1.
138 * Both timer0 and timer1 are counting at 6MHz (P2 6.5MHz).
140 static irqreturn_t omap_mpu_timer_interrupt(int irq, void *dev_id,
141 struct pt_regs *regs)
143 unsigned long now, latency;
145 write_seqlock(&xtime_lock);
146 now = 0 - omap_mpu_timer_read(0);
147 latency = MPU_TICKS_PER_SEC / HZ - omap_mpu_timer_read(1);
148 omap_mpu_timer_last = now - latency;
150 write_sequnlock(&xtime_lock);
155 static struct irqaction omap_mpu_timer_irq = {
157 .flags = SA_INTERRUPT,
158 .handler = omap_mpu_timer_interrupt
161 static __init void omap_init_mpu_timer(void)
163 omap_timer.offset = omap_mpu_timer_gettimeoffset;
164 setup_irq(INT_TIMER2, &omap_mpu_timer_irq);
165 omap_mpu_timer_start(0, 0xffffffff);
166 omap_mpu_timer_start(1, MPU_TIMER_TICK_PERIOD);
170 * ---------------------------------------------------------------------------
171 * Timer initialization
172 * ---------------------------------------------------------------------------
174 void __init omap_timer_init(void)
176 omap_init_mpu_timer();
179 struct sys_timer omap_timer = {
180 .init = omap_timer_init,
181 .offset = NULL, /* Initialized later */