/* ======================================================================
 *   Copyright (C) 2013 Texas Instruments Incorporated
 *
 *   All rights reserved. Property of Texas Instruments Incorporated.
 *   Restricted rights to use, duplicate or disclose this code are
 *   granted through contract.
 *
 *   The program may not be used without the written permission
 *   of Texas Instruments Incorporated or against the terms and conditions
 *   stipulated in the agreement under which this program has been
 *   supplied.
 * ==================================================================== */
/**
 *  \file  timer.c
 *
 *  \brief Timer APIs.
 *
 *   This file contains the device abstraction layer APIs for Timer.
 */

/****************************************************************
*  INCLUDE FILES
****************************************************************/
#include "timer.h"
#include "hw_types.h"

/*******************************************************************************
*                       INTERNAL MACRO DEFINITIONS
*******************************************************************************/

/**
 * \brief   This macro will check for write POSTED status
 *
 * \param   reg          Register whose status has to be checked
 *
 *    'reg' can take the following values \n
 *    TIMER_WRITE_POST_TCLR - Timer Control register \n
 *    TIMER_WRITE_POST_TCRR - Timer Counter register \n
 *    TIMER_WRITE_POST_TLDR - Timer Load register \n
 *    TIMER_WRITE_POST_TTGR - Timer Trigger register \n
 *    TIMER_WRITE_POST_TMAR - Timer Match register \n
 *
 **/
#define TIMERWaitForWrite(reg, baseAdd)                        \
    if(HWREG(baseAdd + TIMER_TSICR) & TIMER_TSICR_POSTED_MASK) \
        while((reg & TIMERWritePostedStatusGet(baseAdd))) ;

/*******************************************************************************
*                        API FUNCTION DEFINITIONS
*******************************************************************************/

/**
 * \brief   This API will start the timer.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  None.
 *
 * \note    The timer must be configured before it is started/enabled.
 *
 **/
void TIMEREnable(uint32_t baseAdd)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    /* Start the timer */
    HWREG(baseAdd + TIMER_TCLR) |= TIMER_TCLR_ST_MASK;
}

/**
 * \brief   This API will stop the timer.
 *
 * \param   baseAdd      Base Address of the Timer Module Register.
 *
 * \return  None.
 *
 **/
void TIMERDisable(uint32_t baseAdd)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    /* Stop the timer */
    HWREG(baseAdd + TIMER_TCLR) &= ~TIMER_TCLR_ST_MASK;
}

/**
 * \brief   This API will configure the timer in combinations of
 *          'One Shot timer' and 'Compare' Mode or 'Auto-reload timer'
 *          and 'Compare' Mode.
 *
 * \param   baseAdd      Base Address of the Timer Module Register.
 * \param   timerMode    Mode for enabling the timer.
 *
 * 'timerMode' can take the following values \n
 *    TIMER_ONESHOT_CMP_ENABLE - One shot and compare mode enabled \n
 *    TIMER_ONESHOT_NOCMP_ENABLE - One shot enabled, compare mode disabled \n
 *    TIMER_AUTORLD_CMP_ENABLE - Auto-reload and compare mode enabled \n
 *    TIMER_AUTORLD_NOCMP_ENABLE - Auto-reload enabled, compare mode
 *                                   disabled \n
 *
 * \return  None.
 *
 **/
void TIMERModeConfigure(uint32_t baseAdd, uint32_t timerMode)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    /* Clear the AR and CE field of TCLR */
    HWREG(baseAdd + TIMER_TCLR) &= ~(TIMER_TCLR_AR_MASK | TIMER_TCLR_CE_MASK);

    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    /* Set the timer mode in TCLR register */
    HWREG(baseAdd + TIMER_TCLR) |= (timerMode & (TIMER_TCLR_AR_MASK |
                                                 TIMER_TCLR_CE_MASK));
}

