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 241182 2011-02-17 21:50:03Z $ 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