aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap1/time.c
diff options
context:
space:
mode:
authorKevin Hilman <khilman@mvista.com>2007-03-08 14:32:19 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-04-21 16:02:55 -0400
commit075192ae807579448afcc0833bd349ccce057825 (patch)
tree5627ae9d65bc8791793fde1bc2ad8f62bc015e44 /arch/arm/mach-omap1/time.c
parent89df127246f23add865f4a8f719c990e41151843 (diff)
[ARM] 4262/1: OMAP: clocksource and clockevent support
Update OMAP1 to enable support for hrtimers and dynticks by using new clocksource and clockevent infrastructure. Signed-off-by: Kevin Hilman <khilman@mvista.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-omap1/time.c')
-rw-r--r--arch/arm/mach-omap1/time.c206
1 files changed, 133 insertions, 73 deletions
diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c
index 1b7e4a506c26..85e048b259f5 100644
--- a/arch/arm/mach-omap1/time.c
+++ b/arch/arm/mach-omap1/time.c
@@ -39,6 +39,10 @@
39#include <linux/interrupt.h> 39#include <linux/interrupt.h>
40#include <linux/sched.h> 40#include <linux/sched.h>
41#include <linux/spinlock.h> 41#include <linux/spinlock.h>
42#include <linux/clk.h>
43#include <linux/err.h>
44#include <linux/clocksource.h>
45#include <linux/clockchips.h>
42 46
43#include <asm/system.h> 47#include <asm/system.h>
44#include <asm/hardware.h> 48#include <asm/hardware.h>
@@ -48,13 +52,7 @@
48#include <asm/mach/irq.h> 52#include <asm/mach/irq.h>
49#include <asm/mach/time.h> 53#include <asm/mach/time.h>
50 54
51struct sys_timer omap_timer;
52 55
53/*
54 * ---------------------------------------------------------------------------
55 * MPU timer
56 * ---------------------------------------------------------------------------
57 */
58#define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE 56#define OMAP_MPU_TIMER_BASE OMAP_MPU_TIMER1_BASE
59#define OMAP_MPU_TIMER_OFFSET 0x100 57#define OMAP_MPU_TIMER_OFFSET 0x100
60 58
@@ -88,21 +86,6 @@ static inline unsigned long long cycles_2_ns(unsigned long long cyc)
88 return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; 86 return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
89} 87}
90 88
91/*
92 * MPU_TICKS_PER_SEC must be an even number, otherwise machinecycles_to_usecs
93 * will break. On P2, the timer count rate is 6.5 MHz after programming PTV
94 * with 0. This divides the 13MHz input by 2, and is undocumented.
95 */
96#if defined(CONFIG_MACH_OMAP_PERSEUS2) || defined(CONFIG_MACH_OMAP_FSAMPLE)
97/* REVISIT: This ifdef construct should be replaced by a query to clock
98 * framework to see if timer base frequency is 12.0, 13.0 or 19.2 MHz.
99 */
100#define MPU_TICKS_PER_SEC (13000000 / 2)
101#else
102#define MPU_TICKS_PER_SEC (12000000 / 2)
103#endif
104
105#define MPU_TIMER_TICK_PERIOD ((MPU_TICKS_PER_SEC / HZ) - 1)
106 89
107typedef struct { 90typedef struct {
108 u32 cntl; /* CNTL_TIMER, R/W */ 91 u32 cntl; /* CNTL_TIMER, R/W */
@@ -120,98 +103,164 @@ static inline unsigned long omap_mpu_timer_read(int nr)
120 return timer->read_tim; 103 return timer->read_tim;
121} 104}
122 105
123static inline void omap_mpu_timer_start(int nr, unsigned long load_val) 106static inline void omap_mpu_set_autoreset(int nr)
124{ 107{
125 volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr); 108 volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
126 109
127 timer->cntl = MPU_TIMER_CLOCK_ENABLE; 110 timer->cntl = timer->cntl | MPU_TIMER_AR;
128 udelay(1);
129 timer->load_tim = load_val;
130 udelay(1);
131 timer->cntl = (MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_AR | MPU_TIMER_ST);
132} 111}
133 112
134unsigned long omap_mpu_timer_ticks_to_usecs(unsigned long nr_ticks) 113static inline void omap_mpu_remove_autoreset(int nr)
135{ 114{
136 unsigned long long nsec; 115 volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
137 116
138 nsec = cycles_2_ns((unsigned long long)nr_ticks); 117 timer->cntl = timer->cntl & ~MPU_TIMER_AR;
139 return (unsigned long)nsec / 1000;
140} 118}
141 119
142/* 120static inline void omap_mpu_timer_start(int nr, unsigned long load_val,
143 * Last processed system timer interrupt 121 int autoreset)
144 */ 122{
145static unsigned long omap_mpu_timer_last = 0; 123 volatile omap_mpu_timer_regs_t* timer = omap_mpu_timer_base(nr);
124 unsigned int timerflags = (MPU_TIMER_CLOCK_ENABLE | MPU_TIMER_ST);
125
126 if (autoreset) timerflags |= MPU_TIMER_AR;
127
128 timer->cntl = MPU_TIMER_CLOCK_ENABLE;
129 udelay(1);
130 timer->load_tim = load_val;
131 udelay(1);
132 timer->cntl = timerflags;
133}
146 134
147/* 135/*
148 * Returns elapsed usecs since last system timer interrupt 136 * ---------------------------------------------------------------------------
137 * MPU timer 1 ... count down to zero, interrupt, reload
138 * ---------------------------------------------------------------------------
149 */ 139 */
150static unsigned long omap_mpu_timer_gettimeoffset(void) 140static int omap_mpu_set_next_event(unsigned long cycles,
141 struct clock_event_device *evt)
151{ 142{
152 unsigned long now = 0 - omap_mpu_timer_read(0); 143 omap_mpu_timer_start(0, cycles, 0);
153 unsigned long elapsed = now - omap_mpu_timer_last; 144 return 0;
145}
154 146
155 return omap_mpu_timer_ticks_to_usecs(elapsed); 147static void omap_mpu_set_mode(enum clock_event_mode mode,
148 struct clock_event_device *evt)
149{
150 switch (mode) {
151 case CLOCK_EVT_MODE_PERIODIC:
152 omap_mpu_set_autoreset(0);
153 break;
154 case CLOCK_EVT_MODE_ONESHOT:
155 omap_mpu_remove_autoreset(0);
156 break;
157 case CLOCK_EVT_MODE_UNUSED:
158 case CLOCK_EVT_MODE_SHUTDOWN:
159 break;
160 }
156} 161}
157 162
158/* 163static struct clock_event_device clockevent_mpu_timer1 = {
159 * Elapsed time between interrupts is calculated using timer0. 164 .name = "mpu_timer1",
160 * Latency during the interrupt is calculated using timer1. 165 .features = CLOCK_EVT_FEAT_PERIODIC, CLOCK_EVT_FEAT_ONESHOT,
161 * Both timer0 and timer1 are counting at 6MHz (P2 6.5MHz). 166 .shift = 32,
162 */ 167 .set_next_event = omap_mpu_set_next_event,
163static irqreturn_t omap_mpu_timer_interrupt(int irq, void *dev_id) 168 .set_mode = omap_mpu_set_mode,
169};
170
171static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id)
164{ 172{
165 unsigned long now, latency; 173 struct clock_event_device *evt = &clockevent_mpu_timer1;
166 174
167 write_seqlock(&xtime_lock); 175 evt->event_handler(evt);
168 now = 0 - omap_mpu_timer_read(0);
169 latency = MPU_TICKS_PER_SEC / HZ - omap_mpu_timer_read(1);
170 omap_mpu_timer_last = now - latency;
171 timer_tick();
172 write_sequnlock(&xtime_lock);
173 176
174 return IRQ_HANDLED; 177 return IRQ_HANDLED;
175} 178}
176 179
177static struct irqaction omap_mpu_timer_irq = { 180static struct irqaction omap_mpu_timer1_irq = {
178 .name = "mpu timer", 181 .name = "mpu_timer1",
179 .flags = IRQF_DISABLED | IRQF_TIMER, 182 .flags = IRQF_DISABLED | IRQF_TIMER,
180 .handler = omap_mpu_timer_interrupt, 183 .handler = omap_mpu_timer1_interrupt,
181}; 184};
182 185
183static unsigned long omap_mpu_timer1_overflows; 186static __init void omap_init_mpu_timer(unsigned long rate)
184static irqreturn_t omap_mpu_timer1_interrupt(int irq, void *dev_id) 187{
188 set_cyc2ns_scale(rate / 1000);
189
190 setup_irq(INT_TIMER1, &omap_mpu_timer1_irq);
191 omap_mpu_timer_start(0, (rate / HZ) - 1, 1);
192
193 clockevent_mpu_timer1.mult = div_sc(rate, NSEC_PER_SEC,
194 clockevent_mpu_timer1.shift);
195 clockevent_mpu_timer1.max_delta_ns =
196 clockevent_delta2ns(-1, &clockevent_mpu_timer1);
197 clockevent_mpu_timer1.min_delta_ns =
198 clockevent_delta2ns(1, &clockevent_mpu_timer1);
199
200 clockevent_mpu_timer1.cpumask = cpumask_of_cpu(0);
201 clockevents_register_device(&clockevent_mpu_timer1);
202}
203
204
205/*
206 * ---------------------------------------------------------------------------
207 * MPU timer 2 ... free running 32-bit clock source and scheduler clock
208 * ---------------------------------------------------------------------------
209 */
210
211static unsigned long omap_mpu_timer2_overflows;
212
213static irqreturn_t omap_mpu_timer2_interrupt(int irq, void *dev_id)
185{ 214{
186 omap_mpu_timer1_overflows++; 215 omap_mpu_timer2_overflows++;
187 return IRQ_HANDLED; 216 return IRQ_HANDLED;
188} 217}
189 218
190static struct irqaction omap_mpu_timer1_irq = { 219static struct irqaction omap_mpu_timer2_irq = {
191 .name = "mpu timer1 overflow", 220 .name = "mpu_timer2",
192 .flags = IRQF_DISABLED, 221 .flags = IRQF_DISABLED,
193 .handler = omap_mpu_timer1_interrupt, 222 .handler = omap_mpu_timer2_interrupt,
194}; 223};
195 224
196static __init void omap_init_mpu_timer(void) 225static cycle_t mpu_read(void)
197{ 226{
198 set_cyc2ns_scale(MPU_TICKS_PER_SEC / 1000); 227 return ~omap_mpu_timer_read(1);
199 omap_timer.offset = omap_mpu_timer_gettimeoffset; 228}
200 setup_irq(INT_TIMER1, &omap_mpu_timer1_irq); 229
201 setup_irq(INT_TIMER2, &omap_mpu_timer_irq); 230static struct clocksource clocksource_mpu = {
202 omap_mpu_timer_start(0, 0xffffffff); 231 .name = "mpu_timer2",
203 omap_mpu_timer_start(1, MPU_TIMER_TICK_PERIOD); 232 .rating = 300,
233 .read = mpu_read,
234 .mask = CLOCKSOURCE_MASK(32),
235 .shift = 24,
236 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
237};
238
239static void __init omap_init_clocksource(unsigned long rate)
240{
241 static char err[] __initdata = KERN_ERR
242 "%s: can't register clocksource!\n";
243
244 clocksource_mpu.mult
245 = clocksource_khz2mult(rate/1000, clocksource_mpu.shift);
246
247 setup_irq(INT_TIMER2, &omap_mpu_timer2_irq);
248 omap_mpu_timer_start(1, ~0, 1);
249
250 if (clocksource_register(&clocksource_mpu))
251 printk(err, clocksource_mpu.name);
204} 252}
205 253
254
206/* 255/*
207 * Scheduler clock - returns current time in nanosec units. 256 * Scheduler clock - returns current time in nanosec units.
208 */ 257 */
209unsigned long long sched_clock(void) 258unsigned long long sched_clock(void)
210{ 259{
211 unsigned long ticks = 0 - omap_mpu_timer_read(0); 260 unsigned long ticks = 0 - omap_mpu_timer_read(1);
212 unsigned long long ticks64; 261 unsigned long long ticks64;
213 262
214 ticks64 = omap_mpu_timer1_overflows; 263 ticks64 = omap_mpu_timer2_overflows;
215 ticks64 <<= 32; 264 ticks64 <<= 32;
216 ticks64 |= ticks; 265 ticks64 |= ticks;
217 266
@@ -225,10 +274,21 @@ unsigned long long sched_clock(void)
225 */ 274 */
226static void __init omap_timer_init(void) 275static void __init omap_timer_init(void)
227{ 276{
228 omap_init_mpu_timer(); 277 struct clk *ck_ref = clk_get(NULL, "ck_ref");
278 unsigned long rate;
279
280 BUG_ON(IS_ERR(ck_ref));
281
282 rate = clk_get_rate(ck_ref);
283 clk_put(ck_ref);
284
285 /* PTV = 0 */
286 rate /= 2;
287
288 omap_init_mpu_timer(rate);
289 omap_init_clocksource(rate);
229} 290}
230 291
231struct sys_timer omap_timer = { 292struct sys_timer omap_timer = {
232 .init = omap_timer_init, 293 .init = omap_timer_init,
233 .offset = NULL, /* Initialized later */
234}; 294};