/**
 * \brief   This API will configure and enable the pre-scaler clock.
 *
 * \param   baseAdd      Base Address of the Timer Module Register.
 * \param   ptv          Pre-scale clock Timer value.
 *
 * 'ptv' can take the following values \n
 *    TIMER_PRESCALER_CLK_DIV_BY_2 - Timer clock divide by 2 \n
 *    TIMER_PRESCALER_CLK_DIV_BY_4 - Timer clock divide by 4 \n
 *    TIMER_PRESCALER_CLK_DIV_BY_8 - Timer clock divide by 8 \n
 *    TIMER_PRESCALER_CLK_DIV_BY_16 - Timer clock divide by 16 \n
 *    TIMER_PRESCALER_CLK_DIV_BY_32 - Timer clock divide by 32 \n
 *    TIMER_PRESCALER_CLK_DIV_BY_64 - Timer clock divide by 64 \n
 *    TIMER_PRESCALER_CLK_DIV_BY_128 - Timer clock divide by 128 \n
 *    TIMER_PRESCALER_CLK_DIV_BY_256 - Timer clock divide by 256 \n
 *
 * \return  None.
 *
 **/
void TIMERPreScalerClkEnable(uint32_t baseAdd, uint32_t ptv)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    /* Clear the PTV field of TCLR */
    HWREG(baseAdd + TIMER_TCLR) &= ~TIMER_TCLR_PTV_MASK;

    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    /* Set the PTV field and enable the pre-scaler clock */
    HWREG(baseAdd +
          TIMER_TCLR) |= (ptv & (TIMER_TCLR_PTV_MASK | TIMER_TCLR_PRE_MASK));
}

/**
 * \brief   This API will disable the pre-scaler clock.
 *
 * \param   baseAdd      Base Address of the Timer Module Register.
 *
 * \return  None.
 *
 **/
void TIMERPreScalerClkDisable(uint32_t baseAdd)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    /* Disable Pre-scaler clock */
    HWREG(baseAdd + TIMER_TCLR) &= ~TIMER_TCLR_PRE_MASK;
}

/**
 * \brief   Set/Write the Counter register with the counter value.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   counter       Count value for the timer.
 *
 * \return  None.
 *
 * \note    Value can be loaded into the counter register when the counter is
 *          stopped or when it is running.
 **/
void TIMERCounterSet(uint32_t baseAdd, uint32_t counter)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCRR, baseAdd);

    /* Set the counter value */
    HWREG(baseAdd + TIMER_TCRR) = counter;
}

/**
 * \brief   Get/Read the counter value from the counter register.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  This API returns the count value present in the Timer Counter
 *          register.
 *
 * \note:   Value can be read from the counter register when the counter is
 *          stopped or when it is running.
 **/
uint32_t TIMERCounterGet(uint32_t baseAdd)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCRR, baseAdd);

    /* Read the counter value from TCRR */
    return (HWREG(baseAdd + TIMER_TCRR));
}

/**
 * \brief   Set the reload count value in the timer load register.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   reload        The reload count value of the timer.
 *
 * \return  None.
 *
 * \note:   It is recommended to not use reload value as 0xFFFFFFFF as it can
 *          lead to undesired results.
 **/
void TIMERReloadSet(uint32_t baseAdd, uint32_t reload)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TLDR, baseAdd);

    /* Load the register with the re-load value */
    HWREG(baseAdd + TIMER_TLDR) = reload;
}

/**
 * \brief   Get the reload count value from the timer load register.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  This API returns the value present in Timer Load Register.
 *
 **/
uint32_t TIMERReloadGet(uint32_t baseAdd)
{
    /* Return the contents of TLDR */
    return (HWREG(baseAdd + TIMER_TLDR));
}

/**
 * \brief   Configure general purpose output pin.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   gpoCfg        General purpose output.
 *
 * 'gpoCfg' can take the following values \n
 *    TIMER_GPO_CFG_0 - PORGPOCFG drives 0 \n
 *    TIMER_GPO_CFG_1 - PORGPOCFG drives 1 \n
 *
 * \return  None.
 *
 **/
