blob: 5c1c9a0e5ed218540606a8f33c30d2112dc8c1c6 [file] [log] [blame]
/*****************************************************************************
* 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 <csp/errno.h>
#include <csp/stdint.h>
#include <csp/tmrHw.h>
#include <mach/csp/tmrHw_reg.h>
#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;
}