*  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 *);