/*
 * Copyright (c) 2015-2018, 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.
 */
/*!*****************************************************************************
 *  @file       GPTimerCC26XX.c
 *  @brief      CC26XX/CC13XX driver implementation for GPTimer peripheral
 *
 *
 *******************************************************************************
 */


#include <ti/drivers/dpl/DebugP.h>
#include <ti/drivers/dpl/HwiP.h>

#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>

#include <ti/drivers/timer/GPTimerCC26XX.h>

#include <ti/devices/DeviceFamily.h>
#include DeviceFamily_constructPath(inc/hw_gpt.h)
#include DeviceFamily_constructPath(driverlib/timer.h)


/* GPTimer configuration array from application */
extern const GPTimerCC26XX_Config GPTimerCC26XX_config[];

/* Register masks used by GPTimerCC26XX_resetHw */
#define GPT_CTL_MASK    (GPT_CTL_TASTALL_M | GPT_CTL_TAEVENT_M | GPT_CTL_TAPWML_M)


/* GPTimerCC26XX internal functions */
static void GPTimerCC26XX_initHw(GPTimerCC26XX_Handle handle, const GPTimerCC26XX_Params *params);
static void GPTimerCC26XX_resetHw(GPTimerCC26XX_Handle handle);
static void GPTimerCC26XXThreadsafeConstraintClr(GPTimerCC26XX_Handle handle);
static void GPTimerCC26XXThreadsafeConstraintSet(GPTimerCC26XX_Handle handle);
static void GPTimerCC26XXHwiFxn(uintptr_t a0);
static void GPTimerCC26XXSetLoadMatch(GPTimerCC26XX_Handle handle, GPTimerCC26XX_Value loadMatchVal, uint32_t regPre, uint32_t regLoadMatch);
static GPTimerCC26XX_Value GPTimerCC26XX_getTimerValue(GPTimerCC26XX_Handle handle, uint32_t reg);

/* GPTimerCC26XX temporary internal functions.
   Will be removed once they are added to driverlib
 */
static inline void TimerSetConfig(uint32_t ui32Base, uint32_t ui32Config);
static inline void TimerSetMode(uint32_t ui32Base, uint32_t timer, uint32_t mode);


/* Lookup table definition for interfacing driverlib and register fields.
   Used to simplify code and to easily look up register fields as several fields
   are not symmetric across timer A and timer B registers (interrupts & dma)
 */
typedef struct GPTimerCC26XX_LUT
{
    uint16_t map;                      /* Timer argument in driverlib (TIMER_A / TIMER_B) */
    uint16_t shift;                    /* Bit shift for registers shared between GPT_A / GPT_B */
    uint16_t offset;                   /* Byte offset for registers sequentially in memory map for GPT_A/GPT_B */
    uint16_t interrupts[GPT_NUM_INTS]; /* Interrupt bitfields for GPTA/B. Order must match GPTimerCC26XX_Interrupt */
} const GPTimerCC26XX_LUT;

/* Lookup table definition for interfacing driverlib and register fields. */
static const GPTimerCC26XX_LUT GPT_LUT[GPT_PARTS_COUNT] =
{
    {
        .map        = TIMER_A,
        .shift      = 0,
        .offset     = 0,
        .interrupts ={ GPT_MIS_TATOMIS,                   GPT_MIS_CAMMIS, GPT_MIS_CAEMIS, GPT_MIS_TAMMIS },
    },
    {
        .map        = TIMER_B,
        .shift      = 8,
        .offset     = 4,
        .interrupts ={ GPT_MIS_TBTOMIS,                   GPT_MIS_CBMMIS, GPT_MIS_CBEMIS, GPT_MIS_TBMMIS },
    },
};

/* Default GPTimer parameters */
static const GPTimerCC26XX_Params GPT_DefaultParams =
{
    .width          = GPT_CONFIG_32BIT,
    .mode           = GPT_MODE_PERIODIC,
    .matchTiming    = GPTimerCC26XX_SET_MATCH_NEXT_CLOCK,
    .direction      = GPTimerCC26XX_DIRECTION_UP,
    .debugStallMode = GPTimerCC26XX_DEBUG_STALL_OFF,
};


