|
@@ -381,6 +381,95 @@ static struct sysdev_class timer_sysclass = {
|
|
|
.resume = timer_resume,
|
|
|
};
|
|
|
|
|
|
+#ifdef CONFIG_NO_IDLE_HZ
|
|
|
+static int timer_dyn_tick_enable(void)
|
|
|
+{
|
|
|
+ struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
|
|
|
+ unsigned long flags;
|
|
|
+ int ret = -ENODEV;
|
|
|
+
|
|
|
+ if (dyn_tick) {
|
|
|
+ write_seqlock_irqsave(&xtime_lock, flags);
|
|
|
+ ret = 0;
|
|
|
+ if (!(dyn_tick->state & DYN_TICK_ENABLED)) {
|
|
|
+ ret = dyn_tick->enable();
|
|
|
+
|
|
|
+ if (ret == 0)
|
|
|
+ dyn_tick->state |= DYN_TICK_ENABLED;
|
|
|
+ }
|
|
|
+ write_sequnlock_irqrestore(&xtime_lock, flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int timer_dyn_tick_disable(void)
|
|
|
+{
|
|
|
+ struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
|
|
|
+ unsigned long flags;
|
|
|
+ int ret = -ENODEV;
|
|
|
+
|
|
|
+ if (dyn_tick) {
|
|
|
+ write_seqlock_irqsave(&xtime_lock, flags);
|
|
|
+ ret = 0;
|
|
|
+ if (dyn_tick->state & DYN_TICK_ENABLED) {
|
|
|
+ ret = dyn_tick->disable();
|
|
|
+
|
|
|
+ if (ret == 0)
|
|
|
+ dyn_tick->state &= ~DYN_TICK_ENABLED;
|
|
|
+ }
|
|
|
+ write_sequnlock_irqrestore(&xtime_lock, flags);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+void timer_dyn_reprogram(void)
|
|
|
+{
|
|
|
+ struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
|
|
|
+ unsigned long flags;
|
|
|
+
|
|
|
+ write_seqlock_irqsave(&xtime_lock, flags);
|
|
|
+ if (dyn_tick->state & DYN_TICK_ENABLED)
|
|
|
+ dyn_tick->reprogram(next_timer_interrupt() - jiffies);
|
|
|
+ write_sequnlock_irqrestore(&xtime_lock, flags);
|
|
|
+}
|
|
|
+
|
|
|
+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)
|
|
|
+{
|
|
|
+ unsigned int enable = simple_strtoul(buf, NULL, 2);
|
|
|
+
|
|
|
+ if (enable)
|
|
|
+ timer_dyn_tick_enable();
|
|
|
+ else
|
|
|
+ timer_dyn_tick_disable();
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick);
|
|
|
+
|
|
|
+/*
|
|
|
+ * dyntick=enable|disable
|
|
|
+ */
|
|
|
+static char dyntick_str[4] __initdata = "";
|
|
|
+
|
|
|
+static int __init dyntick_setup(char *str)
|
|
|
+{
|
|
|
+ if (str)
|
|
|
+ strlcpy(dyntick_str, str, sizeof(dyntick_str));
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+__setup("dyntick=", dyntick_setup);
|
|
|
+#endif
|
|
|
+
|
|
|
static int __init timer_init_sysfs(void)
|
|
|
{
|
|
|
int ret = sysdev_class_register(&timer_sysclass);
|
|
@@ -388,6 +477,20 @@ static int __init timer_init_sysfs(void)
|
|
|
system_timer->dev.cls = &timer_sysclass;
|
|
|
ret = sysdev_register(&system_timer->dev);
|
|
|
}
|
|
|
+
|
|
|
+#ifdef CONFIG_NO_IDLE_HZ
|
|
|
+ if (ret == 0 && system_timer->dyn_tick) {
|
|
|
+ ret = sysdev_create_file(&system_timer->dev, &attr_dyn_tick);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Turn on dynamic tick after calibrate delay
|
|
|
+ * for correct bogomips
|
|
|
+ */
|
|
|
+ if (ret == 0 && dyntick_str[0] == 'e')
|
|
|
+ ret = timer_dyn_tick_enable();
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|