void TIMERGPOConfigure(uint32_t baseAdd, uint32_t gpoCfg)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    /* Clear the GPO_CFG field of TCLR */
    HWREG(baseAdd + TIMER_TCLR) &= ~TIMER_TCLR_GPO_CFG_MASK;

    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    /* Write to the gpoCfg field of TCLR */
    HWREG(baseAdd + TIMER_TCLR) |= (gpoCfg & TIMER_TCLR_GPO_CFG_MASK);
}

/**
 * \brief   Set the match register with the compare value.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   compareVal    Compare value.
 *
 * \return  None.
 *
 **/
void TIMERCompareSet(uint32_t baseAdd, uint32_t compareVal)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TMAR, baseAdd);

    /* Write the compare value to TMAR */
    HWREG(baseAdd + TIMER_TMAR) = compareVal;
}

/**
 * \brief   Get the match register contents.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  This API returns the match register contents.
 *
 **/
uint32_t TIMERCompareGet(uint32_t baseAdd)
{
    /* Return the TMAR value */
    return (HWREG(baseAdd + TIMER_TMAR));
}

/**
 * \brief   Trigger IRQ event by software.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   intFlags      Variable used to trigger the events.
 *
 * 'intFlags' can take the following values \n
 *    TIMER_INT_TCAR_IT_FLAG - Trigger IRQ status for capture \n
 *    TIMER_INT_OVF_IT_FLAG - Trigger IRQ status for overflow \n
 *    TIMER_INT_MAT_IT_FLAG - Trigger IRQ status for match \n
 *
 * \return  None.
 *
 **/
void TIMERIntRawStatusSet(uint32_t baseAdd, uint32_t intFlags)
{
    /* Trigger the events in IRQSTATUS_RAW register */
    HWREG(baseAdd + TIMER_IRQSTATUS_RAW) = (intFlags &
                                            (
                                                TIMER_IRQSTATUS_RAW_MAT_IT_FLAG_MASK
                                                |
                                                TIMER_IRQSTATUS_RAW_OVF_IT_FLAG_MASK
                                                |
                                                TIMER_IRQSTATUS_RAW_TCAR_IT_FLAG_MASK));
}

/**
 * \brief   Read the status of IRQSTATUS_RAW register.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  This API returns the contents of IRQSTATUS_RAW register.
 *
 **/
uint32_t TIMERIntRawStatusGet(uint32_t baseAdd)
{
    /* Return the status of IRQSTATUS_RAW register */
    return (HWREG(baseAdd + TIMER_IRQSTATUS_RAW));
}

/**
 * \brief   Read the status of IRQ_STATUS register.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  This API returns the status of IRQSTATUS register.
 *
 **/
uint32_t TIMERIntStatusGet(uint32_t baseAdd)
{
    /* Return the status of IRQSTATUS register */
    return (HWREG(baseAdd + TIMER_IRQSTATUS));
}

/**
 * \brief   Clear the status of interrupt events.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   intFlags      Variable used to clear the events.
 *
 * 'intFlags' can take the following values \n
 *    TIMER_INT_TCAR_IT_FLAG - Clear IRQ status for capture \n
 *    TIMER_INT_OVF_IT_FLAG - Clear IRQ status for overflow \n
 *    TIMER_INT_MAT_IT_FLAG - Clear IRQ status for match \n
 *
 * \return  None.
 *
 **/
void TIMERIntStatusClear(uint32_t baseAdd, uint32_t intFlags)
{
    /* Clear the interrupt status from IRQSTATUS register */
    HWREG(baseAdd + TIMER_IRQSTATUS) = (intFlags &
                                        (TIMER_IRQSTATUS_TCAR_IT_FLAG_MASK |
                                         TIMER_IRQSTATUS_OVF_IT_FLAG_MASK |
                                         TIMER_IRQSTATUS_MAT_IT_FLAG_MASK));
}

/**
 * \brief   Enable the Timer interrupts.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   intFlags      Variable used to enable the interrupts.
 *
 * 'intFlags' can take the following values \n
 *    TIMER_INT_TCAR_EN_FLAG - IRQ enable for capture \n
 *    TIMER_INT_OVF_EN_FLAG - IRQ enable for overflow \n
 *    TIMER_INT_MAT_EN_FLAG - IRQ enable for match \n
 *
 * \return  None.
 *
 **/
