/******************************************************************************
* $Revision: 423 $
* $Date:: 2017-04-07 16:03:30 +0900#$
*****************************************************************************/
/* __DISCLAIMER_START__                                                      */
/******************************************************************************
* (c)2017, Cypress Semiconductor Corporation
* or a subsidiary of Cypress Semiconductor Corporation. All rights
* reserved.
*
* This software, including source code, documentation and related
* materials ( "Software" ), is owned by Cypress Semiconductor
* Corporation or one of its subsidiaries ( "Cypress" ) and is protected by
* and subject to worldwide patent protection (United States and foreign),
* United States copyright laws and international treaty provisions.
* Therefore, you may use this Software only as provided in the license
* agreement accompanying the software package from which you
* obtained this Software ( "EULA" ).
*
* If no EULA applies, Cypress hereby grants you a personal, nonexclusive,
* non-transferable license to copy, modify, and compile the
* Software source code solely for use in connection with Cypress' s
* integrated circuit products. Any reproduction, modification, translation,
* compilation, or representation of this Software except as specified
* above is prohibited without the express written permission of Cypress.
*
* Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO
* WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING,
* BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE. Cypress reserves the right to make
* changes to the Software without notice. Cypress does not assume any
* liability arising out of the application or use of the Software or any
* product or circuit described in the Software. Cypress does not
* authorize its products for use in any products where a malfunction or
* failure of the Cypress product may reasonably be expected to result in
* significant property damage, injury or death ( "High Risk Product" ). By
* including Cypress' s product in a High Risk Product, the manufacturer
* of such system or application assumes all risk of such use and in doing
* so agrees to indemnify Cypress against all liability.
******************************************************************************/
/* __DISCLAIMER_END__                                                        */
/*****************************************************************************
** \file ppg.c
**
** A detailed description is available at
**
** History:
** - 2015-01-29  0.01  MAs  First version for PPG
*****************************************************************************/


/*****************************************************************************/
/* Include files                                                             */
/*****************************************************************************/
#include "pdl.h"
#include "interrupts.h"

#if defined(PDL_PERIPHERAL_PPG_ACTIVE)

/**
*****************************************************************************
** \addtogroup ppgGroup
*****************************************************************************/
//@{
    
    /*****************************************************************************/
    /* Local pre-processor symbols/macros ('define')                             */
    /*****************************************************************************/
#define ppg_TMCR_CKS2_0       0x07   /* Bit mask for the CKS2-0 Bit */
#define ppg_TMCR2_CKS3        0x08   /* Bit mask for the CKS3 Bit */
    
#define ppg_TMCR_CKS2_0_SHIFT 0x00   /* Shift value for the CKS2-0 Bit */
#define ppg_TMCR2_CKS3_SHIFT  0x03   /* Shift value for the CKS3 Bit */
    
    
    /*****************************************************************************/
    /* Global variable definitions (declared in header file with 'extern')       */
    /*****************************************************************************/
    
    
    /*****************************************************************************/
    /* Local type definitions ('typedef')                                        */
    /*****************************************************************************/
    
    
    /*****************************************************************************/
    /* Local variable definitions ('static')                                     */
    /*****************************************************************************/
    
    
    /*****************************************************************************/
    /* Local function prototypes ('static')                                      */
    /*****************************************************************************/
    static void BtPpgIrqHandler( volatile stc_btn_ppg_t* pstcPpg,
                                stc_ppg_intern_data_t* pstcPpgInternData) ;
    
    
    /*****************************************************************************/
    /* Function implementation - global ('extern') and local ('static')          */
    /*****************************************************************************/
    
