1 /* 2 * linux/kernel/time/tick-oneshot.c 3 * 4 * This file contains functions which manage high resolution tick 5 * related events. 6 * 7 * Copyright(C) 2005-2006, Thomas Gleixner <tglx@linutronix.de> 8 * Copyright(C) 2005-2007, Red Hat, Inc., Ingo Molnar 9 * Copyright(C) 2006-2007, Timesys Corp., Thomas Gleixner 10 * 11 * This code is licenced under the GPL version 2. For details see 12 * kernel-base/COPYING. 13 */ 14 #include <linux/cpu.h> 15 #include <linux/err.h> 16 #include <linux/hrtimer.h> 17 #include <linux/interrupt.h> 18 #include <linux/percpu.h> 19 #include <linux/profile.h> 20 #include <linux/sched.h> 21 #include <linux/tick.h> 22 23 #include "tick-internal.h" 24 25 /** 26 * tick_program_event internal worker function 27 */ 28 int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires, 29 int force) 30 { 31 ktime_t now = ktime_get(); 32 int i; 33 34 for (i = 0;;) { 35 int ret = clockevents_program_event(dev, expires, now); 36 37 if (!ret || !force) 38 return ret; 39 40 /* 41 * We tried 2 times to program the device with the given 42 * min_delta_ns. If that's not working then we double it 43 * and emit a warning. 44 */ 45 if (++i > 2) { 46 printk(KERN_WARNING "CE: __tick_program_event of %s is " 47 "stuck %llx %llx\n", dev->name ? dev->name : "?", 48 now.tv64, expires.tv64); 49 printk(KERN_WARNING 50 "CE: increasing min_delta_ns %ld to %ld nsec\n", 51 dev->min_delta_ns, dev->min_delta_ns << 1); 52 WARN_ON(1); 53 54 /* Double the min. delta and try again */ 55 if (!dev->min_delta_ns) 56 dev->min_delta_ns = 5000; 57 else 58 dev->min_delta_ns <<= 1; 59 i = 0; 60 } 61 62 now = ktime_get(); 63 expires = ktime_add_ns(now, dev->min_delta_ns); 64 } 65 } 66 67 /** 68 * tick_program_event 69 */ 70 int tick_program_event(ktime_t expires, int force) 71 { 72 struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev; 73 74 return tick_dev_program_event(dev, expires, force); 75 } 76 77 /** 78 * tick_resume_onshot - resume oneshot mode 79 */ 80 void tick_resume_oneshot(void) 81 { 82 struct tick_device *td = &__get_cpu_var(tick_cpu_device); 83 struct clock_event_device *dev = td->evtdev; 84 85 clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); 86 tick_program_event(ktime_get(), 1); 87 } 88 89 /** 90 * tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz) 91 */ 92 void tick_setup_oneshot(struct clock_event_device *newdev, 93 void (*handler)(struct clock_event_device *), 94 ktime_t next_event) 95 { 96 newdev->event_handler = handler; 97 clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT); 98 tick_dev_program_event(newdev, next_event, 1); 99 } 100 101 /** 102 * tick_switch_to_oneshot - switch to oneshot mode 103 */ 104 int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)) 105 { 106 struct tick_device *td = &__get_cpu_var(tick_cpu_device); 107 struct clock_event_device *dev = td->evtdev; 108 109 if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) || 110 !tick_device_is_functional(dev)) { 111 112 printk(KERN_INFO "Clockevents: " 113 "could not switch to one-shot mode:"); 114 if (!dev) { 115 printk(" no tick device\n"); 116 } else { 117 if (!tick_device_is_functional(dev)) 118 printk(" %s is not functional.\n", dev->name); 119 else 120 printk(" %s does not support one-shot mode.\n", 121 dev->name); 122 } 123 return -EINVAL; 124 } 125 126 td->mode = TICKDEV_MODE_ONESHOT; 127 dev->event_handler = handler; 128 clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); 129 tick_broadcast_switch_to_oneshot(); 130 return 0; 131 } 132 133 #ifdef CONFIG_HIGH_RES_TIMERS 134 /** 135 * tick_init_highres - switch to high resolution mode 136 * 137 * Called with interrupts disabled. 138 */ 139 int tick_init_highres(void) 140 { 141 return tick_switch_to_oneshot(hrtimer_interrupt); 142 } 143 #endif 144