summaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2005-06-25 19:39:45 +0100
committerRussell King <rmk+kernel@arm.linux.org.uk>2005-06-25 19:39:45 +0100
commit8749af68216e1ebf6460992fce548f400ecf63a4 (patch)
tree6968809c357c1cf8167208f480f0e974bf9af955 /arch/arm
parent321ab6a5fab812658626aee6bce2617f8cfb3a55 (diff)
downloadlinux-8749af68216e1ebf6460992fce548f400ecf63a4.tar.gz
linux-8749af68216e1ebf6460992fce548f400ecf63a4.tar.bz2
linux-8749af68216e1ebf6460992fce548f400ecf63a4.zip
[PATCH] ARM: Generic Dynamic Tick Timer support for ARM, take 4
This patch adds support for Dynamic Tick Timer for ARM. Dynamic Tick is also known as VST (Variable Scheduling Timeouts). Dynamic Tick has been in use in the OMAP tree since last October. The patch is not intrusive, and does not do anything unless CONFIG_NO_IDLE_HZ is defined. This patch has the following fixed based on comments from RMK: - Time is updated before calling interrupt handlers. - Added new interrupt flag SA_TIMER to avoid duplicate timer interrupts - Moved struct dyn_tick_timer to time.h until we at some point probably have an arch independent dyn-tick.h - Cleaned up testing for DYN_TICK_ENABLED in irq.c I've cleaned up this patch to fix some remaining issues: - Call the timer tick handler with irqs disabled, as it would be from a normal interrupt - if we have a dyn_tick, we better implement all methods. - generic timer_dyn_reprogram() call, to be called before sleeping - added command line option - "dyntick=" to allow boot-time control of this feature -- rmk Signed-off-by: Tony Lindgren Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/Kconfig15
-rw-r--r--arch/arm/kernel/irq.c14
-rw-r--r--arch/arm/kernel/time.c103
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
Say Y here if you are building a kernel for a desktop, embedded
or real-time system. Say N if you are unsure.
+config NO_IDLE_HZ
+ bool "Dynamic tick timer"
+ help
+ Select this option if you want to disable continuous timer ticks
+ and have them programmed to occur as required. This option saves
+ power as the system can remain in idle state for longer.
+
+ By default dynamic tick is disabled during the boot, and can be
+ manually enabled with:
+
+ echo 1 > /sys/devices/system/timer/timer0/dyn_tick
+
+ Alternatively, if you want dynamic tick automatically enabled
+ during boot, pass "dyntick=enable" via the kernel command string.
+
config ARCH_DISCONTIGMEM_ENABLE
bool
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 @@
* Copyright (C) 1992 Linus Torvalds
* Modifications for ARM processor Copyright (C) 1995-2000 Russell King.
*
+ * Support for Dynamic Tick Timer Copyright (C) 2004-2005 Nokia Corporation.
+ * Dynamic Tick Timer written by Tony Lindgren <tony@atomide.com> and
+ * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>.
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
@@ -37,6 +41,7 @@
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
/*
* 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)
spin_unlock(&irq_controller_lock);
+#ifdef CONFIG_NO_IDLE_HZ
+ if (!(action->flags & SA_TIMER) && system_timer->dyn_tick != NULL) {
+ write_seqlock(&xtime_lock);
+ if (system_timer->dyn_tick->state & DYN_TICK_ENABLED)
+ system_timer->dyn_tick->handler(irq, 0, regs);
+ write_sequnlock(&xtime_lock);
+ }
+#endif
+
if (!(action->flags & SA_INTERRUPT))
local_irq_enable();
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 = {
.resume = timer_resume,
};
+#ifdef CONFIG_NO_IDLE_HZ
+static int timer_dyn_tick_enable(void)
+{
+ struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
+ unsigned long flags;
+ int ret = -ENODEV;
+
+ if (dyn_tick) {
+ write_seqlock_irqsave(&xtime_lock, flags);
+ ret = 0;
+ if (!(dyn_tick->state & DYN_TICK_ENABLED)) {
+ ret = dyn_tick->enable();
+
+ if (ret == 0)
+ dyn_tick->state |= DYN_TICK_ENABLED;
+ }
+ write_sequnlock_irqrestore(&xtime_lock, flags);
+ }
+
+ return ret;
+}
+
+static int timer_dyn_tick_disable(void)
+{
+ struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
+ unsigned long flags;
+ int ret = -ENODEV;
+
+ if (dyn_tick) {
+ write_seqlock_irqsave(&xtime_lock, flags);
+ ret = 0;
+ if (dyn_tick->state & DYN_TICK_ENABLED) {
+ ret = dyn_tick->disable();
+
+ if (ret == 0)
+ dyn_tick->state &= ~DYN_TICK_ENABLED;
+ }
+ write_sequnlock_irqrestore(&xtime_lock, flags);
+ }
+
+ return ret;
+}
+
+void timer_dyn_reprogram(void)
+{
+ struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
+ unsigned long flags;
+
+ write_seqlock_irqsave(&xtime_lock, flags);
+ if (dyn_tick->state & DYN_TICK_ENABLED)
+ dyn_tick->reprogram(next_timer_interrupt() - jiffies);
+ write_sequnlock_irqrestore(&xtime_lock, flags);
+}
+
+static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf)
+{
+ return sprintf(buf, "%i\n",
+ (system_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1);
+}
+
+static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ unsigned int enable = simple_strtoul(buf, NULL, 2);
+
+ if (enable)
+ timer_dyn_tick_enable();
+ else
+ timer_dyn_tick_disable();
+
+ return count;
+}
+static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick);
+
+/*
+ * dyntick=enable|disable
+ */
+static char dyntick_str[4] __initdata = "";
+
+static int __init dyntick_setup(char *str)
+{
+ if (str)
+ strlcpy(dyntick_str, str, sizeof(dyntick_str));
+ return 1;
+}
+
+__setup("dyntick=", dyntick_setup);
+#endif
+
static int __init timer_init_sysfs(void)
{
int ret = sysdev_class_register(&timer_sysclass);
@@ -388,6 +477,20 @@ static int __init timer_init_sysfs(void)
system_timer->dev.cls = &timer_sysclass;
ret = sysdev_register(&system_timer->dev);
}
+
+#ifdef CONFIG_NO_IDLE_HZ
+ if (ret == 0 && system_timer->dyn_tick) {
+ ret = sysdev_create_file(&system_timer->dev, &attr_dyn_tick);
+
+ /*
+ * Turn on dynamic tick after calibrate delay
+ * for correct bogomips
+ */
+ if (ret == 0 && dyntick_str[0] == 'e')
+ ret = timer_dyn_tick_enable();
+ }
+#endif
+
return ret;
}