| Thomas Gleixner | 79bf2bb | 2007-02-16 01:28:03 -0800 | [diff] [blame] | 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/irq.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 | 
|  | 27 | */ | 
|  | 28 | int tick_program_event(ktime_t expires, int force) | 
|  | 29 | { | 
|  | 30 | struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev; | 
|  | 31 | ktime_t now = ktime_get(); | 
|  | 32 |  | 
|  | 33 | while (1) { | 
|  | 34 | int ret = clockevents_program_event(dev, expires, now); | 
|  | 35 |  | 
|  | 36 | if (!ret || !force) | 
|  | 37 | return ret; | 
|  | 38 | now = ktime_get(); | 
|  | 39 | expires = ktime_add(now, ktime_set(0, dev->min_delta_ns)); | 
|  | 40 | } | 
|  | 41 | } | 
|  | 42 |  | 
|  | 43 | /** | 
| Thomas Gleixner | cd05a1f | 2007-03-17 00:25:52 +0100 | [diff] [blame] | 44 | * tick_resume_onshot - resume oneshot mode | 
|  | 45 | */ | 
|  | 46 | void tick_resume_oneshot(void) | 
|  | 47 | { | 
|  | 48 | struct tick_device *td = &__get_cpu_var(tick_cpu_device); | 
|  | 49 | struct clock_event_device *dev = td->evtdev; | 
|  | 50 |  | 
|  | 51 | clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); | 
|  | 52 | tick_program_event(ktime_get(), 1); | 
|  | 53 | } | 
|  | 54 |  | 
|  | 55 | /** | 
| Thomas Gleixner | 79bf2bb | 2007-02-16 01:28:03 -0800 | [diff] [blame] | 56 | * tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz) | 
|  | 57 | */ | 
|  | 58 | void tick_setup_oneshot(struct clock_event_device *newdev, | 
|  | 59 | void (*handler)(struct clock_event_device *), | 
|  | 60 | ktime_t next_event) | 
|  | 61 | { | 
|  | 62 | newdev->event_handler = handler; | 
|  | 63 | clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT); | 
|  | 64 | clockevents_program_event(newdev, next_event, ktime_get()); | 
|  | 65 | } | 
|  | 66 |  | 
|  | 67 | /** | 
|  | 68 | * tick_switch_to_oneshot - switch to oneshot mode | 
|  | 69 | */ | 
|  | 70 | int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)) | 
|  | 71 | { | 
|  | 72 | struct tick_device *td = &__get_cpu_var(tick_cpu_device); | 
|  | 73 | struct clock_event_device *dev = td->evtdev; | 
|  | 74 |  | 
|  | 75 | if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) || | 
| Ingo Molnar | 820de5c | 2007-07-21 04:37:36 -0700 | [diff] [blame] | 76 | !tick_device_is_functional(dev)) { | 
|  | 77 |  | 
|  | 78 | printk(KERN_INFO "Clockevents: " | 
|  | 79 | "could not switch to one-shot mode:"); | 
|  | 80 | if (!dev) { | 
|  | 81 | printk(" no tick device\n"); | 
|  | 82 | } else { | 
|  | 83 | if (!tick_device_is_functional(dev)) | 
|  | 84 | printk(" %s is not functional.\n", dev->name); | 
|  | 85 | else | 
|  | 86 | printk(" %s does not support one-shot mode.\n", | 
|  | 87 | dev->name); | 
|  | 88 | } | 
| Thomas Gleixner | 79bf2bb | 2007-02-16 01:28:03 -0800 | [diff] [blame] | 89 | return -EINVAL; | 
| Ingo Molnar | 820de5c | 2007-07-21 04:37:36 -0700 | [diff] [blame] | 90 | } | 
| Thomas Gleixner | 79bf2bb | 2007-02-16 01:28:03 -0800 | [diff] [blame] | 91 |  | 
|  | 92 | td->mode = TICKDEV_MODE_ONESHOT; | 
|  | 93 | dev->event_handler = handler; | 
|  | 94 | clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); | 
|  | 95 | tick_broadcast_switch_to_oneshot(); | 
|  | 96 | return 0; | 
|  | 97 | } | 
|  | 98 |  | 
|  | 99 | #ifdef CONFIG_HIGH_RES_TIMERS | 
|  | 100 | /** | 
|  | 101 | * tick_init_highres - switch to high resolution mode | 
|  | 102 | * | 
|  | 103 | * Called with interrupts disabled. | 
|  | 104 | */ | 
|  | 105 | int tick_init_highres(void) | 
|  | 106 | { | 
|  | 107 | return tick_switch_to_oneshot(hrtimer_interrupt); | 
|  | 108 | } | 
|  | 109 | #endif |