/*!
 *  @brief Generic bit vector to lookup table vector implementation
 *
 *  Parses generic lookup table and checks whether inputs exists in lookup table.
 *
 *  @param pLookup      pointer to look-up table
 *  @param inputVector  Input vector
 *  @param length       Number of bits in lookup table
 *
 *  @return A bit vector containing set fields in lookup table
 */
static uint16_t GPTimerCC26XXLookupMask(const uint16_t *pLookup, uint16_t inputVector, uint8_t length)
{
    uint16_t maskLookup = 0;
    uint32_t i;
    /* Fetch data from lookup table */
    for (i = 0; i < length; i++)
    {
        /* Check if current index of lookup table is in input vector */
        if (inputVector & pLookup[i])
        {
            /* Add vector input to looked up mask. */
            maskLookup |= 1 << i;
        }
    }
    return maskLookup;
}

/*!
 *  @brief Generic lookup table to bit vector implementation
 *
 *  Parses input vector and checks which input fields are set
 *
 *  @param pLookup      pointer to look-up table
 *  @param inputVector  Input vector
 *  @param length       Number of bits in lookup table
 *
 *  @return A bit vector mapped from lookup table
 */
static uint16_t GPTimerCC26XXReverseLookupMask(const uint16_t *pLookup, uint16_t inputVector, uint8_t length)
{
    uint16_t revMaskLookup = 0;
    uint32_t i;
    for (i = 0; i < length; i++)
    {
        /* Check if current index is set in input vector */
        if (inputVector & (1 << i))
        {
            /* Add data from lookup table */
            revMaskLookup |= pLookup[i];
        }
    }
    return revMaskLookup;
}

/*!
 *  @brief  Function to initialize the GPTimerCC26XX_Params struct to default
 *          values
 *
 *  @param  params      An pointer to GPTimerCC26XX_Params structure for
 *                      initialization
 *
 *  Defaults values are:
 *  Timer width : 32 bits,
 *  Timer mode  : Periodic mode counting upwards
 *  Timer debug stall mode: Disabled
 */
void GPTimerCC26XX_Params_init(GPTimerCC26XX_Params *params)
{
    *params = GPT_DefaultParams;
}

/*!
 *  @brief  This function opens the given GPTimer peripheral. It will set a
 *          dependency on the timer, configure the timer mode and set timer
 *          as open. If params is NULL default values will be used.
 *
 *  @return A GPTimerCC26XX_Handle on success or NULL on error or if timer is
 *          already open. If NULL is returned further GPTimer API calls will
 *          result in undefined behaviour.
 *
 *  @sa     GPTimerCC26XX_close()
 */
