blob: 9f46f68e72bbf159ed1a2537ee5101c5ae28678e [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
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;
Girish Mahadevan388c3082012-09-10 15:30:36 -0600196 struct timerqueue_node *next;
Girish Mahadevan388c3082012-09-10 15:30:36 -0600197
198 spin_lock_irqsave(&event_timer_lock, flags);
199 if (is_event_active(event))
200 timerqueue_del(&timer_head, &event->node);
201
202 next = timerqueue_getnext(&timer_head);
203 timerqueue_add(&timer_head, &event->node);
204 spin_unlock_irqrestore(&event_timer_lock, flags);
205
206 if (!next ||
207 (next && (ktime_to_ns(event->node.expires) <
208 ktime_to_ns(next->expires)))) {
209 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
210 pr_info("%s: Setting timer for %lu", __func__,
211 (unsigned long)ktime_to_ns(event->node.expires));
Girish Mahadevane73869c2012-11-28 13:25:18 -0700212 create_hrtimer(event->node.expires);
213 }
214}
Girish Mahadevan388c3082012-09-10 15:30:36 -0600215
Girish Mahadevane73869c2012-11-28 13:25:18 -0700216/**
217 * setup_timer() : Helper function to setup timer on primary
218 * core during hrtimer callback.
219 * @event: event handle causing the wakeup.
220 */
221static void setup_event_hrtimer(struct event_timer_info *event)
222{
223 smp_call_function_single(0, create_timer_smp, event, 1);
Girish Mahadevan388c3082012-09-10 15:30:36 -0600224}
225
226/**
227 * activate_event_timer() : Set the expiration time for an event in absolute
228 * ktime. This is a oneshot event timer, clients
229 * should call this again to set another expiration.
230 * @event : event handle.
231 * @event_time : event time in absolute ktime.
232 */
233void activate_event_timer(struct event_timer_info *event, ktime_t event_time)
234{
235 if (!event)
236 return;
237
238 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
239 pr_info("%s: Adding event timer @ %lu", __func__,
240 (unsigned long)ktime_to_us(event_time));
241
242 event->node.expires = event_time;
243 /* Start hr timer and add event to rb tree */
244 setup_event_hrtimer(event);
245}
246
247
248/**
249 * deactivate_event_timer() : Deactivate an event timer, this removes the event from
250 * the time ordered queue of event timers.
251 * @event: event handle.
252 */
253void deactivate_event_timer(struct event_timer_info *event)
254{
255 unsigned long flags;
256
257 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
258 pr_info("%s: Deactivate timer", __func__);
259
260 spin_lock_irqsave(&event_timer_lock, flags);
261 if (is_event_active(event)) {
262 if (is_event_next(event))
263 hrtimer_try_to_cancel(&event_hrtimer);
264
265 timerqueue_del(&timer_head, &event->node);
266 }
267 spin_unlock_irqrestore(&event_timer_lock, flags);
268}
269
270/**
271 * destroy_event_timer() : Free the event info data structure allocated during
272 * add_event_timer().
273 * @event: event handle.
274 */
275void destroy_event_timer(struct event_timer_info *event)
276{
277 unsigned long flags;
278
279 spin_lock_irqsave(&event_timer_lock, flags);
280 if (is_event_active(event)) {
281 if (is_event_next(event))
282 hrtimer_try_to_cancel(&event_hrtimer);
283
284 timerqueue_del(&timer_head, &event->node);
285 }
286 spin_unlock_irqrestore(&event_timer_lock, flags);
287 kfree(event);
288}
289
290/**
291 * get_next_event_timer() - Get the next wakeup event. Returns
292 * a ktime value of the next expiring event.
293 */
294ktime_t get_next_event_time(void)
295{
296 unsigned long flags;
297 struct timerqueue_node *next;
298 ktime_t next_event = ns_to_ktime(0);
299
300 spin_lock_irqsave(&event_timer_lock, flags);
301 next = timerqueue_getnext(&timer_head);
302 spin_unlock_irqrestore(&event_timer_lock, flags);
303
304 if (!next)
305 return next_event;
306
307 next_event = hrtimer_get_remaining(&event_hrtimer);
308 if (msm_event_debug_mask && MSM_EVENT_TIMER_DEBUG)
309 pr_info("%s: Next Event %lu", __func__,
310 (unsigned long)ktime_to_us(next_event));
311
312 return next_event;
313}