1/* 2 * Copyright (C) 2015, 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 241182 2011-02-17 21:50:03Z $ 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