Path: blob/master/arch/arm/mach-bcmring/csp/tmr/tmrHw.c
10820 views
/*****************************************************************************1* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.2*3* Unless you and Broadcom execute a separate written software license4* agreement governing use of this software, this software is licensed to you5* under the terms of the GNU General Public License version 2, available at6* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").7*8* Notwithstanding the above, under no circumstances may you combine this9* software in any way with any other Broadcom software provided under a10* license other than the GPL, without Broadcom's express prior written11* consent.12*****************************************************************************/1314/****************************************************************************/15/**16* @file tmrHw.c17*18* @brief Low level Timer driver routines19*20* @note21*22* These routines provide basic timer functionality only.23*/24/****************************************************************************/2526/* ---- Include Files ---------------------------------------------------- */2728#include <csp/errno.h>29#include <csp/stdint.h>3031#include <csp/tmrHw.h>32#include <mach/csp/tmrHw_reg.h>3334#define tmrHw_ASSERT(a) if (!(a)) *(char *)0 = 035#define tmrHw_MILLISEC_PER_SEC (1000)3637#define tmrHw_LOW_1_RESOLUTION_COUNT (tmrHw_LOW_RESOLUTION_CLOCK / tmrHw_MILLISEC_PER_SEC)38#define tmrHw_LOW_1_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_1_RESOLUTION_COUNT)39#define tmrHw_LOW_16_RESOLUTION_COUNT (tmrHw_LOW_1_RESOLUTION_COUNT / 16)40#define tmrHw_LOW_16_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_16_RESOLUTION_COUNT)41#define tmrHw_LOW_256_RESOLUTION_COUNT (tmrHw_LOW_1_RESOLUTION_COUNT / 256)42#define tmrHw_LOW_256_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_256_RESOLUTION_COUNT)4344#define tmrHw_HIGH_1_RESOLUTION_COUNT (tmrHw_HIGH_RESOLUTION_CLOCK / tmrHw_MILLISEC_PER_SEC)45#define tmrHw_HIGH_1_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_1_RESOLUTION_COUNT)46#define tmrHw_HIGH_16_RESOLUTION_COUNT (tmrHw_HIGH_1_RESOLUTION_COUNT / 16)47#define tmrHw_HIGH_16_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_16_RESOLUTION_COUNT)48#define tmrHw_HIGH_256_RESOLUTION_COUNT (tmrHw_HIGH_1_RESOLUTION_COUNT / 256)49#define tmrHw_HIGH_256_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_256_RESOLUTION_COUNT)5051static void ResetTimer(tmrHw_ID_t timerId)52__attribute__ ((section(".aramtext")));53static int tmrHw_divide(int num, int denom)54__attribute__ ((section(".aramtext")));5556/****************************************************************************/57/**58* @brief Get timer capability59*60* This function returns various capabilities/attributes of a timer61*62* @return Capability63*64*/65/****************************************************************************/66uint32_t tmrHw_getTimerCapability(tmrHw_ID_t timerId, /* [ IN ] Timer Id */67tmrHw_CAPABILITY_e capability /* [ IN ] Timer capability */68) {69switch (capability) {70case tmrHw_CAPABILITY_CLOCK:71return (timerId <=721) ? tmrHw_LOW_RESOLUTION_CLOCK :73tmrHw_HIGH_RESOLUTION_CLOCK;74case tmrHw_CAPABILITY_RESOLUTION:75return 32;76default:77return 0;78}79return 0;80}8182/****************************************************************************/83/**84* @brief Resets a timer85*86* This function initializes timer87*88* @return void89*90*/91/****************************************************************************/92static void ResetTimer(tmrHw_ID_t timerId /* [ IN ] Timer Id */93) {94/* Reset timer */95pTmrHw[timerId].LoadValue = 0;96pTmrHw[timerId].CurrentValue = 0xFFFFFFFF;97pTmrHw[timerId].Control = 0;98pTmrHw[timerId].BackgroundLoad = 0;99/* Always configure as a 32 bit timer */100pTmrHw[timerId].Control |= tmrHw_CONTROL_32BIT;101/* Clear interrupt only if raw status interrupt is set */102if (pTmrHw[timerId].RawInterruptStatus) {103pTmrHw[timerId].InterruptClear = 0xFFFFFFFF;104}105}106107/****************************************************************************/108/**109* @brief Sets counter value for an interval in ms110*111* @return On success: Effective counter value set112* On failure: 0113*114*/115/****************************************************************************/116static tmrHw_INTERVAL_t SetTimerPeriod(tmrHw_ID_t timerId, /* [ IN ] Timer Id */117tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */118) {119uint32_t scale = 0;120uint32_t count = 0;121122if (timerId == 0 || timerId == 1) {123if (msec <= tmrHw_LOW_1_MAX_MILLISEC) {124pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;125scale = tmrHw_LOW_1_RESOLUTION_COUNT;126} else if (msec <= tmrHw_LOW_16_MAX_MILLISEC) {127pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16;128scale = tmrHw_LOW_16_RESOLUTION_COUNT;129} else if (msec <= tmrHw_LOW_256_MAX_MILLISEC) {130pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256;131scale = tmrHw_LOW_256_RESOLUTION_COUNT;132} else {133return 0;134}135136count = msec * scale;137/* Set counter value */138pTmrHw[timerId].LoadValue = count;139pTmrHw[timerId].BackgroundLoad = count;140141} else if (timerId == 2 || timerId == 3) {142if (msec <= tmrHw_HIGH_1_MAX_MILLISEC) {143pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;144scale = tmrHw_HIGH_1_RESOLUTION_COUNT;145} else if (msec <= tmrHw_HIGH_16_MAX_MILLISEC) {146pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16;147scale = tmrHw_HIGH_16_RESOLUTION_COUNT;148} else if (msec <= tmrHw_HIGH_256_MAX_MILLISEC) {149pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256;150scale = tmrHw_HIGH_256_RESOLUTION_COUNT;151} else {152return 0;153}154155count = msec * scale;156/* Set counter value */157pTmrHw[timerId].LoadValue = count;158pTmrHw[timerId].BackgroundLoad = count;159}160return count / scale;161}162163/****************************************************************************/164/**165* @brief Configures a periodic timer in terms of timer interrupt rate166*167* This function initializes a periodic timer to generate specific number of168* timer interrupt per second169*170* @return On success: Effective timer frequency171* On failure: 0172*173*/174/****************************************************************************/175tmrHw_RATE_t tmrHw_setPeriodicTimerRate(tmrHw_ID_t timerId, /* [ IN ] Timer Id */176tmrHw_RATE_t rate /* [ IN ] Number of timer interrupt per second */177) {178uint32_t resolution = 0;179uint32_t count = 0;180ResetTimer(timerId);181182/* Set timer mode periodic */183pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC;184pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT;185/* Set timer in highest resolution */186pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;187188if (rate && (timerId == 0 || timerId == 1)) {189if (rate > tmrHw_LOW_RESOLUTION_CLOCK) {190return 0;191}192resolution = tmrHw_LOW_RESOLUTION_CLOCK;193} else if (rate && (timerId == 2 || timerId == 3)) {194if (rate > tmrHw_HIGH_RESOLUTION_CLOCK) {195return 0;196} else {197resolution = tmrHw_HIGH_RESOLUTION_CLOCK;198}199} else {200return 0;201}202/* Find the counter value */203count = resolution / rate;204/* Set counter value */205pTmrHw[timerId].LoadValue = count;206pTmrHw[timerId].BackgroundLoad = count;207208return resolution / count;209}210211/****************************************************************************/212/**213* @brief Configures a periodic timer to generate timer interrupt after214* certain time interval215*216* This function initializes a periodic timer to generate timer interrupt217* after every time interval in millisecond218*219* @return On success: Effective interval set in milli-second220* On failure: 0221*222*/223/****************************************************************************/224tmrHw_INTERVAL_t tmrHw_setPeriodicTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */225tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */226) {227ResetTimer(timerId);228229/* Set timer mode periodic */230pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC;231pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT;232233return SetTimerPeriod(timerId, msec);234}235236/****************************************************************************/237/**238* @brief Configures a periodic timer to generate timer interrupt just once239* after certain time interval240*241* This function initializes a periodic timer to generate a single ticks after242* certain time interval in millisecond243*244* @return On success: Effective interval set in milli-second245* On failure: 0246*247*/248/****************************************************************************/249tmrHw_INTERVAL_t tmrHw_setOneshotTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */250tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */251) {252ResetTimer(timerId);253254/* Set timer mode oneshot */255pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC;256pTmrHw[timerId].Control |= tmrHw_CONTROL_ONESHOT;257258return SetTimerPeriod(timerId, msec);259}260261/****************************************************************************/262/**263* @brief Configures a timer to run as a free running timer264*265* This function initializes a timer to run as a free running timer266*267* @return Timer resolution (count / sec)268*269*/270/****************************************************************************/271tmrHw_RATE_t tmrHw_setFreeRunningTimer(tmrHw_ID_t timerId, /* [ IN ] Timer Id */272uint32_t divider /* [ IN ] Dividing the clock frequency */273) {274uint32_t scale = 0;275276ResetTimer(timerId);277/* Set timer as free running mode */278pTmrHw[timerId].Control &= ~tmrHw_CONTROL_PERIODIC;279pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT;280281if (divider >= 64) {282pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256;283scale = 256;284} else if (divider >= 8) {285pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16;286scale = 16;287} else {288pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;289scale = 1;290}291292if (timerId == 0 || timerId == 1) {293return tmrHw_divide(tmrHw_LOW_RESOLUTION_CLOCK, scale);294} else if (timerId == 2 || timerId == 3) {295return tmrHw_divide(tmrHw_HIGH_RESOLUTION_CLOCK, scale);296}297298return 0;299}300301/****************************************************************************/302/**303* @brief Starts a timer304*305* This function starts a preconfigured timer306*307* @return -1 - On Failure308* 0 - On Success309*310*/311/****************************************************************************/312int tmrHw_startTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */313) {314pTmrHw[timerId].Control |= tmrHw_CONTROL_TIMER_ENABLE;315return 0;316}317318/****************************************************************************/319/**320* @brief Stops a timer321*322* This function stops a running timer323*324* @return -1 - On Failure325* 0 - On Success326*327*/328/****************************************************************************/329int tmrHw_stopTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */330) {331pTmrHw[timerId].Control &= ~tmrHw_CONTROL_TIMER_ENABLE;332return 0;333}334335/****************************************************************************/336/**337* @brief Gets current timer count338*339* This function returns the current timer value340*341* @return Current downcounting timer value342*343*/344/****************************************************************************/345uint32_t tmrHw_GetCurrentCount(tmrHw_ID_t timerId /* [ IN ] Timer id */346) {347/* return 32 bit timer value */348switch (pTmrHw[timerId].Control & tmrHw_CONTROL_MODE_MASK) {349case tmrHw_CONTROL_FREE_RUNNING:350if (pTmrHw[timerId].CurrentValue) {351return tmrHw_MAX_COUNT - pTmrHw[timerId].CurrentValue;352}353break;354case tmrHw_CONTROL_PERIODIC:355case tmrHw_CONTROL_ONESHOT:356return pTmrHw[timerId].BackgroundLoad -357pTmrHw[timerId].CurrentValue;358}359return 0;360}361362/****************************************************************************/363/**364* @brief Gets timer count rate365*366* This function returns the number of counts per second367*368* @return Count rate369*370*/371/****************************************************************************/372tmrHw_RATE_t tmrHw_getCountRate(tmrHw_ID_t timerId /* [ IN ] Timer id */373) {374uint32_t divider = 0;375376switch (pTmrHw[timerId].Control & tmrHw_CONTROL_PRESCALE_MASK) {377case tmrHw_CONTROL_PRESCALE_1:378divider = 1;379break;380case tmrHw_CONTROL_PRESCALE_16:381divider = 16;382break;383case tmrHw_CONTROL_PRESCALE_256:384divider = 256;385break;386default:387tmrHw_ASSERT(0);388}389390if (timerId == 0 || timerId == 1) {391return tmrHw_divide(tmrHw_LOW_RESOLUTION_CLOCK, divider);392} else {393return tmrHw_divide(tmrHw_HIGH_RESOLUTION_CLOCK, divider);394}395return 0;396}397398/****************************************************************************/399/**400* @brief Enables timer interrupt401*402* This function enables the timer interrupt403*404* @return N/A405*406*/407/****************************************************************************/408void tmrHw_enableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */409) {410pTmrHw[timerId].Control |= tmrHw_CONTROL_INTERRUPT_ENABLE;411}412413/****************************************************************************/414/**415* @brief Disables timer interrupt416*417* This function disable the timer interrupt418*419* @return N/A420*421*/422/****************************************************************************/423void tmrHw_disableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */424) {425pTmrHw[timerId].Control &= ~tmrHw_CONTROL_INTERRUPT_ENABLE;426}427428/****************************************************************************/429/**430* @brief Clears the interrupt431*432* This function clears the timer interrupt433*434* @return N/A435*436* @note437* Must be called under the context of ISR438*/439/****************************************************************************/440void tmrHw_clearInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */441) {442pTmrHw[timerId].InterruptClear = 0x1;443}444445/****************************************************************************/446/**447* @brief Gets the interrupt status448*449* This function returns timer interrupt status450*451* @return Interrupt status452*/453/****************************************************************************/454tmrHw_INTERRUPT_STATUS_e tmrHw_getInterruptStatus(tmrHw_ID_t timerId /* [ IN ] Timer id */455) {456if (pTmrHw[timerId].InterruptStatus) {457return tmrHw_INTERRUPT_STATUS_SET;458} else {459return tmrHw_INTERRUPT_STATUS_UNSET;460}461}462463/****************************************************************************/464/**465* @brief Indentifies a timer causing interrupt466*467* This functions returns a timer causing interrupt468*469* @return 0xFFFFFFFF : No timer causing an interrupt470* ! 0xFFFFFFFF : timer causing an interrupt471* @note472* tmrHw_clearIntrrupt() must be called with a valid timer id after calling this function473*/474/****************************************************************************/475tmrHw_ID_t tmrHw_getInterruptSource(void /* void */476) {477int i;478479for (i = 0; i < tmrHw_TIMER_NUM_COUNT; i++) {480if (pTmrHw[i].InterruptStatus) {481return i;482}483}484485return 0xFFFFFFFF;486}487488/****************************************************************************/489/**490* @brief Displays specific timer registers491*492*493* @return void494*495*/496/****************************************************************************/497void tmrHw_printDebugInfo(tmrHw_ID_t timerId, /* [ IN ] Timer id */498int (*fpPrint) (const char *, ...) /* [ IN ] Print callback function */499) {500(*fpPrint) ("Displaying register contents \n\n");501(*fpPrint) ("Timer %d: Load value 0x%X\n", timerId,502pTmrHw[timerId].LoadValue);503(*fpPrint) ("Timer %d: Background load value 0x%X\n", timerId,504pTmrHw[timerId].BackgroundLoad);505(*fpPrint) ("Timer %d: Control 0x%X\n", timerId,506pTmrHw[timerId].Control);507(*fpPrint) ("Timer %d: Interrupt clear 0x%X\n", timerId,508pTmrHw[timerId].InterruptClear);509(*fpPrint) ("Timer %d: Interrupt raw interrupt 0x%X\n", timerId,510pTmrHw[timerId].RawInterruptStatus);511(*fpPrint) ("Timer %d: Interrupt status 0x%X\n", timerId,512pTmrHw[timerId].InterruptStatus);513}514515/****************************************************************************/516/**517* @brief Use a timer to perform a busy wait delay for a number of usecs.518*519* @return N/A520*/521/****************************************************************************/522void tmrHw_udelay(tmrHw_ID_t timerId, /* [ IN ] Timer id */523unsigned long usecs /* [ IN ] usec to delay */524) {525tmrHw_RATE_t usec_tick_rate;526tmrHw_COUNT_t start_time;527tmrHw_COUNT_t delta_time;528529start_time = tmrHw_GetCurrentCount(timerId);530usec_tick_rate = tmrHw_divide(tmrHw_getCountRate(timerId), 1000000);531delta_time = usecs * usec_tick_rate;532533/* Busy wait */534while (delta_time > (tmrHw_GetCurrentCount(timerId) - start_time))535;536}537538/****************************************************************************/539/**540* @brief Local Divide function541*542* This function does the divide543*544* @return divide value545*546*/547/****************************************************************************/548static int tmrHw_divide(int num, int denom)549{550int r;551int t = 1;552553/* Shift denom and t up to the largest value to optimize algorithm */554/* t contains the units of each divide */555while ((denom & 0x40000000) == 0) { /* fails if denom=0 */556denom = denom << 1;557t = t << 1;558}559560/* Initialize the result */561r = 0;562563do {564/* Determine if there exists a positive remainder */565if ((num - denom) >= 0) {566/* Accumlate t to the result and calculate a new remainder */567num = num - denom;568r = r + t;569}570/* Continue to shift denom and shift t down to 0 */571denom = denom >> 1;572t = t >> 1;573} while (t != 0);574return r;575}576577578