diff options
author | John Stultz <john.stultz@linaro.org> | 2011-01-11 12:42:13 -0500 |
---|---|---|
committer | John Stultz <john.stultz@linaro.org> | 2011-04-26 17:01:44 -0400 |
commit | ff3ead96d17f47ee70c294a5cc2cce9b61e82f0f (patch) | |
tree | e87fc16e1fc4a80b9591c2b1d02c99459254aa95 /kernel/time | |
parent | 88d19cf37952a7e1e38b2bf87a00f0e857e63180 (diff) |
timers: Introduce in-kernel alarm-timer interface
This provides the in kernel interface and infrastructure for
alarm-timers.
Alarm-timers are a hybrid style timer, similar to hrtimers,
but when the system is suspended, the RTC device is set to
fire and wake the system for when the soonest alarm-timer
expires.
The concept for Alarm-timers was inspired by the Android Alarm
driver (by Arve Hjønnevåg) found in the Android kernel tree.
See: http://android.git.kernel.org/?p=kernel/common.git;a=blob;f=drivers/rtc/alarm.c;h=1250edfbdf3302f5e4ea6194847c6ef4bb7beb1c;hb=android-2.6.36
This in-kernel interface should be fairly compatible with the
Android alarm driver in-kernel interface, but has the advantage
of utilizing the new RTC timerqueue code instead of doing direct
RTC manipulation.
CC: Arve Hjønnevåg <arve@android.com>
CC: Thomas Gleixner <tglx@linutronix.de>
CC: Alessandro Zummo <a.zummo@towertech.it>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Diffstat (limited to 'kernel/time')
-rw-r--r-- | kernel/time/Makefile | 2 | ||||
-rw-r--r-- | kernel/time/alarmtimer.c | 375 |
2 files changed, 376 insertions, 1 deletions
diff --git a/kernel/time/Makefile b/kernel/time/Makefile index b0425991e9ac..e2fd74b8e8c2 100644 --- a/kernel/time/Makefile +++ b/kernel/time/Makefile | |||
@@ -1,5 +1,5 @@ | |||
1 | obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o | 1 | obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o timecompare.o |
2 | obj-y += timeconv.o posix-clock.o | 2 | obj-y += timeconv.o posix-clock.o alarmtimer.o |
3 | 3 | ||
4 | obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o | 4 | obj-$(CONFIG_GENERIC_CLOCKEVENTS_BUILD) += clockevents.o |
5 | obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o | 5 | obj-$(CONFIG_GENERIC_CLOCKEVENTS) += tick-common.o |
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c new file mode 100644 index 000000000000..48c2ee949e61 --- /dev/null +++ b/kernel/time/alarmtimer.c | |||
@@ -0,0 +1,375 @@ | |||
1 | /* | ||
2 | * Alarmtimer interface | ||
3 | * | ||
4 | * This interface provides a timer which is similarto hrtimers, | ||
5 | * but triggers a RTC alarm if the box is suspend. | ||
6 | * | ||
7 | * This interface is influenced by the Android RTC Alarm timer | ||
8 | * interface. | ||
9 | * | ||
10 | * Copyright (C) 2010 IBM Corperation | ||
11 | * | ||
12 | * Author: John Stultz <john.stultz@linaro.org> | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License version 2 as | ||
16 | * published by the Free Software Foundation. | ||
17 | */ | ||
18 | #include <linux/time.h> | ||
19 | #include <linux/hrtimer.h> | ||
20 | #include <linux/timerqueue.h> | ||
21 | #include <linux/rtc.h> | ||
22 | #include <linux/alarmtimer.h> | ||
23 | #include <linux/mutex.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/posix-timers.h> | ||
26 | #include <linux/workqueue.h> | ||
27 | #include <linux/freezer.h> | ||
28 | |||
29 | |||
30 | static struct alarm_base { | ||
31 | spinlock_t lock; | ||
32 | struct timerqueue_head timerqueue; | ||
33 | struct hrtimer timer; | ||
34 | ktime_t (*gettime)(void); | ||
35 | clockid_t base_clockid; | ||
36 | struct work_struct irqwork; | ||
37 | } alarm_bases[ALARM_NUMTYPE]; | ||
38 | |||
39 | static struct rtc_timer rtctimer; | ||
40 | static struct rtc_device *rtcdev; | ||
41 | |||
42 | static ktime_t freezer_delta; | ||
43 | static DEFINE_SPINLOCK(freezer_delta_lock); | ||
44 | |||
45 | |||
46 | /************************************************************************** | ||
47 | * alarmtimer management code | ||
48 | */ | ||
49 | |||
50 | /* | ||
51 | * alarmtimer_enqueue - Adds an alarm timer to an alarm_base timerqueue | ||
52 | * @base: pointer to the base where the timer is being run | ||
53 | * @alarm: pointer to alarm being enqueued. | ||
54 | * | ||
55 | * Adds alarm to a alarm_base timerqueue and if necessary sets | ||
56 | * an hrtimer to run. | ||
57 | * | ||
58 | * Must hold base->lock when calling. | ||
59 | */ | ||
60 | static void alarmtimer_enqueue(struct alarm_base *base, struct alarm *alarm) | ||
61 | { | ||
62 | timerqueue_add(&base->timerqueue, &alarm->node); | ||
63 | if (&alarm->node == timerqueue_getnext(&base->timerqueue)) { | ||
64 | hrtimer_try_to_cancel(&base->timer); | ||
65 | hrtimer_start(&base->timer, alarm->node.expires, | ||
66 | HRTIMER_MODE_ABS); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * alarmtimer_remove - Removes an alarm timer from an alarm_base timerqueue | ||
72 | * @base: pointer to the base where the timer is running | ||
73 | * @alarm: pointer to alarm being removed | ||
74 | * | ||
75 | * Removes alarm to a alarm_base timerqueue and if necessary sets | ||
76 | * a new timer to run. | ||
77 | * | ||
78 | * Must hold base->lock when calling. | ||
79 | */ | ||
80 | static void alarmtimer_remove(struct alarm_base *base, struct alarm *alarm) | ||
81 | { | ||
82 | struct timerqueue_node *next = timerqueue_getnext(&base->timerqueue); | ||
83 | |||
84 | timerqueue_del(&base->timerqueue, &alarm->node); | ||
85 | if (next == &alarm->node) { | ||
86 | hrtimer_try_to_cancel(&base->timer); | ||
87 | next = timerqueue_getnext(&base->timerqueue); | ||
88 | if (!next) | ||
89 | return; | ||
90 | hrtimer_start(&base->timer, next->expires, HRTIMER_MODE_ABS); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * alarmtimer_do_work - Handles alarm being fired. | ||
96 | * @work: pointer to workqueue being run | ||
97 | * | ||
98 | * When a timer fires, this runs through the timerqueue to see | ||
99 | * which alarm timers, and run those that expired. If there are | ||
100 | * more alarm timers queued, we set the hrtimer to fire in the | ||
101 | * future. | ||
102 | */ | ||
103 | void alarmtimer_do_work(struct work_struct *work) | ||
104 | { | ||
105 | struct alarm_base *base = container_of(work, struct alarm_base, | ||
106 | irqwork); | ||
107 | struct timerqueue_node *next; | ||
108 | unsigned long flags; | ||
109 | ktime_t now; | ||
110 | |||
111 | spin_lock_irqsave(&base->lock, flags); | ||
112 | now = base->gettime(); | ||
113 | while ((next = timerqueue_getnext(&base->timerqueue))) { | ||
114 | struct alarm *alarm; | ||
115 | ktime_t expired = next->expires; | ||
116 | |||
117 | if (expired.tv64 >= now.tv64) | ||
118 | break; | ||
119 | |||
120 | alarm = container_of(next, struct alarm, node); | ||
121 | |||
122 | timerqueue_del(&base->timerqueue, &alarm->node); | ||
123 | alarm->enabled = 0; | ||
124 | /* Re-add periodic timers */ | ||
125 | if (alarm->period.tv64) { | ||
126 | alarm->node.expires = ktime_add(expired, alarm->period); | ||
127 | timerqueue_add(&base->timerqueue, &alarm->node); | ||
128 | alarm->enabled = 1; | ||
129 | } | ||
130 | spin_unlock_irqrestore(&base->lock, flags); | ||
131 | if (alarm->function) | ||
132 | alarm->function(alarm); | ||
133 | spin_lock_irqsave(&base->lock, flags); | ||
134 | } | ||
135 | |||
136 | if (next) { | ||
137 | hrtimer_start(&base->timer, next->expires, | ||
138 | HRTIMER_MODE_ABS); | ||
139 | } | ||
140 | spin_unlock_irqrestore(&base->lock, flags); | ||
141 | } | ||
142 | |||
143 | |||
144 | /* | ||
145 | * alarmtimer_fired - Handles alarm hrtimer being fired. | ||
146 | * @timer: pointer to hrtimer being run | ||
147 | * | ||
148 | * When a timer fires, this schedules the do_work function to | ||
149 | * be run. | ||
150 | */ | ||
151 | static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer) | ||
152 | { | ||
153 | struct alarm_base *base = container_of(timer, struct alarm_base, timer); | ||
154 | schedule_work(&base->irqwork); | ||
155 | return HRTIMER_NORESTART; | ||
156 | } | ||
157 | |||
158 | |||
159 | /* | ||
160 | * alarmtimer_suspend - Suspend time callback | ||
161 | * @dev: unused | ||
162 | * @state: unused | ||
163 | * | ||
164 | * When we are going into suspend, we look through the bases | ||
165 | * to see which is the soonest timer to expire. We then | ||
166 | * set an rtc timer to fire that far into the future, which | ||
167 | * will wake us from suspend. | ||
168 | */ | ||
169 | static int alarmtimer_suspend(struct device *dev) | ||
170 | { | ||
171 | struct rtc_time tm; | ||
172 | ktime_t min, now; | ||
173 | unsigned long flags; | ||
174 | int i; | ||
175 | |||
176 | spin_lock_irqsave(&freezer_delta_lock, flags); | ||
177 | min = freezer_delta; | ||
178 | freezer_delta = ktime_set(0, 0); | ||
179 | spin_unlock_irqrestore(&freezer_delta_lock, flags); | ||
180 | |||
181 | /* If we have no rtcdev, just return */ | ||
182 | if (!rtcdev) | ||
183 | return 0; | ||
184 | |||
185 | /* Find the soonest timer to expire*/ | ||
186 | for (i = 0; i < ALARM_NUMTYPE; i++) { | ||
187 | struct alarm_base *base = &alarm_bases[i]; | ||
188 | struct timerqueue_node *next; | ||
189 | ktime_t delta; | ||
190 | |||
191 | spin_lock_irqsave(&base->lock, flags); | ||
192 | next = timerqueue_getnext(&base->timerqueue); | ||
193 | spin_unlock_irqrestore(&base->lock, flags); | ||
194 | if (!next) | ||
195 | continue; | ||
196 | delta = ktime_sub(next->expires, base->gettime()); | ||
197 | if (!min.tv64 || (delta.tv64 < min.tv64)) | ||
198 | min = delta; | ||
199 | } | ||
200 | if (min.tv64 == 0) | ||
201 | return 0; | ||
202 | |||
203 | /* XXX - Should we enforce a minimum sleep time? */ | ||
204 | WARN_ON(min.tv64 < NSEC_PER_SEC); | ||
205 | |||
206 | /* Setup an rtc timer to fire that far in the future */ | ||
207 | rtc_timer_cancel(rtcdev, &rtctimer); | ||
208 | rtc_read_time(rtcdev, &tm); | ||
209 | now = rtc_tm_to_ktime(tm); | ||
210 | now = ktime_add(now, min); | ||
211 | |||
212 | rtc_timer_start(rtcdev, &rtctimer, now, ktime_set(0, 0)); | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | |||
218 | /************************************************************************** | ||
219 | * alarm kernel interface code | ||
220 | */ | ||
221 | |||
222 | /* | ||
223 | * alarm_init - Initialize an alarm structure | ||
224 | * @alarm: ptr to alarm to be initialized | ||
225 | * @type: the type of the alarm | ||
226 | * @function: callback that is run when the alarm fires | ||
227 | * | ||
228 | * In-kernel interface to initializes the alarm structure. | ||
229 | */ | ||
230 | void alarm_init(struct alarm *alarm, enum alarmtimer_type type, | ||
231 | void (*function)(struct alarm *)) | ||
232 | { | ||
233 | timerqueue_init(&alarm->node); | ||
234 | alarm->period = ktime_set(0, 0); | ||
235 | alarm->function = function; | ||
236 | alarm->type = type; | ||
237 | alarm->enabled = 0; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * alarm_start - Sets an alarm to fire | ||
242 | * @alarm: ptr to alarm to set | ||
243 | * @start: time to run the alarm | ||
244 | * @period: period at which the alarm will recur | ||
245 | * | ||
246 | * In-kernel interface set an alarm timer. | ||
247 | */ | ||
248 | void alarm_start(struct alarm *alarm, ktime_t start, ktime_t period) | ||
249 | { | ||
250 | struct alarm_base *base = &alarm_bases[alarm->type]; | ||
251 | unsigned long flags; | ||
252 | |||
253 | spin_lock_irqsave(&base->lock, flags); | ||
254 | if (alarm->enabled) | ||
255 | alarmtimer_remove(base, alarm); | ||
256 | alarm->node.expires = start; | ||
257 | alarm->period = period; | ||
258 | alarmtimer_enqueue(base, alarm); | ||
259 | alarm->enabled = 1; | ||
260 | spin_unlock_irqrestore(&base->lock, flags); | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * alarm_cancel - Tries to cancel an alarm timer | ||
265 | * @alarm: ptr to alarm to be canceled | ||
266 | * | ||
267 | * In-kernel interface to cancel an alarm timer. | ||
268 | */ | ||
269 | void alarm_cancel(struct alarm *alarm) | ||
270 | { | ||
271 | struct alarm_base *base = &alarm_bases[alarm->type]; | ||
272 | unsigned long flags; | ||
273 | |||
274 | spin_lock_irqsave(&base->lock, flags); | ||
275 | if (alarm->enabled) | ||
276 | alarmtimer_remove(base, alarm); | ||
277 | alarm->enabled = 0; | ||
278 | spin_unlock_irqrestore(&base->lock, flags); | ||
279 | } | ||
280 | |||
281 | |||
282 | |||
283 | /************************************************************************** | ||
284 | * alarmtimer initialization code | ||
285 | */ | ||
286 | |||
287 | /* Suspend hook structures */ | ||
288 | static const struct dev_pm_ops alarmtimer_pm_ops = { | ||
289 | .suspend = alarmtimer_suspend, | ||
290 | }; | ||
291 | |||
292 | static struct platform_driver alarmtimer_driver = { | ||
293 | .driver = { | ||
294 | .name = "alarmtimer", | ||
295 | .pm = &alarmtimer_pm_ops, | ||
296 | } | ||
297 | }; | ||
298 | |||
299 | /** | ||
300 | * alarmtimer_init - Initialize alarm timer code | ||
301 | * | ||
302 | * This function initializes the alarm bases and registers | ||
303 | * the posix clock ids. | ||
304 | */ | ||
305 | static int __init alarmtimer_init(void) | ||
306 | { | ||
307 | int error = 0; | ||
308 | int i; | ||
309 | |||
310 | /* Initialize alarm bases */ | ||
311 | alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME; | ||
312 | alarm_bases[ALARM_REALTIME].gettime = &ktime_get_real; | ||
313 | alarm_bases[ALARM_BOOTTIME].base_clockid = CLOCK_BOOTTIME; | ||
314 | alarm_bases[ALARM_BOOTTIME].gettime = &ktime_get_boottime; | ||
315 | for (i = 0; i < ALARM_NUMTYPE; i++) { | ||
316 | timerqueue_init_head(&alarm_bases[i].timerqueue); | ||
317 | spin_lock_init(&alarm_bases[i].lock); | ||
318 | hrtimer_init(&alarm_bases[i].timer, | ||
319 | alarm_bases[i].base_clockid, | ||
320 | HRTIMER_MODE_ABS); | ||
321 | alarm_bases[i].timer.function = alarmtimer_fired; | ||
322 | INIT_WORK(&alarm_bases[i].irqwork, alarmtimer_do_work); | ||
323 | } | ||
324 | error = platform_driver_register(&alarmtimer_driver); | ||
325 | platform_device_register_simple("alarmtimer", -1, NULL, 0); | ||
326 | |||
327 | return error; | ||
328 | } | ||
329 | device_initcall(alarmtimer_init); | ||
330 | |||
331 | /** | ||
332 | * has_wakealarm - check rtc device has wakealarm ability | ||
333 | * @dev: current device | ||
334 | * @name_ptr: name to be returned | ||
335 | * | ||
336 | * This helper function checks to see if the rtc device can wake | ||
337 | * from suspend. | ||
338 | */ | ||
339 | static int __init has_wakealarm(struct device *dev, void *name_ptr) | ||
340 | { | ||
341 | struct rtc_device *candidate = to_rtc_device(dev); | ||
342 | |||
343 | if (!candidate->ops->set_alarm) | ||
344 | return 0; | ||
345 | if (!device_may_wakeup(candidate->dev.parent)) | ||
346 | return 0; | ||
347 | |||
348 | *(const char **)name_ptr = dev_name(dev); | ||
349 | return 1; | ||
350 | } | ||
351 | |||
352 | /** | ||
353 | * alarmtimer_init_late - Late initializing of alarmtimer code | ||
354 | * | ||
355 | * This function locates a rtc device to use for wakealarms. | ||
356 | * Run as late_initcall to make sure rtc devices have been | ||
357 | * registered. | ||
358 | */ | ||
359 | static int __init alarmtimer_init_late(void) | ||
360 | { | ||
361 | char *str; | ||
362 | |||
363 | /* Find an rtc device and init the rtc_timer */ | ||
364 | class_find_device(rtc_class, NULL, &str, has_wakealarm); | ||
365 | if (str) | ||
366 | rtcdev = rtc_class_open(str); | ||
367 | if (!rtcdev) { | ||
368 | printk(KERN_WARNING "No RTC device found, ALARM timers will" | ||
369 | " not wake from suspend"); | ||
370 | } | ||
371 | rtc_timer_init(&rtctimer, NULL, NULL); | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | late_initcall(alarmtimer_init_late); | ||