1/*
2 * Copyright 2003, Broadcom Corporation
3 * All Rights Reserved.
4 *
5 * Low resolution timer interface Ecos specific implementation.
6 *
7 * $Id: ecos_timer.c,v 1.2 2007/07/02 17:11:41 Exp $
8 */
9
10#include <stdio.h>
11#include <string.h>
12#include <sys/time.h>
13#include <cyg/hal/drv_api.h>
14#include <sys/param.h>
15#include <osl.h>
16#include "bcmtimer.h"
17
18
19/* forward declaration */
20typedef struct ecos_timer_entry_s	*ecos_timer_entry_p;
21typedef struct ecos_timer_list_s	*ecos_timer_list_p;
22
23/* timer entry */
24typedef struct ecos_timer_entry_s
25{
26	ecos_timer_entry_p	prev, next;
27	ecos_timer_list_p	list;
28	uint				timer;
29	uint				tick;
30	int					flags;
31	void				(*func)(bcm_timer_id id, int data);
32	int					data;
33} ecos_timer_entry_t;
34
35#define TIMER_FLAG_NONE		0x0000
36#define TIMER_FLAG_DEFERRED	0x0001
37#define TIMER_FLAG_FINISHED	0x0002
38#define TIMER_FLAG_IN_USE	0x0004
39#define TIMER_FLAG_ARMED	0x0008
40
41/* timer list */
42typedef struct ecos_timer_list_s
43{
44	ecos_timer_entry_t *entry;
45	ecos_timer_entry_t *used;
46	ecos_timer_entry_t *freed;
47	int					entries;
48	cyg_mutex_t			lock;
49	int					flags;
50} ecos_timer_list_t;
51
52#define TIMER_LIST_FLAG_NONE	0x0000
53#define TIMER_LIST_FLAG_INIT	0x0001
54#define TIMER_LIST_FLAG_EXIT	0x0002
55
56#ifdef TIMER_DEBUG
57#include <stdio.h>
58#define TIMERDBG(fmt, arg...)	printf("%s: "fmt"\n", __FUNCTION__, ##arg)
59#else
60#define TIMERDBG(fmt, arg...)
61#endif /* if TIMER_DEBUG */
62
63/* timer list lock */
64#define TIMER_LIST_LOCK(list)	cyg_mutex_lock(&(list->lock))
65#define TIMER_LIST_UNLOCK(list)	cyg_mutex_unlock(&(list->lock))
66#define TIMER_FREE_LOCK_MECHANISM()
67
68/* Interrupt disable */
69#define INTLOCK() 0
70#define INTUNLOCK(a)
71
72extern int gettimeofday(struct timeval *tv, struct timezone *tz);
73
74static int ecos_timer_count = 0;
75
76static void
77timer_func(int p1)
78{
79	ecos_timer_entry_t *entry = (ecos_timer_entry_t *)p1;
80	ecos_timer_list_t *list = entry->list;
81
82	entry->timer = 0;
83
84	if (list == 0)
85		return;
86
87	if (list->flags & TIMER_LIST_FLAG_EXIT)
88		return;
89
90	entry->flags |= TIMER_FLAG_DEFERRED;
91	entry->flags &= ~TIMER_FLAG_FINISHED;
92
93	/* callback */
94	entry->func((bcm_timer_id)entry, entry->data);
95
96	entry->flags |= TIMER_FLAG_FINISHED;
97	entry->flags &= ~TIMER_FLAG_DEFERRED;
98
99	/* restart timeout */
100	entry->timer = timeout((timeout_fun *)timer_func, entry, entry->tick);
101}
102
103static void
104ecos_del_timer(ecos_timer_entry_t *entry)
105{
106	if (entry == NULL)
107		return;
108
109	if (entry->timer)
110		untimeout((timeout_fun *)timer_func, entry);
111	ecos_timer_count--;
112	return;
113}
114
115static int
116ecos_add_timer(ecos_timer_entry_t *entry, uint ms, int periodic)
117{
118	cyg_tick_count_t ostick;
119
120	if (!entry)
121		return 0;
122	ostick = ms / 10;
123	entry->tick = ostick;
124	entry->timer = timeout((timeout_fun *)timer_func, entry, entry->tick);
125	ecos_timer_count++;
126	return 1;
127}
128
129/* alloc entry from top of list */
130static int
131get_entry(ecos_timer_entry_t **list, ecos_timer_entry_t **entry)
132{
133	/* take an entry from top of the list */
134	TIMERDBG("list = %08x", *list);
135
136	*entry = *list;
137	if (*entry == NULL)
138		return -1;
139
140	*list = (*entry)->next;
141	if (*list != NULL)
142		(*list)->prev = NULL;
143
144	TIMERDBG("new list = %08x", *list);
145	(*entry)->next = NULL;
146	(*entry)->prev = NULL;
147
148	TIMERDBG("entry = %08x", *entry);
149	return 0;
150}
151
152/* add entry into top of list */
153static int
154put_entry(ecos_timer_entry_t **list, ecos_timer_entry_t *entry)
155{
156	/* add the entry into top of the list */
157	TIMERDBG("list = %08x", *list);
158	TIMERDBG("entry = %08x", entry);
159
160	entry->next = *list;
161	entry->prev = NULL;
162
163	if (*list != NULL)
164		(*list)->prev = entry;
165	*list = entry;
166
167	TIMERDBG("new list = %08x", *list);
168	return 0;
169}
170
171/* remove entry from list */
172static int
173remove_entry(ecos_timer_entry_t **list, ecos_timer_entry_t *entry)
174{
175	/* remove the entry from the list */
176	TIMERDBG("list = %08x", *list);
177	TIMERDBG("entry = %08x", entry);
178
179	if (entry->prev != NULL)
180		entry->prev->next = entry->next;
181
182	if (entry->next != NULL)
183		entry->next->prev = entry->prev;
184
185	if (*list == entry)
186		*list = entry->next;
187
188	entry->next = NULL;
189	entry->prev = NULL;
190
191	TIMERDBG("new list = %08x", *list);
192	return 0;
193}
194
195/*
196* Initialize internal resources used in the timer module. It must be called
197* before any other timer function calls. The param 'timer_entries' is used
198* to pre-allocate fixed number of timer entries.
199*/
200int
201bcm_timer_module_init(int timer_entries, bcm_timer_module_id *module_id)
202{
203	int size = timer_entries*sizeof(ecos_timer_entry_t);
204	ecos_timer_list_t *list;
205	int i;
206
207	TIMERDBG("entries %d", timer_entries);
208
209	/* alloc fixed number of entries upfront */
210	list = malloc(sizeof(ecos_timer_list_t)+size);
211	if (list == NULL)
212		goto exit0;
213
214	cyg_mutex_init(&(list->lock));
215	list->flags = TIMER_LIST_FLAG_NONE;
216	list->entry = (ecos_timer_entry_t *)(list + 1);
217	list->entries = timer_entries;
218	TIMERDBG("entry %08x", list->entry);
219
220	/* init the timer entries to form two list - freed and used */
221	list->freed = NULL;
222	list->used = NULL;
223	memset(list->entry, 0, timer_entries*sizeof(ecos_timer_entry_t));
224
225	for (i = 0; i < timer_entries; i ++)
226	{
227		put_entry(&list->freed, &list->entry[i]);
228	}
229	list->flags = TIMER_LIST_FLAG_INIT;
230	*module_id = (bcm_timer_module_id)list;
231
232	TIMERDBG("list %08x freed %08x used %08x", list, list->freed, list->used);
233	return 0;
234
235exit0:
236	return -1;
237}
238
239/*
240* Cleanup internal resources used by this timer module. It deletes all
241* pending timer entries from the backend timer system as well.
242*/
243int
244bcm_timer_module_cleanup(bcm_timer_module_id module_id)
245{
246	ecos_timer_list_t *list = (ecos_timer_list_t *)module_id;
247	ecos_timer_entry_t *entry;
248	int key;
249
250	TIMERDBG("list %08x", list);
251
252	/*
253	* do nothing if the list has not been initialized
254	*/
255	if (!(list->flags&TIMER_LIST_FLAG_INIT))
256		return -1;
257
258	/*
259	* mark the big bang flag here so that no more callbacks
260	* shall be scheduled or called from this point on...
261	*/
262	list->flags |= TIMER_LIST_FLAG_EXIT;
263
264	/*
265	* remove all backend timers here so that no timer expires after here.
266	*/
267	TIMER_LIST_LOCK(list);
268
269	key = INTLOCK();
270	for (entry = list->used; entry != NULL; entry = entry->next)
271	{
272		ecos_del_timer(entry);
273	}
274	INTUNLOCK(key);
275
276	TIMER_LIST_UNLOCK(list);
277
278	/*
279	* have to wait till all expired entries to have been handled
280	*/
281	for (entry = list->used; entry != NULL; entry = entry->next)
282	{
283		if ((entry->flags&TIMER_FLAG_DEFERRED) &&
284		    !(entry->flags&TIMER_FLAG_FINISHED))
285			break;
286	}
287
288	cyg_mutex_destroy(&(list->lock));
289	/* now it should be safe to blindly free all the resources */
290	TIMER_FREE_LOCK_MECHANISM();
291	free(list);
292	TIMERDBG("done");
293	return 0;
294}
295
296int
297bcm_timer_module_enable(bcm_timer_module_id module_id, int enable)
298{
299	return 0;
300}
301
302int
303bcm_timer_create(bcm_timer_module_id module_id, bcm_timer_id *timer_id)
304{
305	ecos_timer_list_t *list = (ecos_timer_list_t *)module_id;
306	ecos_timer_entry_t *entry;
307	int status = -1;
308
309	TIMERDBG("list %08x", list);
310
311	/* lock the timer list */
312	TIMER_LIST_LOCK(list);
313
314	/* check if timer is allowed */
315	if (list->flags & TIMER_LIST_FLAG_EXIT)
316		goto exit0;
317
318	/* alloc an entry first */
319	status = get_entry(&list->freed, &entry);
320	if (status != 0)
321		goto exit0;
322
323	/* add the entry into used list */
324	put_entry(&list->used, entry);
325
326	entry->flags = TIMER_FLAG_IN_USE;
327	entry->list = list;
328	*timer_id = (bcm_timer_id)(void *)entry;
329
330	TIMER_LIST_UNLOCK(list);
331	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
332	return 0;
333
334	/* error handling */
335exit0:
336	TIMER_LIST_UNLOCK(list);
337	return status;
338}
339
340int
341bcm_timer_delete(bcm_timer_id timer_id)
342{
343	ecos_timer_entry_t *entry = (ecos_timer_entry_t *)timer_id;
344	ecos_timer_list_t *list = entry->list;
345	int status;
346	int key;
347
348	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
349
350	/* make sure no interrupts can happen */
351	key = INTLOCK();
352
353	/* lock the timer list */
354	TIMER_LIST_LOCK(list);
355
356	/* remove the entry from the used list first */
357	status = remove_entry(&list->used, entry);
358	if (status != 0)
359		goto exit0;
360
361	/* delete the backend timer */
362	ecos_del_timer(entry);
363
364	/* free the entry back to freed list */
365	put_entry(&list->freed, entry);
366
367	entry->flags = TIMER_FLAG_NONE;
368	entry->list = NULL;
369	TIMER_LIST_UNLOCK(list);
370
371	INTUNLOCK(key);
372
373	TIMERDBG("done");
374	return 0;
375
376	/* error handling */
377exit0:
378	TIMER_LIST_UNLOCK(list);
379	INTUNLOCK(key);
380	return status;
381}
382
383int
384bcm_timer_gettime(bcm_timer_id timer_id, struct itimerspec *timer_spec)
385{
386	return -1;
387
388}
389
390int
391bcm_timer_settime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
392{
393	unsigned int ms;
394	ecos_timer_entry_t *entry = (ecos_timer_entry_t *)timer_id;
395	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
396
397	ms = (timer_spec->it_value.tv_sec*1000) +
398		((timer_spec->it_value.tv_nsec/1000)/1000);
399
400	return ecos_add_timer(entry, ms, 1) ? 0 : 1;
401}
402
403int
404bcm_timer_connect(bcm_timer_id timer_id, bcm_timer_cb func, int data)
405{
406	ecos_timer_entry_t *entry = (ecos_timer_entry_t *)timer_id;
407
408	entry->func = func;
409	entry->data = data;
410	TIMERDBG("entry %08x timer %08x func %08x data %08x",
411	         entry, entry->timer, entry->func, entry->data);
412
413	entry->flags |= TIMER_FLAG_ARMED;
414	return 0;
415}
416
417int
418bcm_timer_cancel(bcm_timer_id timer_id)
419{
420	ecos_timer_entry_t *entry = (ecos_timer_entry_t *)timer_id;
421
422	TIMERDBG("entry %08x timer %08x", entry, entry->timer);
423
424	ecos_del_timer(entry);
425	entry->flags &= ~TIMER_FLAG_ARMED;
426	return 0;
427}
428
429int
430bcm_timer_change_expirytime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
431{
432	bcm_timer_cancel(timer_id);
433	bcm_timer_settime(timer_id, timer_spec);
434	return 1;
435}
436
437#define clockid_t	int
438int
439clock_gettime(clockid_t clock_id, struct timespec *tp)
440{
441	struct timeval tv;
442	int n;
443
444	n = gettimeofday(&tv, NULL);
445	TIMEVAL_TO_TIMESPEC(&tv, tp);
446
447	return n;
448}
449