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