| 1 | /*************************************************************************** |
|---|
| 2 | * Copyright (c) 2005-2012, Broadcom Corporation |
|---|
| 3 | * All Rights Reserved |
|---|
| 4 | * Confidential Property of Broadcom Corporation |
|---|
| 5 | * |
|---|
| 6 | * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE |
|---|
| 7 | * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR |
|---|
| 8 | * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. |
|---|
| 9 | * |
|---|
| 10 | * $brcm_Workfile: btmr.c $ |
|---|
| 11 | * $brcm_Revision: Hydra_Software_Devel/34 $ |
|---|
| 12 | * $brcm_Date: 2/21/12 2:19p $ |
|---|
| 13 | * |
|---|
| 14 | * Module Description: |
|---|
| 15 | * This is a magnum PI module used to control the hardware timers of a BCM7xxx part. |
|---|
| 16 | * This provides a means to create/destroy and start/stop timers when required. |
|---|
| 17 | * |
|---|
| 18 | * Revision History: |
|---|
| 19 | * |
|---|
| 20 | * $brcm_Log: /magnum/basemodules/tmr/7401/btmr.c $ |
|---|
| 21 | * |
|---|
| 22 | * Hydra_Software_Devel/34 2/21/12 2:19p erickson |
|---|
| 23 | * SW7425-2130: fix debug warnings |
|---|
| 24 | * |
|---|
| 25 | * Hydra_Software_Devel/33 8/26/11 5:03p rjlewis |
|---|
| 26 | * SW7425-1187: locking semaphore didn't lock enough. Need to block all |
|---|
| 27 | * other creates without impeding recursive creates (so creating virtual |
|---|
| 28 | * timer can create the two physical timers needed to process virtual |
|---|
| 29 | * timers). |
|---|
| 30 | * |
|---|
| 31 | * Hydra_Software_Devel/32 8/25/11 12:39p rjlewis |
|---|
| 32 | * SW7425-1187: added compile options for handling missing destroy calls. |
|---|
| 33 | * Added mutexes to protect counts (fixes dual destroy fail). Added |
|---|
| 34 | * tagged for destroy. General cleanup. |
|---|
| 35 | * |
|---|
| 36 | * Hydra_Software_Devel/31 8/18/11 4:29p rjlewis |
|---|
| 37 | * SW7346-460: comment around a coverity issue. |
|---|
| 38 | * |
|---|
| 39 | * Hydra_Software_Devel/30 8/18/11 4:10p rjlewis |
|---|
| 40 | * SW7346-460: If create failed -- tell them where it was asked to be |
|---|
| 41 | * created. |
|---|
| 42 | * |
|---|
| 43 | * Hydra_Software_Devel/29 8/18/11 3:36p rjlewis |
|---|
| 44 | * SW7346-460: make sure free-run timer was created before trying to |
|---|
| 45 | * destroy. |
|---|
| 46 | * |
|---|
| 47 | * Hydra_Software_Devel/28 8/16/11 6:50p rjlewis |
|---|
| 48 | * SW7346-460: checked in with "never leave enabled" flag defined. |
|---|
| 49 | * |
|---|
| 50 | * Hydra_Software_Devel/27 8/16/11 6:35p rjlewis |
|---|
| 51 | * SW7346-460: changed destroy timer loop to fix coverity issue. |
|---|
| 52 | * |
|---|
| 53 | * Hydra_Software_Devel/26 8/10/11 3:58p rjlewis |
|---|
| 54 | * SW3548-2526: don't try to remove virtual timer from linked list twice |
|---|
| 55 | * on close with missing destroy. |
|---|
| 56 | * |
|---|
| 57 | * Hydra_Software_Devel/25 8/10/11 2:40p rjlewis |
|---|
| 58 | * SW3548-2526: don't include ANY std includes -- causes compile issues. |
|---|
| 59 | * Don't use stdlib functions (compile errors). clarify that location is |
|---|
| 60 | * creation location. |
|---|
| 61 | * |
|---|
| 62 | * Hydra_Software_Devel/24 8/9/11 9:59a rjlewis |
|---|
| 63 | * SW3548-2526: Removed unused include that was causing build error on |
|---|
| 64 | * some platforms. |
|---|
| 65 | * |
|---|
| 66 | * Hydra_Software_Devel/23 8/8/11 2:54p rjlewis |
|---|
| 67 | * SW3548-2526: Merged in virtual timer branch. PR22129: SW3548-2526: |
|---|
| 68 | * incorrectly using unsigned long and uint32_t where unsigned correct. |
|---|
| 69 | * Missing leave critical in read. fixed names of values to reflect |
|---|
| 70 | * where converted to ticks. Fixed bug: not setting default timer |
|---|
| 71 | * register offset. SWDTV-5441: added support for secondary timer block. |
|---|
| 72 | * Fixed return variables from uint32 to uslongs. SW3548-2526: General |
|---|
| 73 | * clean-up SW3548-2526: general cleanup. Added jump tables for |
|---|
| 74 | * virt/phys processing. Keep track of virtual timers. Added _isr where |
|---|
| 75 | * required. Uses one less phys timer. SW35230-935: update branch so I |
|---|
| 76 | * can continue virtual timer changes before merging back into main. |
|---|
| 77 | * SW7550-300: Read physical timers value instead of virtual to get the |
|---|
| 78 | * time elapsed before deciding where to place new timer. SW7550-197: |
|---|
| 79 | * |
|---|
| 80 | * Hydra_Software_Devel/PR22129/8 7/29/11 2:02p rjlewis |
|---|
| 81 | * SW3548-2526: incorrectly using unsigned long and uint32_t where |
|---|
| 82 | * unsigned correct. Missing leave critical in read. fixed names of |
|---|
| 83 | * values to reflect where converted to ticks. Fixed bug: not setting |
|---|
| 84 | * default timer register offset. |
|---|
| 85 | * |
|---|
| 86 | * Hydra_Software_Devel/PR22129/7 5/25/11 3:48p rjlewis |
|---|
| 87 | * SWDTV-5441: added support for secondary timer block. Fixed return |
|---|
| 88 | * variables from uint32 to uslongs. |
|---|
| 89 | * |
|---|
| 90 | * Hydra_Software_Devel/PR22129/6 3/18/11 6:05p rjlewis |
|---|
| 91 | * SW3548-2526: General clean-up |
|---|
| 92 | * |
|---|
| 93 | * Hydra_Software_Devel/PR22129/5 3/16/11 11:17a rjlewis |
|---|
| 94 | * SW3548-2526: general cleanup. Added jump tables for virt/phys |
|---|
| 95 | * processing. Keep track of virtual timers. Added _isr where required. |
|---|
| 96 | * Uses one less phys timer. |
|---|
| 97 | * |
|---|
| 98 | * Hydra_Software_Devel/PR22129/4 2/9/11 3:42p rjlewis |
|---|
| 99 | * SW35230-935: update branch so I can continue virtual timer changes |
|---|
| 100 | * before merging back into main. |
|---|
| 101 | * |
|---|
| 102 | * Hydra_Software_Devel/22 8/17/10 5:46p rjlewis |
|---|
| 103 | * SW35230-935: Added support for tagged version of create (so we can find |
|---|
| 104 | * where timers are being created). |
|---|
| 105 | * Don't stop non-countdown timers so anyone using timer outside PI |
|---|
| 106 | * (exclusive & shared) won't hang. |
|---|
| 107 | * Cleaned up unregistering of callbacks on close. |
|---|
| 108 | * Cleaned up message debug prints and entry/exit prints. |
|---|
| 109 | * |
|---|
| 110 | * Hydra_Software_Devel/21 8/13/10 7:00p rjlewis |
|---|
| 111 | * SW3548-2526: fixed coverity issues where missing free on error after |
|---|
| 112 | * malloc. |
|---|
| 113 | * |
|---|
| 114 | * Hydra_Software_Devel/20 8/6/10 5:49p rjlewis |
|---|
| 115 | * SW3548-2526: merge virtual timers into main |
|---|
| 116 | * |
|---|
| 117 | * Hydra_Software_Devel/PR22129/3 3/12/10 5:16p rjlewis |
|---|
| 118 | * SW7550-300: Read physical timers value instead of virtual to get the |
|---|
| 119 | * time elapsed before deciding where to place new timer. |
|---|
| 120 | * SW7550-197: Timeing splice allowed timer to interrupt after I took it |
|---|
| 121 | * off the queue. Need to stop it first THEN take it off the queue. |
|---|
| 122 | * |
|---|
| 123 | * Hydra_Software_Devel/PR22129/2 11/20/09 4:52p rjlewis |
|---|
| 124 | * SW3548-2526: rewote virtual timers to use separate hardware timers to |
|---|
| 125 | * control free-run versus count-down virtual timers. |
|---|
| 126 | * |
|---|
| 127 | * Hydra_Software_Devel/PR22129/1 1/30/07 11:57a rjlewis |
|---|
| 128 | * PR22129: added support for virtal timers. |
|---|
| 129 | * |
|---|
| 130 | * Hydra_Software_Devel/19 11/20/09 4:27p rjlewis |
|---|
| 131 | * SW7420-391: Added David's tagged to find where timers being used. This |
|---|
| 132 | * also uses object definitions instead of magic values. |
|---|
| 133 | * SW7335-485: fixes bug in returning error on open when last timer fails |
|---|
| 134 | * to create callback interrupt. |
|---|
| 135 | * |
|---|
| 136 | * Hydra_Software_Devel/18 6/12/08 1:05p vsilyaev |
|---|
| 137 | * PR 43317: Properly report an error |
|---|
| 138 | * |
|---|
| 139 | * Hydra_Software_Devel/17 10/27/06 11:03a rjlewis |
|---|
| 140 | * PR25238: functions with no parameters need void as per ANSI. |
|---|
| 141 | * |
|---|
| 142 | * Hydra_Software_Devel/16 6/8/06 11:22a rjlewis |
|---|
| 143 | * PR18214: remove warning when not compiling for debug. |
|---|
| 144 | * |
|---|
| 145 | * Hydra_Software_Devel/8 4/28/06 1:41p rjlewis |
|---|
| 146 | * PR18214: added an ISR version of the read timer function. |
|---|
| 147 | * |
|---|
| 148 | * Hydra_Software_Devel/7 4/25/06 2:42p jgarrett |
|---|
| 149 | * PR 21220: Removing BCHP_INT_ID defines |
|---|
| 150 | * |
|---|
| 151 | * Hydra_Software_Devel/5 1/13/06 12:32p jgarrett |
|---|
| 152 | * PR 19007: Updating base modules for 7400 |
|---|
| 153 | * |
|---|
| 154 | * Hydra_Software_Devel/Refsw_Devel_7400_A0/1 1/12/06 3:08p jgarrett |
|---|
| 155 | * PR 19007: Update timer for 7400 |
|---|
| 156 | * |
|---|
| 157 | * Hydra_Software_Devel/4 1/10/06 11:26a rjlewis |
|---|
| 158 | * PR18214: remove warnings. |
|---|
| 159 | * |
|---|
| 160 | * Hydra_Software_Devel/3 12/13/05 3:39p rjlewis |
|---|
| 161 | * PR18588: Don't process the interrupt if timer never created. |
|---|
| 162 | * |
|---|
| 163 | * Hydra_Software_Devel/2 12/8/05 11:28a erickson |
|---|
| 164 | * PR18214: remove duplicate typedef. uclibc -pedantic doesn't like it. |
|---|
| 165 | * |
|---|
| 166 | * Hydra_Software_Devel/1 11/21/05 2:05p rjlewis |
|---|
| 167 | * PR18214: Initial version. |
|---|
| 168 | * |
|---|
| 169 | ***************************************************************************/ |
|---|
| 170 | #include "bstd.h" |
|---|
| 171 | #include "bkni.h" |
|---|
| 172 | #include "bkni_multi.h" |
|---|
| 173 | #include "bint.h" |
|---|
| 174 | #include "bchp_common.h" |
|---|
| 175 | #include "bchp_timer.h" |
|---|
| 176 | #include "bchp_int_id_timer.h" |
|---|
| 177 | #include "blst_list.h" /* double linked list code */ |
|---|
| 178 | #include "btmr.h" |
|---|
| 179 | |
|---|
| 180 | /* Can't use std includes -- causes conflicts on some builds */ |
|---|
| 181 | /*#include <stdio.h>*/ |
|---|
| 182 | /*#include <string.h>*/ |
|---|
| 183 | |
|---|
| 184 | #include "bdbg.h" |
|---|
| 185 | BDBG_MODULE(btmr); |
|---|
| 186 | |
|---|
| 187 | /* Note: |
|---|
| 188 | ** This file contains two complete sets of timer control functions. |
|---|
| 189 | ** That is, there is one set to control exclusive and/or shared timers (phys_) and another to control virtual timers (virt_). |
|---|
| 190 | ** The published interface functions test which type of timer is being used and calls the appropriate set. |
|---|
| 191 | */ |
|---|
| 192 | |
|---|
| 193 | /* Shared Timer Notes: |
|---|
| 194 | ** The concept of a shared timer was added prior to adding virtual timers. This allowed users to request a physical timer |
|---|
| 195 | ** for reading only (delay operations) but share it with other requestors. A physical timer is used for this purpose. |
|---|
| 196 | ** Since the address to this timer is returned to each requestor (that wants to read the time without using this PI) |
|---|
| 197 | ** it is possible for any of the users to stop the timer -- this would be bad! When virtual timers were introduced it was |
|---|
| 198 | ** decided to keep the shared timer as there is firmware in ancillary processors/dsps that can't use the PI but can read the |
|---|
| 199 | ** timer registers. This lets them use a timer without having to use a unique physical timer. |
|---|
| 200 | */ |
|---|
| 201 | |
|---|
| 202 | /* Virtual Timer Notes: |
|---|
| 203 | ** This interface supports two types of virtual timers: stop-watch (or free-run) and count-down. |
|---|
| 204 | ** One physical timer is used for the virtual count-down timer and the shared timer is used to support the virtual free-run timers. |
|---|
| 205 | ** The physical timer for count-down timers is ONLY used to generate the interrupt. All the time calculations are done |
|---|
| 206 | ** using the shared free-run timer (for both the virtual count-down and virtual free-run timers). |
|---|
| 207 | ** |
|---|
| 208 | ** When a count-down timer is pending, the physical timer counts the virtual count-down timer that has the smallest count-down period. |
|---|
| 209 | ** A sorted list of the count-down timers is managed. When a count-down timer is added to the list it is added in the list |
|---|
| 210 | ** based on its timeout time before an item that has a larger timeout period. The difference between that item and its |
|---|
| 211 | ** preceeding entry defines how long the timeout needs to be for that timer. |
|---|
| 212 | ** |
|---|
| 213 | ** The timer for free-run virtual timers is started at open time and the current time is used to provide the caller |
|---|
| 214 | ** with a virtual time value (using the time when the user created the virtual timer). |
|---|
| 215 | */ |
|---|
| 216 | |
|---|
| 217 | /* TODO: |
|---|
| 218 | ** 1) Instead of using two timers to support virtual timers, try to use the free-run timer for the timeout by just letting it run |
|---|
| 219 | ** but set a new match value for the next timeout. The pain would be in the inserting of a new virtual count-down timer that is |
|---|
| 220 | ** less (smaller timeout) than the one that is currently running. To calculate the time already run would be a bit more math. |
|---|
| 221 | ** 2) Allow reading the registers for a virtual stop-watch timer (return the registers for the free-run timer used for supporting |
|---|
| 222 | ** the virtual timers). Then convert the create request for a shared timer to a virtual stop-watch and delete the concept of a |
|---|
| 223 | ** shared timer from the documentation (depricated). |
|---|
| 224 | ** 3) BTMR_ReadTimer should not return status as it can never fail -- return value instead. This would require a change to the PI. |
|---|
| 225 | ** 4) Change BTMR_ReadTimerMax to allow specifying a handle in case future timers blocks have different wrap points. |
|---|
| 226 | */ |
|---|
| 227 | |
|---|
| 228 | /**** Compile-time options ****/ |
|---|
| 229 | |
|---|
| 230 | #define _FAIL_ON_CLOSE_ /* enable to cause close to fail on resource leaks (timers not returned). */ |
|---|
| 231 | /* The above is useful when users aren't looking at the error messages on close failures. |
|---|
| 232 | ** Without the fail the user can over-look the issue and not do something about it. |
|---|
| 233 | ** With this enable, BKNI_Fails will occur when handles are not returned on close! |
|---|
| 234 | */ |
|---|
| 235 | |
|---|
| 236 | /*#define _DESTROY_ON_CLOSE_*/ /* enable to cause close to try to clean up the resource leaks. */ |
|---|
| 237 | /* The above will try to destroy all the timers that were left pending. |
|---|
| 238 | ** The main benefit is that we'll return all the memory we allocated (on user's behalf) and will destroy any pending handles. |
|---|
| 239 | ** This means that if anyone tries to use any of the timer functions after the close that we'll assert on the handle. |
|---|
| 240 | ** Note that this is wasted effort if you enable the _FAIL_ON_CLOSE_ option aove. |
|---|
| 241 | */ |
|---|
| 242 | |
|---|
| 243 | /* Define a minimum timeout period where its just not prudent to wait for the timer to timeout -- i.e. close enough! |
|---|
| 244 | ** The danger here is that if they create a timer with less than the minimum hold time then we'll continue to |
|---|
| 245 | ** process that timer in this loop. So, if you want to use the MIN_HOLD you MUST ensure that no periodic timer gets |
|---|
| 246 | ** created with a timeout period less than this timeout or we'll just hang! |
|---|
| 247 | ** Note: this is different than the BTMR_MINIMUM_TIMEOUT value that decides a minimum value for periodic timers. |
|---|
| 248 | */ |
|---|
| 249 | #if 1 |
|---|
| 250 | #define BTMR_MIN_HOLD 27 /* this is microsecond->timer conversion value */ |
|---|
| 251 | #else |
|---|
| 252 | #define BTMR_MIN_HOLD (27*5) |
|---|
| 253 | #endif |
|---|
| 254 | |
|---|
| 255 | /**** Misc definitions ****/ |
|---|
| 256 | |
|---|
| 257 | #define NumberOfTimers 4 /* this hardware has this many instances of this physical timer */ |
|---|
| 258 | /* Note: This definition should be made into a platform definition should any platform implement a different number of sequential timers. */ |
|---|
| 259 | |
|---|
| 260 | #define TimerWrap (1<<30) /* timer wraps at this count (is 39.7 seconds) */ |
|---|
| 261 | #define MaxTimerValue (TimerWrap-1) /* the max timer count is one less than the wrap point */ |
|---|
| 262 | |
|---|
| 263 | #define TimerSelect(timer) (1<<(timer)) /* the timer is selected by the bit number */ |
|---|
| 264 | |
|---|
| 265 | /* Don't print out path when printing out file name (paths are just so long -- makes reading log harder) */ |
|---|
| 266 | /* Note: can't default this because can't include <string.h> without generating compile errors on some builds */ |
|---|
| 267 | #if 0 |
|---|
| 268 | #include <string.h> |
|---|
| 269 | /*char *strchr(const char *s, int c);*/ |
|---|
| 270 | /*char *strrchr(const char *s, int c);*/ |
|---|
| 271 | #define shortenFileName(file) if (strchr((file),'/')) (file) = strrchr((file),'/')+1; |
|---|
| 272 | #else |
|---|
| 273 | #define shortenFileName(file) |
|---|
| 274 | #endif |
|---|
| 275 | |
|---|
| 276 | /* Forward reference */ |
|---|
| 277 | static BERR_Code _BTMR_CreateTimer(BTMR_Handle device, BTMR_TimerHandle *phTimer, const BTMR_TimerSettings *pSettings, const char *file, int line); |
|---|
| 278 | static BERR_Code _BTMR_DestroyTimer(BTMR_TimerHandle timer, const char*file, int line); |
|---|
| 279 | |
|---|
| 280 | /* There are different entry points for each of the timer handler functions based on whether the timer is a physical or virtual timer. |
|---|
| 281 | ** Rather than make the decision each time the function is called I use a table to use the correct function. |
|---|
| 282 | ** This table gets set at the timer creation. |
|---|
| 283 | */ |
|---|
| 284 | typedef struct { |
|---|
| 285 | BERR_Code (*create)(BTMR_Handle device, BTMR_TimerHandle timer, const BTMR_TimerSettings *pSettings); |
|---|
| 286 | BERR_Code (*destroy_isr)(BTMR_TimerHandle timer); |
|---|
| 287 | BERR_Code (*start_isr)(BTMR_TimerHandle timer, unsigned startingValue); |
|---|
| 288 | BERR_Code (*stop_isr)(BTMR_TimerHandle timer); |
|---|
| 289 | unsigned (*read_isr)(BTMR_TimerHandle timer); |
|---|
| 290 | } ProcessingFunctions; |
|---|
| 291 | |
|---|
| 292 | BDBG_OBJECT_ID(btmr_timer_t); |
|---|
| 293 | |
|---|
| 294 | /* This is the data associated with an individual timer. |
|---|
| 295 | ** An opaque pointer to one of these (handle) is returned on a successful create timer request. |
|---|
| 296 | */ |
|---|
| 297 | struct BTMR_P_TimerContext { |
|---|
| 298 | BDBG_OBJECT(btmr_timer_t) /* used to check if structure is valid */ |
|---|
| 299 | |
|---|
| 300 | BTMR_Handle device; /* the device handle for this timer (passed in on create) */ |
|---|
| 301 | BTMR_TimerSettings Settings; /* the settings passed in for this timer (passed in on create) */ |
|---|
| 302 | uint32_t initialValue; /* how long should this timer run (passed in on start) */ |
|---|
| 303 | |
|---|
| 304 | BLST_D_ENTRY(BTMR_P_TimerContext) created; /* for linking queue of created timers */ |
|---|
| 305 | const char *file; int line; /* this is location where timer was created (will help in tracking down missing destroys) */ |
|---|
| 306 | |
|---|
| 307 | BTMR_TimerRegisters Registers; /* the offsets to the control and status registers */ |
|---|
| 308 | |
|---|
| 309 | ProcessingFunctions *functions; /* the table of processing functions for this type of timer (virtual versus physical) */ |
|---|
| 310 | |
|---|
| 311 | int timerNumber; /* this indicates which timer was allocated (-1 for virtual timers) */ |
|---|
| 312 | uint32_t lastValue; /* this is the last value of the timer when it was stopped */ |
|---|
| 313 | bool cbEnabled; /* indicates that I enabled the interrupt in the create */ |
|---|
| 314 | |
|---|
| 315 | BLST_D_ENTRY(BTMR_P_TimerContext) link; /* for linking onto a virtual timeout queue */ |
|---|
| 316 | |
|---|
| 317 | uint32_t startingValue; /* virtual timers need to record the value of the timer when it was started */ |
|---|
| 318 | uint32_t adjustedValue; /* how long should this timer run when put on virtual timeout (difference from the timer before it) */ |
|---|
| 319 | |
|---|
| 320 | bool running; /* flag, so we know if the timer is already running -- shouldn't start a running or stop a stopped timer */ |
|---|
| 321 | bool processing; /* this timer is being processed so don't try to stop or delete */ |
|---|
| 322 | |
|---|
| 323 | volatile bool pleaseStop; /* this periodic timer was stopped in the callback so don't restart it */ |
|---|
| 324 | volatile bool pleaseStart; /* this count-down timer was started in the callback so restart it */ |
|---|
| 325 | unsigned delayedValue; /* the starting value when asked to (re)start from a call-back handler */ |
|---|
| 326 | }; |
|---|
| 327 | |
|---|
| 328 | BDBG_OBJECT_ID(btmr_device_t); |
|---|
| 329 | |
|---|
| 330 | /* This is the data associated with the timer module (group of timers). |
|---|
| 331 | ** An opaque pointer to one of these (handle) is returned on a successful module open call. |
|---|
| 332 | */ |
|---|
| 333 | struct BTMR_P_Context { |
|---|
| 334 | BDBG_OBJECT(btmr_device_t) /* used to check if structure is valid */ |
|---|
| 335 | BCHP_Handle hChip; /* the chip handle (passed in on open) */ |
|---|
| 336 | BREG_Handle hRegister; /* the register handle (passed in on open) */ |
|---|
| 337 | BINT_Handle hInterrupt; /* the interrupt handle (passed in on open) */ |
|---|
| 338 | |
|---|
| 339 | BTMR_DeviceSettings Settings; /* this is the list of available timers -- provided on request */ |
|---|
| 340 | uint32_t inUse; /* bitmask which specifies which timers are already in use by this code */ |
|---|
| 341 | |
|---|
| 342 | BTMR_TimerHandle Timers[NumberOfTimers]; /* list of allocated timers (so we can stop/destroy them on close) */ |
|---|
| 343 | BINT_CallbackHandle CbHandle[NumberOfTimers]; /* interrupt callbacks for each timer we control */ |
|---|
| 344 | |
|---|
| 345 | BINT_CallbackHandle alt_CbHandle; /* interrupt callbacks for secondary block if interrupt is handled as Level 3 */ |
|---|
| 346 | unsigned int alt_CbEnabled; /* flag to indicate whether alternate callback is enabled (enable on 0->1 transition, disable on 1->0 transition) */ |
|---|
| 347 | |
|---|
| 348 | BTMR_TimerHandle SharedTimer; /* timer for shared timer use */ |
|---|
| 349 | BTMR_TimerHandle CountDownTimer; /* timer for managing virtual count-down timers */ |
|---|
| 350 | BTMR_TimerHandle FreeRunTimer; /* timer for managing virtual free-run timers */ |
|---|
| 351 | |
|---|
| 352 | BLST_D_HEAD(TOL, BTMR_P_TimerContext) TimeoutList; /* the list of vitrual count-down/periodic timers waiting to timeout */ |
|---|
| 353 | BLST_D_HEAD(TBPL, BTMR_P_TimerContext) ToBeProcessedList; /* the list of vitrual count-down/periodic timers waiting to be processed */ |
|---|
| 354 | |
|---|
| 355 | BKNI_MutexHandle create_destroy_mutex; /* semaphore to protect against re-entrant code */ |
|---|
| 356 | |
|---|
| 357 | unsigned int SharedCount; /* count of users using this shared timer (destroy when count goes to 0) */ |
|---|
| 358 | unsigned int VirtualCount; /* count of outstanding virtual timers (for displaying error on close) */ |
|---|
| 359 | |
|---|
| 360 | BLST_D_HEAD(VTCL, BTMR_P_TimerContext) VirtualCreateList; /* the list of vitrual timers created */ |
|---|
| 361 | }; |
|---|
| 362 | |
|---|
| 363 | /*\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\*/ |
|---|
| 364 | |
|---|
| 365 | /* The timers run at 27Mhz (27,000,000 times a second) and a microsecond is 1,000,000th of a second, so ... */ |
|---|
| 366 | #define ConversionFactor (27000000/1000000) /* <- yeah, I know, its 27 -- but the compiler will figure that out, right? */ |
|---|
| 367 | #define TimerToMicroSeconds(timerValue) ((unsigned)((unsigned)(timerValue) / ConversionFactor)) |
|---|
| 368 | #define MicroSecondsToTimer(microSeconds) ((unsigned)((unsigned)(microSeconds) * ConversionFactor)) |
|---|
| 369 | |
|---|
| 370 | /*\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\*/ |
|---|
| 371 | |
|---|
| 372 | /* These are used to get the timer control/status register for specific timer set/number */ |
|---|
| 373 | #define CONTROL_REG(device, timer) \ |
|---|
| 374 | (((device)->Settings.baseRegister + (BCHP_TIMER_TIMER0_CTRL-BCHP_TIMER_REG_START)) + ((int)(timer) * (BCHP_TIMER_TIMER1_CTRL-BCHP_TIMER_TIMER0_CTRL))) |
|---|
| 375 | #define STATUS_REG(device, timer) \ |
|---|
| 376 | (((device)->Settings.baseRegister + (BCHP_TIMER_TIMER0_STAT-BCHP_TIMER_REG_START)) + ((int)(timer) * (BCHP_TIMER_TIMER1_STAT-BCHP_TIMER_TIMER0_STAT))) |
|---|
| 377 | |
|---|
| 378 | /* These are used to get the interrupt status and enable/disable the interrupts when using a secondary timer block */ |
|---|
| 379 | #define TIMER_IS_REG(device) \ |
|---|
| 380 | ((device)->Settings.baseRegister + (BCHP_TIMER_TIMER_IS-BCHP_TIMER_REG_START)) |
|---|
| 381 | #define TIMER_IE_REG(device) \ |
|---|
| 382 | ((device)->Settings.baseRegister + (BCHP_TIMER_TIMER_IE0-BCHP_TIMER_REG_START)) |
|---|
| 383 | |
|---|
| 384 | /*\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\*/ |
|---|
| 385 | |
|---|
| 386 | /* This allocates one of the unused timers and returns its timer number (returns -1 if none available) */ |
|---|
| 387 | /* We start from last to first in case someone uses a timer (assuming they picked 0) outside of this PI and didn't tell us! */ |
|---|
| 388 | static int SelectAnAvailableTimer_isr(BTMR_Handle device) |
|---|
| 389 | { |
|---|
| 390 | int timer, selected=-1; |
|---|
| 391 | for (timer=NumberOfTimers-1; timer >= 0; timer--) |
|---|
| 392 | { |
|---|
| 393 | if (!(device->inUse & TimerSelect(timer))) |
|---|
| 394 | { |
|---|
| 395 | device->inUse |= TimerSelect(timer); /* this timer is now in use (should anybody ask) */ |
|---|
| 396 | selected = timer; /* this is the timer I've selected just for you */ |
|---|
| 397 | break; |
|---|
| 398 | } |
|---|
| 399 | } |
|---|
| 400 | return selected; |
|---|
| 401 | } |
|---|
| 402 | |
|---|
| 403 | /* This frees a specific (selected) timer (makes it available again) */ |
|---|
| 404 | static void ReleaseThisTimer_isr(BTMR_Handle device, int timerNumber) |
|---|
| 405 | { |
|---|
| 406 | device->inUse &= ~TimerSelect(timerNumber); /* not in use anymore */ |
|---|
| 407 | } |
|---|
| 408 | |
|---|
| 409 | #if BDBG_DEBUG_BUILD |
|---|
| 410 | static char *_GetTimerType(bool exclusive, BTMR_Type type) |
|---|
| 411 | { |
|---|
| 412 | if (type == BTMR_Type_eSharedFreeRun) return "Shared"; |
|---|
| 413 | if (exclusive) |
|---|
| 414 | { |
|---|
| 415 | switch (type) { |
|---|
| 416 | case BTMR_Type_eCountDown: return "Exclusive/CountDown"; |
|---|
| 417 | case BTMR_Type_ePeriodic: return "Exclusive/Periodic"; |
|---|
| 418 | case BTMR_Type_eStopWatch: return "Exclusive/StopWatch"; |
|---|
| 419 | default: return "Unknown"; |
|---|
| 420 | } |
|---|
| 421 | } |
|---|
| 422 | else |
|---|
| 423 | { |
|---|
| 424 | switch (type) { |
|---|
| 425 | case BTMR_Type_eCountDown: return "Virtual/CountDown"; |
|---|
| 426 | case BTMR_Type_ePeriodic: return "Virtual/Periodic"; |
|---|
| 427 | case BTMR_Type_eStopWatch: return "Virtual/StopWatch"; |
|---|
| 428 | default: return "Unknown"; |
|---|
| 429 | } |
|---|
| 430 | } |
|---|
| 431 | } |
|---|
| 432 | static char *GetTimerType(const BTMR_TimerSettings *pSettings) { return _GetTimerType(pSettings->exclusive, pSettings->type); } |
|---|
| 433 | #endif /*BDBG_DEBUG_BUILD*/ |
|---|
| 434 | |
|---|
| 435 | /*\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\*/ |
|---|
| 436 | |
|---|
| 437 | /* This talks to the hardware to enable the specified timer */ |
|---|
| 438 | static void EnableTimer_isr(BTMR_TimerHandle timer) |
|---|
| 439 | { |
|---|
| 440 | uint32_t control_value, timeout; |
|---|
| 441 | /*uint32_t was = BREG_Read32_isr(timer->device->hRegister, timer->Registers.control);*/ |
|---|
| 442 | |
|---|
| 443 | /* Note: we always use Free-run mode! |
|---|
| 444 | ** I haven't figured out the difference between a count-down and free-run timer. |
|---|
| 445 | ** They seem to function exactly the same (count up to matching value, interrupt, and restart at 0). |
|---|
| 446 | */ |
|---|
| 447 | |
|---|
| 448 | if (timer->Settings.type == BTMR_Type_eStopWatch || timer->Settings.type == BTMR_Type_eSharedFreeRun) { |
|---|
| 449 | /* Stop-watch timers count up until they wrap */ |
|---|
| 450 | /* Counts up from 0 to the maximum timer value that the timer can handle (then it resets to zero and continues to count up) */ |
|---|
| 451 | timeout = MaxTimerValue; |
|---|
| 452 | } else { |
|---|
| 453 | /* Count-down and Periodic timers count up to matching value */ |
|---|
| 454 | /* This is the same as if they STARTED at the initial value and counted down (since I can't get count-down mode to work) */ |
|---|
| 455 | timeout = timer->initialValue; |
|---|
| 456 | } |
|---|
| 457 | |
|---|
| 458 | control_value = BCHP_MASK(TIMER_TIMER0_CTRL,ENA) | (BCHP_FIELD_DATA(TIMER_TIMER0_CTRL, TIMEOUT_VAL, timeout) & BCHP_MASK(TIMER_TIMER0_CTRL,TIMEOUT_VAL)); |
|---|
| 459 | |
|---|
| 460 | /*BDBG_MSG(("EnableTimer_isr: Writing 0x%08x (was 0x%08x) to control register (offset=%08x)", control_value, was, timer->Registers.control));*/ |
|---|
| 461 | BREG_Write32_isr(timer->device->hRegister, timer->Registers.control, control_value); |
|---|
| 462 | timer->running = true; |
|---|
| 463 | } |
|---|
| 464 | |
|---|
| 465 | /* This talks to the hardware to disable the specified timer */ |
|---|
| 466 | static void DisableTimer_isr(BTMR_TimerHandle timer) |
|---|
| 467 | { |
|---|
| 468 | uint32_t control_value, was; |
|---|
| 469 | |
|---|
| 470 | control_value = was = BREG_Read32_isr(timer->device->hRegister, timer->Registers.control); |
|---|
| 471 | control_value &= ~BCHP_MASK(TIMER_TIMER0_CTRL,ENA); |
|---|
| 472 | |
|---|
| 473 | /*BDBG_MSG(("DisableTimer_isr: Writing 0x%08x (was 0x%08x) to control register (offset=%08x)", control_value, was, timer->Registers.control));*/ |
|---|
| 474 | BREG_Write32_isr(timer->device->hRegister, timer->Registers.control, control_value); |
|---|
| 475 | timer->running = false; |
|---|
| 476 | } |
|---|
| 477 | |
|---|
| 478 | /* This is special case version that disables the timer BEFORE we have a timer structure created for this timer (i.e. at init time) */ |
|---|
| 479 | static void DisableTimerNumber(BTMR_Handle device, int timerNumber) |
|---|
| 480 | { |
|---|
| 481 | uint32_t control_register = CONTROL_REG(device, timerNumber); |
|---|
| 482 | uint32_t control_value, was; |
|---|
| 483 | |
|---|
| 484 | control_value = was = BREG_Read32_isr(device->hRegister, control_register); |
|---|
| 485 | control_value &= ~BCHP_MASK(TIMER_TIMER0_CTRL,ENA); |
|---|
| 486 | |
|---|
| 487 | /*BDBG_MSG(("DisableTimerNumber: Writing 0x%08x (was 0x%08x) to control register (offset=%08x)", control_value, was, control_register));*/ |
|---|
| 488 | BREG_Write32(device->hRegister, control_register, control_value); |
|---|
| 489 | } |
|---|
| 490 | |
|---|
| 491 | /* This talks to hardware to get the timer value */ |
|---|
| 492 | static uint32_t GetTimerValue_isr(BTMR_TimerHandle timer) |
|---|
| 493 | { |
|---|
| 494 | uint32_t status_value = BREG_Read32_isr(timer->device->hRegister, timer->Registers.status); |
|---|
| 495 | status_value &= MaxTimerValue; /* bits 30&31 are reserved but are sometimes set for some reason */ |
|---|
| 496 | return status_value; |
|---|
| 497 | } |
|---|
| 498 | |
|---|
| 499 | /* |
|---|
| 500 | ******************************************************************************************************* |
|---|
| 501 | ******************************************************************************************************* |
|---|
| 502 | ** These are the set of PHYSICAL (exclusive or shared) timer functions (and their utility functions). |
|---|
| 503 | ******************************************************************************************************* |
|---|
| 504 | ******************************************************************************************************* |
|---|
| 505 | */ |
|---|
| 506 | |
|---|
| 507 | static BERR_Code JustStartTimer_isr(BTMR_TimerHandle timer, uint32_t initialValue) |
|---|
| 508 | { |
|---|
| 509 | /* We don't allow starting a running timer (why not?) -- you must stop it first (???) */ |
|---|
| 510 | /* The problem here is that if they forgot to stop the timer and believe that this is actually |
|---|
| 511 | ** starting the timer then they aren't going to get what they expected and won't know why. |
|---|
| 512 | ** We have three choices here: 1) don't let them start a started timer, 2) stop the timer and restart |
|---|
| 513 | ** with the new parameters, or 3) just say okay and keep going. |
|---|
| 514 | */ |
|---|
| 515 | if (timer->running) { |
|---|
| 516 | #if 0 |
|---|
| 517 | BDBG_ERR(("BTMR_StartTimer: timer already running!")); |
|---|
| 518 | return BERR_TRACE(BTMR_ERR_ALREADY_STARTED); |
|---|
| 519 | #else |
|---|
| 520 | goto done; /* its already started -- success! (this is choice 3) */ |
|---|
| 521 | #endif |
|---|
| 522 | } |
|---|
| 523 | |
|---|
| 524 | /* Can't use an initial value of zero for count-down and periodic timers! */ |
|---|
| 525 | if ((timer->Settings.type == BTMR_Type_eCountDown || timer->Settings.type == BTMR_Type_ePeriodic) && !initialValue) { |
|---|
| 526 | BDBG_ERR(("BTMR_StartTimer: can't use zero initial value with Count-down and Periodic timers!")); |
|---|
| 527 | return BERR_TRACE(BTMR_ERR_NO_TIMEOUT_GIVEN); |
|---|
| 528 | } |
|---|
| 529 | |
|---|
| 530 | /* We only have specific number of bits worth of timer value */ |
|---|
| 531 | if (initialValue >= MaxTimerValue) { |
|---|
| 532 | BDBG_ERR(("BTMR_StartTimer: initial value too large!")); |
|---|
| 533 | return BERR_TRACE(BTMR_ERR_TIMEOUT_TOO_LARGE); |
|---|
| 534 | } |
|---|
| 535 | |
|---|
| 536 | timer->initialValue = initialValue; |
|---|
| 537 | timer->lastValue = 0; |
|---|
| 538 | |
|---|
| 539 | EnableTimer_isr(timer); |
|---|
| 540 | |
|---|
| 541 | done: |
|---|
| 542 | return BERR_SUCCESS; |
|---|
| 543 | } |
|---|
| 544 | |
|---|
| 545 | static uint32_t JustReadTimer_isr(BTMR_TimerHandle timer) |
|---|
| 546 | { |
|---|
| 547 | uint32_t runTime; |
|---|
| 548 | |
|---|
| 549 | /* Stopwatch (FreeRun) timers count up -- just give them the value of the timer */ |
|---|
| 550 | runTime = GetTimerValue_isr(timer); |
|---|
| 551 | |
|---|
| 552 | /* printf("runtime = %d (0x%x) type=%s [initial = %d (0x%x)]\n", |
|---|
| 553 | runTime, runTime, GetTimerType(&timer->Settings), timer->initialValue, timer->initialValue); |
|---|
| 554 | */ |
|---|
| 555 | /* Count-down and Periodic timers count down (but don't let it go negative) */ |
|---|
| 556 | /* Note: we always use the hardware timer in a count UP mode (i.e. count up to matching value). |
|---|
| 557 | ** If free-run then reading the timer IS the count. In count-down we want the value of the timer |
|---|
| 558 | ** (or what it WOULD be if it was really counting down). Thus, we do some math and make sure its capped at zero. |
|---|
| 559 | */ |
|---|
| 560 | if (runTime && (timer->Settings.type == BTMR_Type_eCountDown || timer->Settings.type == BTMR_Type_ePeriodic)) |
|---|
| 561 | runTime = (timer->initialValue > runTime) ? (timer->initialValue - runTime) : 0; |
|---|
| 562 | |
|---|
| 563 | /* printf("runtime = %d (0x%x) type=%s [initial = %d (0x%x)]\n", |
|---|
| 564 | runTime, runTime, GetTimerType(&timer->Settings), timer->initialValue, timer->initialValue); |
|---|
| 565 | */ |
|---|
| 566 | return runTime; |
|---|
| 567 | } |
|---|
| 568 | |
|---|
| 569 | static BERR_Code JustStopTimer_isr(BTMR_TimerHandle timer) |
|---|
| 570 | { |
|---|
| 571 | /* Should I let them stop a stopped timer??? */ |
|---|
| 572 | /* We have two choices here: 1) don't let them stop a stopped timer or 2) just say okay and keep going. */ |
|---|
| 573 | if (!timer->running) { |
|---|
| 574 | #if 0 |
|---|
| 575 | BDBG_ERR(("BTMR_StopTimer: timer already stopped!")); |
|---|
| 576 | return BERR_TRACE(BTMR_ERR_ALREADY_STOPPED); |
|---|
| 577 | #else |
|---|
| 578 | goto done; /* its already stopped -- success! (this is choice 2) */ |
|---|
| 579 | #endif |
|---|
| 580 | } |
|---|
| 581 | |
|---|
| 582 | /* We're going to save off the last value of the timer when they stopped it. |
|---|
| 583 | ** That way, if they read it AFTER it stopped we can tell them what it was. |
|---|
| 584 | ** I'm pretty sure this is required as reading the status after stopping returns zero! |
|---|
| 585 | */ |
|---|
| 586 | timer->lastValue = JustReadTimer_isr(timer); |
|---|
| 587 | |
|---|
| 588 | DisableTimer_isr(timer); |
|---|
| 589 | |
|---|
| 590 | done: |
|---|
| 591 | return BERR_SUCCESS; |
|---|
| 592 | } |
|---|
| 593 | |
|---|
| 594 | /* |
|---|
| 595 | ** This is my ISR function for the physical Timers! |
|---|
| 596 | ** Its registered at 'open' time, de-registered at 'close', enabled at 'create', and disabled at 'destroy'. |
|---|
| 597 | ** This performs the callbacks to the owner of the count-down or periodic timer. |
|---|
| 598 | */ |
|---|
| 599 | |
|---|
| 600 | static void Physical_Timer_Isr(BTMR_Handle device, int timerNumber) |
|---|
| 601 | { |
|---|
| 602 | BTMR_TimerHandle timer; |
|---|
| 603 | BTMR_TimerSettings *context; |
|---|
| 604 | |
|---|
| 605 | /* We're supposed to use asserts to catch logic errors (not run-time errors). |
|---|
| 606 | ** So is getting an invalid handle or timer number a logic error?? |
|---|
| 607 | ** We should never be given a timerNumber for a timer that we don't control. |
|---|
| 608 | */ |
|---|
| 609 | #if 1 |
|---|
| 610 | BDBG_ASSERT(device); |
|---|
| 611 | BDBG_OBJECT_ASSERT(device, btmr_device_t); |
|---|
| 612 | BDBG_ASSERT((timerNumber >= 0) && (timerNumber < NumberOfTimers)); |
|---|
| 613 | #else |
|---|
| 614 | if (!device) { |
|---|
| 615 | BDBG_ERR(("device parameter is null in ISR!")); |
|---|
| 616 | return; |
|---|
| 617 | } |
|---|
| 618 | if (timerNumber < 0 || timerNumber >= NumberOfTimers) { |
|---|
| 619 | BDBG_ERR(("ISR received an invalid timer number (=%d)!", timerNumber)); |
|---|
| 620 | return; |
|---|
| 621 | } |
|---|
| 622 | #endif |
|---|
| 623 | |
|---|
| 624 | /*BDBG_WRN(("Physical interrupt (timer %d)", timerNumber));*/ |
|---|
| 625 | |
|---|
| 626 | #if 0 |
|---|
| 627 | /* We should never get an interrupt for a timer we don't control with this PI */ |
|---|
| 628 | if (device->Settings.timerMask & TimerSelect(timerNumber)) return; |
|---|
| 629 | |
|---|
| 630 | /* We should never get an interrupt for a timer we didn't open */ |
|---|
| 631 | if (!(device->inUse & TimerSelect(timerNumber))) return; |
|---|
| 632 | #endif |
|---|
| 633 | |
|---|
| 634 | /* Note: for the above cases, returning without handling the interrupt could mean we get called back forever, but not much else I can do... */ |
|---|
| 635 | |
|---|
| 636 | /* Its been reported that we can be called (once??) at startup before anyone has created a timer. |
|---|
| 637 | ** This is either an interrupt for a timer being used externally (and they neglected to indicate this in the open) |
|---|
| 638 | ** or the timer was running and it generated an interrupt before the timer is created (why?). |
|---|
| 639 | ** If we assert here they won't know why this it happening. So don't use: BDBG_ASSERT(timer); |
|---|
| 640 | ** But ignoring it is not great either -- in this case we're getting called for an interrupt we're not controlling |
|---|
| 641 | ** and doing nothing with the interrupt. I think this is the better choice (keeps the code from crashing). |
|---|
| 642 | */ |
|---|
| 643 | timer = device->Timers[timerNumber]; |
|---|
| 644 | if (!timer) { |
|---|
| 645 | BDBG_ERR(("timer is NULL in ISR!")); |
|---|
| 646 | return; |
|---|
| 647 | } |
|---|
| 648 | |
|---|
| 649 | /* Note: we cannot validate the timer with the following assert: |
|---|
| 650 | ** BDBG_ASSERT(timer->running == true); |
|---|
| 651 | ** This is because we allow for exclusive timers that are created through the PI but not started and stopped via the PI. |
|---|
| 652 | ** This means that if someone is using a timer outside of this PI and it causes an interrupt that I'll not know what to |
|---|
| 653 | ** do with the timer. This is bad but there is no way to control this. |
|---|
| 654 | */ |
|---|
| 655 | |
|---|
| 656 | /* If the timer is being used externally, then I may not have an object created to handle this (unless we use this |
|---|
| 657 | ** timer in the PI too). This will catch anyone using this without telling me in the Open. |
|---|
| 658 | */ |
|---|
| 659 | BDBG_OBJECT_ASSERT(timer, btmr_timer_t); |
|---|
| 660 | |
|---|
| 661 | JustStopTimer_isr(timer); |
|---|
| 662 | timer->lastValue = 0; /* count down timers always stop at zero */ |
|---|
| 663 | |
|---|
| 664 | /* We flag this timer as processing in case they try to start/stop the timer in the call-back function */ |
|---|
| 665 | timer->processing = true; |
|---|
| 666 | timer->running = false; |
|---|
| 667 | |
|---|
| 668 | /* If we have a callback function for this timer, then its time to call it! */ |
|---|
| 669 | /* Note: this is processing count-down timers so there had BETTER be a call-back function!!! */ |
|---|
| 670 | /* We ALWAYS call back with the timer stopped! */ |
|---|
| 671 | context = &timer->Settings; |
|---|
| 672 | if (context->cb_isr) |
|---|
| 673 | context->cb_isr(context->pParm1, context->parm2); |
|---|
| 674 | |
|---|
| 675 | /* We're done processing this timer so its okay to stop or delete it now */ |
|---|
| 676 | timer->processing = false; |
|---|
| 677 | |
|---|
| 678 | /* Periodic timers continue to go off until stopped -- we stopped it earlier so start it up again! */ |
|---|
| 679 | /* They may have asked us to stop the timer in the callback. We delayed this until done processing the call-back. */ |
|---|
| 680 | /* If they asked us to stop the timer then just don't start it back up. */ |
|---|
| 681 | if (context->type == BTMR_Type_ePeriodic && !timer->pleaseStop) { |
|---|
| 682 | JustStartTimer_isr(timer, timer->initialValue); |
|---|
| 683 | timer->running = true; |
|---|
| 684 | } |
|---|
| 685 | |
|---|
| 686 | /* If they requested a count-down timer to be restarted in the call-back then do it now. */ |
|---|
| 687 | if (context->type == BTMR_Type_eCountDown && timer->pleaseStart) { |
|---|
| 688 | BTMR_StartTimer_isr(timer, timer->delayedValue); |
|---|
| 689 | } |
|---|
| 690 | |
|---|
| 691 | /* Make sure these are cleared before leaving (in case they tried to start a periodic or stop a countdown) */ |
|---|
| 692 | timer->pleaseStart = false; |
|---|
| 693 | timer->pleaseStop = false; |
|---|
| 694 | } |
|---|
| 695 | |
|---|
| 696 | /* |
|---|
| 697 | ** This PI supports two different timer blocks with two different interrupt methods. |
|---|
| 698 | ** The primary (TMR) has a separate level two interrupt register for determining which of the four timers generated the interrupt. |
|---|
| 699 | ** In the second method, this implementation is handled as a level three (the level one references a level two block). All I am |
|---|
| 700 | ** told in this method is one (or more) of the timers went off but not which one. I have to figure that out myself by checking |
|---|
| 701 | ** the level three timer interrupt status register and clearing the appropriate interrupt after processing. |
|---|
| 702 | */ |
|---|
| 703 | |
|---|
| 704 | static void Physical_Timer_Selection_Isr(BTMR_Handle device) |
|---|
| 705 | { |
|---|
| 706 | int timerNumber; |
|---|
| 707 | uint32_t status, mask=0; |
|---|
| 708 | |
|---|
| 709 | /* This shouldn't be necessary because I didn't enable anything not being used, but ... */ |
|---|
| 710 | for (timerNumber=0; timerNumber < NumberOfTimers; timerNumber++) mask |= (1<<timerNumber); |
|---|
| 711 | |
|---|
| 712 | /* Figure out which one of the timers interrupted by reading the status register */ |
|---|
| 713 | status = BREG_Read32_isr(device->hRegister, TIMER_IS_REG(device)) & mask; |
|---|
| 714 | if (!status) return; |
|---|
| 715 | |
|---|
| 716 | /* Clear the level three interrupts I'm about to process -- I'll work off the copy. |
|---|
| 717 | ** By doing this before processing means that if any interrupt callback starts a timer that causes a NEW interrupt |
|---|
| 718 | ** we won't kill that interrupt before leaving. We'll get called right back, but we'll allow other interrupts |
|---|
| 719 | ** to process before we do get called back. |
|---|
| 720 | */ |
|---|
| 721 | BREG_Write32_isr(device->hRegister, TIMER_IS_REG(device), status); |
|---|
| 722 | |
|---|
| 723 | /* Process each of the timers sequentially */ |
|---|
| 724 | for (timerNumber=0; timerNumber < NumberOfTimers; timerNumber++) |
|---|
| 725 | { |
|---|
| 726 | if (status & (1<<timerNumber)) { |
|---|
| 727 | /* Note: this print could print a LOT! */ |
|---|
| 728 | /*BDBG_MSG(("Processing interrupt for timer number (=%d)!", timerNumber));*/ |
|---|
| 729 | Physical_Timer_Isr(device, timerNumber); |
|---|
| 730 | } |
|---|
| 731 | } |
|---|
| 732 | |
|---|
| 733 | /* Note: when I return the level two interrupt status will be cleared */ |
|---|
| 734 | } |
|---|
| 735 | |
|---|
| 736 | /*\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\*/ |
|---|
| 737 | |
|---|
| 738 | static BERR_Code phys_CreateTimer_isr(BTMR_Handle device, BTMR_TimerHandle timer, const BTMR_TimerSettings *pSettings) |
|---|
| 739 | { |
|---|
| 740 | BERR_Code retCode = BERR_SUCCESS; |
|---|
| 741 | int timerNumber; |
|---|
| 742 | |
|---|
| 743 | /* First, we have to select an available timer from the list of the available timers */ |
|---|
| 744 | BDBG_MSG(("BTMR_CreateTimer: looking for an available timer")); |
|---|
| 745 | timerNumber = SelectAnAvailableTimer_isr(device); |
|---|
| 746 | if (timerNumber < 0) { |
|---|
| 747 | BDBG_ERR(("BTMR_CreateTimer: No timers available for create!")); |
|---|
| 748 | retCode = BERR_TRACE(BTMR_ERR_NO_TIMERS_AVAILABLE); |
|---|
| 749 | goto done; |
|---|
| 750 | } |
|---|
| 751 | BDBG_MSG(("BTMR_CreateTimer: Using timer %d", timerNumber)); |
|---|
| 752 | |
|---|
| 753 | /* If they requested a callback (and are allowed to request a callback) then register for the interrupt! */ |
|---|
| 754 | if (pSettings->cb_isr) |
|---|
| 755 | { |
|---|
| 756 | if (device->alt_CbHandle) |
|---|
| 757 | { |
|---|
| 758 | uint32_t status; |
|---|
| 759 | |
|---|
| 760 | if (!device->alt_CbEnabled) |
|---|
| 761 | { |
|---|
| 762 | /* This enables the level 2 interrupt for this timer */ |
|---|
| 763 | retCode = BINT_EnableCallback_isr(device->alt_CbHandle); |
|---|
| 764 | if (retCode != BERR_SUCCESS) { |
|---|
| 765 | retCode = BERR_TRACE(retCode); |
|---|
| 766 | BDBG_ERR(("BTMR_CreateTimer: Enable Callback failed to register interrupt!")); |
|---|
| 767 | ReleaseThisTimer_isr(device, timerNumber); |
|---|
| 768 | goto done; |
|---|
| 769 | } |
|---|
| 770 | |
|---|
| 771 | device->alt_CbEnabled++; |
|---|
| 772 | } |
|---|
| 773 | |
|---|
| 774 | /* Enable the level 3 interrupt for this timer */ |
|---|
| 775 | /* I have to do this myself since this is a level 3 interrupt */ |
|---|
| 776 | status = BREG_Read32_isr(device->hRegister, TIMER_IE_REG(device)); |
|---|
| 777 | status |= 1<<timerNumber; |
|---|
| 778 | BREG_Write32_isr(device->hRegister, TIMER_IE_REG(device), status); |
|---|
| 779 | } |
|---|
| 780 | else |
|---|
| 781 | { |
|---|
| 782 | retCode = BINT_EnableCallback_isr(device->CbHandle[timerNumber]); |
|---|
| 783 | if (retCode != BERR_SUCCESS) { |
|---|
| 784 | retCode = BERR_TRACE(retCode); |
|---|
| 785 | BDBG_ERR(("BTMR_CreateTimer: Enable Callback failed to register interrupt (timer=%d)!", timerNumber)); |
|---|
| 786 | ReleaseThisTimer_isr(device, timerNumber); |
|---|
| 787 | goto done; |
|---|
| 788 | } |
|---|
| 789 | } |
|---|
| 790 | |
|---|
| 791 | timer->cbEnabled = true; /* remember we enabled this so we can disable in destroy */ |
|---|
| 792 | BDBG_MSG(("BTMR_CreateTimer: Enabled the callback for timer %d", timerNumber)); |
|---|
| 793 | } |
|---|
| 794 | |
|---|
| 795 | /* Physical timers have a timer value */ |
|---|
| 796 | timer->timerNumber = timerNumber; |
|---|
| 797 | |
|---|
| 798 | /* *** Special case: *** Ignore exclusive flag on shared timers! |
|---|
| 799 | ** This lets anyone using the shared timer get its registers and stop it without notifying the others sharing the timer. |
|---|
| 800 | ** This is bad, but necessary -- this lets us maximize the usage of the shared timer by hardware functions that need |
|---|
| 801 | ** exclusive access but promise not to stop the timer. |
|---|
| 802 | */ |
|---|
| 803 | if (pSettings->type == BTMR_Type_eSharedFreeRun) |
|---|
| 804 | timer->Settings.exclusive = false; |
|---|
| 805 | |
|---|
| 806 | /* This is the offsets to the registers for this timer */ |
|---|
| 807 | /* When exclusive or shared access is requested we'll return these to the user (this is how they control this timer) */ |
|---|
| 808 | timer->Registers.control = CONTROL_REG(device, timerNumber); |
|---|
| 809 | timer->Registers.status = STATUS_REG(device, timerNumber); |
|---|
| 810 | |
|---|
| 811 | device->Timers[timerNumber] = timer; /* so we can destroy created timers on close */ |
|---|
| 812 | |
|---|
| 813 | /* Just to be safe, make sure the timer isn't running until they specifically start it. |
|---|
| 814 | ** Note that this will stop an external timer that they forgot to include in the open mask. |
|---|
| 815 | ** Sorry, but there's nothing I can do about that ... you should have told me to exclude it. |
|---|
| 816 | */ |
|---|
| 817 | DisableTimer_isr(timer); |
|---|
| 818 | |
|---|
| 819 | /* If they asked to create a Free Run timer then this is the first use of the shared timer */ |
|---|
| 820 | /* Start it ourselves -- they should NOT stop it (or restart it). */ |
|---|
| 821 | if (pSettings->type == BTMR_Type_eSharedFreeRun) |
|---|
| 822 | { |
|---|
| 823 | device->SharedTimer = timer; |
|---|
| 824 | device->SharedCount = 1; |
|---|
| 825 | JustStartTimer_isr(timer, 0); |
|---|
| 826 | } |
|---|
| 827 | |
|---|
| 828 | BDBG_MSG(("BTMR_CreateTimer: Successfully created new physical timer!")); |
|---|
| 829 | |
|---|
| 830 | done: |
|---|
| 831 | return (retCode); |
|---|
| 832 | } |
|---|
| 833 | static BERR_Code phys_CreateTimer(BTMR_Handle device, BTMR_TimerHandle timer, const BTMR_TimerSettings *pSettings) |
|---|
| 834 | { |
|---|
| 835 | BERR_Code status; |
|---|
| 836 | BKNI_EnterCriticalSection(); |
|---|
| 837 | status = phys_CreateTimer_isr(device, timer, pSettings); |
|---|
| 838 | BKNI_LeaveCriticalSection(); |
|---|
| 839 | return status; |
|---|
| 840 | } |
|---|
| 841 | |
|---|
| 842 | static BERR_Code phys_DestroyTimer_isr(BTMR_TimerHandle timer) |
|---|
| 843 | { |
|---|
| 844 | BTMR_Handle device = timer->device; |
|---|
| 845 | int timerNumber = timer->timerNumber; |
|---|
| 846 | |
|---|
| 847 | /* If I enabled the callback in the create then I need to disable it now */ |
|---|
| 848 | if (timer->cbEnabled) |
|---|
| 849 | { |
|---|
| 850 | /* Don't leave the timer running -- we don't want any interrupts from this timer anymore */ |
|---|
| 851 | /* Hack: because I give out the registers for the shared timer and the fact that they can |
|---|
| 852 | ** access those registers outside of this PI, I can't control how they use this timer. |
|---|
| 853 | ** If they try to read the timer after I stop it they could hang. There is no way for me |
|---|
| 854 | ** to fix this or tell them this is happening. |
|---|
| 855 | */ |
|---|
| 856 | if (timer->running) { |
|---|
| 857 | BDBG_MSG(("BTMR_DestroyTimer: stopping timer!")); |
|---|
| 858 | JustStopTimer_isr(timer); |
|---|
| 859 | } |
|---|
| 860 | |
|---|
| 861 | if (device->alt_CbHandle) |
|---|
| 862 | { |
|---|
| 863 | uint32_t status; |
|---|
| 864 | |
|---|
| 865 | device->alt_CbEnabled--; |
|---|
| 866 | |
|---|
| 867 | /* Disable the level two interrupt if this is the last disable */ |
|---|
| 868 | if (!device->alt_CbEnabled) |
|---|
| 869 | BINT_DisableCallback_isr(device->alt_CbHandle); |
|---|
| 870 | |
|---|
| 871 | /* Disable the level 3 interrupt for this timer */ |
|---|
| 872 | /* I have to do this myself since this is a level 3 interrupt */ |
|---|
| 873 | status = BREG_Read32_isr(device->hRegister, TIMER_IE_REG(device)); |
|---|
| 874 | status &= ~(1<<timerNumber); |
|---|
| 875 | BREG_Write32_isr(device->hRegister, TIMER_IE_REG(device), status); |
|---|
| 876 | } |
|---|
| 877 | else |
|---|
| 878 | { |
|---|
| 879 | BINT_DisableCallback_isr(device->CbHandle[timerNumber]); |
|---|
| 880 | } |
|---|
| 881 | |
|---|
| 882 | timer->cbEnabled = false; |
|---|
| 883 | BDBG_MSG(("BTMR_DestroyTimer: Disabled the callback for timer %d", timerNumber)); |
|---|
| 884 | } |
|---|
| 885 | |
|---|
| 886 | /* On the last destroy we have no more shared timers -- will be re-created on next create */ |
|---|
| 887 | if (timer->Settings.type == BTMR_Type_eSharedFreeRun) { |
|---|
| 888 | device->SharedTimer = NULL; |
|---|
| 889 | device->SharedCount = 0; |
|---|
| 890 | JustStopTimer_isr(timer); |
|---|
| 891 | } |
|---|
| 892 | |
|---|
| 893 | device->Timers[timerNumber] = NULL; /* we're done with this timer -- its gone! */ |
|---|
| 894 | ReleaseThisTimer_isr(device, timerNumber); |
|---|
| 895 | |
|---|
| 896 | BDBG_MSG(("BTMR_DestroyTimer: Successfully destroyed physical timer!")); |
|---|
| 897 | return BERR_SUCCESS; |
|---|
| 898 | } |
|---|
| 899 | #if 0 |
|---|
| 900 | static BERR_Code phys_DestroyTimer(BTMR_TimerHandle timer) |
|---|
| 901 | { |
|---|
| 902 | BERR_Code status; |
|---|
| 903 | BKNI_EnterCriticalSection(); |
|---|
| 904 | status = phys_DestroyTimer_isr(timer); |
|---|
| 905 | BKNI_LeaveCriticalSection(); |
|---|
| 906 | return status; |
|---|
| 907 | } |
|---|
| 908 | #endif |
|---|
| 909 | |
|---|
| 910 | static BERR_Code phys_StartTimer_isr(BTMR_TimerHandle timer, unsigned startingValue) |
|---|
| 911 | { |
|---|
| 912 | uint32_t initial; |
|---|
| 913 | |
|---|
| 914 | #if 0 /* exclusive used to mean controlled externally -- now it means not virtual and don't share it */ |
|---|
| 915 | /* They are only supposed to call this function IF the timer is NOT exclusive */ |
|---|
| 916 | if (timer->Settings.exclusive) { |
|---|
| 917 | BDBG_ERR(("BTMR_StartTimer: requested START operation on exclusive timer!")); |
|---|
| 918 | return BERR_TRACE(BTMR_ERR_EXCLUSIVE_TIMER); |
|---|
| 919 | } |
|---|
| 920 | #endif |
|---|
| 921 | |
|---|
| 922 | /* Starting a shared timer could mess up the others sharing this timer */ |
|---|
| 923 | if (timer->Settings.type == BTMR_Type_eSharedFreeRun) { |
|---|
| 924 | BDBG_ERR(("BTMR_StartTimer: requested START operation on shared timer!")); |
|---|
| 925 | return BERR_TRACE(BTMR_ERR_FREE_RUN_TIMER); |
|---|
| 926 | } |
|---|
| 927 | |
|---|
| 928 | /* They talk microseconds, we talk timer ticks */ |
|---|
| 929 | initial = MicroSecondsToTimer(startingValue); |
|---|
| 930 | |
|---|
| 931 | return JustStartTimer_isr(timer, initial); |
|---|
| 932 | } |
|---|
| 933 | |
|---|
| 934 | static BERR_Code phys_StopTimer_isr(BTMR_TimerHandle timer) |
|---|
| 935 | { |
|---|
| 936 | #if 0 /* exclusive used to mean controlled externally -- now it means not virtual and don't share it */ |
|---|
| 937 | /* They are only supposed to call this function IF the timer is NOT exclusive */ |
|---|
| 938 | if (timer->Settings.exclusive) { |
|---|
| 939 | BDBG_ERR(("BTMR_StopTimer: requested STOP operation on exclusive timer!")); |
|---|
| 940 | return BERR_TRACE(BTMR_ERR_EXCLUSIVE_TIMER); |
|---|
| 941 | } |
|---|
| 942 | #endif |
|---|
| 943 | |
|---|
| 944 | /* Stopping a shared timer could mess up the others sharing this timer */ |
|---|
| 945 | if (timer->Settings.type == BTMR_Type_eSharedFreeRun) { |
|---|
| 946 | BDBG_ERR(("BTMR_StopTimer: requested STOP operation on shared timer!")); |
|---|
| 947 | return BERR_TRACE(BTMR_ERR_FREE_RUN_TIMER); |
|---|
| 948 | } |
|---|
| 949 | |
|---|
| 950 | return JustStopTimer_isr(timer); |
|---|
| 951 | } |
|---|
| 952 | |
|---|
| 953 | static unsigned phys_ReadTimer_isr(BTMR_TimerHandle timer) |
|---|
| 954 | { |
|---|
| 955 | uint32_t runTime; |
|---|
| 956 | |
|---|
| 957 | /* Stopped timers return the value of the timer when it was stopped! */ |
|---|
| 958 | if (!timer->running) { |
|---|
| 959 | runTime = timer->lastValue; |
|---|
| 960 | BDBG_MSG(("BTMR_ReadTimer: (phys) read time from stopped timer (value=%x)!", runTime)); |
|---|
| 961 | } else { |
|---|
| 962 | runTime = JustReadTimer_isr(timer); |
|---|
| 963 | } |
|---|
| 964 | |
|---|
| 965 | /* Stopwatch timers are relative to the initial value they supplied */ |
|---|
| 966 | if (timer->Settings.type == BTMR_Type_eStopWatch) { |
|---|
| 967 | runTime += timer->initialValue; |
|---|
| 968 | if (runTime >= TimerWrap) |
|---|
| 969 | runTime -= TimerWrap; |
|---|
| 970 | } |
|---|
| 971 | |
|---|
| 972 | /* They talk microseconds, we talk timer ticks */ |
|---|
| 973 | return TimerToMicroSeconds(runTime); |
|---|
| 974 | } |
|---|
| 975 | |
|---|
| 976 | /* These are the functions used by physical timers */ |
|---|
| 977 | static const ProcessingFunctions physicalFunctions = { phys_CreateTimer, phys_DestroyTimer_isr, phys_StartTimer_isr, phys_StopTimer_isr, phys_ReadTimer_isr }; |
|---|
| 978 | |
|---|
| 979 | /* |
|---|
| 980 | *********************************************************************************** |
|---|
| 981 | *********************************************************************************** |
|---|
| 982 | ** These are the set of VIRTUAL timer functions (and their utility functions). |
|---|
| 983 | *********************************************************************************** |
|---|
| 984 | *********************************************************************************** |
|---|
| 985 | */ |
|---|
| 986 | |
|---|
| 987 | /* This uses the physical free-run timer to get the runtime of the virtual timer */ |
|---|
| 988 | static uint32_t JustReadVirtualTimer_isr(BTMR_TimerHandle timer) |
|---|
| 989 | { |
|---|
| 990 | BTMR_Handle device = timer->device; |
|---|
| 991 | uint32_t currentTime, runTime, timeLeft; |
|---|
| 992 | |
|---|
| 993 | /* Start off with whatever the timer is currently at (currentTime) */ |
|---|
| 994 | /* This starts with whatever is in the actual timer (its counting up) */ |
|---|
| 995 | currentTime = GetTimerValue_isr(device->FreeRunTimer); |
|---|
| 996 | |
|---|
| 997 | /* Account for wrap by the physical timer */ |
|---|
| 998 | if (currentTime > timer->startingValue) |
|---|
| 999 | runTime = currentTime - timer->startingValue; |
|---|
| 1000 | else |
|---|
| 1001 | runTime = (TimerWrap - timer->startingValue) + currentTime; |
|---|
| 1002 | |
|---|
| 1003 | if (timer->Settings.type == BTMR_Type_eStopWatch) |
|---|
| 1004 | { |
|---|
| 1005 | /* Stop Watch timers return the time expired (run-time). */ |
|---|
| 1006 | /* Stop-watch timers can be supplied with a starting value -- account for that time! */ |
|---|
| 1007 | runTime += timer->initialValue; |
|---|
| 1008 | if (runTime >= TimerWrap) |
|---|
| 1009 | runTime -= TimerWrap; |
|---|
| 1010 | /*printf("SW: current=%x, starting=%x, runTime=%x, initial=%x\n", currentTime, timer->startingValue, runTime, timer->initialValue);*/ |
|---|
| 1011 | return runTime; |
|---|
| 1012 | } |
|---|
| 1013 | else |
|---|
| 1014 | { |
|---|
| 1015 | /* Count-down timers return the time left to run. */ |
|---|
| 1016 | /* Count-down timers count down to zero -- make sure it stops at zero! */ |
|---|
| 1017 | timeLeft = (timer->initialValue > runTime) ? (timer->initialValue - runTime) : 0; |
|---|
| 1018 | /*printf("CD: current=%x, starting=%x, runTime=%x, initial=%x, timeleft=%x\n", currentTime, timer->startingValue, runTime, timer->initialValue, timeLeft);*/ |
|---|
| 1019 | return timeLeft; |
|---|
| 1020 | } |
|---|
| 1021 | } |
|---|
| 1022 | |
|---|
| 1023 | /* A sorted list is kept for the virtual count-down timers. The order determines who times out next! */ |
|---|
| 1024 | /* Each item on the list has an adjusted timeout value that is the time DIFFERENCE from the item before it. */ |
|---|
| 1025 | |
|---|
| 1026 | /* This puts items onto the sorted time-out list (on start timer request). |
|---|
| 1027 | ** We use a physical timer to timeout the smallest virtual timeout reqest. If the new item's timeout |
|---|
| 1028 | ** is smaller than the currently running timer (if any) then it will stop the physical timer, |
|---|
| 1029 | ** account for the time the timer already ran, and restart the timer with the new timeout. The old |
|---|
| 1030 | ** (was running) timer's timeout value will be adjusted for time already processed (its already on the queue). |
|---|
| 1031 | */ |
|---|
| 1032 | static void PutTimerOnTimeoutQueue_isr(BTMR_Handle device, BTMR_TimerHandle timer) |
|---|
| 1033 | { |
|---|
| 1034 | timer->adjustedValue = timer->initialValue; |
|---|
| 1035 | |
|---|
| 1036 | /* There are three cases to consider here: |
|---|
| 1037 | ** 1) Empty list -- no timer currently running. |
|---|
| 1038 | ** 2) New head item -- new timeout is less than currently running timer's timeout value. |
|---|
| 1039 | ** 3) Other -- new timeout is larger than currently running timer's timeout value. |
|---|
| 1040 | */ |
|---|
| 1041 | |
|---|
| 1042 | if (BLST_D_EMPTY(&device->TimeoutList)) |
|---|
| 1043 | { |
|---|
| 1044 | /* 1) If list is empty, then this is a new timeout value -- just start the timer */ |
|---|
| 1045 | BLST_D_INSERT_HEAD(&device->TimeoutList, timer, link); |
|---|
| 1046 | JustStartTimer_isr(device->CountDownTimer, timer->adjustedValue); |
|---|
| 1047 | } |
|---|
| 1048 | else |
|---|
| 1049 | { |
|---|
| 1050 | /* If the list is NOT empty then we need to add this timer to the list in the appropriate location */ |
|---|
| 1051 | uint32_t timeLeft; |
|---|
| 1052 | BTMR_TimerHandle walker = BLST_D_FIRST(&device->TimeoutList); |
|---|
| 1053 | |
|---|
| 1054 | timeLeft = JustReadTimer_isr(device->CountDownTimer); |
|---|
| 1055 | |
|---|
| 1056 | /* 2) Is the timeout of the new item is smaller than the remaining timeout of the currently running timer? */ |
|---|
| 1057 | /* If so then we need to stop the timer, count off how must time has elapsed, and adjust the head item's timeout value. */ |
|---|
| 1058 | if (timer->initialValue < timeLeft) |
|---|
| 1059 | { |
|---|
| 1060 | JustStopTimer_isr(device->CountDownTimer); |
|---|
| 1061 | |
|---|
| 1062 | BLST_D_INSERT_BEFORE(&device->TimeoutList, walker, timer, link); |
|---|
| 1063 | walker->adjustedValue = timeLeft - timer->initialValue; |
|---|
| 1064 | |
|---|
| 1065 | JustStartTimer_isr(device->CountDownTimer, timer->initialValue); |
|---|
| 1066 | } |
|---|
| 1067 | else |
|---|
| 1068 | { |
|---|
| 1069 | bool added = false; |
|---|
| 1070 | BTMR_TimerHandle last = walker; |
|---|
| 1071 | |
|---|
| 1072 | /* Account for the timeout of the currently running (or soon to be running) timer */ |
|---|
| 1073 | timer->adjustedValue -= timeLeft; |
|---|
| 1074 | |
|---|
| 1075 | /* 3) We need to find the best place to put this new timeout. We adjust his timeout value as we skip over items. */ |
|---|
| 1076 | /* Once found, we insert him and adjust the timeout of the guy that comes after him to account for the new timeout */ |
|---|
| 1077 | for (walker = BLST_D_NEXT(walker, link); walker; last = walker, walker = BLST_D_NEXT(walker, link)) |
|---|
| 1078 | { |
|---|
| 1079 | if (timer->adjustedValue < walker->adjustedValue) |
|---|
| 1080 | { |
|---|
| 1081 | BLST_D_INSERT_BEFORE(&device->TimeoutList, walker, timer, link); |
|---|
| 1082 | walker->adjustedValue -= timer->adjustedValue; |
|---|
| 1083 | added = true; |
|---|
| 1084 | break; |
|---|
| 1085 | } |
|---|
| 1086 | timer->adjustedValue -= walker->adjustedValue; |
|---|
| 1087 | } |
|---|
| 1088 | |
|---|
| 1089 | /* If the new item is larger timeout then all the others then it goes onto the end of the list! */ |
|---|
| 1090 | if (!added) { |
|---|
| 1091 | BLST_D_INSERT_AFTER(&device->TimeoutList, last, timer, link); |
|---|
| 1092 | } |
|---|
| 1093 | } |
|---|
| 1094 | } |
|---|
| 1095 | } |
|---|
| 1096 | |
|---|
| 1097 | /* This takes an item off the sorted timeout list (on stop timer request). |
|---|
| 1098 | ** If the item was the head then it stops the timer and starts the next item on the queue. |
|---|
| 1099 | ** This assumes that the hardware timer is already running with either this timer or another timer. |
|---|
| 1100 | */ |
|---|
| 1101 | static void TakeTimerOffTimeoutQueue_isr(BTMR_Handle device, BTMR_TimerHandle timer) |
|---|
| 1102 | { |
|---|
| 1103 | BTMR_TimerHandle head, next; |
|---|
| 1104 | |
|---|
| 1105 | /* There are two cases to consider here: |
|---|
| 1106 | ** 1) We're removing the head entry (and need to reset the timer). |
|---|
| 1107 | ** 2) Other -- we're removing a scheduled timeout that hasn't been scheduled yet. |
|---|
| 1108 | */ |
|---|
| 1109 | |
|---|
| 1110 | head = BLST_D_FIRST(&device->TimeoutList); |
|---|
| 1111 | if (!head) { |
|---|
| 1112 | BDBG_ERR(("Told to delete timer from an empty timeout queue -- ignoring!")); |
|---|
| 1113 | /*BKNI_Fail();*/ /* I should really panic, right??? */ |
|---|
| 1114 | return; |
|---|
| 1115 | } |
|---|
| 1116 | next = BLST_D_NEXT(timer, link); |
|---|
| 1117 | |
|---|
| 1118 | if (timer == head) |
|---|
| 1119 | { |
|---|
| 1120 | /* 1) If the item being deleted is the head item we have to account for the amount of time the timer has left to run... */ |
|---|
| 1121 | uint32_t timeLeft = JustReadVirtualTimer_isr(timer); |
|---|
| 1122 | |
|---|
| 1123 | JustStopTimer_isr(device->CountDownTimer); |
|---|
| 1124 | |
|---|
| 1125 | BLST_D_REMOVE(&device->TimeoutList, timer, link); |
|---|
| 1126 | |
|---|
| 1127 | /* Adjust any next item AND start up a new timer (if there is one) */ |
|---|
| 1128 | if (next) { |
|---|
| 1129 | next->adjustedValue += timeLeft; |
|---|
| 1130 | JustStartTimer_isr(device->CountDownTimer, next->adjustedValue); |
|---|
| 1131 | } |
|---|
| 1132 | } |
|---|
| 1133 | else |
|---|
| 1134 | { |
|---|
| 1135 | BLST_D_REMOVE(&device->TimeoutList, timer, link); |
|---|
| 1136 | |
|---|
| 1137 | /* 2) If the item being deleted is NOT the head item then we only need to adjust the next guy's timeout value */ |
|---|
| 1138 | if (next) |
|---|
| 1139 | next->adjustedValue += timer->adjustedValue; |
|---|
| 1140 | } |
|---|
| 1141 | } |
|---|
| 1142 | |
|---|
| 1143 | /* |
|---|
| 1144 | ** This is my ISR function for the virtual count-down timers! |
|---|
| 1145 | ** This is registered as the callback for the physical timer used to process the virtual count-down timers. |
|---|
| 1146 | ** It is registered at at 'create' and disabled at 'destroy'. |
|---|
| 1147 | ** This performs the callbacks to the owner of the virtual timer. |
|---|
| 1148 | ** This is called back from the hardware ISR so this is run at ISR level. |
|---|
| 1149 | */ |
|---|
| 1150 | |
|---|
| 1151 | static void Virtual_Timer_Isr(BTMR_Handle device) |
|---|
| 1152 | { |
|---|
| 1153 | BTMR_TimerHandle timer, last; |
|---|
| 1154 | BTMR_TimerSettings *context; |
|---|
| 1155 | |
|---|
| 1156 | /* Not getting a device means we have a logic error in our physical timer processing */ |
|---|
| 1157 | BDBG_ASSERT(device); |
|---|
| 1158 | /* We should never be called here when there are no virtual timers running! */ |
|---|
| 1159 | BDBG_ASSERT(!BLST_D_EMPTY(&device->TimeoutList)); |
|---|
| 1160 | /* There should never be anything on the "to do" list */ |
|---|
| 1161 | BDBG_ASSERT(BLST_D_EMPTY(&device->ToBeProcessedList)); |
|---|
| 1162 | |
|---|
| 1163 | /* The timer should already be stopped, but ... */ |
|---|
| 1164 | JustStopTimer_isr(device->CountDownTimer); |
|---|
| 1165 | |
|---|
| 1166 | /*BDBG_WRN(("Virtual timer interrupt"));*/ |
|---|
| 1167 | |
|---|
| 1168 | /* This is called from the registered hardware ISR for this timer (for the CountDownTimer). |
|---|
| 1169 | ** It stopped the timer as part of the ISR processing. |
|---|
| 1170 | ** The timer only interrupts if it counted up to its adjusted value. |
|---|
| 1171 | */ |
|---|
| 1172 | |
|---|
| 1173 | /* This interrupt occured because we have (at least) one count-down timer running and the next timer |
|---|
| 1174 | ** to timeout (smallest timeout) just timed out. We pull off ALL the timers that expired (could be |
|---|
| 1175 | ** more than one) and queue them up for processing. The benefit of delaying the processing is that |
|---|
| 1176 | ** a timer's callback function will not impact us from starting up the next virtual timer. |
|---|
| 1177 | */ |
|---|
| 1178 | timer = BLST_D_FIRST(&device->TimeoutList); |
|---|
| 1179 | BDBG_ASSERT(timer); |
|---|
| 1180 | last = NULL; |
|---|
| 1181 | |
|---|
| 1182 | do { |
|---|
| 1183 | BLST_D_REMOVE_HEAD(&device->TimeoutList, link); |
|---|
| 1184 | |
|---|
| 1185 | if (BLST_D_EMPTY(&device->ToBeProcessedList)) |
|---|
| 1186 | BLST_D_INSERT_HEAD(&device->ToBeProcessedList, timer, link); |
|---|
| 1187 | else |
|---|
| 1188 | BLST_D_INSERT_AFTER(&device->ToBeProcessedList, last, timer, link); |
|---|
| 1189 | |
|---|
| 1190 | /* We flag this timer as processing in case they try to start/stop the timer in the call-back function */ |
|---|
| 1191 | timer->processing = true; |
|---|
| 1192 | timer->running = false; |
|---|
| 1193 | |
|---|
| 1194 | /* We don't need to physically stop the timer as it doesn't really exist */ |
|---|
| 1195 | timer->lastValue = 0; /* count down timers always stop at zero */ |
|---|
| 1196 | |
|---|
| 1197 | last = timer; |
|---|
| 1198 | timer = BLST_D_FIRST(&device->TimeoutList); |
|---|
| 1199 | |
|---|
| 1200 | } while (timer && timer->adjustedValue <= BTMR_MIN_HOLD); |
|---|
| 1201 | |
|---|
| 1202 | /* If there is another timer waiting to start -- well, then start it! */ |
|---|
| 1203 | if (timer) JustStartTimer_isr(device->CountDownTimer, timer->adjustedValue); |
|---|
| 1204 | |
|---|
| 1205 | /* We now have a list of timers that need to be processed (i.e. process the call-back). |
|---|
| 1206 | ** If its a periodic timer, put it back onto the timer list. |
|---|
| 1207 | ** We do this as a separate pass so that we can get the timer running again (with the next timer) as soon as possible. |
|---|
| 1208 | ** This way, the callback from a timer will not effect the time of the next timer. |
|---|
| 1209 | ** This will however affect the start time of a periodic timer -- we'll restart the timer after the call-back returns. |
|---|
| 1210 | */ |
|---|
| 1211 | while ((timer = BLST_D_FIRST(&device->ToBeProcessedList))) |
|---|
| 1212 | { |
|---|
| 1213 | BLST_D_REMOVE_HEAD(&device->ToBeProcessedList, link); |
|---|
| 1214 | |
|---|
| 1215 | /* This timer expired! Let the owner know it expired! */ |
|---|
| 1216 | /* If we have a callback function for this timer, then its time to call it! */ |
|---|
| 1217 | /* Note: this is processing count-down timers so there had BETTER be a call-back function!!! */ |
|---|
| 1218 | context = &timer->Settings; |
|---|
| 1219 | if (context->cb_isr) |
|---|
| 1220 | context->cb_isr(context->pParm1, context->parm2); |
|---|
| 1221 | |
|---|
| 1222 | /* We're done processing this timer so its okay to stop or delete it now */ |
|---|
| 1223 | timer->processing = false; |
|---|
| 1224 | |
|---|
| 1225 | /* Periodic timers continue to go off until stopped -- start it up again! */ |
|---|
| 1226 | /* They may have asked us to stop the timer in the callback. We delayed this until done processing the call-back. */ |
|---|
| 1227 | /* If they asked us to stop the timer then just don't start it back up. */ |
|---|
| 1228 | if (context->type == BTMR_Type_ePeriodic && !timer->pleaseStop) { |
|---|
| 1229 | timer->startingValue = GetTimerValue_isr(device->FreeRunTimer); |
|---|
| 1230 | PutTimerOnTimeoutQueue_isr(device, timer); |
|---|
| 1231 | timer->running = true; |
|---|
| 1232 | } |
|---|
| 1233 | |
|---|
| 1234 | /* If they requested a count-down timer to be restarted in the call-back then do it now. */ |
|---|
| 1235 | if (context->type == BTMR_Type_eCountDown && timer->pleaseStart) { |
|---|
| 1236 | BTMR_StartTimer_isr(timer, timer->delayedValue); |
|---|
| 1237 | } |
|---|
| 1238 | |
|---|
| 1239 | /* Make sure these are cleared before leaving (in case they tried to start a periodic or stop a countdown) */ |
|---|
| 1240 | timer->pleaseStop = false; |
|---|
| 1241 | timer->pleaseStart = false; |
|---|
| 1242 | } |
|---|
| 1243 | } |
|---|
| 1244 | |
|---|
| 1245 | /*\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\*/ |
|---|
| 1246 | |
|---|
| 1247 | /* Create the (two) timers we need to handle virtual timers */ |
|---|
| 1248 | /* This is used in the open call when requested to allocate these timers statically or in create call for dynamic when these are actually needed. */ |
|---|
| 1249 | static BERR_Code CreateUtilityTimers(BTMR_Handle device) |
|---|
| 1250 | { |
|---|
| 1251 | /* If we've already created the timers needed for supporting virtual timers then nothing to do */ |
|---|
| 1252 | if (device->CountDownTimer) return BERR_SUCCESS; |
|---|
| 1253 | |
|---|
| 1254 | BDBG_MSG(("Creating utility timers ...")); |
|---|
| 1255 | |
|---|
| 1256 | /* We need one physical timer in order to use virtual count-down timers. |
|---|
| 1257 | ** This timer will be controlled by our call back function (runs at ISR time). |
|---|
| 1258 | */ |
|---|
| 1259 | { |
|---|
| 1260 | BTMR_TimerSettings settings = { BTMR_Type_eCountDown, (BTMR_CallbackFunc)Virtual_Timer_Isr, NULL, 0, true }; |
|---|
| 1261 | settings.pParm1 = device; /* needs to be set at runtime */ |
|---|
| 1262 | if (_BTMR_CreateTimer(device, &device->CountDownTimer, &settings, "CountDown", 0) != BERR_SUCCESS) { |
|---|
| 1263 | BDBG_ERR(("Failed to create physical timer needed for supporting virtual count-down timers!")); |
|---|
| 1264 | return BERR_TRACE(BTMR_ERR_NO_TIMERS_AVAILABLE); |
|---|
| 1265 | } |
|---|
| 1266 | BLST_D_INIT(&device->TimeoutList); |
|---|
| 1267 | BLST_D_INIT(&device->ToBeProcessedList); |
|---|
| 1268 | BDBG_MSG(("Created physical timer needed for supporting virtual CD timers (using timer %d)!", device->CountDownTimer->timerNumber)); |
|---|
| 1269 | } |
|---|
| 1270 | |
|---|
| 1271 | /* We'll use THE one shared timer in order to use virtual stop-watch (free-run) timers. |
|---|
| 1272 | ** This just runs the timer and uses the timer value to generate a current time value. |
|---|
| 1273 | ** Note: if a shared timer was allocated previously then this will use that shared timer. |
|---|
| 1274 | ** If no shared timer was previously created then this call will create THE shared timer. |
|---|
| 1275 | */ |
|---|
| 1276 | { |
|---|
| 1277 | BTMR_TimerSettings settings = { BTMR_Type_eSharedFreeRun, NULL, NULL, 0, true }; |
|---|
| 1278 | settings.pParm1 = device; /* needs to be set at runtime */ |
|---|
| 1279 | if (_BTMR_CreateTimer(device, &device->FreeRunTimer, &settings, "FreeRun", 0) != BERR_SUCCESS) { |
|---|
| 1280 | _BTMR_DestroyTimer(device->CountDownTimer, "CountDown", 0); |
|---|
| 1281 | device->CountDownTimer = NULL; |
|---|
| 1282 | BDBG_ERR(("Failed to create shared timer needed for supporting virtual free-run timers!")); |
|---|
| 1283 | return BERR_TRACE(BTMR_ERR_NO_TIMERS_AVAILABLE); |
|---|
| 1284 | } |
|---|
| 1285 | BDBG_MSG(("Created (shared) timer needed for supporting virtual FR timers (using timer %d)!", device->FreeRunTimer->timerNumber)); |
|---|
| 1286 | } |
|---|
| 1287 | |
|---|
| 1288 | return BERR_SUCCESS; |
|---|
| 1289 | } |
|---|
| 1290 | |
|---|
| 1291 | /* Destroy the timers we created for handling the virtual timers */ |
|---|
| 1292 | static void DestroyUtilityTimers(BTMR_Handle device) |
|---|
| 1293 | { |
|---|
| 1294 | /* If the timers were never created than nothing to destroy */ |
|---|
| 1295 | if (!device->CountDownTimer) return; |
|---|
| 1296 | |
|---|
| 1297 | BDBG_MSG(("Destroying utility timers ...")); |
|---|
| 1298 | |
|---|
| 1299 | if (device->CountDownTimer) { |
|---|
| 1300 | _BTMR_DestroyTimer(device->CountDownTimer, "CountDown", 0); |
|---|
| 1301 | device->CountDownTimer = NULL; |
|---|
| 1302 | } |
|---|
| 1303 | if (device->FreeRunTimer) { |
|---|
| 1304 | _BTMR_DestroyTimer(device->FreeRunTimer, "FreeRun", 0); |
|---|
| 1305 | device->FreeRunTimer = NULL; |
|---|
| 1306 | } |
|---|
| 1307 | BDBG_MSG(("Destroyed timers needed for supporting virtual timers!")); |
|---|
| 1308 | } |
|---|
| 1309 | |
|---|
| 1310 | /* This is used by open to clean up on error and close to disable level two interrupts */ |
|---|
| 1311 | static void DestroyCallbacks(BTMR_Handle device) |
|---|
| 1312 | { |
|---|
| 1313 | int timerNumber; |
|---|
| 1314 | |
|---|
| 1315 | /* The first thing we want to do is stop any running timers and make sure we won't be getting anymore interrupts */ |
|---|
| 1316 | for (timerNumber=0; timerNumber<NumberOfTimers; timerNumber++) |
|---|
| 1317 | { |
|---|
| 1318 | /* Skip timers we don't control (excluded in the open) */ |
|---|
| 1319 | if (device->Settings.timerMask & TimerSelect(timerNumber)) continue; |
|---|
| 1320 | |
|---|
| 1321 | /* If we created the callback in the open, make sure we don't have any pending interrupts */ |
|---|
| 1322 | if (device->Settings.interruptNumber) |
|---|
| 1323 | { |
|---|
| 1324 | if (device->alt_CbHandle) { |
|---|
| 1325 | /*BINT_ClearCallback(device->alt_CbHandle);*/ /* not necessary -- destroy handles this */ |
|---|
| 1326 | BINT_DisableCallback(device->alt_CbHandle); |
|---|
| 1327 | BINT_DestroyCallback(device->alt_CbHandle); |
|---|
| 1328 | device->alt_CbHandle = NULL; |
|---|
| 1329 | device->alt_CbEnabled = 0; |
|---|
| 1330 | } |
|---|
| 1331 | } |
|---|
| 1332 | else |
|---|
| 1333 | { |
|---|
| 1334 | if (device->CbHandle[timerNumber]) { |
|---|
| 1335 | /*BINT_ClearCallback(device->CbHandle[timerNumber]);*/ /* not necessary -- destroy handles this */ |
|---|
| 1336 | BINT_DisableCallback(device->CbHandle[timerNumber]); |
|---|
| 1337 | BINT_DestroyCallback(device->CbHandle[timerNumber]); |
|---|
| 1338 | device->CbHandle[timerNumber] = NULL; |
|---|
| 1339 | } |
|---|
| 1340 | } |
|---|
| 1341 | |
|---|
| 1342 | /* If the callback is enabled for this timer then it isn't anymore */ |
|---|
| 1343 | if (device->Timers[timerNumber]) |
|---|
| 1344 | device->Timers[timerNumber]->cbEnabled = false; |
|---|
| 1345 | } |
|---|
| 1346 | } |
|---|
| 1347 | |
|---|
| 1348 | /*\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\*/ |
|---|
| 1349 | |
|---|
| 1350 | static BERR_Code virt_CreateTimer_isr(BTMR_Handle device, BTMR_TimerHandle timer, const BTMR_TimerSettings *pSettings) |
|---|
| 1351 | { |
|---|
| 1352 | BERR_Code retCode = BERR_SUCCESS; |
|---|
| 1353 | |
|---|
| 1354 | BSTD_UNUSED(pSettings); /* the phys version needs the settings, the virt version does not */ |
|---|
| 1355 | |
|---|
| 1356 | /* Virtual timers don't have an actual timer number -- only physical timers do */ |
|---|
| 1357 | timer->timerNumber = -1; |
|---|
| 1358 | |
|---|
| 1359 | BDBG_MSG(("BTMR_CreateTimer: Successfully created new virtual timer!")); |
|---|
| 1360 | BLST_D_INSERT_HEAD(&device->VirtualCreateList, timer, created); |
|---|
| 1361 | device->VirtualCount++; |
|---|
| 1362 | |
|---|
| 1363 | return (retCode); |
|---|
| 1364 | } |
|---|
| 1365 | static BERR_Code virt_CreateTimer(BTMR_Handle device, BTMR_TimerHandle timer, const BTMR_TimerSettings *pSettings) |
|---|
| 1366 | { |
|---|
| 1367 | BERR_Code status; |
|---|
| 1368 | |
|---|
| 1369 | /* We need some utility timers for handling virtual timers, so create them on first use */ |
|---|
| 1370 | /* Note: This will succeed if they already exist (i.e. allocated statically in Open) */ |
|---|
| 1371 | if (device->VirtualCount == 0) { |
|---|
| 1372 | if (CreateUtilityTimers(device) != BERR_SUCCESS) { |
|---|
| 1373 | return BERR_TRACE(BTMR_ERR_NO_TIMERS_AVAILABLE); |
|---|
| 1374 | } |
|---|
| 1375 | } |
|---|
| 1376 | |
|---|
| 1377 | BKNI_EnterCriticalSection(); |
|---|
| 1378 | status = virt_CreateTimer_isr(device, timer, pSettings); |
|---|
| 1379 | BKNI_LeaveCriticalSection(); |
|---|
| 1380 | return status; |
|---|
| 1381 | } |
|---|
| 1382 | |
|---|
| 1383 | static BERR_Code virt_DestroyTimer_isr(BTMR_TimerHandle timer) |
|---|
| 1384 | { |
|---|
| 1385 | BTMR_Handle device = timer->device; |
|---|
| 1386 | |
|---|
| 1387 | /* It would be bad to free up a timer that is running -- it could be linked onto a list, etc. |
|---|
| 1388 | ** We have to make sure its stopped before we delete it! |
|---|
| 1389 | ** Don't use virt_StopTimer_isr as the processing tests gets done in the higher level functions. |
|---|
| 1390 | */ |
|---|
| 1391 | if (timer->running) BTMR_StopTimer_isr(timer); |
|---|
| 1392 | |
|---|
| 1393 | BDBG_MSG(("BTMR_DestroyTimer: Successfully destroyed virtual timer!")); |
|---|
| 1394 | BLST_D_REMOVE(&device->VirtualCreateList, timer, created); |
|---|
| 1395 | device->VirtualCount--; |
|---|
| 1396 | |
|---|
| 1397 | return BERR_SUCCESS; |
|---|
| 1398 | } |
|---|
| 1399 | #if 0 |
|---|
| 1400 | static BERR_Code virt_DestroyTimer(BTMR_TimerHandle timer) |
|---|
| 1401 | { |
|---|
| 1402 | BERR_Code status; |
|---|
| 1403 | BTMR_Handle device = timer->device; |
|---|
| 1404 | |
|---|
| 1405 | #if 0 |
|---|
| 1406 | /* We created some utility timers needed for processing virtual timers. |
|---|
| 1407 | ** If this is the last virtual timer then we don't need these anymore. |
|---|
| 1408 | */ |
|---|
| 1409 | if (device->VirtualCount == 1) |
|---|
| 1410 | DestroyUtilityTimers(device); |
|---|
| 1411 | #endif |
|---|
| 1412 | |
|---|
| 1413 | BKNI_EnterCriticalSection(); |
|---|
| 1414 | status = virt_DestroyTimer_isr(timer); |
|---|
| 1415 | BKNI_LeaveCriticalSection(); |
|---|
| 1416 | return status; |
|---|
| 1417 | } |
|---|
| 1418 | #endif |
|---|
| 1419 | |
|---|
| 1420 | static BERR_Code virt_StartTimer_isr(BTMR_TimerHandle timer, unsigned startingValue) |
|---|
| 1421 | { |
|---|
| 1422 | BTMR_Handle device = timer->device; |
|---|
| 1423 | |
|---|
| 1424 | /* Should I let them start a running timer??? */ |
|---|
| 1425 | /* The problem here is that if they forgot to stop the timer and believe that this is actually |
|---|
| 1426 | ** starting the timer then they aren't going to get what they expected and won't know why. |
|---|
| 1427 | ** We have three choices here: 1) don't let them start a started timer, 2) stop the timer and restart |
|---|
| 1428 | ** with the new parameters, or 3) just say okay and keep going. |
|---|
| 1429 | */ |
|---|
| 1430 | if (timer->running) |
|---|
| 1431 | { |
|---|
| 1432 | #if 0 |
|---|
| 1433 | BDBG_ERR(("BTMR_StartTimer: timer already started!")); |
|---|
| 1434 | return BERR_TRACE(BTMR_ERR_ALREADY_STARTED); |
|---|
| 1435 | #else |
|---|
| 1436 | goto done; /* its already started -- success! (this is choice 3) */ |
|---|
| 1437 | #endif |
|---|
| 1438 | } |
|---|
| 1439 | |
|---|
| 1440 | /* They talk microseconds, we talk timer ticks */ |
|---|
| 1441 | timer->initialValue = MicroSecondsToTimer(startingValue); |
|---|
| 1442 | |
|---|
| 1443 | /* We'll use the free running timer to provide time values when user tries to read the current time */ |
|---|
| 1444 | timer->startingValue = GetTimerValue_isr(device->FreeRunTimer); |
|---|
| 1445 | timer->lastValue = 0; |
|---|
| 1446 | |
|---|
| 1447 | /* If its a free run timer then its already running -- we use the physical free run timer for the time calculations. |
|---|
| 1448 | ** If its a count-down type timer then we need to use the physical count-down timer to do the timing. |
|---|
| 1449 | ** That timer is shared by other count-down type timers so we queue it up for processing. |
|---|
| 1450 | */ |
|---|
| 1451 | if (timer->Settings.type == BTMR_Type_eCountDown || timer->Settings.type == BTMR_Type_ePeriodic) |
|---|
| 1452 | PutTimerOnTimeoutQueue_isr(device, timer); |
|---|
| 1453 | |
|---|
| 1454 | timer->running = true; |
|---|
| 1455 | |
|---|
| 1456 | done: |
|---|
| 1457 | return BERR_SUCCESS; |
|---|
| 1458 | } |
|---|
| 1459 | |
|---|
| 1460 | static BERR_Code virt_StopTimer_isr(BTMR_TimerHandle timer) |
|---|
| 1461 | { |
|---|
| 1462 | BTMR_Handle device = timer->device; |
|---|
| 1463 | |
|---|
| 1464 | /* Should I let them stop a stopped timer??? */ |
|---|
| 1465 | /* We have two choices here: 1) don't let them stop a stopped timer or 2) just say okay and keep going. */ |
|---|
| 1466 | if (!timer->running) |
|---|
| 1467 | { |
|---|
| 1468 | #if 0 |
|---|
| 1469 | BDBG_ERR(("BTMR_StopTimer: timer already stopped!")); |
|---|
| 1470 | return BERR_TRACE(BTMR_ERR_ALREADY_STOPPED); |
|---|
| 1471 | #else |
|---|
| 1472 | goto done; /* its already stopped -- success! (this is choice 2) */ |
|---|
| 1473 | #endif |
|---|
| 1474 | } |
|---|
| 1475 | |
|---|
| 1476 | timer->lastValue = JustReadVirtualTimer_isr(timer); |
|---|
| 1477 | |
|---|
| 1478 | /* If its a free-run timer then we save a "last" (stopped) value above. |
|---|
| 1479 | ** If its a count-down type timer then its either running, queued to be run, or stopped. |
|---|
| 1480 | ** Regardless, we just dequeue the item so that it doesn't continue to run. |
|---|
| 1481 | */ |
|---|
| 1482 | if (timer->Settings.type == BTMR_Type_eCountDown || timer->Settings.type == BTMR_Type_ePeriodic) |
|---|
| 1483 | TakeTimerOffTimeoutQueue_isr(device, timer); |
|---|
| 1484 | |
|---|
| 1485 | timer->running = false; |
|---|
| 1486 | |
|---|
| 1487 | done: |
|---|
| 1488 | return BERR_SUCCESS; |
|---|
| 1489 | } |
|---|
| 1490 | |
|---|
| 1491 | static unsigned virt_ReadTimer_isr(BTMR_TimerHandle timer) |
|---|
| 1492 | { |
|---|
| 1493 | uint32_t runTime; |
|---|
| 1494 | |
|---|
| 1495 | /* Stopped timers return the value of the timer when it was stopped! */ |
|---|
| 1496 | if (!timer->running) { |
|---|
| 1497 | runTime = timer->lastValue; |
|---|
| 1498 | BDBG_MSG(("BTMR_ReadTimer: (virt) read time from stopped timer (value=%x)!", runTime)); |
|---|
| 1499 | } else { |
|---|
| 1500 | runTime = JustReadVirtualTimer_isr(timer); |
|---|
| 1501 | } |
|---|
| 1502 | |
|---|
| 1503 | /* They talk microseconds, we talk timer ticks */ |
|---|
| 1504 | return TimerToMicroSeconds(runTime); |
|---|
| 1505 | } |
|---|
| 1506 | |
|---|
| 1507 | /* These are the functions used by virtual timers */ |
|---|
| 1508 | static const ProcessingFunctions virtualFunctions = { virt_CreateTimer, virt_DestroyTimer_isr, virt_StartTimer_isr, virt_StopTimer_isr, virt_ReadTimer_isr }; |
|---|
| 1509 | |
|---|
| 1510 | /* |
|---|
| 1511 | *********************************************************************************** |
|---|
| 1512 | *********************************************************************************** |
|---|
| 1513 | ** These are the set of published timer functions (available for external use). |
|---|
| 1514 | *********************************************************************************** |
|---|
| 1515 | *********************************************************************************** |
|---|
| 1516 | */ |
|---|
| 1517 | |
|---|
| 1518 | BERR_Code BTMR_Open(BTMR_Handle *phDevice, |
|---|
| 1519 | BCHP_Handle hChip, BREG_Handle hRegister, BINT_Handle hInterrupt, |
|---|
| 1520 | const BTMR_DeviceSettings *pOpenSettings) |
|---|
| 1521 | { |
|---|
| 1522 | BTMR_Handle device; |
|---|
| 1523 | BERR_Code retCode = BERR_SUCCESS; |
|---|
| 1524 | int timerNumber, timersInitialized = 0; |
|---|
| 1525 | |
|---|
| 1526 | /* Sanity check on the handles we've been given */ |
|---|
| 1527 | BDBG_ENTER(BTMR_Open); |
|---|
| 1528 | BDBG_ASSERT(phDevice); |
|---|
| 1529 | BDBG_ASSERT(hRegister); |
|---|
| 1530 | BDBG_ASSERT(hInterrupt); |
|---|
| 1531 | /* Note: They don't have to give me the settings, I'll just use the default */ |
|---|
| 1532 | |
|---|
| 1533 | /* Some say I shouldn't mess with the return variable unless I return success... */ |
|---|
| 1534 | *phDevice = NULL; /* just to be sure caller doesn't use it */ |
|---|
| 1535 | |
|---|
| 1536 | device = (BTMR_Handle) BKNI_Malloc(sizeof(*device)); |
|---|
| 1537 | if (device == NULL) |
|---|
| 1538 | { |
|---|
| 1539 | BDBG_ERR(("BTMR_Open: Memory allocation failed!")); |
|---|
| 1540 | retCode = BERR_TRACE(BERR_OUT_OF_SYSTEM_MEMORY); |
|---|
| 1541 | goto done; |
|---|
| 1542 | } |
|---|
| 1543 | |
|---|
| 1544 | BKNI_Memset(device, 0, sizeof(*device)); |
|---|
| 1545 | BDBG_OBJECT_SET(device, btmr_device_t); |
|---|
| 1546 | |
|---|
| 1547 | retCode = BKNI_CreateMutex(&device->create_destroy_mutex); |
|---|
| 1548 | if (retCode != BERR_SUCCESS) |
|---|
| 1549 | { |
|---|
| 1550 | BDBG_ERR(("BTMR_Open: Mutext (create_destroy) allocation failed!")); |
|---|
| 1551 | retCode = BERR_TRACE(retCode); |
|---|
| 1552 | goto error2; |
|---|
| 1553 | } |
|---|
| 1554 | |
|---|
| 1555 | device->hChip = hChip; |
|---|
| 1556 | device->hRegister = hRegister; |
|---|
| 1557 | device->hInterrupt = hInterrupt; |
|---|
| 1558 | |
|---|
| 1559 | if (pOpenSettings) |
|---|
| 1560 | BKNI_Memcpy(&device->Settings, pOpenSettings, sizeof(BTMR_DeviceSettings)); |
|---|
| 1561 | else |
|---|
| 1562 | BTMR_GetDefaultDeviceSettings(&device->Settings); |
|---|
| 1563 | |
|---|
| 1564 | /* Default this to the default timer set (in case they didn't use get defaults) */ |
|---|
| 1565 | if (!device->Settings.baseRegister) { |
|---|
| 1566 | device->Settings.baseRegister = BCHP_TIMER_REG_START; |
|---|
| 1567 | device->Settings.interruptNumber = 0; |
|---|
| 1568 | } |
|---|
| 1569 | |
|---|
| 1570 | BDBG_MSG(("BTMR_Open: device=%p using MASK %#x (pOpenSettings=%p)", (void*)device, device->Settings.timerMask, pOpenSettings)); |
|---|
| 1571 | BDBG_CASSERT(NumberOfTimers==4); /* note: you have to fix the table below when changing the number of timers */ |
|---|
| 1572 | |
|---|
| 1573 | /* If this timer block uses a level three interrupt then we have ONE callback for all the timers */ |
|---|
| 1574 | /* Level two interrupts have their own individual interrupts */ |
|---|
| 1575 | if (device->Settings.interruptNumber) |
|---|
| 1576 | { |
|---|
| 1577 | BDBG_MSG(("Interrupt number specified (using level 3)")); |
|---|
| 1578 | |
|---|
| 1579 | for (timerNumber=0; timerNumber<NumberOfTimers; timerNumber++) |
|---|
| 1580 | { |
|---|
| 1581 | /* We only setup a callback for the timers we're supposed to control (i.e. ones NOT reserved) */ |
|---|
| 1582 | if (device->Settings.timerMask & TimerSelect(timerNumber)) { |
|---|
| 1583 | BDBG_WRN(("Timer %u is reserved (skipping)", timerNumber)); |
|---|
| 1584 | continue; |
|---|
| 1585 | } |
|---|
| 1586 | |
|---|
| 1587 | /* Make sure this timer is not running from a previous run (if they ^C out before) */ |
|---|
| 1588 | DisableTimerNumber(device, timerNumber); |
|---|
| 1589 | |
|---|
| 1590 | timersInitialized++; /* got at least one! */ |
|---|
| 1591 | } |
|---|
| 1592 | |
|---|
| 1593 | if (timersInitialized) |
|---|
| 1594 | { |
|---|
| 1595 | BERR_Code result; |
|---|
| 1596 | |
|---|
| 1597 | /* This is where we setup the interrupt callback for the timers. */ |
|---|
| 1598 | /* Note: we have to fail if we couldn't enable the level two interrupt for all these timers */ |
|---|
| 1599 | result = BINT_CreateCallback(&device->alt_CbHandle, hInterrupt, device->Settings.interruptNumber, |
|---|
| 1600 | (BINT_CallbackFunc)Physical_Timer_Selection_Isr, (void *)device, 0); |
|---|
| 1601 | if (result != BERR_SUCCESS) { |
|---|
| 1602 | BDBG_ERR(("BTMR_Open: Create Callback for interrupt failed!")); |
|---|
| 1603 | goto error2; |
|---|
| 1604 | } |
|---|
| 1605 | |
|---|
| 1606 | BINT_ClearCallback(device->alt_CbHandle); |
|---|
| 1607 | } |
|---|
| 1608 | } |
|---|
| 1609 | else |
|---|
| 1610 | { |
|---|
| 1611 | BDBG_MSG(("Interrupt number not specified (using level 1)")); |
|---|
| 1612 | |
|---|
| 1613 | /* Create a callback for each of the timers we're controlling (but enable later) */ |
|---|
| 1614 | for (timerNumber=0; timerNumber<NumberOfTimers; timerNumber++) |
|---|
| 1615 | { |
|---|
| 1616 | BERR_Code result; |
|---|
| 1617 | BINT_Id intId=0; |
|---|
| 1618 | |
|---|
| 1619 | /* We only setup a callback for the timers we're supposed to control (i.e. ones NOT reserved) */ |
|---|
| 1620 | if (device->Settings.timerMask & TimerSelect(timerNumber)) { |
|---|
| 1621 | BDBG_WRN(("Timer %u is reserved (skipping)", timerNumber)); |
|---|
| 1622 | continue; |
|---|
| 1623 | } |
|---|
| 1624 | |
|---|
| 1625 | /* Make sure this timer is not running from a previous run (if they ^C out before) */ |
|---|
| 1626 | DisableTimerNumber(device, timerNumber); |
|---|
| 1627 | |
|---|
| 1628 | /*TODO: think of a way to set the intId value without having to resort to using the separate masks? */ |
|---|
| 1629 | |
|---|
| 1630 | switch (timerNumber) { |
|---|
| 1631 | case 0: intId = BCHP_INT_ID_TMR0TO; break; |
|---|
| 1632 | case 1: intId = BCHP_INT_ID_TMR1TO; break; |
|---|
| 1633 | case 2: intId = BCHP_INT_ID_TMR2TO; break; |
|---|
| 1634 | case 3: intId = BCHP_INT_ID_TMR3TO; break; |
|---|
| 1635 | } |
|---|
| 1636 | |
|---|
| 1637 | /* This is where we setup the interrupt callback for the timers WE control. */ |
|---|
| 1638 | /* Note: we don't have to fail just because we can't setup this call back -- just make it un-available! */ |
|---|
| 1639 | result = BINT_CreateCallback(&device->CbHandle[timerNumber], hInterrupt, intId, |
|---|
| 1640 | (BINT_CallbackFunc)Physical_Timer_Isr, (void *)device, timerNumber); |
|---|
| 1641 | if (result != BERR_SUCCESS) { |
|---|
| 1642 | BDBG_ERR(("BTMR_Open: Create Callback for interrupt failed (timer=%d)!", timerNumber)); |
|---|
| 1643 | device->Settings.timerMask |= TimerSelect(timerNumber); |
|---|
| 1644 | device->CbHandle[timerNumber] = NULL; |
|---|
| 1645 | continue; |
|---|
| 1646 | } |
|---|
| 1647 | |
|---|
| 1648 | /* Make sure this interrupt is clear long before we enable it in the create funtion */ |
|---|
| 1649 | /* Note: the comments for the create says is clears this, but an extra call shouldn't hurt */ |
|---|
| 1650 | BINT_ClearCallback(device->CbHandle[timerNumber]); |
|---|
| 1651 | timersInitialized++; /* got at least one! */ |
|---|
| 1652 | } |
|---|
| 1653 | } |
|---|
| 1654 | |
|---|
| 1655 | /* Its possible that all of the timers are being used outside of this driver or that we couldn't create the callback for |
|---|
| 1656 | ** any of the timers that we will control. This means we can't create the timers we need to support virtual timers! |
|---|
| 1657 | ** This can either be considered an error or return success and then fail whenever someone wants to create a timer. |
|---|
| 1658 | ** I think this should be an error! |
|---|
| 1659 | */ |
|---|
| 1660 | if (!timersInitialized) { |
|---|
| 1661 | BDBG_ERR(("BTMR_Open: failed to initialize any physical timers (all physical timers are being used externally)!")); |
|---|
| 1662 | retCode = BERR_TRACE(BTMR_ERR_NO_TIMERS_AVAILABLE); |
|---|
| 1663 | goto error2; |
|---|
| 1664 | } |
|---|
| 1665 | |
|---|
| 1666 | /* Assume that any timer reserved is already "in use" */ |
|---|
| 1667 | device->inUse = device->Settings.timerMask; |
|---|
| 1668 | |
|---|
| 1669 | /* If using static utility timers, create the physical timers we need to handle virtual timers */ |
|---|
| 1670 | /* Otherwise, we'll create the timers needed when someone requests a virtual timer */ |
|---|
| 1671 | if (device->Settings.preallocUtilTimers && CreateUtilityTimers(device) != BERR_SUCCESS) { |
|---|
| 1672 | BDBG_ERR(("BTMR_Open: failed to create utility timers needed to support virtual timers!")); |
|---|
| 1673 | retCode = BERR_TRACE(BTMR_ERR_NO_TIMERS_AVAILABLE); |
|---|
| 1674 | goto error1; |
|---|
| 1675 | } |
|---|
| 1676 | |
|---|
| 1677 | BLST_D_INIT(&device->VirtualCreateList); |
|---|
| 1678 | |
|---|
| 1679 | *phDevice = device; /* here's your handle for creating timers! */ |
|---|
| 1680 | goto done; |
|---|
| 1681 | |
|---|
| 1682 | error1: |
|---|
| 1683 | DestroyCallbacks(device); |
|---|
| 1684 | |
|---|
| 1685 | error2: |
|---|
| 1686 | if (device->create_destroy_mutex) BKNI_DestroyMutex(device->create_destroy_mutex); |
|---|
| 1687 | BDBG_OBJECT_DESTROY(device, btmr_device_t); |
|---|
| 1688 | BKNI_Free(device); |
|---|
| 1689 | |
|---|
| 1690 | done: |
|---|
| 1691 | BDBG_LEAVE(BTMR_Open); |
|---|
| 1692 | return (retCode); |
|---|
| 1693 | } |
|---|
| 1694 | |
|---|
| 1695 | BERR_Code BTMR_Close(BTMR_Handle device) |
|---|
| 1696 | { |
|---|
| 1697 | int timerNumber, timersLeftOpen; |
|---|
| 1698 | BTMR_TimerHandle timer; |
|---|
| 1699 | |
|---|
| 1700 | BDBG_ENTER(BTMR_Close); |
|---|
| 1701 | BDBG_OBJECT_ASSERT(device, btmr_device_t); |
|---|
| 1702 | |
|---|
| 1703 | BDBG_MSG(("BTMR_Close: device=%p", (void*)device)); |
|---|
| 1704 | |
|---|
| 1705 | /* When our close is called, modules with outstanding timers should have already destroyed them. |
|---|
| 1706 | ** But that doesn't mean they did. There are a couple of things we can do about this: |
|---|
| 1707 | ** 1) indicate that there were virtual timers left open and clean them up. |
|---|
| 1708 | ** 2) indicate there are outstanding shared timers (no clean-up necessary). |
|---|
| 1709 | ** 3) indicate that there are pending exclusive timers left open and clean them up. |
|---|
| 1710 | ** The clean-up is only so that we don't get yelled at for closing with allocated memory pending. |
|---|
| 1711 | ** We allocated it for someone else, but it'll still be our name on the error message and problem report. |
|---|
| 1712 | ** Use the compile flags to not do the cleanup (_DESTROY_ON_CLOSE_) or not be so nice about it (_FAIL_ON_CLOSE_). |
|---|
| 1713 | */ |
|---|
| 1714 | |
|---|
| 1715 | /* Disable any interrupts and callbacks associated with the registered interrupt */ |
|---|
| 1716 | DestroyCallbacks(device); |
|---|
| 1717 | |
|---|
| 1718 | /* From this point on we won't be getting any interrupts from any timers we control! */ |
|---|
| 1719 | |
|---|
| 1720 | /* Don't leave any of the timers running (not that it'll hurt anything). |
|---|
| 1721 | ** Note: if someone is still using this timer, even though it should be closed, this could cause a problem if he enters a wait loop. |
|---|
| 1722 | */ |
|---|
| 1723 | BKNI_EnterCriticalSection(); |
|---|
| 1724 | for (timerNumber=0; timerNumber<NumberOfTimers; timerNumber++) |
|---|
| 1725 | { |
|---|
| 1726 | /* Skip timers we don't control and stop the ones we do */ |
|---|
| 1727 | if (device->Settings.timerMask & TimerSelect(timerNumber)) continue; |
|---|
| 1728 | if (device->Timers[timerNumber]) JustStopTimer_isr(device->Timers[timerNumber]); |
|---|
| 1729 | } |
|---|
| 1730 | BKNI_LeaveCriticalSection(); |
|---|
| 1731 | |
|---|
| 1732 | /* If we have outstanding virtual timers then we allocated context for each one. |
|---|
| 1733 | ** The owner was supposed to destroy the timer before the close was called, but ... |
|---|
| 1734 | ** We should probably make this into a fail at some point (but not yet)! |
|---|
| 1735 | */ |
|---|
| 1736 | if (device->VirtualCount) |
|---|
| 1737 | { |
|---|
| 1738 | BDBG_ERR(("BTMR_Close: called with %d outstanding virtual timers (resource leak)!", device->VirtualCount)); |
|---|
| 1739 | timer = BLST_D_FIRST(&device->VirtualCreateList); |
|---|
| 1740 | while (timer) |
|---|
| 1741 | { |
|---|
| 1742 | BTMR_TimerHandle next = BLST_D_NEXT(timer, created); |
|---|
| 1743 | BDBG_ERR(("BTMR_Close: virtual timer created but never destroyed (created: %s,%d)", timer->file, timer->line)); |
|---|
| 1744 | #ifdef _DESTROY_ON_CLOSE_ |
|---|
| 1745 | BTMR_DestroyTimer(timer); |
|---|
| 1746 | #endif |
|---|
| 1747 | timer = next; |
|---|
| 1748 | } |
|---|
| 1749 | #ifdef _FAIL_ON_CLOSE_ |
|---|
| 1750 | BKNI_Fail(); |
|---|
| 1751 | #endif |
|---|
| 1752 | } |
|---|
| 1753 | |
|---|
| 1754 | /* We may have statically allocated a shared or virtual timer as part of the open. Free them up now. */ |
|---|
| 1755 | DestroyUtilityTimers(device); |
|---|
| 1756 | |
|---|
| 1757 | /* This is just informative. |
|---|
| 1758 | ** Unlike virtual timers (where we created context) all the shared timers use the same context so we can't keep track of who created them. |
|---|
| 1759 | ** So its not important that we clean these up other than to make sure the timer is stopped. |
|---|
| 1760 | */ |
|---|
| 1761 | if (device->SharedCount) { |
|---|
| 1762 | BDBG_ERR(("BTMR_Close: called with %d outstanding shared timers (resource leak)!", device->SharedCount)); |
|---|
| 1763 | device->SharedCount = 1; /* hack to make sure we really destroy the timer below (gets done on 1->0 transition) */ |
|---|
| 1764 | #ifdef _FAIL_ON_CLOSE_ |
|---|
| 1765 | BKNI_Fail(); |
|---|
| 1766 | #endif |
|---|
| 1767 | } |
|---|
| 1768 | |
|---|
| 1769 | /* |
|---|
| 1770 | ** Note: At this point, we've cleaned up after ourselves. We've also done some clean up after others (virtual and shared timers). |
|---|
| 1771 | ** The only thing left to do is clean up any pending exclusive timers that did not get freed up before calling close. |
|---|
| 1772 | ** This is not something we're required to do, but this will prevent someone from thinking the TMR code exited with allocated memory. |
|---|
| 1773 | */ |
|---|
| 1774 | |
|---|
| 1775 | timersLeftOpen = 0; |
|---|
| 1776 | for (timerNumber=0; timerNumber<NumberOfTimers; timerNumber++) |
|---|
| 1777 | { |
|---|
| 1778 | /* Destroy any existing timers (frees resources used in create) */ |
|---|
| 1779 | /* Note: this doesn't prevent someone that didn't destroy their timer from attempting to continue to use it... */ |
|---|
| 1780 | if ((timer = device->Timers[timerNumber])) |
|---|
| 1781 | { |
|---|
| 1782 | /* Special case the shared timer -- it won't correctly reflect who created but didn't destroy */ |
|---|
| 1783 | if (timer->Settings.type == BTMR_Type_eSharedFreeRun) { |
|---|
| 1784 | BDBG_ERR(("BTMR_Close: physical timer created but never destroyed")); |
|---|
| 1785 | } else { |
|---|
| 1786 | BDBG_ERR(("BTMR_Close: physical timer created but never destroyed (created: %s,%d)", timer->file, timer->line)); |
|---|
| 1787 | } |
|---|
| 1788 | #ifdef _DESTROY_ON_CLOSE_ |
|---|
| 1789 | BTMR_DestroyTimer(timer); |
|---|
| 1790 | #endif |
|---|
| 1791 | timersLeftOpen++; |
|---|
| 1792 | } |
|---|
| 1793 | } |
|---|
| 1794 | if (timersLeftOpen) { |
|---|
| 1795 | BDBG_ERR(("BTMR_Close: called with %d outstanding physical timers pending (resource leak)!", timersLeftOpen)); |
|---|
| 1796 | #ifdef _FAIL_ON_CLOSE_ |
|---|
| 1797 | BKNI_Fail(); |
|---|
| 1798 | #endif |
|---|
| 1799 | } |
|---|
| 1800 | |
|---|
| 1801 | BKNI_DestroyMutex(device->create_destroy_mutex); |
|---|
| 1802 | |
|---|
| 1803 | /* Mark it invalid in case he tries to give it to me again (close twice) */ |
|---|
| 1804 | /* Note: this probably won't happen as the memory will be reassigned and stepped on, but ... */ |
|---|
| 1805 | BDBG_OBJECT_DESTROY(device, btmr_device_t); |
|---|
| 1806 | BKNI_Free(device); |
|---|
| 1807 | |
|---|
| 1808 | BDBG_LEAVE(BTMR_Close); |
|---|
| 1809 | return BERR_SUCCESS; |
|---|
| 1810 | } |
|---|
| 1811 | |
|---|
| 1812 | static const BTMR_DeviceSettings DefaultDeviceSettings = { |
|---|
| 1813 | 0, /* don't reserve any timers for private use */ |
|---|
| 1814 | false, /* dynamically allocate timer(s) needed for virtual timers */ |
|---|
| 1815 | BCHP_TIMER_REG_START, /* default to primary TMR block */ |
|---|
| 1816 | 0 /* use default interrupt numbers */ |
|---|
| 1817 | }; |
|---|
| 1818 | |
|---|
| 1819 | BERR_Code BTMR_GetDefaultDeviceSettings(BTMR_DeviceSettings *pSettings) |
|---|
| 1820 | { |
|---|
| 1821 | BDBG_ASSERT(pSettings); /* don't write results to zero */ |
|---|
| 1822 | |
|---|
| 1823 | BKNI_Memcpy(pSettings, &DefaultDeviceSettings, sizeof(BTMR_DeviceSettings)); |
|---|
| 1824 | |
|---|
| 1825 | #if 0 && defined(BCHP_WKTMR_REG_START) |
|---|
| 1826 | /* Don't use timer 3 on chips that are lacking WKTMR support, the kernel may be using it */ |
|---|
| 1827 | /* This should be handled outside of this PI (in the open call) but might be easier to handle it here. */ |
|---|
| 1828 | pSettings->timerMask |= 1<<3; |
|---|
| 1829 | BDBG_ERR(("Setting mask to %p", pSettings->timerMask)); |
|---|
| 1830 | #endif |
|---|
| 1831 | |
|---|
| 1832 | return BERR_SUCCESS; |
|---|
| 1833 | } |
|---|
| 1834 | |
|---|
| 1835 | /* We need to recursively create timers when dynamically creating the utility timers needed for processing virtual timers. |
|---|
| 1836 | ** Because the CreateTimer function is re-entrant, this could allow us to try to crete the utility timers from two different tasks. |
|---|
| 1837 | ** A semaphore was added to mutually exclude the timer creates from occurring at the same time. This function allows us to |
|---|
| 1838 | ** recursively enter the CreateTimer and by-pass the locking semaphore. |
|---|
| 1839 | */ |
|---|
| 1840 | static BERR_Code _BTMR_CreateTimer(BTMR_Handle device, BTMR_TimerHandle *phTimer, const BTMR_TimerSettings *pSettings, const char *file, int line) |
|---|
| 1841 | { |
|---|
| 1842 | BERR_Code errCode = BERR_SUCCESS; |
|---|
| 1843 | BTMR_TimerHandle timer; |
|---|
| 1844 | |
|---|
| 1845 | /* If they want to use the Shared Free Run timer (and it already exists) -- give it to them! */ |
|---|
| 1846 | if (pSettings->type == BTMR_Type_eSharedFreeRun && device->SharedTimer) |
|---|
| 1847 | { |
|---|
| 1848 | device->SharedCount++; /* one more user sharing this timer! */ |
|---|
| 1849 | *phTimer = device->SharedTimer; /* here's your handle for using this timer! */ |
|---|
| 1850 | BDBG_MSG(("BTMR_CreateTimer: NOT creating THE timer for shared timer [already created, count=%d] (%s,%d)!", device->SharedCount, file, line)); |
|---|
| 1851 | return BERR_SUCCESS; |
|---|
| 1852 | } |
|---|
| 1853 | |
|---|
| 1854 | /* If not shared (or shared doesn't yet exist) then this is a new timer and we need a handle for further use */ |
|---|
| 1855 | timer = (BTMR_TimerHandle) BKNI_Malloc(sizeof(*timer)); |
|---|
| 1856 | if (timer == NULL) |
|---|
| 1857 | { |
|---|
| 1858 | BDBG_ERR(("BTMR_CreateTimer: Memory allocation failed!")); |
|---|
| 1859 | return BERR_TRACE(BERR_OUT_OF_SYSTEM_MEMORY); |
|---|
| 1860 | } |
|---|
| 1861 | |
|---|
| 1862 | BKNI_Memset(timer, 0, sizeof(*timer)); |
|---|
| 1863 | BDBG_OBJECT_SET(timer, btmr_timer_t); |
|---|
| 1864 | |
|---|
| 1865 | /* |
|---|
| 1866 | ** We'd like to enforce the use of virtual timers here by over-riding the exclusive setting to false. |
|---|
| 1867 | ** But that might break someone's code that really does want to manipulate the timer outside of this PI. |
|---|
| 1868 | ** That is something you can't do with Virtual Timers (could I fake this???). |
|---|
| 1869 | ** So don't do this! |
|---|
| 1870 | */ |
|---|
| 1871 | |
|---|
| 1872 | /* Use the appropriate processing functions for this timer based on the requested type */ |
|---|
| 1873 | if (pSettings->exclusive || pSettings->type == BTMR_Type_eSharedFreeRun) |
|---|
| 1874 | timer->functions = (ProcessingFunctions*)&physicalFunctions; |
|---|
| 1875 | else |
|---|
| 1876 | timer->functions = (ProcessingFunctions*)&virtualFunctions; |
|---|
| 1877 | |
|---|
| 1878 | timer->device = device; |
|---|
| 1879 | BKNI_Memcpy(&timer->Settings, pSettings, sizeof(BTMR_TimerSettings)); |
|---|
| 1880 | |
|---|
| 1881 | timer->file = file; /* remember where this timer was created */ |
|---|
| 1882 | timer->line = line; /* so we can tell the user on exit where an un-destroyed timer was created */ |
|---|
| 1883 | |
|---|
| 1884 | BDBG_MSG(("BTMR_CreateTimer: creating %s(%d) timer (%s,%d)", GetTimerType(pSettings), pSettings->type, file, line)); |
|---|
| 1885 | errCode = timer->functions->create(device, timer, pSettings); |
|---|
| 1886 | if (errCode) { |
|---|
| 1887 | BDBG_ERR(("BTMR_CreateTimer: failed to create %s(%d) timer (at %s,%d)", GetTimerType(pSettings), pSettings->type, file, line)); |
|---|
| 1888 | BKNI_Free(timer); |
|---|
| 1889 | return BERR_TRACE(errCode); |
|---|
| 1890 | } |
|---|
| 1891 | |
|---|
| 1892 | *phTimer = timer; /* here's your handle for using this timer! */ |
|---|
| 1893 | |
|---|
| 1894 | return BERR_SUCCESS; |
|---|
| 1895 | } |
|---|
| 1896 | |
|---|
| 1897 | BERR_Code BTMR_CreateTimer_tagged(BTMR_Handle device, BTMR_TimerHandle *phTimer, const BTMR_TimerSettings *pSettings, const char *file, int line) |
|---|
| 1898 | { |
|---|
| 1899 | BERR_Code errCode = BERR_SUCCESS; |
|---|
| 1900 | |
|---|
| 1901 | BDBG_ENTER(BTMR_CreateTimer); |
|---|
| 1902 | BDBG_OBJECT_ASSERT(device, btmr_device_t); |
|---|
| 1903 | BDBG_ASSERT(phTimer); /* this is where I put his handle -- don't write to zero */ |
|---|
| 1904 | BDBG_ASSERT(pSettings); /* this is the timer settings -- he MUST give me some settings */ |
|---|
| 1905 | |
|---|
| 1906 | /* Some say I shouldn't mess with the return variable unless I return success... */ |
|---|
| 1907 | *phTimer = NULL; /* just to be sure caller doesn't use it -- you don't have a timer till I give you one! */ |
|---|
| 1908 | |
|---|
| 1909 | shortenFileName(file); |
|---|
| 1910 | |
|---|
| 1911 | #if 1 |
|---|
| 1912 | /* |
|---|
| 1913 | ** Now I COULD be nice and let them provide a callback when not expected (I just won't call it) |
|---|
| 1914 | ** or not provide a callback when I do expect it (nothing provided then nothing to call) but ... |
|---|
| 1915 | */ |
|---|
| 1916 | if (pSettings->type == BTMR_Type_eStopWatch || pSettings->type == BTMR_Type_eSharedFreeRun) |
|---|
| 1917 | { |
|---|
| 1918 | /* StopWatch and FreeRun timers DON'T GET callbacks! Only used for sampling */ |
|---|
| 1919 | if (pSettings->cb_isr) { |
|---|
| 1920 | BDBG_ERR(("BTMR_CreateTimer: StopWatch timer supplied with call back routine -- illegal!")); |
|---|
| 1921 | return BERR_TRACE(BERR_INVALID_PARAMETER); |
|---|
| 1922 | } |
|---|
| 1923 | } |
|---|
| 1924 | else |
|---|
| 1925 | { |
|---|
| 1926 | /* Count-down and Periodic timers MUST supply a callback! */ |
|---|
| 1927 | if (!pSettings->cb_isr) { |
|---|
| 1928 | BDBG_ERR(("BTMR_CreateTimer: Count-down or Periodic timer NOT supplied with call back routine -- illegal!")); |
|---|
| 1929 | return BERR_TRACE(BERR_INVALID_PARAMETER); |
|---|
| 1930 | } |
|---|
| 1931 | } |
|---|
| 1932 | #endif |
|---|
| 1933 | |
|---|
| 1934 | /* |
|---|
| 1935 | ** We have a re-entrancy issue with creating and destroying timers. |
|---|
| 1936 | ** Multiple processes can try to create/destroy a timer that can cause the Shared Timer and Virtual Timer counts to get stepped on. |
|---|
| 1937 | ** Because much of the work of creating a timer gets done at _isr level (in Critical Section) we have to block almost the entire |
|---|
| 1938 | ** create/destroy process. |
|---|
| 1939 | */ |
|---|
| 1940 | |
|---|
| 1941 | BKNI_AcquireMutex(device->create_destroy_mutex); |
|---|
| 1942 | errCode = _BTMR_CreateTimer(device, phTimer, pSettings, file, line); |
|---|
| 1943 | BKNI_ReleaseMutex(device->create_destroy_mutex); |
|---|
| 1944 | |
|---|
| 1945 | BDBG_LEAVE(BTMR_CreateTimer); |
|---|
| 1946 | return errCode; |
|---|
| 1947 | } |
|---|
| 1948 | |
|---|
| 1949 | /* We need to recursively destroy timers. |
|---|
| 1950 | ** This can happen when we're destroying the utility timers that are no longer needed after deleting the last virtual timer. |
|---|
| 1951 | */ |
|---|
| 1952 | static BERR_Code _BTMR_DestroyTimer(BTMR_TimerHandle timer, const char*file, int line) |
|---|
| 1953 | { |
|---|
| 1954 | BERR_Code errCode = BERR_SUCCESS; |
|---|
| 1955 | BTMR_Handle device; |
|---|
| 1956 | |
|---|
| 1957 | BDBG_OBJECT_ASSERT(timer, btmr_timer_t); |
|---|
| 1958 | BSTD_UNUSED(file); |
|---|
| 1959 | BSTD_UNUSED(line); |
|---|
| 1960 | |
|---|
| 1961 | device = timer->device; |
|---|
| 1962 | |
|---|
| 1963 | /* Shared Free Run timers only get destroyed on last destroy */ |
|---|
| 1964 | if (timer->Settings.type == BTMR_Type_eSharedFreeRun && device->SharedCount > 1) |
|---|
| 1965 | { |
|---|
| 1966 | BDBG_MSG(("BTMR_DestroyTimer: NOT deleting THE shared timer [still in use by others, count=%d] (%s,%d)!", device->SharedCount, file, line)); |
|---|
| 1967 | device->SharedCount--; |
|---|
| 1968 | return BERR_SUCCESS; |
|---|
| 1969 | } |
|---|
| 1970 | |
|---|
| 1971 | /* The shared timer reflects the first one to create one, not necessarily the one destroying it */ |
|---|
| 1972 | BDBG_MSG(("BTMR_DestroyTimer: destroying %s timer %d (%s,%d)", GetTimerType(&timer->Settings), timer->timerNumber, file, line)); |
|---|
| 1973 | if (timer->Settings.type != BTMR_Type_eSharedFreeRun) |
|---|
| 1974 | { |
|---|
| 1975 | BDBG_MSG(("BTMR_DestroyTimer: destroying %s timer %d (created: %s,%d)", |
|---|
| 1976 | GetTimerType(&timer->Settings), timer->timerNumber, timer->file, timer->line)); |
|---|
| 1977 | } |
|---|
| 1978 | |
|---|
| 1979 | BKNI_EnterCriticalSection(); |
|---|
| 1980 | errCode = timer->functions->destroy_isr(timer); |
|---|
| 1981 | BKNI_LeaveCriticalSection(); |
|---|
| 1982 | |
|---|
| 1983 | /* Mark it as invalid in case he tries to give it to me again (destroy twice, or operation after destroy) */ |
|---|
| 1984 | /* Note: this probably won't happen as the memory will be reassigned and stepped on, but ... */ |
|---|
| 1985 | BDBG_OBJECT_DESTROY(timer, btmr_timer_t); |
|---|
| 1986 | BKNI_Free(timer); |
|---|
| 1987 | |
|---|
| 1988 | return errCode; |
|---|
| 1989 | } |
|---|
| 1990 | |
|---|
| 1991 | BERR_Code BTMR_DestroyTimer_tagged(BTMR_TimerHandle timer, const char*file, int line) |
|---|
| 1992 | { |
|---|
| 1993 | BERR_Code errCode = BERR_SUCCESS; |
|---|
| 1994 | BTMR_Handle device; |
|---|
| 1995 | |
|---|
| 1996 | BDBG_ENTER(BTMR_DestroyTimer); |
|---|
| 1997 | BDBG_OBJECT_ASSERT(timer, btmr_timer_t); |
|---|
| 1998 | |
|---|
| 1999 | device = timer->device; |
|---|
| 2000 | shortenFileName(file); |
|---|
| 2001 | |
|---|
| 2002 | /* You are not allowed to delete a timer from within the count-down or periodic timer's callback function. |
|---|
| 2003 | ** This is just a bad thing to do. Besides, the call-back is running from ISR and this isn't an _isr routine. |
|---|
| 2004 | */ |
|---|
| 2005 | if (timer->processing) { |
|---|
| 2006 | BDBG_ERR(("BTMR_DestroyTimer: request to destroy timer from a timer callback operation!")); |
|---|
| 2007 | return BERR_TRACE(BTMR_ERR_DELETE_FROM_ISR); |
|---|
| 2008 | } |
|---|
| 2009 | |
|---|
| 2010 | BKNI_AcquireMutex(device->create_destroy_mutex); |
|---|
| 2011 | errCode = _BTMR_DestroyTimer(timer, file, line); |
|---|
| 2012 | BKNI_ReleaseMutex(device->create_destroy_mutex); |
|---|
| 2013 | |
|---|
| 2014 | BDBG_LEAVE(BTMR_DestroyTimer); |
|---|
| 2015 | return errCode; |
|---|
| 2016 | } |
|---|
| 2017 | |
|---|
| 2018 | BERR_Code BTMR_GetTimerRegisters(BTMR_TimerHandle timer, BTMR_TimerRegisters *pRegisters) |
|---|
| 2019 | { |
|---|
| 2020 | BDBG_ENTER(BTMR_GetTimerRegisters); |
|---|
| 2021 | BDBG_OBJECT_ASSERT(timer, btmr_timer_t); |
|---|
| 2022 | BDBG_ASSERT(pRegisters); /* don't write results to zero */ |
|---|
| 2023 | |
|---|
| 2024 | /*BDBG_MSG(("Getting the registers for timer number %d (%s)", timer->timerNumber, GetTimerType(&timer->Settings)));*/ |
|---|
| 2025 | |
|---|
| 2026 | /* They are only supposed to call this function IF the timer was created as exclusive. |
|---|
| 2027 | ** Special case: we allow anyone using shared timer to also get its registers. |
|---|
| 2028 | ** We NEVER allow them to get the register values of a virtual timer (they don't exist). |
|---|
| 2029 | */ |
|---|
| 2030 | if (!timer->Settings.exclusive && !(timer->Settings.type == BTMR_Type_eSharedFreeRun)) { |
|---|
| 2031 | BDBG_ERR(("BTMR_GetTimerRegisters: asked for registers to non-exclusive/non-shared timer!")); |
|---|
| 2032 | return BERR_TRACE(BTMR_ERR_EXCLUSIVE_OPERATION); |
|---|
| 2033 | } |
|---|
| 2034 | |
|---|
| 2035 | BKNI_Memcpy(pRegisters, &timer->Registers, sizeof(BTMR_TimerRegisters)); |
|---|
| 2036 | |
|---|
| 2037 | BDBG_LEAVE(BTMR_GetTimerRegisters); |
|---|
| 2038 | return BERR_SUCCESS; |
|---|
| 2039 | } |
|---|
| 2040 | |
|---|
| 2041 | static const BTMR_TimerSettings DefaultTimerSettings = { BTMR_Type_eStopWatch, NULL, NULL, 0, false }; |
|---|
| 2042 | |
|---|
| 2043 | BERR_Code BTMR_GetDefaultTimerSettings(BTMR_TimerSettings *pSettings) |
|---|
| 2044 | { |
|---|
| 2045 | BDBG_ASSERT(pSettings); /* don't write results to zero */ |
|---|
| 2046 | |
|---|
| 2047 | BKNI_Memcpy(pSettings, &DefaultTimerSettings, sizeof(BTMR_TimerSettings)); |
|---|
| 2048 | return BERR_SUCCESS; |
|---|
| 2049 | } |
|---|
| 2050 | |
|---|
| 2051 | /* This gets the current settings for THIS timer as opposed to the above that gets the default for ANY timer */ |
|---|
| 2052 | BERR_Code BTMR_GetTimerSettings(BTMR_TimerHandle timer, BTMR_TimerSettings *pSettings) |
|---|
| 2053 | { |
|---|
| 2054 | BDBG_OBJECT_ASSERT(timer, btmr_timer_t); |
|---|
| 2055 | BDBG_ASSERT(pSettings); /* don't write results to zero */ |
|---|
| 2056 | |
|---|
| 2057 | BKNI_Memcpy(pSettings, &timer->Settings, sizeof(BTMR_TimerSettings)); |
|---|
| 2058 | return BERR_SUCCESS; |
|---|
| 2059 | } |
|---|
| 2060 | |
|---|
| 2061 | BERR_Code BTMR_StartTimer_isr(BTMR_TimerHandle timer, unsigned startingValue) |
|---|
| 2062 | { |
|---|
| 2063 | BDBG_OBJECT_ASSERT(timer, btmr_timer_t); |
|---|
| 2064 | |
|---|
| 2065 | /* If this was called from the count-down timer's callback function then we don't actually start it here. |
|---|
| 2066 | ** This could mess up the processing in the interrupt service routines. So just mark it to be started. |
|---|
| 2067 | ** Note: we'll get called back here for virtual timers but with "processing" set to false this time. |
|---|
| 2068 | ** Also note that we can't provide proper status for this request because it doesn't happen until later. |
|---|
| 2069 | */ |
|---|
| 2070 | if (timer->processing) { |
|---|
| 2071 | timer->delayedValue = startingValue; |
|---|
| 2072 | timer->pleaseStart = true; |
|---|
| 2073 | return BERR_SUCCESS; |
|---|
| 2074 | } |
|---|
| 2075 | |
|---|
| 2076 | /* What should I do with a zero timer request? Ignore request? Error? Make believe the timer expired (i.e. callback)? */ |
|---|
| 2077 | if (!startingValue && (timer->Settings.type == BTMR_Type_eCountDown || timer->Settings.type == BTMR_Type_ePeriodic)) { |
|---|
| 2078 | BDBG_ERR(("BTMR_StartTimer: request to start count-down timer but given a zero count -- don't know what to do with that!!")); |
|---|
| 2079 | return BERR_TRACE(BTMR_ERR_NO_TIMEOUT_GIVEN); |
|---|
| 2080 | } |
|---|
| 2081 | |
|---|
| 2082 | /* A periodic timer with too small a timeout will cause a hang (timer continuously times out) */ |
|---|
| 2083 | if (startingValue < BTMR_MINIMUM_TIMEOUT && timer->Settings.type == BTMR_Type_ePeriodic) { |
|---|
| 2084 | BDBG_ERR(("BTMR_StartTimer: request to start periodic timer with too small a timeout -- will cause a interrupt hang!!")); |
|---|
| 2085 | return BERR_TRACE(BTMR_ERR_TIMEOUT_TOO_SMALL); |
|---|
| 2086 | } |
|---|
| 2087 | |
|---|
| 2088 | /* We can't allow a periodic timer to start up with a timeout less than our minimum. |
|---|
| 2089 | ** This would allow us to continue to add the timer to the timeout list and then immediately time it out (and hang). |
|---|
| 2090 | ** Note: this is different than the MINIMUM_TIMEOUT value (and check) above. |
|---|
| 2091 | */ |
|---|
| 2092 | if (startingValue < BTMR_MIN_HOLD && timer->Settings.type == BTMR_Type_ePeriodic) |
|---|
| 2093 | startingValue = BTMR_MIN_HOLD + 1; |
|---|
| 2094 | |
|---|
| 2095 | BDBG_MSG(("BTMR_StartTimer: starting %s timer %d (initial=%08x)", GetTimerType(&timer->Settings), timer->timerNumber, startingValue)); |
|---|
| 2096 | return timer->functions->start_isr(timer, startingValue); |
|---|
| 2097 | } |
|---|
| 2098 | BERR_Code BTMR_StartTimer(BTMR_TimerHandle timer, unsigned startingValue) |
|---|
| 2099 | { |
|---|
| 2100 | BERR_Code result; |
|---|
| 2101 | BDBG_ENTER(BTMR_StartTimer); |
|---|
| 2102 | BKNI_EnterCriticalSection(); |
|---|
| 2103 | result = BTMR_StartTimer_isr(timer, startingValue); |
|---|
| 2104 | BKNI_LeaveCriticalSection(); |
|---|
| 2105 | BDBG_LEAVE(BTMR_StartTimer); |
|---|
| 2106 | return result; |
|---|
| 2107 | } |
|---|
| 2108 | |
|---|
| 2109 | BERR_Code BTMR_StopTimer_isr(BTMR_TimerHandle timer) |
|---|
| 2110 | { |
|---|
| 2111 | BDBG_OBJECT_ASSERT(timer, btmr_timer_t); |
|---|
| 2112 | |
|---|
| 2113 | /* If this was called from the count-down timer's callback function then we don't actually stop it here. |
|---|
| 2114 | ** This could mess up the processing in the interrupt service routines. So just mark it to be stopped. |
|---|
| 2115 | ** Note that we can't provide proper status for this request because it doesn't happen until later. |
|---|
| 2116 | */ |
|---|
| 2117 | if (timer->processing) { |
|---|
| 2118 | timer->pleaseStop = true; |
|---|
| 2119 | return BERR_SUCCESS; |
|---|
| 2120 | } |
|---|
| 2121 | |
|---|
| 2122 | BDBG_MSG(("BTMR_StopTimer: stopping %s timer %d", GetTimerType(&timer->Settings), timer->timerNumber)); |
|---|
| 2123 | return timer->functions->stop_isr(timer); |
|---|
| 2124 | } |
|---|
| 2125 | BERR_Code BTMR_StopTimer(BTMR_TimerHandle timer) |
|---|
| 2126 | { |
|---|
| 2127 | BERR_Code result; |
|---|
| 2128 | BDBG_ENTER(BTMR_StopTimer); |
|---|
| 2129 | BKNI_EnterCriticalSection(); |
|---|
| 2130 | result = BTMR_StopTimer_isr(timer); |
|---|
| 2131 | BKNI_LeaveCriticalSection(); |
|---|
| 2132 | BDBG_LEAVE(BTMR_StopTimer); |
|---|
| 2133 | return result; |
|---|
| 2134 | } |
|---|
| 2135 | |
|---|
| 2136 | BERR_Code BTMR_ReadTimer_isr(BTMR_TimerHandle timer, unsigned *pValue) |
|---|
| 2137 | { |
|---|
| 2138 | BDBG_OBJECT_ASSERT(timer, btmr_timer_t); |
|---|
| 2139 | if (pValue) *pValue = timer->functions->read_isr(timer); |
|---|
| 2140 | return BERR_SUCCESS; |
|---|
| 2141 | } |
|---|
| 2142 | BERR_Code BTMR_ReadTimer(BTMR_TimerHandle timer, unsigned *pValue) |
|---|
| 2143 | { |
|---|
| 2144 | BERR_Code result; |
|---|
| 2145 | BDBG_ENTER(BTMR_ReadTimer); |
|---|
| 2146 | BKNI_EnterCriticalSection(); |
|---|
| 2147 | result = BTMR_ReadTimer_isr(timer, pValue); |
|---|
| 2148 | BKNI_LeaveCriticalSection(); |
|---|
| 2149 | BDBG_LEAVE(BTMR_ReadTimer); |
|---|
| 2150 | return result; |
|---|
| 2151 | } |
|---|
| 2152 | |
|---|
| 2153 | /* Note: we passed in a handle in case we ever have different wraps for different timer blocks */ |
|---|
| 2154 | unsigned BTMR_ReadTimerMax(void) |
|---|
| 2155 | { |
|---|
| 2156 | return TimerToMicroSeconds(MaxTimerValue); |
|---|
| 2157 | } |
|---|
| 2158 | |
|---|
| 2159 | /* End of File */ |
|---|
| 2160 | |
|---|