clocksource: Cleanup clocksource selection
[linux-2.6.git] / kernel / time / tick-oneshot.c
index 2e8b7ff863cc3396387e0e6b790074d5b53ad381..a96c0e2b89cf82b574de560971256dc7f2bf0931 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/cpu.h>
 #include <linux/err.h>
 #include <linux/hrtimer.h>
-#include <linux/irq.h>
+#include <linux/interrupt.h>
 #include <linux/percpu.h>
 #include <linux/profile.h>
 #include <linux/sched.h>
 #include "tick-internal.h"
 
 /**
- * tick_program_event
+ * tick_program_event internal worker function
  */
-int tick_program_event(ktime_t expires, int force)
+int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires,
+                          int force)
 {
-       struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
        ktime_t now = ktime_get();
+       int i;
 
-       while (1) {
+       for (i = 0;;) {
                int ret = clockevents_program_event(dev, expires, now);
 
                if (!ret || !force)
                        return ret;
+
+               /*
+                * We tried 2 times to program the device with the given
+                * min_delta_ns. If that's not working then we double it
+                * and emit a warning.
+                */
+               if (++i > 2) {
+                       /* Increase the min. delta and try again */
+                       if (!dev->min_delta_ns)
+                               dev->min_delta_ns = 5000;
+                       else
+                               dev->min_delta_ns += dev->min_delta_ns >> 1;
+
+                       printk(KERN_WARNING
+                              "CE: %s increasing min_delta_ns to %lu nsec\n",
+                              dev->name ? dev->name : "?",
+                              dev->min_delta_ns << 1);
+
+                       i = 0;
+               }
+
                now = ktime_get();
-               expires = ktime_add(now, ktime_set(0, dev->min_delta_ns));
+               expires = ktime_add_ns(now, dev->min_delta_ns);
        }
 }
 
+/**
+ * tick_program_event
+ */
+int tick_program_event(ktime_t expires, int force)
+{
+       struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev;
+
+       return tick_dev_program_event(dev, expires, force);
+}
+
+/**
+ * tick_resume_onshot - resume oneshot mode
+ */
+void tick_resume_oneshot(void)
+{
+       struct tick_device *td = &__get_cpu_var(tick_cpu_device);
+       struct clock_event_device *dev = td->evtdev;
+
+       clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
+       tick_program_event(ktime_get(), 1);
+}
+
 /**
  * tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz)
  */
@@ -49,7 +93,7 @@ void tick_setup_oneshot(struct clock_event_device *newdev,
 {
        newdev->event_handler = handler;
        clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT);
-       clockevents_program_event(newdev, next_event, ktime_get());
+       tick_dev_program_event(newdev, next_event, 1);
 }
 
 /**
@@ -61,8 +105,21 @@ int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
        struct clock_event_device *dev = td->evtdev;
 
        if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) ||
-           !tick_device_is_functional(dev))
+                   !tick_device_is_functional(dev)) {
+
+               printk(KERN_INFO "Clockevents: "
+                      "could not switch to one-shot mode:");
+               if (!dev) {
+                       printk(" no tick device\n");
+               } else {
+                       if (!tick_device_is_functional(dev))
+                               printk(" %s is not functional.\n", dev->name);
+                       else
+                               printk(" %s does not support one-shot mode.\n",
+                                      dev->name);
+               }
                return -EINVAL;
+       }
 
        td->mode = TICKDEV_MODE_ONESHOT;
        dev->event_handler = handler;
@@ -71,6 +128,23 @@ int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
        return 0;
 }
 
+/**
+ * tick_check_oneshot_mode - check whether the system is in oneshot mode
+ *
+ * returns 1 when either nohz or highres are enabled. otherwise 0.
+ */
+int tick_oneshot_mode_active(void)
+{
+       unsigned long flags;
+       int ret;
+
+       local_irq_save(flags);
+       ret = __get_cpu_var(tick_cpu_device).mode == TICKDEV_MODE_ONESHOT;
+       local_irq_restore(flags);
+
+       return ret;
+}
+
 #ifdef CONFIG_HIGH_RES_TIMERS
 /**
  * tick_init_highres - switch to high resolution mode