blob: e06dad464c56cda1bb1a981f734338b8f7208ffa [file] [log] [blame]
Girish Mahadevan3cdb8602012-09-10 15:30:36 -06001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/clocksource.h>
16#include <linux/clockchips.h>
17#include <linux/init.h>
18#include <linux/slab.h>
19#include <mach/event_timer.h>
20
21#define __INIT_HEAD(x) { .head = RB_ROOT,\
22 .next = NULL, }
23
24#define DEFINE_TIME_HEAD(x) struct timerqueue_head x = __INIT_HEAD(x)
25
26/**
27 * struct event_timer_info - basic event timer structure
28 * @node: timerqueue node to track time ordered data structure
29 * of event timers
30 * @timer: hrtimer created for this event.
31 * @function : callback function for event timer.
32 * @data : callback data for event timer.
33 */
34struct event_timer_info {
35 struct timerqueue_node node;
36 void (*function)(void *);
37 void *data;
38};
39
40
41static DEFINE_TIME_HEAD(timer_head);
42static DEFINE_SPINLOCK(event_timer_lock);
43static struct hrtimer event_hrtimer;
44static enum hrtimer_restart event_hrtimer_cb(struct hrtimer *hrtimer);
45
46static int msm_event_debug_mask;
47module_param_named(
48 debug_mask, msm_event_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP
49);
50
51enum {
52 MSM_EVENT_TIMER_DEBUG = 1U << 0,
53};
54
55
56/**
57 * add_event_timer() : Add a wakeup event. Intended to be called
58 * by clients once. Returns a handle to be used
59 * for future transactions.
60 * @function : The callback function will be called when event
61 * timer expires.
62 * @data: callback data provided by client.
63 */
64struct event_timer_info *add_event_timer(void (*function)(void *), void *data)
65{
66 struct event_timer_info *event_info =
67 kzalloc(sizeof(struct event_timer_info), GFP_KERNEL);
68
69 if (!event_info)
70 return NULL;
71
72 event_info->function = function;
73 event_info->data = data;
74 /* Init rb node and hr timer */
75 timerqueue_init(&event_info->node);
76
77 return event_info;
78}
79
80/**
81 * is_event_next(): Helper function to check if the event is the next
82 * next expiring event
83 * @event : handle to the event to be checked.
84 */
85static bool is_event_next(struct event_timer_info *event)
86{
87 struct event_timer_info *next_event;
88 struct timerqueue_node *next;
89 bool ret = false;
90
91 next = timerqueue_getnext(&timer_head);
92 if (!next)
93 goto exit_is_next_event;
94
95 next_event = container_of(next, struct event_timer_info, node);
96 if (!next_event)
97 goto exit_is_next_event;
98
99 if (next_event == event)
100 ret = true;
101
102exit_is_next_event:
103 return ret;
104}
105
106/**
107 * is_event_active(): Helper function to check if the timer for a given event
108 * has been started.
109 * @event : handle to the event to be checked.
110 */
111static bool is_event_active(struct event_timer_info *event)
112{
113 struct timerqueue_node *next;
114 struct event_timer_info *next_event;
115 bool ret = false;
116
117 for (next = timerqueue_getnext(&timer_head); next;
118 next = timerqueue_iterate_next(next)) {
119 next_event = container_of(next, struct event_timer_info, node);
120
121 if (event == next_event) {
122 ret = true;
123 break;
124 }
125 }
126 return ret;
127}
128
129/**
130 * create_httimer(): Helper function to setup hrtimer.
131 */
132static void create_hrtimer(ktime_t expires)
133{
134 static bool timer_initialized;
135
136 if (!timer_initialized) {
137 hrtimer_init(&event_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
138 timer_initialized = true;
139 }
140
141 event_hrtimer.function = event_hrtimer_cb;
142 hrtimer_start(&event_hrtimer, expires, HRTIMER_MODE_ABS);
143
144 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
145 pr_info("%s: Setting timer for %lu", __func__,
146 (unsigned long)ktime_to_ns(expires));
147}
148
149/**
150 * event_hrtimer_cb() : Callback function for hr timer.
151 * Make the client CB from here and remove the event
152 * from the time ordered queue.
153 */
154static enum hrtimer_restart event_hrtimer_cb(struct hrtimer *hrtimer)
155{
156 struct event_timer_info *event;
157 struct timerqueue_node *next;
158
159 next = timerqueue_getnext(&timer_head);
160
161 while (next && (ktime_to_ns(next->expires)
162 <= ktime_to_ns(hrtimer->node.expires))) {
163 if (!next)
164 goto hrtimer_cb_exit;
165
166 event = container_of(next, struct event_timer_info, node);
167 if (!event)
168 goto hrtimer_cb_exit;
169
170 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
171 pr_info("%s: Deleting event @ %lu", __func__,
172 (unsigned long)ktime_to_ns(next->expires));
173
174 timerqueue_del(&timer_head, &event->node);
175
176 if (event->function)
177 event->function(event->data);
178 next = timerqueue_getnext(&timer_head);
179 }
180
181 if (next)
182 create_hrtimer(next->expires);
183
184hrtimer_cb_exit:
185 return HRTIMER_NORESTART;
186}
187
188/**
189 * create_timer_smp(): Helper function used setting up timer on core 0.
190 */
191static void create_timer_smp(void *data)
192{
193 unsigned long flags;
194 struct event_timer_info *event =
195 (struct event_timer_info *)data;
196
197 local_irq_save(flags);
198 create_hrtimer(event->node.expires);
199 local_irq_restore(flags);
200}
201
202/**
203 * setup_timer() : Helper function to setup timer on primary
204 * core during hrtimer callback.
205 * @event: event handle causing the wakeup.
206 */
207static void setup_event_hrtimer(struct event_timer_info *event)
208{
209 struct timerqueue_node *next;
210 unsigned long flags;
211
212 spin_lock_irqsave(&event_timer_lock, flags);
213 if (is_event_active(event))
214 timerqueue_del(&timer_head, &event->node);
215
216 next = timerqueue_getnext(&timer_head);
217 timerqueue_add(&timer_head, &event->node);
218 spin_unlock_irqrestore(&event_timer_lock, flags);
219
220 if (!next ||
221 (next && (ktime_to_ns(event->node.expires) <
222 ktime_to_ns(next->expires)))) {
223 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
224 pr_info("%s: Setting timer for %lu", __func__,
225 (unsigned long)ktime_to_ns(event->node.expires));
226
227 smp_call_function_single(0, create_timer_smp, event, 1);
228 }
229}
230
231/**
232 * activate_event_timer() : Set the expiration time for an event in absolute
233 * ktime. This is a oneshot event timer, clients
234 * should call this again to set another expiration.
235 * @event : event handle.
236 * @event_time : event time in absolute ktime.
237 */
238void activate_event_timer(struct event_timer_info *event, ktime_t event_time)
239{
240 if (!event)
241 return;
242
243 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
244 pr_info("%s: Adding event timer @ %lu", __func__,
245 (unsigned long)ktime_to_us(event_time));
246
247 event->node.expires = event_time;
248 /* Start hr timer and add event to rb tree */
249 setup_event_hrtimer(event);
250}
251
252
253/**
254 * deactivate_event_timer() : Deactivate an event timer, this removes the event from
255 * the time ordered queue of event timers.
256 * @event: event handle.
257 */
258void deactivate_event_timer(struct event_timer_info *event)
259{
260 unsigned long flags;
261
262 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
263 pr_info("%s: Deactivate timer", __func__);
264
265 spin_lock_irqsave(&event_timer_lock, flags);
266 if (is_event_active(event)) {
267 if (is_event_next(event))
268 hrtimer_try_to_cancel(&event_hrtimer);
269
270 timerqueue_del(&timer_head, &event->node);
271 }
272 spin_unlock_irqrestore(&event_timer_lock, flags);
273}
274
275/**
276 * destroy_event_timer() : Free the event info data structure allocated during
277 * add_event_timer().
278 * @event: event handle.
279 */
280void destroy_event_timer(struct event_timer_info *event)
281{
282 unsigned long flags;
283
284 spin_lock_irqsave(&event_timer_lock, flags);
285 if (is_event_active(event)) {
286 if (is_event_next(event))
287 hrtimer_try_to_cancel(&event_hrtimer);
288
289 timerqueue_del(&timer_head, &event->node);
290 }
291 spin_unlock_irqrestore(&event_timer_lock, flags);
292 kfree(event);
293}
294
295/**
296 * get_next_event_timer() - Get the next wakeup event. Returns
297 * a ktime value of the next expiring event.
298 */
299ktime_t get_next_event_time(void)
300{
301 unsigned long flags;
302 struct timerqueue_node *next;
303 ktime_t next_event = ns_to_ktime(0);
304
305 spin_lock_irqsave(&event_timer_lock, flags);
306 next = timerqueue_getnext(&timer_head);
307 spin_unlock_irqrestore(&event_timer_lock, flags);
308
309 if (!next)
310 return next_event;
311
312 next_event = hrtimer_get_remaining(&event_hrtimer);
313 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
314 pr_info("%s: Next Event %lu", __func__,
315 (unsigned long)ktime_to_us(next_event));
316
317 return next_event;
318}