GPTimerCC26XX_Handle GPTimerCC26XX_open(unsigned int index, const GPTimerCC26XX_Params *params)
{
    unsigned int         key;
    GPTimerCC26XX_Handle handle = (GPTimerCC26XX_Handle) & GPTimerCC26XX_config[index];

    /* Get the pointer to the object and hwAttrs and timer*/
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    GPTimerCC26XX_Object        *object  = handle->object;


    /*
     *  Input argument checks
     */

    /* Fail if no params supplied */
    if (params == NULL)
    {
        DebugP_log1("Timer:(%p): No params supplied, using default.", hwAttrs->baseAddr);
        params = &GPT_DefaultParams;
    }

    if (params->width == GPT_CONFIG_32BIT)
    {
        /* Fail if invalid combination of mode and configuration
           32-bit config  are only valid for periodic / oneshot modes */
        if (params->mode != GPT_MODE_ONESHOT_UP &&
            params->mode != GPT_MODE_PERIODIC_UP)
        {
            DebugP_log1("Timer:(%p): Invalid combination of mode and configuration", hwAttrs->baseAddr);
            return(NULL);
        }
        /* Fail if invalid combination of timer unit and configuration.
           32-bit config is only valid in combination with GPT A */
        if (handle->timerPart != GPT_A)
        {
            DebugP_log1("Timer:(%p): Invalid combination of configuration and timer unit", hwAttrs->baseAddr);
            return(NULL);
        }
    }

    /*
     * Check if Timer is already opened
     */

    /* Disable preemption while checking */
    key = HwiP_disable();

    bool isOpen;
    /* If trying to open a 32-bit mode timer then both A and B must be available */
    if (params->width == GPT_CONFIG_32BIT)
    {
        /* Timer already opened if one unit opened */
        isOpen = object->isOpen[GPT_A] | object->isOpen[GPT_B];
    }
    else
    {
        isOpen = object->isOpen[handle->timerPart];
    }
    /* Fail if corresponding timer is already open or if one of the two
       units needed is taken for 32-bit mode */
    if (isOpen)
    {
        HwiP_restore(key);
        DebugP_log2("Timer:(%p), Unit: (%u): Already in use.", hwAttrs->baseAddr, handle->timerPart);
        return NULL;
    }

    /*
     * Open timer
     */
    if (params->width == GPT_CONFIG_32BIT)
    {
        object->isOpen[GPT_A] = true;
        object->isOpen[GPT_B] = true;
    }
    else
    {
        object->isOpen[handle->timerPart] = true;
    }
    /* Restore preemption again */
    HwiP_restore(key);

    /*  Configure timer object */
    object->width = params->width;

    /* Register power dependency - i.e. power up and enable clock for GPTimer.
       If both GPT_A and GPT_B is used then two dependencies will be set on GPTimer */
    Power_setDependency(hwAttrs->powerMngrId);

    /* Initialize HW */
    GPTimerCC26XX_initHw(handle, params);

    return handle;
}

/*!
 *  @brief  This function closes an opened GPTimer peripheral defined by the
 *          handle. It will remove the dependency on the timer and write all
 *          configured timer registers to its default values. Timer must be
 *          stopped beforing closing it.
 *
 *  @sa     GPTimerCC26XX_stop()
 *  @sa     GPTimerCC26XX_close()
 */
void GPTimerCC26XX_close(GPTimerCC26XX_Handle handle)
{
    /* Get the pointer to the object and hwAttrs */
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    GPTimerCC26XX_Object        *object  = handle->object;

    /* Stop and reset timer */
    GPTimerCC26XX_resetHw(handle);

    /* Release dependency for timer */
    Power_releaseDependency(hwAttrs->powerMngrId);

    /* Mark the Timer unit as available */
    uint32_t key = HwiP_disable();

    /* Close Timer(s) */
    if (object->width == GPT_CONFIG_32BIT)
    {
        object->isOpen[GPT_A] = false;
        object->isOpen[GPT_B] = false;
    }
    else
    {
        object->isOpen[handle->timerPart] = false;
    }
    HwiP_restore(key);
}

/*!
 *  @brief  This function will start a GPTimer defined by the handle.
 *  @sa     GPTimerCC26XX_stop()
 */
void GPTimerCC26XX_start(GPTimerCC26XX_Handle handle)
{
    /* Get the pointer to the object and hwAttrs */
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;

    /* Enable timer */
    uint32_t timer = GPT_LUT[handle->timerPart].map;
    TimerEnable(hwAttrs->baseAddr, timer);

    /* Set constraint to disallow standby while running */
    GPTimerCC26XXThreadsafeConstraintSet(handle);
}

/*!
 *  @brief  This function will stop a running GPTimer defined by the handle
 *  @sa     GPTimerCC26XX_start()
 */
void GPTimerCC26XX_stop(GPTimerCC26XX_Handle handle)
{
    /* Get the pointer to the object and hwAttrs */
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;

    /* Disable timer */
    uint32_t timer = GPT_LUT[handle->timerPart].map;
    TimerDisable(hwAttrs->baseAddr, timer);

    /* Clear constraint to allow standby again */
    GPTimerCC26XXThreadsafeConstraintClr(handle);
}

/*!
 *  @brief  Shared code to be used by GPTimerCC26XX_setLoadValue / GPTimerCC26XX_setMatchValue
 *          Sets load/match values using input value and register offset for
 *          prescaler and load/match register
 *          Functions calling this should specifiy which the register offset
 *          within the module base to the corresponding timer A register.
 */
