blob: 543c003fa9f54894a73d69d1358398c9b1e01ab1 [file] [log] [blame]
/*
* Copyright (c) 2017, Texas Instruments Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/dpl/SemaphoreP.h>
#include <ti/drivers/dpl/ClockP.h>
#include <ti/drivers/Timer.h>
#include <ti/drivers/power/PowerCC32XX.h>
#include <ti/drivers/timer/TimerCC32XX.h>
#include <ti/devices/cc32xx/inc/hw_types.h>
#include <ti/devices/cc32xx/inc/hw_memmap.h>
#include <ti/devices/cc32xx/inc/hw_timer.h>
#include <ti/devices/cc32xx/driverlib/timer.h>
/*
* This macro is used to determine a logical shift value for the
* timerState.bitMask. Each timer peripheral occupies two bits in
* timerState.bitMask.
*
* The timer peripherals' base addresses have an offset of 0x1000 starting at
* 0x40030000. That byte is masked using 0xF000 which can result in a value
* ranging from 0x0000 to 0x3000 for this particular hardware instance. This
* value is then shifted right by 12 into the LSB. Lastly, the value is
* multiplied by two because there are two bits in the timerState.bitMask for
* each timer. The value returned is used for the logical shift.
*/
#define timerMaskShift(baseAddress) ((((baseAddress) & 0XF000) >> 12) * 2)
void TimerCC32XX_close(Timer_Handle handle);
int_fast16_t TimerCC32XX_control(Timer_Handle handle,
uint_fast16_t cmd, void *arg);
uint32_t TimerCC32XX_getCount(Timer_Handle handle);
void TimerCC32XX_init(Timer_Handle handle);
Timer_Handle TimerCC32XX_open(Timer_Handle handle, Timer_Params *params);
int32_t TimerCC32XX_start(Timer_Handle handle);
void TimerCC32XX_stop(Timer_Handle handle);
/* Internal static Functions */
static void initHw(Timer_Handle handle);
static void getPrescaler(Timer_Handle handle);
static uint32_t getPowerMgrId(uint32_t baseAddress);
static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
uintptr_t clientArg);
static void TimerCC32XX_hwiIntFunction(uintptr_t arg);
/* Function table for TimerCC32XX implementation */
const Timer_FxnTable TimerCC32XX_fxnTable = {
.closeFxn = TimerCC32XX_close,
.openFxn = TimerCC32XX_open,
.startFxn = TimerCC32XX_start,
.stopFxn = TimerCC32XX_stop,
.initFxn = TimerCC32XX_init,
.getCountFxn = TimerCC32XX_getCount,
.controlFxn = TimerCC32XX_control
};
/*
* Internal Timer status structure
*
* bitMask: Each timer peripheral occupies two bits in the bitMask. The least
* significant bit represents the first half width timer, TimerCC32XX_timer16A
* and the most significant bit represents the second half width timer,
* TimerCC32XX_timer16B. If the full width timer, TimerCC32XX_timer32, is used,
* both bits are set to 1.
* 31 - 8 7 - 6 5 - 4 3 - 2 1 - 0
* ------------------------------------------------
* | Reserved | Timer3 | Timer2 | Timer1 | Timer0 |
* ------------------------------------------------
*/
static struct {
uint32_t bitMask;
} timerState;
/*
* ======== initHw ========
*/
static void initHw(Timer_Handle handle)
{
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
TimerCC32XX_Object const *object = handle->object;
/* Ensure the timer is disabled */
TimerDisable(hwAttrs->baseAddress, object->timer);
if (object->timer == TIMER_A) {
HWREG(hwAttrs->baseAddress + TIMER_O_TAMR) = TIMER_TAMR_TAMR_PERIOD;
}
else {
HWREG(hwAttrs->baseAddress + TIMER_O_TBMR) = TIMER_TBMR_TBMR_PERIOD;
}
if (hwAttrs->subTimer == TimerCC32XX_timer32) {
HWREG(hwAttrs->baseAddress + TIMER_O_CFG) = TIMER_CFG_32_BIT_TIMER;
}
else {
HWREG(hwAttrs->baseAddress + TIMER_O_CFG) = TIMER_CFG_16_BIT;
}
/* Disable all interrupts */
HWREG(hwAttrs->baseAddress + TIMER_O_IMR) = ~object->timer;
/* Writing the PSR Register has no effect for full width 32-bit mode */
TimerPrescaleSet(hwAttrs->baseAddress, object->timer, object->prescaler);
TimerLoadSet(hwAttrs->baseAddress, object->timer, object->period);
/* This function controls the stall response for the timer. When true,
* the timer stops counting if the processor enters debug mode. The
* default setting for the hardware is false.
*/
TimerControlStall(hwAttrs->baseAddress, object->timer, true);
}
/*
* ========= getPrescaler =========
* This function calculates the prescaler and timer interval load register
* for a half timer. The handle is assumed to contain a object->period which
* represents the number of clock cycles in the desired period. The calling
* function, TimerCC32XX_open() checks for overflow before calling this function.
* Therefore, this function is guaranteed to never fail.
*/
static void getPrescaler(Timer_Handle handle)
{
TimerCC32XX_Object *object = handle->object;
uint32_t bestDiff = ~0, bestPsr = 0, bestIload = 0;
uint32_t diff, intervalLoad, prescaler;
/* Loop over the 8-bit prescaler */
for (prescaler = 1; prescaler < 256; prescaler++) {
/* Calculate timer interval load */
intervalLoad = object->period / (prescaler + 1);
/* Will this fit in 16-bits? */
if (intervalLoad > (uint16_t) ~0) {
continue;
}
/* How close is the intervalLoad to what we actually want? */
diff = object->period - intervalLoad * (prescaler + 1);
/* If it is closer to what we want */
if (diff <= bestDiff) {
/* If its a perfect match */
if (diff == 0) {
object->period = intervalLoad;
object->prescaler = prescaler;
return;
}
/* Snapshot in case we don't find something better */
bestDiff = diff;
bestPsr = prescaler;
bestIload = intervalLoad;
}
}
/* Never found a perfect match, settle for the best */
object->period = bestIload;
object->prescaler = bestPsr;
}
/*
* ======== getPowerMgrId ========
*/
static uint32_t getPowerMgrId(uint32_t baseAddress)
{
switch (baseAddress) {
case TIMERA0_BASE:
return (PowerCC32XX_PERIPH_TIMERA0);
case TIMERA1_BASE:
return (PowerCC32XX_PERIPH_TIMERA1);
case TIMERA2_BASE:
return (PowerCC32XX_PERIPH_TIMERA2);
case TIMERA3_BASE:
return (PowerCC32XX_PERIPH_TIMERA3);
default:
return ((uint32_t) -1);
}
}
/*
* ======== postNotifyFxn ========
* This functions is called when a transition from LPDS mode is made.
* clientArg should be a handle of a previously opened Timer instance.
*/
static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
uintptr_t clientArg)
{
initHw((Timer_Handle) clientArg);
return (Power_NOTIFYDONE);
}
/*
* ======== TimerCC32XX_allocateTimerResource ========
*/
bool TimerCC32XX_allocateTimerResource(uint32_t baseAddress,
TimerCC32XX_SubTimer subTimer)
{
uintptr_t key;
uint32_t mask;
uint32_t powerMgrId;
bool status;
powerMgrId = getPowerMgrId(baseAddress);
if (powerMgrId == (uint32_t) -1) {
return (false);
}
mask = subTimer << timerMaskShift(baseAddress);
key = HwiP_disable();
if (timerState.bitMask & mask) {
status = false;
}
else {
Power_setDependency(powerMgrId);
timerState.bitMask = timerState.bitMask | mask;
status = true;
}
HwiP_restore(key);
return (status);
}
/*
* ======== TimerCC32XX_close ========
*/
void TimerCC32XX_close(Timer_Handle handle)
{
TimerCC32XX_Object *object = handle->object;
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
/* Stopping the Timer before closing it */
TimerCC32XX_stop(handle);
Power_unregisterNotify(&(object->notifyObj));
if (object->hwiHandle) {
HwiP_clearInterrupt(hwAttrs->intNum);
HwiP_delete(object->hwiHandle);
object->hwiHandle = NULL;
}
if (object->timerSem) {
SemaphoreP_delete(object->timerSem);
object->timerSem = NULL;
}
TimerCC32XX_freeTimerResource(hwAttrs->baseAddress, hwAttrs->subTimer);
}
/*
* ======== TimerCC32XX_control ========
*/
int_fast16_t TimerCC32XX_control(Timer_Handle handle,
uint_fast16_t cmd, void *arg)
{
return (Timer_STATUS_UNDEFINEDCMD);
}
/*
* ======== TimerCC32XX_freeTimerResource ========
*/
void TimerCC32XX_freeTimerResource(uint32_t baseAddress,
TimerCC32XX_SubTimer subTimer)
{
uintptr_t key;
uint32_t mask;
mask = subTimer << timerMaskShift(baseAddress);
key = HwiP_disable();
timerState.bitMask = (timerState.bitMask & ~mask);
Power_releaseDependency(getPowerMgrId(baseAddress));
HwiP_restore(key);
}
/*
* ======== TimerCC32XX_getCount ========
*/
uint32_t TimerCC32XX_getCount(Timer_Handle handle)
{
TimerCC32XX_HWAttrs const *hWAttrs = handle->hwAttrs;
TimerCC32XX_Object const *object = handle->object;
uint32_t count;
if (object->timer == TIMER_A) {
count = HWREG(hWAttrs->baseAddress + TIMER_O_TAR);
}
else {
count = HWREG(hWAttrs->baseAddress + TIMER_O_TBR);
}
/* Virtual up counter */
count = object->period - count;
return (count);
}
/*
* ======== TimerCC32XX_hwiIntFunction ========
*/
void TimerCC32XX_hwiIntFunction(uintptr_t arg)
{
Timer_Handle handle = (Timer_Handle) arg;
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
TimerCC32XX_Object const *object = handle->object;
uint32_t interruptMask;
/* Only clear the interrupt for this->object->timer */
interruptMask = object->timer & (TIMER_TIMA_TIMEOUT | TIMER_TIMB_TIMEOUT);
TimerIntClear(hwAttrs->baseAddress, interruptMask);
/* Hwi is not created when using Timer_FREE_RUNNING */
if (object->mode != Timer_CONTINUOUS_CALLBACK) {
TimerCC32XX_stop(handle);
}
if (object-> mode != Timer_ONESHOT_BLOCKING) {
object->callBack(handle);
}
}
/*
* ======== TimerCC32XX_init ========
*/
void TimerCC32XX_init(Timer_Handle handle)
{
return;
}
/*
* ======== TimerCC32XX_open ========
*/
Timer_Handle TimerCC32XX_open(Timer_Handle handle, Timer_Params *params)
{
TimerCC32XX_Object *object = handle->object;
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
SemaphoreP_Params semParams;
HwiP_Params hwiParams;
ClockP_FreqHz clockFreq;
/* Check for valid parameters */
if (((params->timerMode == Timer_ONESHOT_CALLBACK ||
params->timerMode == Timer_CONTINUOUS_CALLBACK) &&
params->timerCallback == NULL) ||
params->period == 0) {
return (NULL);
}
if (!TimerCC32XX_allocateTimerResource(hwAttrs->baseAddress,
hwAttrs->subTimer)) {
return (NULL);
}
Power_registerNotify(&(object->notifyObj), PowerCC32XX_AWAKE_LPDS,
postNotifyFxn, (uintptr_t) handle);
object->mode = params->timerMode;
object->isRunning = false;
object->callBack = params->timerCallback;
object->period = params->period;
object->prescaler = 0;
if (hwAttrs->subTimer == TimerCC32XX_timer16B) {
object->timer = TIMER_B;
}
else {
object->timer = TIMER_A;
}
if (object->mode != Timer_FREE_RUNNING) {
HwiP_Params_init(&hwiParams);
hwiParams.arg = (uintptr_t) handle;
hwiParams.priority = hwAttrs->intPriority;
object->hwiHandle = HwiP_create(hwAttrs->intNum,
TimerCC32XX_hwiIntFunction, &hwiParams);
if (object->hwiHandle == NULL) {
TimerCC32XX_close(handle);
return (NULL);
}
}
/* Creating the semaphore if mode is blocking */
if (params->timerMode == Timer_ONESHOT_BLOCKING) {
SemaphoreP_Params_init(&semParams);
semParams.mode = SemaphoreP_Mode_BINARY;
object->timerSem = SemaphoreP_create(0, &semParams);
if (object->timerSem == NULL) {
TimerCC32XX_close(handle);
return (NULL);
}
}
/* Formality; CC32XX System Clock fixed to 80.0 MHz */
ClockP_getCpuFreq(&clockFreq);
if (params->periodUnits == Timer_PERIOD_US) {
/* Checks if the calculated period will fit in 32-bits */
if (object->period >= ((uint32_t) ~0) / (clockFreq.lo / 1000000)) {
TimerCC32XX_close(handle);
return (NULL);
}
object->period = object->period * (clockFreq.lo / 1000000);
}
else if (params->periodUnits == Timer_PERIOD_HZ) {
/* If (object->period) > clockFreq */
if ((object->period = clockFreq.lo / object->period) == 0) {
TimerCC32XX_close(handle);
return (NULL);
}
}
/* If using a half timer */
if (hwAttrs->subTimer != TimerCC32XX_timer32) {
if (object->period > 0xFFFF) {
/* 24-bit resolution for the half timer */
if (object->period >= (1 << 24)) {
TimerCC32XX_close(handle);
return (NULL);
}
getPrescaler(handle);
}
}
initHw(handle);
return (handle);
}
/*
* ======== TimerCC32XX_start ========
*/
int32_t TimerCC32XX_start(Timer_Handle handle)
{
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
TimerCC32XX_Object *object = handle->object;
uint32_t interruptMask;
uintptr_t key;
interruptMask = object->timer & (TIMER_TIMB_TIMEOUT | TIMER_TIMA_TIMEOUT);
key = HwiP_disable();
if (object->isRunning) {
HwiP_restore(key);
return (Timer_STATUS_ERROR);
}
object->isRunning = true;
if (object->hwiHandle) {
TimerIntEnable(hwAttrs->baseAddress, interruptMask);
}
Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);
/* Reload the timer */
if (object->timer == TIMER_A) {
HWREG(hwAttrs->baseAddress + TIMER_O_TAMR) |= TIMER_TAMR_TAILD;
}
else {
HWREG(hwAttrs->baseAddress + TIMER_O_TBMR) |= TIMER_TBMR_TBILD;
}
TimerEnable(hwAttrs->baseAddress, object->timer);
HwiP_restore(key);
if (object->mode == Timer_ONESHOT_BLOCKING) {
/* Pend forever, ~0 */
SemaphoreP_pend(object->timerSem, ~0);
}
return (Timer_STATUS_SUCCESS);
}
/*
* ======== TimerCC32XX_stop ========
*/
void TimerCC32XX_stop(Timer_Handle handle)
{
TimerCC32XX_HWAttrs const *hwAttrs = handle->hwAttrs;
TimerCC32XX_Object *object = handle->object;
uint32_t interruptMask;
uintptr_t key;
interruptMask = object->timer & (TIMER_TIMB_TIMEOUT | TIMER_TIMA_TIMEOUT);
key = HwiP_disable();
if (object->isRunning) {
object->isRunning = false;
/* Post the Semaphore when called from the Hwi */
if (object->mode == Timer_ONESHOT_BLOCKING) {
SemaphoreP_post(object->timerSem);
}
TimerDisable(hwAttrs->baseAddress, object->timer);
TimerIntDisable(hwAttrs->baseAddress, interruptMask);
Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
}
HwiP_restore(key);
}