From 6f09bc26fa5196a33de4bc7b5309549b4aae346a Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 9 May 2005 13:05:34 -0700 Subject: [PATCH] ARM: Add support for dynamic tick timer Adds ARM generic support for dynamic tick timer. Signed-off-by: Tony Lindgren --- arch/arm/kernel/irq.c | 11 ++++++ arch/arm/kernel/time.c | 76 +++++++++++++++++++++++++++++++++++++ include/asm-arm/mach/time.h | 20 ++++++++++ include/linux/signal.h | 2 + 4 files changed, 109 insertions(+) diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index ff187f4308f..32a144e361e 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 and + * Tuukka Tikkanen . + * * 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 #include #include +#include /* * Maximum IRQ count. Currently, this is arbitary. However, it should @@ -332,6 +337,12 @@ __do_irq(unsigned int irq, struct irqaction *action, struct pt_regs *regs) if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); +#ifdef CONFIG_NO_IDLE_HZ + if ((!(action->flags & SA_TIMER)) && system_timer->dyn_tick->handler && + (system_timer->dyn_tick->state & DYN_TICK_ENABLED)) + system_timer->dyn_tick->handler(irq, 0, regs); +#endif + status = 0; do { ret = action->handler(irq, action->dev_id, regs); diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index c232f24f4a6..d0d4fed333e 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -350,6 +351,49 @@ void timer_tick(struct pt_regs *regs) #endif } +#ifdef CONFIG_NO_IDLE_HZ +int timer_dyn_tick_enable(void) +{ + unsigned long flags; + int ret = -ENODEV; + + write_seqlock_irqsave(&xtime_lock, flags); + if (!system_timer->dyn_tick || !system_timer->dyn_tick->enable) + goto out_err; + + ret = system_timer->dyn_tick->enable(); + if (ret != 0) + goto out_err; + + if (system_timer->dyn_tick->handler) + system_timer->dyn_tick->state |= DYN_TICK_ENABLED; + + write_sequnlock_irqrestore(&xtime_lock, flags); + + return ret; + +out_err: + system_timer->dyn_tick->state &= ~DYN_TICK_ENABLED; + write_sequnlock_irqrestore(&xtime_lock, flags); + return ret; +} + +int timer_dyn_tick_disable(void) +{ + unsigned long flags; + int ret = 0; + + write_seqlock_irqsave(&xtime_lock, flags); + if (system_timer->dyn_tick && system_timer->dyn_tick->disable) + ret = system_timer->dyn_tick->disable(); + + system_timer->dyn_tick->state &= ~DYN_TICK_ENABLED; + write_sequnlock_irqrestore(&xtime_lock, flags); + + return ret; +} +#endif + #ifdef CONFIG_PM static int timer_suspend(struct sys_device *dev, pm_message_t state) { @@ -381,6 +425,29 @@ static struct sysdev_class timer_sysclass = { .resume = timer_resume, }; +#ifdef CONFIG_NO_IDLE_HZ +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) +{ + int ret = 0; + unsigned int enable = simple_strtoul(buf, NULL, 2); + + if (enable) + ret = timer_dyn_tick_enable(); + else + ret = timer_dyn_tick_disable(); + + return count; +} +static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick); +#endif + static int __init timer_init_sysfs(void) { int ret = sysdev_class_register(&timer_sysclass); @@ -388,6 +455,15 @@ static int __init timer_init_sysfs(void) system_timer->dev.cls = &timer_sysclass; ret = sysdev_register(&system_timer->dev); } + +#ifdef CONFIG_NO_IDLE_HZ + ret = sysdev_create_file(&system_timer->dev, &attr_dyn_tick); +#if defined(CONFIG_NO_IDLE_HZ_ENABLED) + /* Turn on dynamic tick after calibrate delay for correct bogomips */ + ret = timer_dyn_tick_enable(); +#endif +#endif + return ret; } diff --git a/include/asm-arm/mach/time.h b/include/asm-arm/mach/time.h index 5cf4fd659fd..c46914855bc 100644 --- a/include/asm-arm/mach/time.h +++ b/include/asm-arm/mach/time.h @@ -39,7 +39,27 @@ struct sys_timer { void (*suspend)(void); void (*resume)(void); unsigned long (*offset)(void); + +#ifdef CONFIG_NO_IDLE_HZ + struct dyn_tick_timer *dyn_tick; +#endif + +}; + +#ifdef CONFIG_NO_IDLE_HZ + +#define DYN_TICK_SKIPPING (1 << 2) +#define DYN_TICK_ENABLED (1 << 1) +#define DYN_TICK_SUITABLE (1 << 0) + +struct dyn_tick_timer { + unsigned int state; /* Current state */ + int (*enable)(void); /* Enables dynamic tick */ + int (*disable)(void); /* Disables dynamic tick */ + void (*reprogram)(void); /* Reprograms the timer */ + int (*handler)(int, void *, struct pt_regs *); }; +#endif extern struct sys_timer *system_timer; extern void timer_tick(struct pt_regs *); diff --git a/include/linux/signal.h b/include/linux/signal.h index 0a98f5ec5ca..4c877abdc3f 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -14,8 +14,10 @@ * * SA_INTERRUPT is also used by the irq handling routines. * SA_SHIRQ is for shared interrupt support on PCI and EISA. + * SA_TIMER is used by dynamic tick timer. */ #define SA_PROBE SA_ONESHOT +#define SA_TIMER SA_NOMASK #define SA_SAMPLE_RANDOM SA_RESTART #define SA_SHIRQ 0x04000000 -- 2.41.1