void TIMERIntEnable(uint32_t baseAdd, uint32_t intFlags)
{
    /* Enable the Timer interrupts represented by intFlags */
    HWREG(baseAdd + TIMER_IRQENABLE_SET) = (intFlags &
                                            (
                                                TIMER_IRQENABLE_SET_TCAR_EN_FLAG_MASK
                                                |
                                                TIMER_IRQENABLE_SET_OVF_EN_FLAG_MASK
                                                |
                                                TIMER_IRQENABLE_SET_MAT_EN_FLAG_MASK));
}

/**
 * \brief   Disable the Timer interrupts.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   intFlags      Variable used to disable the interrupts.
 *
 * 'intFlags' can take the following values \n
 *    TIMER_INT_TCAR_EN_FLAG - IRQ disable for capture \n
 *    TIMER_INT_OVF_EN_FLAG - IRQ disable for overflow \n
 *    TIMER_INT_MAT_EN_FLAG - IRQ disable for match \n
 *
 * \return  None.
 *
 **/
void TIMERIntDisable(uint32_t baseAdd, uint32_t intFlags)
{
    /* Disable the Timer interrupts represented by intFlags */
    HWREG(baseAdd + TIMER_IRQENABLE_CLR) = (intFlags &
                                            (
                                                TIMER_IRQENABLE_CLR_TCAR_EN_FLAG_MASK
                                                |
                                                TIMER_IRQENABLE_CLR_OVF_EN_FLAG_MASK
                                                |
                                                TIMER_IRQENABLE_CLR_MAT_EN_FLAG_MASK));
}

/**
 * \brief   Set/enable the trigger write access.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  None.
 *
 * \note    When we have enabled the timer in Auto-reload mode, the value from
 *          TLDR is reloaded into TCRR when a overflow condition occurs. But if
 *           we want to load the contents from TLDR to TCRR before overflow
 *          occurs then call this API.
 **/
void TIMERTriggerSet(uint32_t baseAdd)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TTGR, baseAdd);

    /* Write a value to the register */
    HWREG(baseAdd + TIMER_TTGR) = TIMER_TTGR_TTGR_VALUE_MASK;
}

/**
 * \brief   Read the status of IRQENABLE_SET register.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  This API returns the status of IRQENABLE_SET register.
 *
 **/
uint32_t TIMERIntEnableGet(uint32_t baseAdd)
{
    /* Return the status of register IRQENABLE_SET */
    return (HWREG(baseAdd + TIMER_IRQENABLE_SET));
}

/**
 * \brief   Enable/Disable software reset for Timer module.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   rstOption     Enable/Disable reset option for TIMER.
 *
 * 'rstOption' can take the following values \n
 *    TIMER_SFT_RESET_ENABLE - Software reset is enabled \n
 *    TIMER_SFT_RESET_DISABLE - Software reset is disabled \n
 *
 * \return  None.
 *
 **/
void TIMERResetConfigure(uint32_t baseAdd, uint32_t rstOption)
{
    /* Clear the SFT field of TSICR */
    HWREG(baseAdd + TIMER_TSICR) &= ~TIMER_TSICR_SFT_MASK;

    /* Write the option sent by user to SFT field of TSICR */
    HWREG(baseAdd + TIMER_TSICR) |= (rstOption & TIMER_TSICR_SFT_MASK);
}

/**
 * \brief   Reset the Timer module.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  None.
 *
 **/
void TIMERReset(uint32_t baseAdd)
{
    /* Reset the Timer module */
    HWREG(baseAdd + TIMER_TIOCP_CFG) |= TIMER_TIOCP_CFG_SOFTRESET_MASK;

    while(TIMER_TIOCP_CFG_SOFTRESET_MASK == (HWREG(baseAdd + TIMER_TIOCP_CFG)
                                             & TIMER_TIOCP_CFG_SOFTRESET_MASK)) ;
}