#include "bt_ppg_generated.c"
    
    /**
    *****************************************************************************
    ** \brief ISR callback for ppg
    **
    ** This callback is called by the global ppg ISR whenever a ppg triggers an
    ** interrupt. It in return calls the callback function that has been given
    ** during ppg configuration.
    **
    ** \param [in] pstcPpg             PPG instance that caused the interrupt
    ** \param [in] pstcppgInternData   Internal data associated with the ppg instance
    *****************************************************************************/
    static void BtPpgIrqHandler( volatile stc_btn_ppg_t* pstcPpg,
                                stc_ppg_intern_data_t* pstcPpgInternData)
    {
        /* Check if a trigger interrupt is raised */
        if ( pstcPpg->unSTC.stcField.u1TGIR == 1 )
        {
            /* clear trigger flag of Ppg */
            PDL_WRITE_REG_SYNC(pstcPpg->unSTCC.stcField.u1TGIRC, 1);
            
            /* Call ppg callback function */
            if ( pstcPpgInternData->pfnTriggerCallback != NULL )
            {
                pstcPpgInternData->pfnTriggerCallback() ;
            }
        } /* if ( pstcPpg->unSTC.stcField.u1TGIR == 1 ) */
        
        /* Check if a underflow interrupt is raised */
        if ( pstcPpg->unSTC.stcField.u1UDIR == 1 )
        {
            /* clear underflow flag of RLT */
            PDL_WRITE_REG_SYNC(pstcPpg->unSTCC.stcField.u1UDIRC, 1);
            
            /* Call RLT callback function */
            if ( pstcPpgInternData->pfnUnderflowCallback != NULL )
            {
                pstcPpgInternData->pfnUnderflowCallback() ;
            }
        } /* if ( pstcPpg->unSTC.stcField.u1UDIR == 1 ) */
        
    } /* BtPpgIrqHandler */
    
    
    /**
    *****************************************************************************
    ** \brief Initialize a PPG.
    **
    ** This function initializes the given ppg instance with the provided
    ** parameters. Before configuration the ppg is disabled.
    ** The given callback is stored internally.
    **
    **
    ** \param [in]  pstcPpg       ppg instance
    ** \param [in]  pstcConfig    ppg configuration
    **
    ** \retval Ok                    ppg configuration has been set
    ** \retval ErrorInvalidParameter If one of the following conditions is met:
    **          - pstcPpg == NULL
    **          - pstcConfig == NULL
    **          - pstcppgInternData == NULL
    **
    *****************************************************************************/
    en_result_t Bt_Ppg_Init( volatile stc_btn_ppg_t* pstcPpg,
                            stc_ppg_config_t* pstcConfig)
    {
        stc_ppg_intern_data_t* pstcInternData = NULL;
        
        /* Local buffer to prepare the settings */
        un_btn_ppg_tmcr_t     unTMCR  = { 0 } ;
        un_btn_ppg_stc_t      unSTC   = { 0 } ;
        un_btn_ppg_tmcr2_t    unTMCR2 = { 0 } ;
        
        /* Check for NULL pointers */
        if ( pstcPpg    == NULL ||
            pstcConfig == NULL)
        {
            /* One of the passed parameters is a NULL pointer */
            return ErrorInvalidParameter;
        }
        
        /* Get the pointer to the internal data structure for the given ppg instance */
        pstcInternData = PpgGetInternDataPtr( pstcPpg ) ;
        
        // ... and check for NULL pointer
        if ( pstcInternData == NULL )
        {
            return ErrorInvalidParameter ;
        }
        
        /* Write STC register to reset value which also disables the ppg */
        /* operation. u1TGIRC and u1UDIRC are set to 1 to clear the interrupt flag */
        /* First of all, disable possible interrupts */
        PDL_WRITE_REG_SYNC(pstcPpg->unSTCC.stcField.u1TGIRC, 1);
        PDL_WRITE_REG_SYNC(pstcPpg->unSTCC.stcField.u1UDIRC, 1);
        
        /* Set the timer function */
        unTMCR.stcField.u3FMD = 2;     /* Select 16-bit ppg timer */
        
        /* Store the callback that shall be called within the interrupt routine in */
        /* the internal data structure. */
        pstcInternData->pfnTriggerCallback = pstcConfig->pfnTriggerCallback ;
        pstcInternData->pfnUnderflowCallback = pstcConfig->pfnUnderflowCallback ;
        
        /* Setup clock settings */
        unTMCR.stcField.u3CKS2_0 = (pstcConfig->enPrescaler & ppg_TMCR_CKS2_0);
        unTMCR2.stcField.u1CKS3  = (pstcConfig->enPrescaler & ppg_TMCR2_CKS3) >> ppg_TMCR2_CKS3_SHIFT;
        
        /* Set the Trigger input edge selection */
        unTMCR.stcField.u2EGS = pstcConfig->enTriggerInputEdgeSelection;
        
        /* configure rest of ppg */
        unTMCR.stcField.u1RTGEN = (pstcConfig->bRestartEnable           == TRUE) ? 1 : 0 ;
        unTMCR.stcField.u1PMSK  = (pstcConfig->bPulseOutputMaskEnable   == TRUE) ? 1 : 0 ;
        unTMCR.stcField.u1OSEL  = (pstcConfig->bOutputPolarityInverse   == TRUE) ? 1 : 0 ;
        unTMCR.stcField.u1MDSE  = (pstcConfig->bOneShotOperation        == TRUE) ? 1 : 0 ;
        
        /* Write back shadow registers to real HW */
        pstcPpg->unTMCR   = unTMCR;
        pstcPpg->unSTC    = unSTC;
        pstcPpg->unTMCR2  = unTMCR2;
        
        return Ok;
        
    } /* Bt_Ppg_Init */
    
    
    /**
    *****************************************************************************
    ** \brief Start the ppg
    **
    ** ppg_Init() must have Called before
    **
    ** This function starts ppg cycle and enables operation.
    ** If a callback was set by ppg_Init(), the ppg interrupt is enabled and the
    ** internal ISR in turn calls the given callback.
    **
    ** If bSwTrigger is TRUE, the ppg is triggered immediately.
    **
    ** \param [in]  pstcPpg          ppg instance
    ** \param [in]  pstcCycle        ppg cycle configuration
    ** \param [in]  bSwTrigger       TRUE generates a software trigger to directly
    **                                start operation
    **
    ** \retval Ok                    ppg has been started
    ** \retval ErrorInvalidParameter If one of the following conditions are met:
    **          - one of the passed parameters is a NULL pointer
    **          - pstcPpgInternData == NULL (pstcPpg is an invalid address)
    **            i.E. invalid or disabled ppg unit
    *****************************************************************************/
    en_result_t Bt_Ppg_Start( volatile stc_btn_ppg_t* pstcPpg,
                             stc_ppg_cycle_config_t* pstcCycle,
                             boolean_t bSwTrigger )
    {
        /* Pointer to the internal data structure */
        stc_ppg_intern_data_t* pstcInternData ;
        
        /* Check for NULL pointer */
        if ( pstcPpg == NULL || pstcCycle == NULL )
        {
            /* Passed parameter is a NULL pointer */
            return ErrorInvalidParameter ;
        }
        
        /* Get pointer to internal data ... */
        pstcInternData = PpgGetInternDataPtr( pstcPpg ) ;
        
        /* ... and check for NULL pointer */
        if ( pstcInternData == NULL )
        {
            /* The pstcPpg that was passed is not available or not enabled */
            return ErrorInvalidParameter ;
        }
        
        /* If necessary register ISR */
        if ( pstcInternData->pfnTriggerCallback != NULL )
        {
            /* Enable interrupt */
            pstcPpg->unSTCS.stcField.u1TGIES = 1;
        }
        
        if ( pstcInternData->pfnUnderflowCallback != NULL )
        {
            /* Enable interrupt */
            pstcPpg->unSTCS.stcField.u1UDIES = 1;
        }
        
        /* Set Low Width */
        pstcPpg->unPRLL.u16Register = pstcCycle->u16LowWidth;
        
        /* Set High width */
        pstcPpg->unPRLH.u16Register = pstcCycle->u16HighWidth;
        
        /* Enable timer and wait for trigger */
        pstcPpg->unTMCR.stcField.u1CTEN = 1;
        
        if ( bSwTrigger == TRUE )
        {
            pstcPpg->unTMCR.stcField.u1STRG = 1;
        } /* if ( bSwTrigger == TRUE ) */
        
        return Ok;
        
    } /* Bt_Ppg_Start */
    
    /**
    *****************************************************************************
    ** \brief Set duty cycle of PPG in 16-bit mode
    **
    ** \pre \a Bt_PpgInit() must have run before.
    ** \pre \a Bt_Ppg_Start() must have run before.
    **
    ** \param [in]  pstcPpg          PPG instance
    ** \param [in]  u16HighWidth     High width of PPG
    ** \param [in]  u16LowWidth      Low width of PPG
    **
    ** \retval Ok                    Duty cycle has been set
    ** \retval ErrorInvalidParameter If pstcPpg == NULL
    *****************************************************************************/
    en_result_t Bt_Ppg_SetDuty16( volatile stc_btn_ppg_t* pstcPpg,
                                 uint16_t u16HighWidth,
                                 uint16_t u16LowWidth)
    {
        // Check for NULL pointer
        if (pstcPpg == NULL)
        {
            // Passed parameter is a NULL pointer
            return ErrorInvalidParameter;
        }
        
        // Set new duty
        pstcPpg->unPRLL.u16Register = u16LowWidth;
        pstcPpg->unPRLH.u16Register = u16HighWidth;
        
        //Re-Trigger to update values
        pstcPpg->unTMCR.stcField.u1STRG = 1;
        
        return Ok;
        
    } // Bt_Ppg_SetDuty16
    
    /**
    *****************************************************************************
    ** \brief Stop the ppg
    **
    ** This function disables the ppg interrupt and stops the ppg operation
    ** \pre \a Bt_PpgInit() must have run before.
    **
    ** \param [in]  pstcPpg          ppg instance
    **
    ** \retval Ok                    ppg has been stopped
    ** \retval ErrorInvalidParameter If pstcPpg == NULL
    *****************************************************************************/
    en_result_t Bt_Ppg_Stop( volatile stc_btn_ppg_t* pstcPpg )
    {
        /* Check for NULL pointer */
        if ( pstcPpg == NULL )
        {
            /* Passed parameter is a NULL pointer */
            return ErrorInvalidParameter ;
        }
        
        /* disable timer before anything else is done */
        pstcPpg->unTMCR.stcField.u1CTEN = 0 ;
        
        /* disable interrupts */
        PDL_SAFELY_DISABLE_INTERRUPT(pstcPpg->unSTCC.stcField.u1TGIEC, 1)
        PDL_SAFELY_DISABLE_INTERRUPT(pstcPpg->unSTCC.stcField.u1UDIEC, 1)
        
        return Ok;
        
    } /* Bt_ppg_Stop */
    
    /**
    *****************************************************************************
    ** \brief Enable Interupt
    **
    ** This Function Enables interupts if corresponding ISR are provided to
    ** handle at the time of configuration.
    ** \pre  Bt_PpgInit() must have run before.
    **
    ** \param [in]  pstcPpg                   Pointer to PPG instance register area
    ** \param [in]  bEnableTrigger            Trigger Interupt Enable requested
    ** \param [in]  bEnableUnderflow          Underflow Interupt Enable requested
    **
    ** \retval Ok                             Interrupt Enabled successfully
    ** \retval ErrorInvalidParameter If one of following conditions are met:
    **         - pstcPpg == NULL
    **         - pstcInternData == NULL
    **
    *****************************************************************************/
    en_result_t Bt_Ppg_EnableInterupt(volatile stc_btn_ppg_t* pstcPpg,
                                      boolean_t bEnableTrigger,
                                      boolean_t bEnableUnderflow)
    {
        // Check for NULL-Pointer
        if (pstcPpg == NULL)
        {
            return ErrorInvalidParameter;
        }
        
        // Pointer to the internal data structure
        stc_ppg_intern_data_t* pstcInternData ;
        
        // Get pointer to internal data ...
        pstcInternData = PpgGetInternDataPtr( pstcPpg ) ;
        
        //check for NULL pointer */
        if ( pstcInternData == NULL )
        {
            // The pstcPpg that was passed is not available or not enabled
            return ErrorInvalidParameter ;
        }
        
        //Enable interupts
        if(bEnableTrigger == TRUE)//Check if trigger Interupt are requested
        {
            if (( pstcInternData->pfnTriggerCallback != NULL ))
            {
                // Enable interrupt
                pstcPpg->unSTCS.stcField.u1TGIES = 1;
            }
            
        }
        else if(bEnableUnderflow == TRUE)//Check if Underflow Interupt is requested
        {
            if(pstcInternData->pfnUnderflowCallback != NULL )
            {
                //Enable underflow Interupt
                pstcPpg->unSTCS.stcField.u1UDIES = 1;
            }
        }
        
        return Ok;
        
    }/* Bt_Ppg_EnableInterupt */
    
    /**
    *****************************************************************************
    ** \brief Disable Interupt
    **
    ** This Function Disable interupts.
    ** \pre  Bt_PpgInit() must have run before.
    **
    ** \param [in]  pstcPpg                   Pointer to ppg instance register area
    ** \param [in]  bDisableTrigger            Status Interupt Disable requested
    ** \param [in]  bDisableUnderflow                Reception Interupt disable requested
    **
    ** \retval Ok                             Interrupt Disabled successfully
    ** \retval ErrorInvalidParameter If one of following conditions are met:
    **         - pstcPpg == NULL
    **         - pstcInternData == NULL
    **
    *****************************************************************************/
    en_result_t Bt_Ppg_DisableInterupt(volatile stc_btn_ppg_t* pstcPpg,
                                       boolean_t bDisableTrigger,
                                       boolean_t bDisableUnderflow)
    {
        // Check for NULL-Pointer
        if (pstcPpg == NULL)
        {
            return ErrorInvalidParameter;
        }
        
        if(bDisableTrigger == TRUE)//Check if trigger Interupt are requested
        {
            //Disable Error/staus Interupts
            pstcPpg->unSTCC.stcField.u1TGIEC = 1;
        }
        else if(bDisableUnderflow == TRUE)//Check if underflow Interupt are requested
        {
            //Disable Reception Interupt
            pstcPpg->unSTCC.stcField.u1UDIEC = 1;
        }
        
        return Ok;
        
    }/* Bt_Ppg_DisableInterupt */
    
    
    /**
    *****************************************************************************
    ** \brief De-Initializes the PPG
    **
    ** This Function Releases all resources of PPG instance and reset all ppg
    ** registers to default values.
    **
    ** \pre Mfs_ppg_Init() must be called
    **
    ** \param [in] pstcPpg               Pointer to ppg instance register area
    **
    ** \retval Ok                        Deinitialized successfully
    ** \retval ErrorInvalidParameter If one of the following conditions are met:
    **          - pstcPpg == NULL
    **          - pstcInternData == NULL(invalid or disabled ppg unit )
    **
    *****************************************************************************/
    en_result_t Bt_Ppg_DeInit(volatile stc_btn_ppg_t* pstcPpg)
    {
        // Check for NULL-Pointer
        if (pstcPpg == NULL)
        {
            return ErrorInvalidParameter;
        }
        
        // Pointer to the internal data structure
        stc_ppg_intern_data_t* pstcInternData ;
        
        // Get pointer to internal data ...
        pstcInternData = PpgGetInternDataPtr( pstcPpg ) ;
        
        //check for NULL pointer */
        if ( pstcInternData == NULL )
        {
            // The pstcPpg that was passed is not available or not enabled
            return ErrorInvalidParameter ;
        }
        
        // Reset internal data for the instance
        // Clear the callback ISRs
        pstcInternData->pfnTriggerCallback              =    NULL;
        pstcInternData->pfnUnderflowCallback            =    NULL;
        
        // Clear and disable Interupts and reset the registers
        // Now Reset HW registers
        pstcPpg->unTMCR.u16Register    = 0x0000;
        pstcPpg->unSTCC.u8Register     = 0xFF;
        pstcPpg->unTMCR2.u8Register    = 0x00;
        
        return Ok;
        
    } /* Bt_Ppg_DeInit */
    
//@}
#endif /* PDL_PERIPHERAL_PPG_ACTIVE */
/*****************************************************************************/
/* EOF (not truncated)                                                       */
/*****************************************************************************/
