diff options
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/Kconfig | 15 | ||||
-rw-r--r-- | arch/arm/kernel/irq.c | 14 | ||||
-rw-r--r-- | arch/arm/kernel/time.c | 103 |
3 files changed, 132 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index bfcf42280368..c8d94dcd8ef7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -346,6 +346,21 @@ config PREEMPT | |||
346 | Say Y here if you are building a kernel for a desktop, embedded | 346 | Say Y here if you are building a kernel for a desktop, embedded |
347 | or real-time system. Say N if you are unsure. | 347 | or real-time system. Say N if you are unsure. |
348 | 348 | ||
349 | config NO_IDLE_HZ | ||
350 | bool "Dynamic tick timer" | ||
351 | help | ||
352 | Select this option if you want to disable continuous timer ticks | ||
353 | and have them programmed to occur as required. This option saves | ||
354 | power as the system can remain in idle state for longer. | ||
355 | |||
356 | By default dynamic tick is disabled during the boot, and can be | ||
357 | manually enabled with: | ||
358 | |||
359 | echo 1 > /sys/devices/system/timer/timer0/dyn_tick | ||
360 | |||
361 | Alternatively, if you want dynamic tick automatically enabled | ||
362 | during boot, pass "dyntick=enable" via the kernel command string. | ||
363 | |||
349 | config ARCH_DISCONTIGMEM_ENABLE | 364 | config ARCH_DISCONTIGMEM_ENABLE |
350 | bool | 365 | bool |
351 | default (ARCH_LH7A40X && !LH7A40X_CONTIGMEM) | 366 | default (ARCH_LH7A40X && !LH7A40X_CONTIGMEM) |
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index ff187f4308f0..395137a8fad2 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c | |||
@@ -4,6 +4,10 @@ | |||
4 | * Copyright (C) 1992 Linus Torvalds | 4 | * Copyright (C) 1992 Linus Torvalds |
5 | * Modifications for ARM processor Copyright (C) 1995-2000 Russell King. | 5 | * Modifications for ARM processor Copyright (C) 1995-2000 Russell King. |
6 | * | 6 | * |
7 | * Support for Dynamic Tick Timer Copyright (C) 2004-2005 Nokia Corporation. | ||
8 | * Dynamic Tick Timer written by Tony Lindgren <tony@atomide.com> and | ||
9 | * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>. | ||
10 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | 11 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 12 | * it under the terms of the GNU General Public License version 2 as |
9 | * published by the Free Software Foundation. | 13 | * published by the Free Software Foundation. |
@@ -37,6 +41,7 @@ | |||
37 | #include <asm/irq.h> | 41 | #include <asm/irq.h> |
38 | #include <asm/system.h> | 42 | #include <asm/system.h> |
39 | #include <asm/mach/irq.h> | 43 | #include <asm/mach/irq.h> |
44 | #include <asm/mach/time.h> | ||
40 | 45 | ||
41 | /* | 46 | /* |
42 | * Maximum IRQ count. Currently, this is arbitary. However, it should | 47 | * Maximum IRQ count. Currently, this is arbitary. However, it should |
@@ -329,6 +334,15 @@ __do_irq(unsigned int irq, struct irqaction *action, struct pt_regs *regs) | |||
329 | 334 | ||
330 | spin_unlock(&irq_controller_lock); | 335 | spin_unlock(&irq_controller_lock); |
331 | 336 | ||
337 | #ifdef CONFIG_NO_IDLE_HZ | ||
338 | if (!(action->flags & SA_TIMER) && system_timer->dyn_tick != NULL) { | ||
339 | write_seqlock(&xtime_lock); | ||
340 | if (system_timer->dyn_tick->state & DYN_TICK_ENABLED) | ||
341 | system_timer->dyn_tick->handler(irq, 0, regs); | ||
342 | write_sequnlock(&xtime_lock); | ||
343 | } | ||
344 | #endif | ||
345 | |||
332 | if (!(action->flags & SA_INTERRUPT)) | 346 | if (!(action->flags & SA_INTERRUPT)) |
333 | local_irq_enable(); | 347 | local_irq_enable(); |
334 | 348 | ||
diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index c232f24f4a60..06054c9ba074 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c | |||
@@ -381,6 +381,95 @@ static struct sysdev_class timer_sysclass = { | |||
381 | .resume = timer_resume, | 381 | .resume = timer_resume, |
382 | }; | 382 | }; |
383 | 383 | ||
384 | #ifdef CONFIG_NO_IDLE_HZ | ||
385 | static int timer_dyn_tick_enable(void) | ||
386 | { | ||
387 | struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick; | ||
388 | unsigned long flags; | ||
389 | int ret = -ENODEV; | ||
390 | |||
391 | if (dyn_tick) { | ||
392 | write_seqlock_irqsave(&xtime_lock, flags); | ||
393 | ret = 0; | ||
394 | if (!(dyn_tick->state & DYN_TICK_ENABLED)) { | ||
395 | ret = dyn_tick->enable(); | ||
396 | |||
397 | if (ret == 0) | ||
398 | dyn_tick->state |= DYN_TICK_ENABLED; | ||
399 | } | ||
400 | write_sequnlock_irqrestore(&xtime_lock, flags); | ||
401 | } | ||
402 | |||
403 | return ret; | ||
404 | } | ||
405 | |||
406 | static int timer_dyn_tick_disable(void) | ||
407 | { | ||
408 | struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick; | ||
409 | unsigned long flags; | ||
410 | int ret = -ENODEV; | ||
411 | |||
412 | if (dyn_tick) { | ||
413 | write_seqlock_irqsave(&xtime_lock, flags); | ||
414 | ret = 0; | ||
415 | if (dyn_tick->state & DYN_TICK_ENABLED) { | ||
416 | ret = dyn_tick->disable(); | ||
417 | |||
418 | if (ret == 0) | ||
419 | dyn_tick->state &= ~DYN_TICK_ENABLED; | ||
420 | } | ||
421 | write_sequnlock_irqrestore(&xtime_lock, flags); | ||
422 | } | ||
423 | |||
424 | return ret; | ||
425 | } | ||
426 | |||
427 | void timer_dyn_reprogram(void) | ||
428 | { | ||
429 | struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick; | ||
430 | unsigned long flags; | ||
431 | |||
432 | write_seqlock_irqsave(&xtime_lock, flags); | ||
433 | if (dyn_tick->state & DYN_TICK_ENABLED) | ||
434 | dyn_tick->reprogram(next_timer_interrupt() - jiffies); | ||
435 | write_sequnlock_irqrestore(&xtime_lock, flags); | ||
436 | } | ||
437 | |||
438 | static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf) | ||
439 | { | ||
440 | return sprintf(buf, "%i\n", | ||
441 | (system_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1); | ||
442 | } | ||
443 | |||
444 | static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf, | ||
445 | size_t count) | ||
446 | { | ||
447 | unsigned int enable = simple_strtoul(buf, NULL, 2); | ||
448 | |||
449 | if (enable) | ||
450 | timer_dyn_tick_enable(); | ||
451 | else | ||
452 | timer_dyn_tick_disable(); | ||
453 | |||
454 | return count; | ||
455 | } | ||
456 | static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick); | ||
457 | |||
458 | /* | ||
459 | * dyntick=enable|disable | ||
460 | */ | ||
461 | static char dyntick_str[4] __initdata = ""; | ||
462 | |||
463 | static int __init dyntick_setup(char *str) | ||
464 | { | ||
465 | if (str) | ||
466 | strlcpy(dyntick_str, str, sizeof(dyntick_str)); | ||
467 | return 1; | ||
468 | } | ||
469 | |||
470 | __setup("dyntick=", dyntick_setup); | ||
471 | #endif | ||
472 | |||
384 | static int __init timer_init_sysfs(void) | 473 | static int __init timer_init_sysfs(void) |
385 | { | 474 | { |
386 | int ret = sysdev_class_register(&timer_sysclass); | 475 | int ret = sysdev_class_register(&timer_sysclass); |
@@ -388,6 +477,20 @@ static int __init timer_init_sysfs(void) | |||
388 | system_timer->dev.cls = &timer_sysclass; | 477 | system_timer->dev.cls = &timer_sysclass; |
389 | ret = sysdev_register(&system_timer->dev); | 478 | ret = sysdev_register(&system_timer->dev); |
390 | } | 479 | } |
480 | |||
481 | #ifdef CONFIG_NO_IDLE_HZ | ||
482 | if (ret == 0 && system_timer->dyn_tick) { | ||
483 | ret = sysdev_create_file(&system_timer->dev, &attr_dyn_tick); | ||
484 | |||
485 | /* | ||
486 | * Turn on dynamic tick after calibrate delay | ||
487 | * for correct bogomips | ||
488 | */ | ||
489 | if (ret == 0 && dyntick_str[0] == 'e') | ||
490 | ret = timer_dyn_tick_enable(); | ||
491 | } | ||
492 | #endif | ||
493 | |||
391 | return ret; | 494 | return ret; |
392 | } | 495 | } |
393 | 496 | ||