1/*
2 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 *
16 * Low resolution timer interface. Timer handlers may be called
17 * in a deferred manner in a different task context after the
18 * timer expires or in the task context from which the timer
19 * was created, depending on the implementation.
20 *
21 * $Id: rte_timers.c,v 1.4 2006/01/13 17:29:25 Exp $
22 */
23
24#include <typedefs.h>
25#include <bcmtimer.h>
26#include <osl.h>
27#include <hndrte.h>
28
29#include <stdlib.h>
30
31
32/* Forward declarations */
33typedef struct bcm_timer_s *bcm_timer_p;
34typedef struct bcm_timer_list_s *bcm_timer_list_p;
35
36/* timer entry */
37typedef struct bcm_timer_s
38{
39	bcm_timer_p prev, next;
40	bcm_timer_list_p list;
41	hndrte_timer_t *timer;
42	int flags;
43	void (*func)(bcm_timer_id id, int data);
44	int data;
45} bcm_timer_t;
46
47#define TIMER_FLAG_NONE	0x0000
48#define TIMER_FLAG_DEFERRED	0x0001
49#define TIMER_FLAG_FINISHED	0x0002
50#define TIMER_FLAG_IN_USE	0x0004
51#define TIMER_FLAG_ARMED	0x0008
52
53
54typedef struct bcm_timer_list_s
55{
56	bcm_timer_t *entry;
57	bcm_timer_t *used;
58	bcm_timer_t *freed;
59	int entries;
60	int flags;
61} bcm_timer_list_t;
62
63#define TIMER_LIST_FLAG_NONE	0x0000
64#define TIMER_LIST_FLAG_INIT	0x0001
65#define TIMER_LIST_FLAG_EXIT	0x0002
66
67#define TIMER_DEBUG 0
68#if TIMER_DEBUG
69#include <stdio.h>
70#define TIMERDBG(fmt, arg...)	printf("%s: "fmt"\n", __FUNCTION__, ##arg)
71#else
72#define TIMERDBG(fmt, arg...)
73#endif /* if TIMER_DEBUG */
74
75/* timer list lock - currently no need for this in RTE, no mechanism either */
76#define TIMER_LIST_LOCK(list) 0
77#define TIMER_LIST_UNLOCK(list)
78#define TIMER_FREE_LOCK_MECHANISM()
79/* Interrupt disable */
80#define INT_LOCK() 0
81#define INT_UNLOCK(a)
82
83/* alloc entry from top of list */
84static int get_entry(bcm_timer_t **list, bcm_timer_t **entry)
85{
86	/* take an entry from top of the list */
87	TIMERDBG("list = %08x", *list);
88	*entry = *list;
89	if (*entry == NULL)
90		return -1;
91	*list = (*entry)->next;
92	if (*list != NULL)
93		(*list)->prev = NULL;
94	TIMERDBG("new list = %08x", *list);
95	(*entry)->next = NULL;
96	(*entry)->prev = NULL;
97	TIMERDBG("entry = %08x", *entry);
98	return 0;
99}
100
101/* add entry into top of list */
102static int put_entry(bcm_timer_t **list, bcm_timer_t *entry)
103{
104	/* add the entry into top of the list */
105	TIMERDBG("list = %08x", *list);
106	TIMERDBG("entry = %08x", entry);
107	entry->next = *list;
108	entry->prev = NULL;
109	if (*list != NULL)
110		(*list)->prev = entry;
111	*list = entry;
112	TIMERDBG("new list = %08x", *list);
113	return 0;
114}
115
116
117/* remove entry from list */
118static int remove_entry(bcm_timer_t **list, bcm_timer_t *entry)
119{
120	/* remove the entry from the list */
121	TIMERDBG("list = %08x", *list);
122	TIMERDBG("entry = %08x", entry);
123	if (entry->prev != NULL)
124		entry->prev->next = entry->next;
125	if (entry->next != NULL)
126		entry->next->prev = entry->prev;
127	if (*list == entry)
128		*list = entry->next;
129	entry->next = NULL;
130	entry->prev = NULL;
131	TIMERDBG("new list = %08x", *list);
132	return 0;
133}
134
135/* internal deferred timer callback function */
136static void exc_func(hndrte_task_t *timer)
137{
138	bcm_timer_t *entry = (bcm_timer_t*)timer->data;
139	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
140	/* make the callback first */
141	TIMERDBG("func %08x data %08x", entry->func, entry->data);
142	entry->func((bcm_timer_id)entry, entry->data);
143	entry->flags |= TIMER_FLAG_FINISHED;
144	entry->flags &= ~TIMER_FLAG_DEFERRED;
145	TIMERDBG("done");
146	hndrte_free_timer(timer);
147	return;
148}
149
150/* internal timer callback function */
151static void timer_hdlr(hndrte_timer_t *timer)
152{
153	bcm_timer_t *entry = (bcm_timer_t *)timer->data;
154	bcm_timer_list_t *list = entry->list;
155	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
156	if (list->flags&TIMER_LIST_FLAG_EXIT)
157		return;
158	entry->flags |= TIMER_FLAG_DEFERRED;
159	entry->flags &= ~TIMER_FLAG_FINISHED;
160	/* defer the real callback to a task context */
161	TIMERDBG("job %08x data %08x", exc_func, entry);
162	hndrte_schedule_work(NULL, entry, exc_func, 0);
163	TIMERDBG("done");
164	return;
165}
166
167/*
168* Initialize internal resources used in the timer module. It must be called
169* before any other timer function calls. The param 'timer_entries' is used
170* to pre-allocate fixed number of timer entries.
171*/
172int
173bcm_timer_module_init(int timer_entries, bcm_timer_module_id *module_id)
174{
175	int size = timer_entries * sizeof(bcm_timer_t);
176	bcm_timer_list_t *list;
177	int i;
178	TIMERDBG("entries %d", timer_entries);
179	/* alloc fixed number of entries upfront */
180	list = hndrte_malloc(sizeof(bcm_timer_list_t)+size);
181	if (list == NULL)
182		goto exit0;
183	list->flags = TIMER_LIST_FLAG_NONE;
184	list->entry = (bcm_timer_t *)(list + 1);
185	list->entries = timer_entries;
186	TIMERDBG("entry %08x", list->entry);
187	/* init the timer entries to form two list - freed and used */
188	list->freed = NULL;
189	list->used = NULL;
190	memset(list->entry, 0, timer_entries*sizeof(bcm_timer_t));
191	for (i = 0; i < timer_entries; i ++)
192	{
193		put_entry(&list->freed, &list->entry[i]);
194	}
195	list->flags = TIMER_LIST_FLAG_INIT;
196	*module_id = (bcm_timer_module_id)list;
197	TIMERDBG("list %08x freed %08x used %08x", list, list->freed, list->used);
198	return 0;
199exit0:
200	return -1;
201}
202
203/*
204* Cleanup internal resources used by this timer module. It deletes all
205* pending timer entries from the backend timer system as well.
206*/
207int
208bcm_timer_module_cleanup(bcm_timer_module_id module_id)
209{
210	bcm_timer_list_t *list = (bcm_timer_list_t *)module_id;
211	bcm_timer_t *entry;
212	int status;
213	int key;
214
215	ASSERT(0); /* This hasn't been tested */
216
217	TIMERDBG("list %08x", list);
218	/*
219	* do nothing if the list has not been initialized
220	*/
221	if (!(list->flags&TIMER_LIST_FLAG_INIT))
222		return -1;
223	/*
224	* mark the big bang flag here so that no more callbacks
225	* shall be scheduled or called from this point on...
226	*/
227	list->flags |= TIMER_LIST_FLAG_EXIT;
228	/*
229	* remove all backend timers here so that no timer expires after here.
230	*/
231	status = TIMER_LIST_LOCK(list);
232	key = INT_LOCK();
233	if (status != 0)
234		return status;
235	for (entry = list->used; entry != NULL; entry = entry->next)
236	{
237		hndrte_free_timer(entry->timer);
238	}
239
240	TIMER_LIST_UNLOCK(list);
241	INT_UNLOCK(key);
242	/*
243	* have to wait till all expired entries to have been handled
244	*/
245	for (entry = list->used; entry != NULL; entry = entry->next)
246	{
247		if ((entry->flags&TIMER_FLAG_DEFERRED) &&
248		    !(entry->flags&TIMER_FLAG_FINISHED))
249			break;
250	}
251
252	/* now it should be safe to blindly free all the resources */
253	TIMER_FREE_LOCK_MECHANISM();
254	hndrte_free(list);
255	TIMERDBG("done");
256	return 0;
257}
258
259int
260bcm_timer_module_enable(bcm_timer_module_id module_id, int enable)
261{
262	return 0;
263}
264
265int
266bcm_timer_create(bcm_timer_module_id module_id, bcm_timer_id *timer_id)
267{
268	bcm_timer_list_t *list = (bcm_timer_list_t *)module_id;
269	bcm_timer_t *entry;
270	int status;
271	TIMERDBG("list %08x", list);
272	/* lock the timer list */
273	status = TIMER_LIST_LOCK(list);
274	if (status != 0)
275		goto exit0;
276	/* check if timer is allowed */
277	if (list->flags & TIMER_LIST_FLAG_EXIT)
278		goto exit1;
279	/* alloc an entry first */
280	status = get_entry(&list->freed, &entry);
281	if (status != 0)
282		goto exit1;
283	/* create backend timer */
284	entry->timer = hndrte_init_timer(NULL, entry, timer_hdlr, NULL);
285	if (!entry->timer) {
286		TIMERDBG("bcm_timer_create couldn't allocate timer");
287		goto exit2;
288	}
289	/* add the entry into used list */
290	status = put_entry(&list->used, entry);
291	if (status != 0)
292		goto exit3;
293	entry->flags = TIMER_FLAG_IN_USE;
294	entry->list = list;
295	*timer_id = (bcm_timer_id)(void *)entry;
296	TIMER_LIST_UNLOCK(list);
297	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
298	return 0;
299	/* error handling */
300exit3:
301	hndrte_del_timer(entry->timer);
302exit2:
303	put_entry(&list->freed, entry);
304exit1:
305	TIMER_LIST_UNLOCK(list);
306exit0:
307	return status;
308}
309
310int
311bcm_timer_delete(bcm_timer_id timer_id)
312{
313	bcm_timer_t *entry = (bcm_timer_t *)timer_id;
314	bcm_timer_list_t *list = entry->list;
315	int status;
316	int key;
317	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
318	/* make sure no interrupts can happen */
319	key = INT_LOCK();
320	/* lock the timer list */
321	status = TIMER_LIST_LOCK(list);
322	if (status != 0)
323		goto exit0;
324	/* remove the entry from the used list first */
325	status = remove_entry(&list->used, entry);
326	if (status != 0)
327		goto exit1;
328	/* delete the backend timer */
329	status = hndrte_del_timer(entry->timer) ? 0 : 1;
330	if (status != 0)
331		goto exit2;
332	hndrte_free_timer(entry->timer);
333
334	/* free the entry back to freed list */
335	status = put_entry(&list->freed, entry);
336	if (status != 0)
337		goto exit3;
338	entry->flags = TIMER_FLAG_NONE;
339	entry->list = NULL;
340	TIMER_LIST_UNLOCK(list);
341	INT_UNLOCK(key);
342	TIMERDBG("done");
343	return 0;
344	/* error handling */
345exit3:
346	TIMERDBG("put_entry failed");
347exit2:
348	TIMERDBG("timer_delete failed");
349exit1:
350	TIMER_LIST_UNLOCK(list);
351exit0:
352	INT_UNLOCK(key);
353	return status;
354}
355
356int
357bcm_timer_gettime(bcm_timer_id timer_id, struct itimerspec *timerspec)
358{
359	return -1;
360}
361
362int
363bcm_timer_settime(bcm_timer_id timer_id, const struct itimerspec *timerspec)
364{
365	unsigned int ms;
366	bcm_timer_t *entry = (bcm_timer_t *)timer_id;
367	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
368
369	/*	ASSERT(timerspec->it_interval.tv_sec != timerspec->it_value.tv_sec); */
370	ms = (timerspec->it_value.tv_sec*1000) +
371		((timerspec->it_value.tv_nsec/1000)/1000);
372
373	return hndrte_add_timer(entry->timer, ms, 1) ? 0 : 1;
374}
375
376int
377bcm_timer_connect(bcm_timer_id timer_id, bcm_timer_cb func, int data)
378{
379	bcm_timer_t *entry = (bcm_timer_t *)timer_id;
380
381	entry->func = func;
382	entry->data = data;
383	TIMERDBG("entry %08x timer %08x func %08x data %08x",
384		entry, entry->timer, entry->func, entry->data);
385	entry->flags |= TIMER_FLAG_ARMED;
386	return 0;
387}
388
389int
390bcm_timer_cancel(bcm_timer_id timer_id)
391{
392	bcm_timer_t *entry = (bcm_timer_t *)timer_id;
393
394	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
395	if (!hndrte_del_timer(entry->timer)) {
396		TIMERDBG("bcm_timer_cancel couldn't delete entry");
397		goto exit0;
398	}
399	entry->flags &= ~TIMER_FLAG_ARMED;
400	return 0;
401	/* error handling */
402exit0:
403	return 1;
404}
405
406int
407bcm_timer_change_expirytime(bcm_timer_id timer_id,
408                            const struct itimerspec *timer_spec)
409{
410	bcm_timer_cancel(timer_id);
411	bcm_timer_settime(timer_id, timer_spec);
412	return 1;
413}
414