blob: df79e42c96a317f63f95f596a6fd324354eb6423 [file] [log] [blame]
Girish Mahadevan388c3082012-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
Girish Mahadevan388c3082012-09-10 15:30:36 -060040static DEFINE_TIME_HEAD(timer_head);
41static DEFINE_SPINLOCK(event_timer_lock);
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -070042static DEFINE_SPINLOCK(event_setup_lock);
Girish Mahadevan388c3082012-09-10 15:30:36 -060043static 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);
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -070076 pr_debug("%s: New Event Added. Event 0x%x.",
77 __func__,
78 (unsigned int)event_info);
Girish Mahadevan388c3082012-09-10 15:30:36 -060079
80 return event_info;
81}
82
83/**
84 * is_event_next(): Helper function to check if the event is the next
85 * next expiring event
86 * @event : handle to the event to be checked.
87 */
88static bool is_event_next(struct event_timer_info *event)
89{
90 struct event_timer_info *next_event;
91 struct timerqueue_node *next;
92 bool ret = false;
93
94 next = timerqueue_getnext(&timer_head);
95 if (!next)
96 goto exit_is_next_event;
97
98 next_event = container_of(next, struct event_timer_info, node);
99 if (!next_event)
100 goto exit_is_next_event;
101
102 if (next_event == event)
103 ret = true;
104
105exit_is_next_event:
106 return ret;
107}
108
109/**
110 * is_event_active(): Helper function to check if the timer for a given event
111 * has been started.
112 * @event : handle to the event to be checked.
113 */
114static bool is_event_active(struct event_timer_info *event)
115{
116 struct timerqueue_node *next;
117 struct event_timer_info *next_event;
118 bool ret = false;
119
120 for (next = timerqueue_getnext(&timer_head); next;
121 next = timerqueue_iterate_next(next)) {
122 next_event = container_of(next, struct event_timer_info, node);
123
124 if (event == next_event) {
125 ret = true;
126 break;
127 }
128 }
129 return ret;
130}
131
132/**
133 * create_httimer(): Helper function to setup hrtimer.
134 */
135static void create_hrtimer(ktime_t expires)
136{
137 static bool timer_initialized;
138
139 if (!timer_initialized) {
140 hrtimer_init(&event_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
141 timer_initialized = true;
142 }
143
144 event_hrtimer.function = event_hrtimer_cb;
145 hrtimer_start(&event_hrtimer, expires, HRTIMER_MODE_ABS);
Girish Mahadevan388c3082012-09-10 15:30:36 -0600146}
147
148/**
149 * event_hrtimer_cb() : Callback function for hr timer.
150 * Make the client CB from here and remove the event
151 * from the time ordered queue.
152 */
153static enum hrtimer_restart event_hrtimer_cb(struct hrtimer *hrtimer)
154{
155 struct event_timer_info *event;
156 struct timerqueue_node *next;
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -0700157 unsigned long flags;
Girish Mahadevan388c3082012-09-10 15:30:36 -0600158
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -0700159 spin_lock_irqsave(&event_timer_lock, flags);
Girish Mahadevan388c3082012-09-10 15:30:36 -0600160 next = timerqueue_getnext(&timer_head);
161
162 while (next && (ktime_to_ns(next->expires)
163 <= ktime_to_ns(hrtimer->node.expires))) {
164 if (!next)
165 goto hrtimer_cb_exit;
166
167 event = container_of(next, struct event_timer_info, node);
168 if (!event)
169 goto hrtimer_cb_exit;
170
171 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -0700172 pr_info("%s: Deleting event 0x%x @ %lu", __func__,
173 (unsigned int)event,
Girish Mahadevan388c3082012-09-10 15:30:36 -0600174 (unsigned long)ktime_to_ns(next->expires));
175
176 timerqueue_del(&timer_head, &event->node);
177
178 if (event->function)
179 event->function(event->data);
180 next = timerqueue_getnext(&timer_head);
181 }
182
183 if (next)
184 create_hrtimer(next->expires);
185
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -0700186 spin_unlock_irqrestore(&event_timer_lock, flags);
Girish Mahadevan388c3082012-09-10 15:30:36 -0600187hrtimer_cb_exit:
188 return HRTIMER_NORESTART;
189}
190
191/**
192 * create_timer_smp(): Helper function used setting up timer on core 0.
193 */
194static void create_timer_smp(void *data)
195{
196 unsigned long flags;
197 struct event_timer_info *event =
198 (struct event_timer_info *)data;
Girish Mahadevan388c3082012-09-10 15:30:36 -0600199 struct timerqueue_node *next;
Girish Mahadevan388c3082012-09-10 15:30:36 -0600200
201 spin_lock_irqsave(&event_timer_lock, flags);
202 if (is_event_active(event))
203 timerqueue_del(&timer_head, &event->node);
204
205 next = timerqueue_getnext(&timer_head);
206 timerqueue_add(&timer_head, &event->node);
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -0700207 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
208 pr_info("%s: Adding Event 0x%x for %lu", __func__,
209 (unsigned int)event,
210 (unsigned long)ktime_to_ns(event->node.expires));
Girish Mahadevan388c3082012-09-10 15:30:36 -0600211
212 if (!next ||
213 (next && (ktime_to_ns(event->node.expires) <
214 ktime_to_ns(next->expires)))) {
215 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
216 pr_info("%s: Setting timer for %lu", __func__,
217 (unsigned long)ktime_to_ns(event->node.expires));
Girish Mahadevane73869c2012-11-28 13:25:18 -0700218 create_hrtimer(event->node.expires);
219 }
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -0700220 spin_unlock_irqrestore(&event_timer_lock, flags);
Girish Mahadevane73869c2012-11-28 13:25:18 -0700221}
Girish Mahadevan388c3082012-09-10 15:30:36 -0600222
Girish Mahadevane73869c2012-11-28 13:25:18 -0700223/**
224 * setup_timer() : Helper function to setup timer on primary
225 * core during hrtimer callback.
226 * @event: event handle causing the wakeup.
227 */
228static void setup_event_hrtimer(struct event_timer_info *event)
229{
230 smp_call_function_single(0, create_timer_smp, event, 1);
Girish Mahadevan388c3082012-09-10 15:30:36 -0600231}
232
233/**
234 * activate_event_timer() : Set the expiration time for an event in absolute
235 * ktime. This is a oneshot event timer, clients
236 * should call this again to set another expiration.
237 * @event : event handle.
238 * @event_time : event time in absolute ktime.
239 */
240void activate_event_timer(struct event_timer_info *event, ktime_t event_time)
241{
242 if (!event)
243 return;
244
245 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
246 pr_info("%s: Adding event timer @ %lu", __func__,
247 (unsigned long)ktime_to_us(event_time));
248
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -0700249 spin_lock(&event_setup_lock);
Girish Mahadevan388c3082012-09-10 15:30:36 -0600250 event->node.expires = event_time;
251 /* Start hr timer and add event to rb tree */
252 setup_event_hrtimer(event);
Girish Mahadevan09a4f4d2013-01-08 15:59:30 -0700253 spin_unlock(&event_setup_lock);
Girish Mahadevan388c3082012-09-10 15:30:36 -0600254}
255
256
257/**
258 * deactivate_event_timer() : Deactivate an event timer, this removes the event from
259 * the time ordered queue of event timers.
260 * @event: event handle.
261 */
262void deactivate_event_timer(struct event_timer_info *event)
263{
264 unsigned long flags;
265
266 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
267 pr_info("%s: Deactivate timer", __func__);
268
269 spin_lock_irqsave(&event_timer_lock, flags);
270 if (is_event_active(event)) {
271 if (is_event_next(event))
272 hrtimer_try_to_cancel(&event_hrtimer);
273
274 timerqueue_del(&timer_head, &event->node);
275 }
276 spin_unlock_irqrestore(&event_timer_lock, flags);
277}
278
279/**
280 * destroy_event_timer() : Free the event info data structure allocated during
281 * add_event_timer().
282 * @event: event handle.
283 */
284void destroy_event_timer(struct event_timer_info *event)
285{
286 unsigned long flags;
287
288 spin_lock_irqsave(&event_timer_lock, flags);
289 if (is_event_active(event)) {
290 if (is_event_next(event))
291 hrtimer_try_to_cancel(&event_hrtimer);
292
293 timerqueue_del(&timer_head, &event->node);
294 }
295 spin_unlock_irqrestore(&event_timer_lock, flags);
296 kfree(event);
297}
298
299/**
300 * get_next_event_timer() - Get the next wakeup event. Returns
301 * a ktime value of the next expiring event.
302 */
303ktime_t get_next_event_time(void)
304{
305 unsigned long flags;
306 struct timerqueue_node *next;
307 ktime_t next_event = ns_to_ktime(0);
308
309 spin_lock_irqsave(&event_timer_lock, flags);
310 next = timerqueue_getnext(&timer_head);
311 spin_unlock_irqrestore(&event_timer_lock, flags);
312
313 if (!next)
314 return next_event;
315
316 next_event = hrtimer_get_remaining(&event_hrtimer);
317 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
318 pr_info("%s: Next Event %lu", __func__,
319 (unsigned long)ktime_to_us(next_event));
320
321 return next_event;
322}