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 @@
* 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
......@@ -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);
......
......@@ -28,6 +28,7 @@
#include <linux/profile.h>
#include <linux/sysdev.h>
#include <linux/timer.h>
#include <linux/pm.h>
#include <asm/hardware.h>
#include <asm/io.h>
......@@ -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;
}
......
......@@ -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 *);
......
......@@ -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
......
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