static void GPTimerCC26XXSetLoadMatch(GPTimerCC26XX_Handle handle, GPTimerCC26XX_Value loadMatchVal, uint32_t regPre, uint32_t regLoadMatch)
{
    /* Get the pointer to the object and hwAttrs */
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    GPTimerCC26XX_Object        *object  = handle->object;
    uint32_t offset = GPT_LUT[handle->timerPart].offset;

    /* Split value into correct timer and prescaler register for 16 bit modes. */
    if (object->width == GPT_CONFIG_16BIT)
    {
        /* Upper byte is used by prescaler */
        uint8_t prescaleValue = 0xFF & (loadMatchVal >> 16);
        /* Discard upper byte (24 bits max) */
        loadMatchVal &= 0xFFFF;

        /* Set prescale value */
        HWREG(hwAttrs->baseAddr + offset + regPre) = prescaleValue;
    }

    /* Set load / match value */
    HWREG(hwAttrs->baseAddr + offset + regLoadMatch) = loadMatchVal;
}

/*!
 *  @brief  Set GPTimer load value. For 32-bit configuration all 32 bits can
 *          be used. For split mode / 16-bit mode maximum value is 24 bits.
 *          Function concatenates prescaler functionality automatically
 */
void GPTimerCC26XX_setLoadValue(GPTimerCC26XX_Handle handle, GPTimerCC26XX_Value loadValue)
{
    GPTimerCC26XXSetLoadMatch(handle, loadValue, GPT_O_TAPR, GPT_O_TAILR);
}

/*!
 *  @brief  Set GPTimer match value. For 32-bit configuration all 32 bits can
 *          be used. For split mode / 16-bit mode maximum value is 24 bits.
 *          Function concatenates prescaler functionality automatically
 */
void GPTimerCC26XX_setMatchValue(GPTimerCC26XX_Handle handle, GPTimerCC26XX_Value matchValue)
{
    GPTimerCC26XXSetLoadMatch(handle, matchValue, GPT_O_TAPMR, GPT_O_TAMATCHR);
}


/*!
 *  @brief  Function to set which input event edge the GPTimer capture should
 *          use. Applies to edge-count and edge-time modes
 *          be called while GPTimer is running.
 */
void GPTimerCC26XX_setCaptureEdge(GPTimerCC26XX_Handle handle, GPTimerCC26XX_Edge event)
{
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;

    uint32_t shift = GPT_LUT[handle->timerPart].shift;

    /* Disable interrupts during RMW operation */
    uint32_t key = HwiP_disable();

    uint32_t ctl = HWREG(hwAttrs->baseAddr + GPT_O_CTL);
    /* Clear old setting */
    ctl &= ~(GPT_CTL_TAEVENT_M << shift);
    /* Apply new setting */
    HWREG(hwAttrs->baseAddr + GPT_O_CTL) = ctl | (event << shift);
    /* Restore HW interrupts */
    HwiP_restore(key);
}

/*!
 *  @brief Shared code used to retrieve timer values by
 *         GPTimerCC26XX_getFreeRunValue and GPTimerCC26XX_getValue
 *         Functions calling this should specifiy which the register offset
 *         within the module base to the corresponding timer A register.
 */
static GPTimerCC26XX_Value GPTimerCC26XX_getTimerValue(GPTimerCC26XX_Handle handle, uint32_t reg)
{
    /* Get the pointer to the object and hwAttrs */
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    GPTimerCC26XX_Object        *object  = handle->object;
    uint32_t offset = GPT_LUT[handle->timerPart].offset;

    uint32_t value = HWREG(hwAttrs->baseAddr + offset + reg);

    /* Correct for 16-bit mode (remove bits 31:24)
       Supported 16-bit modes uses prescaler as timer extension only.
       In 16-bit mode, current prescaler value is found in bits 23:16 of the timer value register
     */

    if (object->width == GPT_CONFIG_16BIT)
    {
        /* Discard any upper byte */
        value = value & 0xFFFFFF;
    }
    return (GPTimerCC26XX_Value)value;
}


/*!
 *  @brief  Retrieve current free-running value from GPTimer
 *          In 16-bit modes the function will return a 24-bit word where the
 *          8-bit prescaler value is included.
 */
