aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk@dyn-67.arm.linux.org.uk>2005-06-25 14:39:45 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2005-06-25 14:39:45 -0400
commit8749af68216e1ebf6460992fce548f400ecf63a4 (patch)
tree6968809c357c1cf8167208f480f0e974bf9af955
parent321ab6a5fab812658626aee6bce2617f8cfb3a55 (diff)
[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>
-rw-r--r--arch/arm/Kconfig15
-rw-r--r--arch/arm/kernel/irq.c14
-rw-r--r--arch/arm/kernel/time.c103
-rw-r--r--include/asm-arm/mach/time.h21
-rw-r--r--include/asm-arm/signal.h1
5 files changed, 154 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
349config 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
349config ARCH_DISCONTIGMEM_ENABLE 364config 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
385static 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
406static 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
427void 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
438static 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
444static 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}
456static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick);
457
458/*
459 * dyntick=enable|disable
460 */
461static char dyntick_str[4] __initdata = "";
462
463static 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
384static int __init timer_init_sysfs(void) 473static 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
diff --git a/include/asm-arm/mach/time.h b/include/asm-arm/mach/time.h
index 5cf4fd659fd5..047980ad18d1 100644
--- a/include/asm-arm/mach/time.h
+++ b/include/asm-arm/mach/time.h
@@ -39,8 +39,29 @@ struct sys_timer {
39 void (*suspend)(void); 39 void (*suspend)(void);
40 void (*resume)(void); 40 void (*resume)(void);
41 unsigned long (*offset)(void); 41 unsigned long (*offset)(void);
42
43#ifdef CONFIG_NO_IDLE_HZ
44 struct dyn_tick_timer *dyn_tick;
45#endif
46};
47
48#ifdef CONFIG_NO_IDLE_HZ
49
50#define DYN_TICK_SKIPPING (1 << 2)
51#define DYN_TICK_ENABLED (1 << 1)
52#define DYN_TICK_SUITABLE (1 << 0)
53
54struct dyn_tick_timer {
55 unsigned int state; /* Current state */
56 int (*enable)(void); /* Enables dynamic tick */
57 int (*disable)(void); /* Disables dynamic tick */
58 void (*reprogram)(unsigned long); /* Reprograms the timer */
59 int (*handler)(int, void *, struct pt_regs *);
42}; 60};
43 61
62void timer_dyn_reprogram(void);
63#endif
64
44extern struct sys_timer *system_timer; 65extern struct sys_timer *system_timer;
45extern void timer_tick(struct pt_regs *); 66extern void timer_tick(struct pt_regs *);
46 67
diff --git a/include/asm-arm/signal.h b/include/asm-arm/signal.h
index 46e69ae395af..760f6e65af05 100644
--- a/include/asm-arm/signal.h
+++ b/include/asm-arm/signal.h
@@ -114,6 +114,7 @@ typedef unsigned long sigset_t;
114#define SIGSTKSZ 8192 114#define SIGSTKSZ 8192
115 115
116#ifdef __KERNEL__ 116#ifdef __KERNEL__
117#define SA_TIMER 0x40000000
117#define SA_IRQNOMASK 0x08000000 118#define SA_IRQNOMASK 0x08000000
118#endif 119#endif
119 120