Root/
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 | /* Limit min_delta to a jiffie */ |
26 | #define MIN_DELTA_LIMIT (NSEC_PER_SEC / HZ) |
27 | |
28 | static int tick_increase_min_delta(struct clock_event_device *dev) |
29 | { |
30 | /* Nothing to do if we already reached the limit */ |
31 | if (dev->min_delta_ns >= MIN_DELTA_LIMIT) |
32 | return -ETIME; |
33 | |
34 | if (dev->min_delta_ns < 5000) |
35 | dev->min_delta_ns = 5000; |
36 | else |
37 | dev->min_delta_ns += dev->min_delta_ns >> 1; |
38 | |
39 | if (dev->min_delta_ns > MIN_DELTA_LIMIT) |
40 | dev->min_delta_ns = MIN_DELTA_LIMIT; |
41 | |
42 | printk(KERN_WARNING "CE: %s increased min_delta_ns to %llu nsec\n", |
43 | dev->name ? dev->name : "?", |
44 | (unsigned long long) dev->min_delta_ns); |
45 | return 0; |
46 | } |
47 | |
48 | /** |
49 | * tick_program_event internal worker function |
50 | */ |
51 | int tick_dev_program_event(struct clock_event_device *dev, ktime_t expires, |
52 | int force) |
53 | { |
54 | ktime_t now = ktime_get(); |
55 | int i; |
56 | |
57 | for (i = 0;;) { |
58 | int ret = clockevents_program_event(dev, expires, now); |
59 | |
60 | if (!ret || !force) |
61 | return ret; |
62 | |
63 | dev->retries++; |
64 | /* |
65 | * We tried 3 times to program the device with the given |
66 | * min_delta_ns. If that's not working then we increase it |
67 | * and emit a warning. |
68 | */ |
69 | if (++i > 2) { |
70 | /* Increase the min. delta and try again */ |
71 | if (tick_increase_min_delta(dev)) { |
72 | /* |
73 | * Get out of the loop if min_delta_ns |
74 | * hit the limit already. That's |
75 | * better than staying here forever. |
76 | * |
77 | * We clear next_event so we have a |
78 | * chance that the box survives. |
79 | */ |
80 | printk(KERN_WARNING |
81 | "CE: Reprogramming failure. Giving up\n"); |
82 | dev->next_event.tv64 = KTIME_MAX; |
83 | return -ETIME; |
84 | } |
85 | i = 0; |
86 | } |
87 | |
88 | now = ktime_get(); |
89 | expires = ktime_add_ns(now, dev->min_delta_ns); |
90 | } |
91 | } |
92 | |
93 | /** |
94 | * tick_program_event |
95 | */ |
96 | int tick_program_event(ktime_t expires, int force) |
97 | { |
98 | struct clock_event_device *dev = __get_cpu_var(tick_cpu_device).evtdev; |
99 | |
100 | return tick_dev_program_event(dev, expires, force); |
101 | } |
102 | |
103 | /** |
104 | * tick_resume_onshot - resume oneshot mode |
105 | */ |
106 | void tick_resume_oneshot(void) |
107 | { |
108 | struct tick_device *td = &__get_cpu_var(tick_cpu_device); |
109 | struct clock_event_device *dev = td->evtdev; |
110 | |
111 | clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); |
112 | tick_program_event(ktime_get(), 1); |
113 | } |
114 | |
115 | /** |
116 | * tick_setup_oneshot - setup the event device for oneshot mode (hres or nohz) |
117 | */ |
118 | void tick_setup_oneshot(struct clock_event_device *newdev, |
119 | void (*handler)(struct clock_event_device *), |
120 | ktime_t next_event) |
121 | { |
122 | newdev->event_handler = handler; |
123 | clockevents_set_mode(newdev, CLOCK_EVT_MODE_ONESHOT); |
124 | tick_dev_program_event(newdev, next_event, 1); |
125 | } |
126 | |
127 | /** |
128 | * tick_switch_to_oneshot - switch to oneshot mode |
129 | */ |
130 | int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *)) |
131 | { |
132 | struct tick_device *td = &__get_cpu_var(tick_cpu_device); |
133 | struct clock_event_device *dev = td->evtdev; |
134 | |
135 | if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) || |
136 | !tick_device_is_functional(dev)) { |
137 | |
138 | printk(KERN_INFO "Clockevents: " |
139 | "could not switch to one-shot mode:"); |
140 | if (!dev) { |
141 | printk(" no tick device\n"); |
142 | } else { |
143 | if (!tick_device_is_functional(dev)) |
144 | printk(" %s is not functional.\n", dev->name); |
145 | else |
146 | printk(" %s does not support one-shot mode.\n", |
147 | dev->name); |
148 | } |
149 | return -EINVAL; |
150 | } |
151 | |
152 | td->mode = TICKDEV_MODE_ONESHOT; |
153 | dev->event_handler = handler; |
154 | clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT); |
155 | tick_broadcast_switch_to_oneshot(); |
156 | return 0; |
157 | } |
158 | |
159 | /** |
160 | * tick_check_oneshot_mode - check whether the system is in oneshot mode |
161 | * |
162 | * returns 1 when either nohz or highres are enabled. otherwise 0. |
163 | */ |
164 | int tick_oneshot_mode_active(void) |
165 | { |
166 | unsigned long flags; |
167 | int ret; |
168 | |
169 | local_irq_save(flags); |
170 | ret = __get_cpu_var(tick_cpu_device).mode == TICKDEV_MODE_ONESHOT; |
171 | local_irq_restore(flags); |
172 | |
173 | return ret; |
174 | } |
175 | |
176 | #ifdef CONFIG_HIGH_RES_TIMERS |
177 | /** |
178 | * tick_init_highres - switch to high resolution mode |
179 | * |
180 | * Called with interrupts disabled. |
181 | */ |
182 | int tick_init_highres(void) |
183 | { |
184 | return tick_switch_to_oneshot(hrtimer_interrupt); |
185 | } |
186 | #endif |
187 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9