GPTimerCC26XX_Value GPTimerCC26XX_getFreeRunValue(GPTimerCC26XX_Handle handle)
{
    return GPTimerCC26XX_getTimerValue(handle, GPT_O_TAV);
}

/*!
 *  @brief  Retrieve the current value of timer
 *          This returns the value of the timer in all modes except for
 *          input edge count and input edge time mode.
 *          In edge count mode, this register contains the number of edges that
 *          have occurred. In input edge time, this register contains the
 *          timer value at which the last edge event took place.
 *          In 16-bit modes the function will return a 24-bit word where the
 *          8-bit prescaler value is included.
 */
GPTimerCC26XX_Value GPTimerCC26XX_getValue(GPTimerCC26XX_Handle handle)
{
    return GPTimerCC26XX_getTimerValue(handle, GPT_O_TAR);
}

/*!
 *  @brief  Register interrupts for timer handle.
 *          This function must only be called once after opening a timer.
 *          Interrupts should be unregistered again before closing
 *          the timer resource.
 */
void GPTimerCC26XX_registerInterrupt(GPTimerCC26XX_Handle handle, GPTimerCC26XX_HwiFxn callback, GPTimerCC26XX_IntMask intMask)
{
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    GPTimerCC26XX_Object        *object  = handle->object;

    /* Store callback function */
    object->hwiCallbackFxn[handle->timerPart] = callback;
    /* Construct RTOS HWI */
    HwiP_Struct *pHwi = &object->hwi[handle->timerPart];
    HwiP_Params  hp;
    HwiP_Params_init(&hp);
    hp.arg       = (uintptr_t)handle;
    hp.enableInt = true;
    hp.priority  = hwAttrs->intPriority;
    HwiP_construct(pHwi, hwAttrs->intNum, GPTimerCC26XXHwiFxn, &hp);

    GPTimerCC26XX_enableInterrupt(handle, intMask);
}

/*!
 *  @brief  Destruct interrupt for timer handle.
 *          This function must only be called once after opening a timer and
 *          should not be called before calling GPTimerCC26XX_registerInterrupt
 */
void GPTimerCC26XX_unregisterInterrupt(GPTimerCC26XX_Handle handle)
{
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    GPTimerCC26XX_Object        *object  = handle->object;

    uint32_t ui32Base = hwAttrs->baseAddr;
    uint32_t timer    = GPT_LUT[handle->timerPart].map;

    /* Disable all timer unit interrupts, use "timer" variable as mask */
    TimerIntDisable(ui32Base, timer);

    /* Destroy callback function */
    object->hwiCallbackFxn[handle->timerPart] = NULL;
    /* Destruct HWI */
    HwiP_Struct *pHwi = &object->hwi[handle->timerPart];
    HwiP_destruct(pHwi);
}

/*!
 *  @brief  Enable interrupt source for current GPTimer unit. CPU interrupt
 *          for timer will not be enabled before calling
 *          GPTimerCC26XX_registerInterrupt
 */
void GPTimerCC26XX_enableInterrupt(GPTimerCC26XX_Handle handle, GPTimerCC26XX_IntMask intMask)
{
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    uint32_t ui32Base = hwAttrs->baseAddr;

    /* Interrupt registers are shared by TA and TB but bit fields are not symmetric
       Fetch mask from lookup table.*/
    uint32_t intMaskLookup = GPTimerCC26XXReverseLookupMask(GPT_LUT[handle->timerPart].interrupts, intMask, GPT_NUM_INTS);

    /* Enable interrupts in timer unit */
    TimerIntEnable(ui32Base, intMaskLookup);
}

/*!
 *  @brief  Disable interrupt source for current GPTimer unit. CPU interrupt
 *          for timer will not be disabled before calling GPTimerCC26XX_unregisterInterrupt
 */
