diff options
author | Tony Lindgren <tony@atomide.com> | 2006-04-02 12:46:21 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-04-02 12:46:21 -0400 |
commit | a569c6ec37e78eef4299d0a60ae4028459b27117 (patch) | |
tree | 6f51fde75ecd91661e1fc3268cdea47c47f4dd74 /arch/arm/mach-omap1 | |
parent | b824efae120b656fef562b2e81e1ed6aa88f8d24 (diff) |
[ARM] 3427/1: ARM: OMAP: 2/8 Update timers
Patch from Tony Lindgren
Update OMAP timers from linux-omap tree. The highlights of the
patch are:
- Move timer32k code from mach-omap1 to plat-omap and make it
work also on omap24xx by Tony Lindgren
- Add support for dmtimer idle check for PM by Tuukka Tikkanen
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-omap1')
-rw-r--r-- | arch/arm/mach-omap1/time.c | 197 |
1 files changed, 0 insertions, 197 deletions
diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index cdbf4d7620c6..a85fe6066bc4 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c | |||
@@ -51,8 +51,6 @@ | |||
51 | 51 | ||
52 | struct sys_timer omap_timer; | 52 | struct sys_timer omap_timer; |
53 | 53 | ||
54 | #ifdef CONFIG_OMAP_MPU_TIMER | ||
55 | |||
56 | /* | 54 | /* |
57 | * --------------------------------------------------------------------------- | 55 | * --------------------------------------------------------------------------- |
58 | * MPU timer | 56 | * MPU timer |
@@ -222,195 +220,6 @@ unsigned long long sched_clock(void) | |||
222 | 220 | ||
223 | return cycles_2_ns(ticks64); | 221 | return cycles_2_ns(ticks64); |
224 | } | 222 | } |
225 | #endif /* CONFIG_OMAP_MPU_TIMER */ | ||
226 | |||
227 | #ifdef CONFIG_OMAP_32K_TIMER | ||
228 | |||
229 | #ifdef CONFIG_ARCH_OMAP15XX | ||
230 | #error OMAP 32KHz timer does not currently work on 15XX! | ||
231 | #endif | ||
232 | |||
233 | /* | ||
234 | * --------------------------------------------------------------------------- | ||
235 | * 32KHz OS timer | ||
236 | * | ||
237 | * This currently works only on 16xx, as 1510 does not have the continuous | ||
238 | * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track | ||
239 | * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer | ||
240 | * on 1510 would be possible, but the timer would not be as accurate as | ||
241 | * with the 32KHz synchronized timer. | ||
242 | * --------------------------------------------------------------------------- | ||
243 | */ | ||
244 | #define OMAP_32K_TIMER_BASE 0xfffb9000 | ||
245 | #define OMAP_32K_TIMER_CR 0x08 | ||
246 | #define OMAP_32K_TIMER_TVR 0x00 | ||
247 | #define OMAP_32K_TIMER_TCR 0x04 | ||
248 | |||
249 | #define OMAP_32K_TICKS_PER_HZ (32768 / HZ) | ||
250 | |||
251 | /* | ||
252 | * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 | ||
253 | * so with HZ = 100, TVR = 327.68. | ||
254 | */ | ||
255 | #define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) | ||
256 | #define TIMER_32K_SYNCHRONIZED 0xfffbc410 | ||
257 | |||
258 | #define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ | ||
259 | (((nr_jiffies) * (clock_rate)) / HZ) | ||
260 | |||
261 | static inline void omap_32k_timer_write(int val, int reg) | ||
262 | { | ||
263 | omap_writew(val, reg + OMAP_32K_TIMER_BASE); | ||
264 | } | ||
265 | |||
266 | static inline unsigned long omap_32k_timer_read(int reg) | ||
267 | { | ||
268 | return omap_readl(reg + OMAP_32K_TIMER_BASE) & 0xffffff; | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * The 32KHz synchronized timer is an additional timer on 16xx. | ||
273 | * It is always running. | ||
274 | */ | ||
275 | static inline unsigned long omap_32k_sync_timer_read(void) | ||
276 | { | ||
277 | return omap_readl(TIMER_32K_SYNCHRONIZED); | ||
278 | } | ||
279 | |||
280 | static inline void omap_32k_timer_start(unsigned long load_val) | ||
281 | { | ||
282 | omap_32k_timer_write(load_val, OMAP_32K_TIMER_TVR); | ||
283 | omap_32k_timer_write(0x0f, OMAP_32K_TIMER_CR); | ||
284 | } | ||
285 | |||
286 | static inline void omap_32k_timer_stop(void) | ||
287 | { | ||
288 | omap_32k_timer_write(0x0, OMAP_32K_TIMER_CR); | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * Rounds down to nearest usec. Note that this will overflow for larger values. | ||
293 | */ | ||
294 | static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k) | ||
295 | { | ||
296 | return (ticks_32k * 5*5*5*5*5*5) >> 9; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * Rounds down to nearest nsec. | ||
301 | */ | ||
302 | static inline unsigned long long | ||
303 | omap_32k_ticks_to_nsecs(unsigned long ticks_32k) | ||
304 | { | ||
305 | return (unsigned long long) ticks_32k * 1000 * 5*5*5*5*5*5 >> 9; | ||
306 | } | ||
307 | |||
308 | static unsigned long omap_32k_last_tick = 0; | ||
309 | |||
310 | /* | ||
311 | * Returns elapsed usecs since last 32k timer interrupt | ||
312 | */ | ||
313 | static unsigned long omap_32k_timer_gettimeoffset(void) | ||
314 | { | ||
315 | unsigned long now = omap_32k_sync_timer_read(); | ||
316 | return omap_32k_ticks_to_usecs(now - omap_32k_last_tick); | ||
317 | } | ||
318 | |||
319 | /* | ||
320 | * Returns current time from boot in nsecs. It's OK for this to wrap | ||
321 | * around for now, as it's just a relative time stamp. | ||
322 | */ | ||
323 | unsigned long long sched_clock(void) | ||
324 | { | ||
325 | return omap_32k_ticks_to_nsecs(omap_32k_sync_timer_read()); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this | ||
330 | * function is also called from other interrupts to remove latency | ||
331 | * issues with dynamic tick. In the dynamic tick case, we need to lock | ||
332 | * with irqsave. | ||
333 | */ | ||
334 | static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id, | ||
335 | struct pt_regs *regs) | ||
336 | { | ||
337 | unsigned long flags; | ||
338 | unsigned long now; | ||
339 | |||
340 | write_seqlock_irqsave(&xtime_lock, flags); | ||
341 | now = omap_32k_sync_timer_read(); | ||
342 | |||
343 | while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) { | ||
344 | omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ; | ||
345 | timer_tick(regs); | ||
346 | } | ||
347 | |||
348 | /* Restart timer so we don't drift off due to modulo or dynamic tick. | ||
349 | * By default we program the next timer to be continuous to avoid | ||
350 | * latencies during high system load. During dynamic tick operation the | ||
351 | * continuous timer can be overridden from pm_idle to be longer. | ||
352 | */ | ||
353 | omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now); | ||
354 | write_sequnlock_irqrestore(&xtime_lock, flags); | ||
355 | |||
356 | return IRQ_HANDLED; | ||
357 | } | ||
358 | |||
359 | #ifdef CONFIG_NO_IDLE_HZ | ||
360 | /* | ||
361 | * Programs the next timer interrupt needed. Called when dynamic tick is | ||
362 | * enabled, and to reprogram the ticks to skip from pm_idle. Note that | ||
363 | * we can keep the timer continuous, and don't need to set it to run in | ||
364 | * one-shot mode. This is because the timer will get reprogrammed again | ||
365 | * after next interrupt. | ||
366 | */ | ||
367 | void omap_32k_timer_reprogram(unsigned long next_tick) | ||
368 | { | ||
369 | omap_32k_timer_start(JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1); | ||
370 | } | ||
371 | |||
372 | static struct irqaction omap_32k_timer_irq; | ||
373 | extern struct timer_update_handler timer_update; | ||
374 | |||
375 | static int omap_32k_timer_enable_dyn_tick(void) | ||
376 | { | ||
377 | /* No need to reprogram timer, just use the next interrupt */ | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int omap_32k_timer_disable_dyn_tick(void) | ||
382 | { | ||
383 | omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | static struct dyn_tick_timer omap_dyn_tick_timer = { | ||
388 | .enable = omap_32k_timer_enable_dyn_tick, | ||
389 | .disable = omap_32k_timer_disable_dyn_tick, | ||
390 | .reprogram = omap_32k_timer_reprogram, | ||
391 | .handler = omap_32k_timer_interrupt, | ||
392 | }; | ||
393 | #endif /* CONFIG_NO_IDLE_HZ */ | ||
394 | |||
395 | static struct irqaction omap_32k_timer_irq = { | ||
396 | .name = "32KHz timer", | ||
397 | .flags = SA_INTERRUPT | SA_TIMER, | ||
398 | .handler = omap_32k_timer_interrupt, | ||
399 | }; | ||
400 | |||
401 | static __init void omap_init_32k_timer(void) | ||
402 | { | ||
403 | |||
404 | #ifdef CONFIG_NO_IDLE_HZ | ||
405 | omap_timer.dyn_tick = &omap_dyn_tick_timer; | ||
406 | #endif | ||
407 | |||
408 | setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); | ||
409 | omap_timer.offset = omap_32k_timer_gettimeoffset; | ||
410 | omap_32k_last_tick = omap_32k_sync_timer_read(); | ||
411 | omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); | ||
412 | } | ||
413 | #endif /* CONFIG_OMAP_32K_TIMER */ | ||
414 | 223 | ||
415 | /* | 224 | /* |
416 | * --------------------------------------------------------------------------- | 225 | * --------------------------------------------------------------------------- |
@@ -419,13 +228,7 @@ static __init void omap_init_32k_timer(void) | |||
419 | */ | 228 | */ |
420 | static void __init omap_timer_init(void) | 229 | static void __init omap_timer_init(void) |
421 | { | 230 | { |
422 | #if defined(CONFIG_OMAP_MPU_TIMER) | ||
423 | omap_init_mpu_timer(); | 231 | omap_init_mpu_timer(); |
424 | #elif defined(CONFIG_OMAP_32K_TIMER) | ||
425 | omap_init_32k_timer(); | ||
426 | #else | ||
427 | #error No system timer selected in Kconfig! | ||
428 | #endif | ||
429 | } | 232 | } |
430 | 233 | ||
431 | struct sys_timer omap_timer = { | 234 | struct sys_timer omap_timer = { |