Commit 6f09bc26 authored by Tony Lindgren's avatar Tony Lindgren

ARM: Add support for dynamic tick timer

Adds ARM generic support for dynamic tick timer.
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 6ccf5bd0
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
* Copyright (C) 1992 Linus Torvalds * Copyright (C) 1992 Linus Torvalds
* Modifications for ARM processor Copyright (C) 1995-2000 Russell King. * 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 * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
...@@ -37,6 +41,7 @@ ...@@ -37,6 +41,7 @@
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/mach/irq.h> #include <asm/mach/irq.h>
#include <asm/mach/time.h>
/* /*
* Maximum IRQ count. Currently, this is arbitary. However, it should * 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) ...@@ -332,6 +337,12 @@ __do_irq(unsigned int irq, struct irqaction *action, struct pt_regs *regs)
if (!(action->flags & SA_INTERRUPT)) if (!(action->flags & SA_INTERRUPT))
local_irq_enable(); 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; status = 0;
do { do {
ret = action->handler(irq, action->dev_id, regs); ret = action->handler(irq, action->dev_id, regs);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/profile.h> #include <linux/profile.h>
#include <linux/sysdev.h> #include <linux/sysdev.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/pm.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -350,6 +351,49 @@ void timer_tick(struct pt_regs *regs) ...@@ -350,6 +351,49 @@ void timer_tick(struct pt_regs *regs)
#endif #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 #ifdef CONFIG_PM
static int timer_suspend(struct sys_device *dev, pm_message_t state) static int timer_suspend(struct sys_device *dev, pm_message_t state)
{ {
...@@ -381,6 +425,29 @@ static struct sysdev_class timer_sysclass = { ...@@ -381,6 +425,29 @@ static struct sysdev_class timer_sysclass = {
.resume = timer_resume, .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) static int __init timer_init_sysfs(void)
{ {
int ret = sysdev_class_register(&timer_sysclass); int ret = sysdev_class_register(&timer_sysclass);
...@@ -388,6 +455,15 @@ static int __init timer_init_sysfs(void) ...@@ -388,6 +455,15 @@ static int __init timer_init_sysfs(void)
system_timer->dev.cls = &timer_sysclass; system_timer->dev.cls = &timer_sysclass;
ret = sysdev_register(&system_timer->dev); 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; return ret;
} }
......
...@@ -39,7 +39,27 @@ struct sys_timer { ...@@ -39,7 +39,27 @@ struct sys_timer {
void (*suspend)(void); void (*suspend)(void);
void (*resume)(void); void (*resume)(void);
unsigned long (*offset)(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 struct sys_timer *system_timer;
extern void timer_tick(struct pt_regs *); extern void timer_tick(struct pt_regs *);
......
...@@ -14,8 +14,10 @@ ...@@ -14,8 +14,10 @@
* *
* SA_INTERRUPT is also used by the irq handling routines. * SA_INTERRUPT is also used by the irq handling routines.
* SA_SHIRQ is for shared interrupt support on PCI and EISA. * 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_PROBE SA_ONESHOT
#define SA_TIMER SA_NOMASK
#define SA_SAMPLE_RANDOM SA_RESTART #define SA_SAMPLE_RANDOM SA_RESTART
#define SA_SHIRQ 0x04000000 #define SA_SHIRQ 0x04000000
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment