/***************************************************************************** * Copyright 2003 - 2008 Broadcom Corporation. All rights reserved. * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2, available at * http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a * license other than the GPL, without Broadcom's express prior written * consent. *****************************************************************************/ /****************************************************************************/ /** * @file tmrHw.c * * @brief Low level Timer driver routines * * @note * * These routines provide basic timer functionality only. */ /****************************************************************************/ /* ---- Include Files ---------------------------------------------------- */ #include #include #include #include #define tmrHw_ASSERT(a) if (!(a)) *(char *)0 = 0 #define tmrHw_MILLISEC_PER_SEC (1000) #define tmrHw_LOW_1_RESOLUTION_COUNT (tmrHw_LOW_RESOLUTION_CLOCK / tmrHw_MILLISEC_PER_SEC) #define tmrHw_LOW_1_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_1_RESOLUTION_COUNT) #define tmrHw_LOW_16_RESOLUTION_COUNT (tmrHw_LOW_1_RESOLUTION_COUNT / 16) #define tmrHw_LOW_16_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_16_RESOLUTION_COUNT) #define tmrHw_LOW_256_RESOLUTION_COUNT (tmrHw_LOW_1_RESOLUTION_COUNT / 256) #define tmrHw_LOW_256_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_256_RESOLUTION_COUNT) #define tmrHw_HIGH_1_RESOLUTION_COUNT (tmrHw_HIGH_RESOLUTION_CLOCK / tmrHw_MILLISEC_PER_SEC) #define tmrHw_HIGH_1_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_1_RESOLUTION_COUNT) #define tmrHw_HIGH_16_RESOLUTION_COUNT (tmrHw_HIGH_1_RESOLUTION_COUNT / 16) #define tmrHw_HIGH_16_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_16_RESOLUTION_COUNT) #define tmrHw_HIGH_256_RESOLUTION_COUNT (tmrHw_HIGH_1_RESOLUTION_COUNT / 256) #define tmrHw_HIGH_256_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_256_RESOLUTION_COUNT) static void ResetTimer(tmrHw_ID_t timerId) __attribute__ ((section(".aramtext"))); static int tmrHw_divide(int num, int denom) __attribute__ ((section(".aramtext"))); /****************************************************************************/ /** * @brief Get timer capability * * This function returns various capabilities/attributes of a timer * * @return Capability * */ /****************************************************************************/ uint32_t tmrHw_getTimerCapability(tmrHw_ID_t timerId, /* [ IN ] Timer Id */ tmrHw_CAPABILITY_e capability /* [ IN ] Timer capability */ ) { switch (capability) { case tmrHw_CAPABILITY_CLOCK: return (timerId <= 1) ? tmrHw_LOW_RESOLUTION_CLOCK : tmrHw_HIGH_RESOLUTION_CLOCK; case tmrHw_CAPABILITY_RESOLUTION: return 32; default: return 0; } return 0; } /****************************************************************************/ /** * @brief Resets a timer * * This function initializes timer * * @return void * */ /****************************************************************************/ static void ResetTimer(tmrHw_ID_t timerId /* [ IN ] Timer Id */ ) { /* Reset timer */ pTmrHw[timerId].LoadValue = 0; pTmrHw[timerId].CurrentValue = 0xFFFFFFFF; pTmrHw[timerId].Control = 0; pTmrHw[timerId].BackgroundLoad = 0; /* Always configure as a 32 bit timer */ pTmrHw[timerId].Control |= tmrHw_CONTROL_32BIT; /* Clear interrupt only if raw status interrupt is set */ if (pTmrHw[timerId].RawInterruptStatus) { pTmrHw[timerId].InterruptClear = 0xFFFFFFFF; } } /****************************************************************************/ /** * @brief Sets counter value for an interval in ms * * @return On success: Effective counter value set * On failure: 0 * */ /****************************************************************************/ static tmrHw_INTERVAL_t SetTimerPeriod(tmrHw_ID_t timerId, /* [ IN ] Timer Id */ tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */ ) { uint32_t scale = 0; uint32_t count = 0; if (timerId == 0 || timerId == 1) { if (msec <= tmrHw_LOW_1_MAX_MILLISEC) { pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1; scale = tmrHw_LOW_1_RESOLUTION_COUNT; } else if (msec <= tmrHw_LOW_16_MAX_MILLISEC) { pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16; scale = tmrHw_LOW_16_RESOLUTION_COUNT; } else if (msec <= tmrHw_LOW_256_MAX_MILLISEC) { pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256; scale = tmrHw_LOW_256_RESOLUTION_COUNT; } else { return 0; } count = msec * scale; /* Set counter value */ pTmrHw[timerId].LoadValue = count; pTmrHw[timerId].BackgroundLoad = count; } else if (timerId == 2 || timerId == 3) { if (msec <= tmrHw_HIGH_1_MAX_MILLISEC) { pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1; scale = tmrHw_HIGH_1_RESOLUTION_COUNT; } else if (msec <= tmrHw_HIGH_16_MAX_MILLISEC) { pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16; scale = tmrHw_HIGH_16_RESOLUTION_COUNT; } else if (msec <= tmrHw_HIGH_256_MAX_MILLISEC) { pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256; scale = tmrHw_HIGH_256_RESOLUTION_COUNT; } else { return 0; } count = msec * scale; /* Set counter value */ pTmrHw[timerId].LoadValue = count; pTmrHw[timerId].BackgroundLoad = count; } return count / scale; } /****************************************************************************/ /** * @brief Configures a periodic timer in terms of timer interrupt rate * * This function initializes a periodic timer to generate specific number of * timer interrupt per second * * @return On success: Effective timer frequency * On failure: 0 * */ /****************************************************************************/ tmrHw_RATE_t tmrHw_setPeriodicTimerRate(tmrHw_ID_t timerId, /* [ IN ] Timer Id */ tmrHw_RATE_t rate /* [ IN ] Number of timer interrupt per second */ ) { uint32_t resolution = 0; uint32_t count = 0; ResetTimer(timerId); /* Set timer mode periodic */ pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC; pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT; /* Set timer in highest resolution */ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1; if (rate && (timerId == 0 || timerId == 1)) { if (rate > tmrHw_LOW_RESOLUTION_CLOCK) { return 0; } resolution = tmrHw_LOW_RESOLUTION_CLOCK; } else if (rate && (timerId == 2 || timerId == 3)) { if (rate > tmrHw_HIGH_RESOLUTION_CLOCK) { return 0; } else { resolution = tmrHw_HIGH_RESOLUTION_CLOCK; } } else { return 0; } /* Find the counter value */ count = resolution / rate; /* Set counter value */ pTmrHw[timerId].LoadValue = count; pTmrHw[timerId].BackgroundLoad = count; return resolution / count; } /****************************************************************************/ /** * @brief Configures a periodic timer to generate timer interrupt after * certain time interval * * This function initializes a periodic timer to generate timer interrupt * after every time interval in millisecond * * @return On success: Effective interval set in milli-second * On failure: 0 * */ /****************************************************************************/ tmrHw_INTERVAL_t tmrHw_setPeriodicTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */ tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */ ) { ResetTimer(timerId); /* Set timer mode periodic */ pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC; pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT; return SetTimerPeriod(timerId, msec); } /****************************************************************************/ /** * @brief Configures a periodic timer to generate timer interrupt just once * after certain time interval * * This function initializes a periodic timer to generate a single ticks after * certain time interval in millisecond * * @return On success: Effective interval set in milli-second * On failure: 0 * */ /****************************************************************************/ tmrHw_INTERVAL_t tmrHw_setOneshotTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */ tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */ ) { ResetTimer(timerId); /* Set timer mode oneshot */ pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC; pTmrHw[timerId].Control |= tmrHw_CONTROL_ONESHOT; return SetTimerPeriod(timerId, msec); } /****************************************************************************/ /** * @brief Configures a timer to run as a free running timer * * This function initializes a timer to run as a free running timer * * @return Timer resolution (count / sec) * */ /****************************************************************************/ tmrHw_RATE_t tmrHw_setFreeRunningTimer(tmrHw_ID_t timerId, /* [ IN ] Timer Id */ uint32_t divider /* [ IN ] Dividing the clock frequency */ ) { uint32_t scale = 0; ResetTimer(timerId); /* Set timer as free running mode */ pTmrHw[timerId].Control &= ~tmrHw_CONTROL_PERIODIC; pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT; if (divider >= 64) { pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256; scale = 256; } else if (divider >= 8) { pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16; scale = 16; } else { pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1; scale = 1; } if (timerId == 0 || timerId == 1) { return tmrHw_divide(tmrHw_LOW_RESOLUTION_CLOCK, scale); } else if (timerId == 2 || timerId == 3) { return tmrHw_divide(tmrHw_HIGH_RESOLUTION_CLOCK, scale); } return 0; } /****************************************************************************/ /** * @brief Starts a timer * * This function starts a preconfigured timer * * @return -1 - On Failure * 0 - On Success * */ /****************************************************************************/ int tmrHw_startTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */ ) { pTmrHw[timerId].Control |= tmrHw_CONTROL_TIMER_ENABLE; return 0; } /****************************************************************************/ /** * @brief Stops a timer * * This function stops a running timer * * @return -1 - On Failure * 0 - On Success * */ /****************************************************************************/ int tmrHw_stopTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */ ) { pTmrHw[timerId].Control &= ~tmrHw_CONTROL_TIMER_ENABLE; return 0; } /****************************************************************************/ /** * @brief Gets current timer count * * This function returns the current timer value * * @return Current downcounting timer value * */ /****************************************************************************/ uint32_t tmrHw_GetCurrentCount(tmrHw_ID_t timerId /* [ IN ] Timer id */ ) { /* return 32 bit timer value */ switch (pTmrHw[timerId].Control & tmrHw_CONTROL_MODE_MASK) { case tmrHw_CONTROL_FREE_RUNNING: if (pTmrHw[timerId].CurrentValue) { return tmrHw_MAX_COUNT - pTmrHw[timerId].CurrentValue; } break; case tmrHw_CONTROL_PERIODIC: case tmrHw_CONTROL_ONESHOT: return pTmrHw[timerId].BackgroundLoad - pTmrHw[timerId].CurrentValue; } return 0; } /****************************************************************************/ /** * @brief Gets timer count rate * * This function returns the number of counts per second * * @return Count rate * */ /****************************************************************************/ tmrHw_RATE_t tmrHw_getCountRate(tmrHw_ID_t timerId /* [ IN ] Timer id */ ) { uint32_t divider = 0; switch (pTmrHw[timerId].Control & tmrHw_CONTROL_PRESCALE_MASK) { case tmrHw_CONTROL_PRESCALE_1: divider = 1; break; case tmrHw_CONTROL_PRESCALE_16: divider = 16; break; case tmrHw_CONTROL_PRESCALE_256: divider = 256; break; default: tmrHw_ASSERT(0); } if (timerId == 0 || timerId == 1) { return tmrHw_divide(tmrHw_LOW_RESOLUTION_CLOCK, divider); } else { return tmrHw_divide(tmrHw_HIGH_RESOLUTION_CLOCK, divider); } return 0; } /****************************************************************************/ /** * @brief Enables timer interrupt * * This function enables the timer interrupt * * @return N/A * */ /****************************************************************************/ void tmrHw_enableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */ ) { pTmrHw[timerId].Control |= tmrHw_CONTROL_INTERRUPT_ENABLE; } /****************************************************************************/ /** * @brief Disables timer interrupt * * This function disable the timer interrupt * * @return N/A * */ /****************************************************************************/ void tmrHw_disableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */ ) { pTmrHw[timerId].Control &= ~tmrHw_CONTROL_INTERRUPT_ENABLE; } /****************************************************************************/ /** * @brief Clears the interrupt * * This function clears the timer interrupt * * @return N/A * * @note * Must be called under the context of ISR */ /****************************************************************************/ void tmrHw_clearInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */ ) { pTmrHw[timerId].InterruptClear = 0x1; } /****************************************************************************/ /** * @brief Gets the interrupt status * * This function returns timer interrupt status * * @return Interrupt status */ /****************************************************************************/ tmrHw_INTERRUPT_STATUS_e tmrHw_getInterruptStatus(tmrHw_ID_t timerId /* [ IN ] Timer id */ ) { if (pTmrHw[timerId].InterruptStatus) { return tmrHw_INTERRUPT_STATUS_SET; } else { return tmrHw_INTERRUPT_STATUS_UNSET; } } /****************************************************************************/ /** * @brief Indentifies a timer causing interrupt * * This functions returns a timer causing interrupt * * @return 0xFFFFFFFF : No timer causing an interrupt * ! 0xFFFFFFFF : timer causing an interrupt * @note * tmrHw_clearIntrrupt() must be called with a valid timer id after calling this function */ /****************************************************************************/ tmrHw_ID_t tmrHw_getInterruptSource(void /* void */ ) { int i; for (i = 0; i < tmrHw_TIMER_NUM_COUNT; i++) { if (pTmrHw[i].InterruptStatus) { return i; } } return 0xFFFFFFFF; } /****************************************************************************/ /** * @brief Displays specific timer registers * * * @return void * */ /****************************************************************************/ void tmrHw_printDebugInfo(tmrHw_ID_t timerId, /* [ IN ] Timer id */ int (*fpPrint) (const char *, ...) /* [ IN ] Print callback function */ ) { (*fpPrint) ("Displaying register contents \n\n"); (*fpPrint) ("Timer %d: Load value 0x%X\n", timerId, pTmrHw[timerId].LoadValue); (*fpPrint) ("Timer %d: Background load value 0x%X\n", timerId, pTmrHw[timerId].BackgroundLoad); (*fpPrint) ("Timer %d: Control 0x%X\n", timerId, pTmrHw[timerId].Control); (*fpPrint) ("Timer %d: Interrupt clear 0x%X\n", timerId, pTmrHw[timerId].InterruptClear); (*fpPrint) ("Timer %d: Interrupt raw interrupt 0x%X\n", timerId, pTmrHw[timerId].RawInterruptStatus); (*fpPrint) ("Timer %d: Interrupt status 0x%X\n", timerId, pTmrHw[timerId].InterruptStatus); } /****************************************************************************/ /** * @brief Use a timer to perform a busy wait delay for a number of usecs. * * @return N/A */ /****************************************************************************/ void tmrHw_udelay(tmrHw_ID_t timerId, /* [ IN ] Timer id */ unsigned long usecs /* [ IN ] usec to delay */ ) { tmrHw_RATE_t usec_tick_rate; tmrHw_COUNT_t start_time; tmrHw_COUNT_t delta_time; start_time = tmrHw_GetCurrentCount(timerId); usec_tick_rate = tmrHw_divide(tmrHw_getCountRate(timerId), 1000000); delta_time = usecs * usec_tick_rate; /* Busy wait */ while (delta_time > (tmrHw_GetCurrentCount(timerId) - start_time)) ; } /****************************************************************************/ /** * @brief Local Divide function * * This function does the divide * * @return divide value * */ /****************************************************************************/ static int tmrHw_divide(int num, int denom) { int r; int t = 1; /* Shift denom and t up to the largest value to optimize algorithm */ /* t contains the units of each divide */ while ((denom & 0x40000000) == 0) { /* fails if denom=0 */ denom = denom << 1; t = t << 1; } /* Intialize the result */ r = 0; do { /* Determine if there exists a positive remainder */ if ((num - denom) >= 0) { /* Accumlate t to the result and calculate a new remainder */ num = num - denom; r = r + t; } /* Continue to shift denom and shift t down to 0 */ denom = denom >> 1; t = t >> 1; } while (t != 0); return r; }