/**
 * \brief   This API stores the context of the TIMER
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   contextPtr    Pointer to the structure where the DM timer context
 *                        need to be saved.
 *
 * \return  None.
 *
 **/
void TIMERContextSave(uint32_t baseAdd, TIMERCONTEXT *contextPtr)
{
    contextPtr->tldr         = HWREG(baseAdd + TIMER_TLDR);
    contextPtr->tmar         = HWREG(baseAdd + TIMER_TMAR);
    contextPtr->irqenableset = HWREG(baseAdd + TIMER_IRQENABLE_SET);

    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCRR, baseAdd);

    contextPtr->tcrr = HWREG(baseAdd + TIMER_TCRR);
    contextPtr->tclr = HWREG(baseAdd + TIMER_TCLR);
}

/**
 * \brief   This API restores the context of the TIMER
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   contextPtr    Pointer to the structure where the DM timer context
 *                        need to be restored from.
 *
 * \return  None.
 *
 **/
void TIMERContextRestore(uint32_t baseAdd, TIMERCONTEXT *contextPtr)
{
    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TLDR, baseAdd);

    HWREG(baseAdd + TIMER_TLDR) = contextPtr->tldr;

    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TMAR, baseAdd);

    HWREG(baseAdd + TIMER_TMAR)          = contextPtr->tmar;
    HWREG(baseAdd + TIMER_IRQENABLE_SET) = contextPtr->irqenableset;

    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCRR, baseAdd);

    HWREG(baseAdd + TIMER_TCRR) = contextPtr->tcrr;

    /* Wait for previous write to complete */
    TIMERWaitForWrite(TIMER_WRITE_POST_TCLR, baseAdd);

    HWREG(baseAdd + TIMER_TCLR) = contextPtr->tclr;
}

/**
 * \brief   Configure the posted mode of TIMER.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 * \param   postMode      Posted mode configuration.
 *
 * 'postMode' can take the following values \n
 *    TIMER_NONPOSTED - Posted mode not used \n
 *    TIMER_POSTED - Posted mode used \n
 *
 * \return  None.
 *
 **/
void TIMERPostedModeConfig(uint32_t baseAdd, uint32_t postMode)
{
    /* Clear the POSTED field of TSICR */
    HWREG(baseAdd + TIMER_TSICR) &= ~TIMER_TSICR_POSTED_MASK;

    /* Write to the POSTED field of TSICR */
    HWREG(baseAdd + TIMER_TSICR) |= (postMode & TIMER_TSICR_POSTED_MASK);
}

/**
 * \brief   Read the status of Write Posted Status register.
 *
 * \param   baseAdd       Base Address of the Timer Module Register.
 *
 * \return  This API returns the status of TWPS register.
 *
 **/
uint32_t TIMERWritePostedStatusGet(uint32_t baseAdd)
{
    /* Return the status of TWPS register */
    return (HWREG(baseAdd + TIMER_TWPS));
}

/**
 * \brief   Configure the timer status in emulation mode.
 *
 * \param   baseAdd         Base Address of the Timer Module Register.
 *
 * \param   emuModeOption   Emulation mode configuration.
 *
 * 'emuModeOption' can take the following values \n
 *    TIMER_FROZEN - Timer is frozen in emuMode \n
 *    TIMER_FREE - Timer is free in emuMode \n
 *
 * \return  None.
 *
 **/
void TIMEREmuModeConfigure(uint32_t baseAdd, uint32_t emuModeOption)
{
    /* Clear the EMUFREE field of TIOCP_CFG */
    HWREG(baseAdd + TIMER_TIOCP_CFG) &= ~TIMER_TIOCP_CFG_EMUFREE_MASK;

    /* Write to the EMUFREE field of TIOCP_CFG */
    HWREG(baseAdd + TIMER_TIOCP_CFG) |= ((emuModeOption
                                          << TIMER_TIOCP_CFG_EMUFREE_SHIFT)
                                         & TIMER_TIOCP_CFG_EMUFREE_MASK);
}