void GPTimerCC26XX_disableInterrupt(GPTimerCC26XX_Handle handle, GPTimerCC26XX_IntMask intMask)
{
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    uint32_t ui32Base = hwAttrs->baseAddr;

    /* Interrupt registers are shared by TA and TB but bit fields are not symmetric
       Fetch mask from lookup table. */
    uint32_t intMaskLookup = GPTimerCC26XXReverseLookupMask(GPT_LUT[handle->timerPart].interrupts, intMask, GPT_NUM_INTS);

    /* Enable interrupts in timer unit */
    TimerIntDisable(ui32Base, intMaskLookup);
}

static void GPTimerCC26XX_initHw(GPTimerCC26XX_Handle handle, const GPTimerCC26XX_Params *params)
{
    /* Get the pointer to the object and hwAttrs */
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    GPTimerCC26XX_Object const  *object  = handle->object;

    TimerSetConfig(hwAttrs->baseAddr, object->width);
    uint32_t timer = GPT_LUT[handle->timerPart].map;

    uint32_t mode = (uint32_t) params->mode;

    if (params->matchTiming == GPTimerCC26XX_SET_MATCH_ON_TIMEOUT)
    {
        /* Same bit position is also valid for timer B in the TBMR register. */
        mode |= GPT_TAMR_TAMRSU_TOUPDATE;
    }
    else
    {
        /* Same bit position is also valid for timer B in the TBMR register. */
        mode |= GPT_TAMR_TAMRSU_CYCLEUPDATE;
    }

    if (params->direction == GPTimerCC26XX_DIRECTION_UP)
    {
        /* Same bit position also valid for timer B in the TBMR register. */
        mode |= GPT_TAMR_TACDIR_UP;
    }
    else
    {
        /* Same bit position also valid for timer B in the TBMR register. */
        mode |= GPT_TAMR_TACDIR_DOWN;
    }

    TimerSetMode(hwAttrs->baseAddr, timer, mode);

    GPTimerCC26XX_configureDebugStall(handle, params->debugStallMode);
}

/*!
 *  @brief  Restore GPTimer unit registers back to reset values.
 *          Needed since module does not have reset functionality.
 */
static void GPTimerCC26XX_resetHw(GPTimerCC26XX_Handle handle)
{
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    uint32_t ui32Base = hwAttrs->baseAddr;

    uint32_t timer = GPT_LUT[handle->timerPart].map;
    uint32_t regMask;
    /* Some registers are shared by TA and TB.
       Shift bit fields by 1 byte for timer B. */
    uint32_t shift = GPT_LUT[handle->timerPart].shift;
    /* Some registers are offset by one word (4 bytes) for Timer B. */
    uint32_t offset = GPT_LUT[handle->timerPart].offset;

    /* Disable timer before configuring */
    TimerDisable(ui32Base, timer);

    /* Reset control regs for timer N (CTL) */
    regMask = ~(GPT_CTL_MASK << shift);
    HWREG(ui32Base + GPT_O_CTL) &= regMask;

    /* Reset interrupt mask for Timer N (IMR).
       Equivalent to TimerIntDisable(ui32Base, regMask) */
    regMask = 0;
    uint8_t i;
    for (i = 0; i < GPT_NUM_INTS; i++)
    {
        regMask |= (uint32_t)(GPT_LUT[handle->timerPart].interrupts[i]);
    }
    HWREG(ui32Base + GPT_O_IMR) &= ~regMask;


    /* Clear interrupts for Timer N ( ICLR).  Same regMask as GPT_O_IMR.
       Equivalent to TimerIntClear(ui32Base, regMask) */
    HWREG(ui32Base + GPT_O_ICLR) = regMask;

    /* Reset load register for timer N.
       Equivalent to TimerLoadSet(ui32Base, timer, 0xFFFFFFF) */
    HWREG(ui32Base + offset + GPT_O_TAILR) = 0xFFFFFFFF;

    /* Reset match register for timer N.
       Equivalent to TimerMatchSet(ui32Base, timer, 0xFFFFFFFF) */
    HWREG(ui32Base + offset + GPT_O_TAMATCHR) = 0xFFFFFFFF;

    /* Reset pre-scale register for timer N.
       Equivalent to TimerPrescaleSet(ui32Base, timer, 0) */
    HWREG(ui32Base + offset + GPT_O_TAPR) = 0;

    /* Reset pre-scale match register for timer N.
       Equivalent to TimerPrescaleMatchSet(ui32Base, timer, 0) */
    HWREG(ui32Base + offset + GPT_O_TAPMR) = 0;
}


/*!
 *  @brief  GPTimer interrupt handler - Clears corresponding interrupt(s)
 *          and calls callback function with handle and bitmask argument
 *          as given in implementation header file.
 */
static void GPTimerCC26XXHwiFxn(uintptr_t a0)
{
    GPTimerCC26XX_Handle         handle  = (GPTimerCC26XX_Handle)a0;
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    GPTimerCC26XX_Object        *object  = handle->object;

    GPTimerCC26XX_HwiFxn         callbackFxn = object->hwiCallbackFxn[handle->timerPart];
    uint32_t timer = GPT_LUT[handle->timerPart].map;

    /* Full width raw interrupt status */
    uint32_t interrupts = HWREG(hwAttrs->baseAddr + GPT_O_MIS);
    /* Interrupt mask to clear (byte 0 or 1) */
    uint32_t interruptClr = timer & interrupts;
    /* Clear interrupts */
    HWREG(hwAttrs->baseAddr + GPT_O_ICLR) = interruptClr;

    /* Interrupt registers are shared by TA and TB but bit fields are not
       symmetric. Need to go through LUT and fetch bit vector based on interrupts
     */
    uint16_t intMaskLookup = GPTimerCC26XXLookupMask(GPT_LUT[handle->timerPart].interrupts, interrupts, GPT_NUM_INTS);

    if (callbackFxn != NULL)
    {
        callbackFxn(handle, intMaskLookup);
    }
}

/*!
 *  @brief  Set Standby constraint while using timer to avoid TI RTOS
 *          going into standby when timer is running.  As constraints are
 *          counting, store constraint status and access atomically and only
 *          once per timer resource.
 */
static inline void GPTimerCC26XXThreadsafeConstraintSet(GPTimerCC26XX_Handle handle)
{
    GPTimerCC26XX_Object *object = handle->object;

    uint32_t key = HwiP_disable();
    /* Only set if not already set */
    if (object->powerConstraint[handle->timerPart])
    {
        HwiP_restore(key);
        return;
    }
    object->powerConstraint[handle->timerPart] = true;
    HwiP_restore(key);
    Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY);
}

/*!
 *  @brief  Clear Standby constraint while using timer to avoid TI RTOS
 *          going into standby when timer is running.  As constraints are
 *          counting, store constraint status and access atomically and only
 *          once per timer resource.
 */
static inline void GPTimerCC26XXThreadsafeConstraintClr(GPTimerCC26XX_Handle handle)
{
    GPTimerCC26XX_Object *object = handle->object;

    uint32_t key = HwiP_disable();
    /* Only release if constraint set */
    if (!object->powerConstraint[handle->timerPart])
    {
        HwiP_restore(key);
        return;
    }
    object->powerConstraint[handle->timerPart] = false;
    HwiP_restore(key);
    Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY);
}

/*!
 *  @brief  Configure debug stall mode in timer. When enabled the timer will
 *          stop when the CPU is halted by the debugger.
 */
void GPTimerCC26XX_configureDebugStall(GPTimerCC26XX_Handle handle, GPTimerCC26XX_DebugMode mode)
{
    GPTimerCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs;
    uint32_t timer = GPT_LUT[handle->timerPart].map;
    TimerStallControl(hwAttrs->baseAddr, timer, mode);
}

/*!
 *  @brief  Set timer configuration. Will be moved to driverlib.
 */
static inline void TimerSetConfig(uint32_t ui32Base, uint32_t ui32Config)
{
    HWREG(ui32Base + GPT_O_CFG) = ui32Config;
}
/*!
 *  @brief  Set timer mode. Will be moved to driverlib.
 */
static inline void TimerSetMode(uint32_t ui32Base, uint32_t timer, uint32_t mode)
{
    uint32_t addr = ui32Base;

    if (timer == TIMER_B)
    {
        addr += GPT_O_TBMR;
    }
    else
    {
        addr += GPT_O_TAMR;
    }
    HWREG(addr) = mode;
}
