/******************************************************************************
 * $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 icu.c
 **
 ** A detailed description is available at
 ** @link IcuGroup ICU Module description @endlink
 **
 ** History:
 ** - 2014-06-25  0.01  ST  First version
 *****************************************************************************/


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


#if defined (PDL_PERIPHERAL_ICU_ACTIVE)

/*****************************************************************************/
/* Local pre-processor symbols/macros (' define')                              */
/*****************************************************************************/


/*****************************************************************************/
/* Global variable definitions (declared in header file with 'extern')       */
/*****************************************************************************/


/*****************************************************************************/
/* Local type definitions ('typedef')                                        */
/*****************************************************************************/


/*****************************************************************************/
/* Local variable definitions ('static')                                     */
/*****************************************************************************/


/*****************************************************************************/
/* Local function prototypes ('static')                                      */
/*****************************************************************************/

/* IRQ handler channel 0 prototype */
static void  IcuIrqHandlerChannel0( volatile stc_icun_t* pstcIcu,
                                    stc_icu_intern_data_t* pstcIcuInternData
                                  ) ;
/* IRQ handler channel 1 prototype */
static void  IcuIrqHandlerChannel1( volatile stc_icun_t* pstcIcu,
                                    stc_icu_intern_data_t* pstcIcuInternData
                                  ) ;


/*****************************************************************************/
/* Function implementation - global ('extern') and local ('static')          */
/*****************************************************************************/

#include "icu_generated.c"

/**
 *****************************************************************************
 ** \brief ISR callback for ICU channel 0
 **
 ** This callback is called by the global ICU ISR whenever an ICU triggers an
 ** interrupt on channel 0. It calls the callback function that has been given
 ** during ICU initialization (see Icu_Init() and #stc_icu_config_t).
 **
 ** The interrupt request flag is cleared by the ISR
 **
 ** \param [in] pstcIcu           Input Capture Unit instance
 ** \param [in] pstcIcuInternData Internal data associated with the ICU instance
 *****************************************************************************/
static void IcuIrqHandlerChannel0( volatile stc_icun_t* pstcIcu,
                                   stc_icu_intern_data_t* pstcIcuInternData
                                 )
{
    /* Clear interrupt flag of channel raised the interrupt */
    PDL_WRITE_REG_SYNC( pstcIcu->unICSC.stcField.u1ICP0C, 1 );

    /* Call callback, if setup */
    if ( pstcIcuInternData->pfnCallbackChannel0 != NULL )
    {
        pstcIcuInternData->pfnCallbackChannel0() ;
    }
} /* IcuIrqHandlerChannel0 */


/**
 *****************************************************************************
 ** \brief ISR callback for ICU channel 1
 **
 ** This callback is called by the global ICU ISR whenever an ICU triggers an
 ** interrupt on channel 1. It calls the callback function that has been given
 ** during ICU initialization (see Icu_Init() and #stc_icu_config_t).
 **
 ** The interrupt request flag is cleared by the ISR
 **
 ** \param [in] pstcIcu           Input Capture Unit instance
 ** \param [in] pstcIcuInternData Internal data associated with the ICU instance
 *****************************************************************************/
static void IcuIrqHandlerChannel1( volatile stc_icun_t* pstcIcu,
                                   stc_icu_intern_data_t* pstcIcuInternData
                                 )
{
    /* Clear interrupt flag of channel raised the interrupt */
    PDL_WRITE_REG_SYNC( pstcIcu->unICSC.stcField.u1ICP1C, 1 );

    /* Call callback, if setup */
    if ( pstcIcuInternData->pfnCallbackChannel1 != NULL )
    {
        pstcIcuInternData->pfnCallbackChannel1() ;
    }
} /* IcuIrqHandlerChannel1 */


/**
 *****************************************************************************
 ** \brief Initialize ICU
 **
 ** This function initializes the ICU module and sets up the internal
 ** data structures
 **
 ** \note This function does not initialze the ICU channels itself.
 **       Icu_SetChannelConfig() has to be called for each channel you wish to
 **       use after Icu_Init().
 **
 ** \param [in]  pstcIcu          Input Capture Unit instance
 **
 ** \retval Ok                    Internal data has been setup
 ** \retval ErrorInvalidParameter pstcIcu == NULL
 *****************************************************************************/
en_result_t Icu_Init( volatile stc_icun_t* pstcIcu )
{
    /* Pointer to internal data */
    stc_icu_intern_data_t* pstcIcuInternData ;

    /* Check for NULL pointer */
    if ( pstcIcu == NULL )
    {
        return ErrorInvalidParameter ;
    }

    /* Get pointer to internal data structure ... */
    pstcIcuInternData = IcuGetInternDataPtr( pstcIcu ) ;
    /* ... and check for NULL */
    if ( pstcIcuInternData != NULL )
    {
        pstcIcuInternData->pfnCallbackChannel0 = NULL ;
        pstcIcuInternData->pfnCallbackChannel1 = NULL ;
    }
    return Ok ;
} /* Icu_Init */


/**
 *****************************************************************************
 ** \brief Set ICU channel configuration
 **
 ** This function initializes the given ICU channel. The parameters are checked
 ** for plausibilty and the given callback is stored internally.
 **
 ** The channel is active if stc_icu_channel_config_t#enEdgeDetection is not
 ** #IcuNoEdge.
 **
 ** \param [in]  pstcIcu    Input Capture Unit instance
 ** \param [in]  u8Channel  ICU channel (0...ICU_CHANNEL_COUNT-1)
 ** \param [in]  pstcConfig ICU configuration parameters
 **
 ** \retval Ok                    Channel configuration has been set
 ** \retval ErrorInvalidParameter If one of the following conditions is met
 **         - pstcIcu    == NULL
 **         - pstcConfig == NULL
 **         - pstcIcuInternData == NULL (invalid or disabled ICU unit
 **                                                 (PDL_PERIPHERAL_ENABLE_ICUn))
 **         - u8Channel >= ICU_CHANNEL_COUNT
 *****************************************************************************/
en_result_t Icu_SetChannelConfig( volatile stc_icun_t* pstcIcu,
                                  uint8_t u8Channel,
                                  const stc_icu_channel_config_t* pstcConfig
                                )
{
    /* Pointer to internal data */
    stc_icu_intern_data_t* pstcIcuInternData ;

    /* Register shadow to avoid (multiple) RMW */
    un_icun_ics_t unICS ;


    /* Check for NULL pointers */
    if ( pstcIcu    == NULL ||
         pstcConfig == NULL
       )
    {
        return ErrorInvalidParameter ;
    }

    /* Check channel range */
    if ( u8Channel >= ICU_CHANNEL_COUNT )
    {
        return ErrorInvalidParameter ;
    }

    /* Get pointer to internal data structure ... */
    pstcIcuInternData = IcuGetInternDataPtr( pstcIcu ) ;
    /* ... and check for NULL */
    if ( pstcIcuInternData == NULL )
    {
      return ErrorInvalidParameter ;
    }

    /* Read current data */
    unICS.u32Register = pstcIcu->unICS.u32Register ;

    /* Configure channels */
    if ( u8Channel == 0 )
    {
        /* Setup for channel 0 */

        /* First of all, disable possible interrupts */
        PDL_SAFELY_DISABLE_INTERRUPT( pstcIcu->unICSC.stcField.u1ICE0C, 1 ); /* Disable INT */
        PDL_WRITE_REG_SYNC( pstcIcu->unICSC.stcField.u1ICP0C, 1 ); /* Clear possible pending request */

        /* Store callback */
        pstcIcuInternData->pfnCallbackChannel0 = pstcConfig->pfnCallback ;

        /* Setup Interrupt Enable */
        if ( pstcIcuInternData->pfnCallbackChannel0 != NULL && 
             pstcConfig->enEdgeDetection != IcuNoEdge )
        {
            unICS.stcField.u1ICE0 = 1;
        }
        else
        {
            unICS.stcField.u1ICE0 = 0;
        }
            
        /* Setup edge detection */
        unICS.stcField.u2EG0   = pstcConfig->enEdgeDetection ;
    }
    else  /* if ( u8Channel == 0 ) */
    {
        /* Setup for channel 1 */

        /* First of all, disable possible interrupts */
        PDL_SAFELY_DISABLE_INTERRUPT( pstcIcu->unICSC.stcField.u1ICE1C, 1 ); /* Disable INT */
        PDL_WRITE_REG_SYNC( pstcIcu->unICSC.stcField.u1ICP1C, 1 ); /* Clear possible pending request */

        /* Store callback */
        pstcIcuInternData->pfnCallbackChannel1 = pstcConfig->pfnCallback ;

        /* Setup Interrupt Enable */
        if ( pstcIcuInternData->pfnCallbackChannel1 != NULL && 
             pstcConfig->enEdgeDetection != IcuNoEdge )
        {
            unICS.stcField.u1ICE1 = 1;
        }
        else
        {
            unICS.stcField.u1ICE1 = 0;
        }

        /* Setup edge detection */
        unICS.stcField.u2EG1   = pstcConfig->enEdgeDetection ;

    } /* if ( u8Channel == 0 ) */

    /* Now write setup at once */
    pstcIcu->unICS.u32Register = unICS.u32Register ;

    return Ok ;
} /* Icu_SetChannelConfig */


/**
 *****************************************************************************
 ** \brief Start Input Capture Unit channel
 **
 ** This function enables the given ICU channel if
 ** Icu_EnableChannel#enEdgeDetection is not #IcuNoEdge.
 **
 ** The parameters are checked for plausibility and the ICU interrupt is enabled
 **
 ** \param [in]  pstcIcu         Input Capture Unit instance
 ** \param [in]  u8Channel       ICU channel (0...ICU_CHANNEL_COUNT-1)
 ** \param [in]  enEdgeDetection Edge detection mode (should something
 **               else than #IcuNoEdge to really enable the channel) see also
 **               #stc_icu_channel_config_t
 **
 ** \retval Ok Channel is enabled
 ** \retval ErrorInvalidParameter If one of the following conditions is met
 **         - pstcIcu == NULL
 **         - pstcIcuInternData == NULL (invalid or disabled ICU unit
 **                                                 (PDL_PERIPHERAL_ENABLE_ICUn))
 **         - u8Channel  out of range
 *****************************************************************************/
en_result_t Icu_EnableChannel( volatile stc_icun_t* pstcIcu,
                               uint8_t u8Channel,
                               en_icu_edge_detection_t enEdgeDetection
                             )
{
    /* Pointer to internal data */
    stc_icu_intern_data_t* pstcIcuInternData ;

    /* Check for NULL pointers */
    if ( pstcIcu == NULL )
    {
        return ErrorInvalidParameter ;
    }

    /* Check channel range */
    if ( u8Channel >= ICU_CHANNEL_COUNT )
    {
        return ErrorInvalidParameter ;
    }

    /* Get pointer to internal data structure ... */
    pstcIcuInternData = IcuGetInternDataPtr( pstcIcu ) ;
    /* ... and check for NULL */
    if ( pstcIcuInternData == NULL )
    {
      return ErrorInvalidParameter ;
    }

    /* Enable channels */
    if ( u8Channel == 0 )
    {
        /* Setup for channel 0 */

        /* First of all disable interrupt for channel 0 and clear any pending requests */
        PDL_SAFELY_DISABLE_INTERRUPT( pstcIcu->unICSC.stcField.u1ICE0C, 1 ); /* Disable INT */
        PDL_WRITE_REG_SYNC( pstcIcu->unICSC.stcField.u1ICP0C, 1 ); /* Clear possible pending request */

        /* Setup edge detection */
        pstcIcu->unICS.stcField.u2EG0 = enEdgeDetection ;

        /* Interrupt config */
        if ( pstcIcuInternData->pfnCallbackChannel0 != NULL &&
             enEdgeDetection != IcuNoEdge
           )
        {
            /* Enable channel 0 interrupt, if callback is provided AND edge
               detection is in use */
            pstcIcu->unICSS.stcField.u1ICE0S = 1;
        }
    }
    else /* if ( u8Channel == 0 ) */
    {
        /* Setup for channel 1 */

        /* First of all disable interrupt for channel 1 and clear any pending requests */
        PDL_SAFELY_DISABLE_INTERRUPT( pstcIcu->unICSC.stcField.u1ICE1C, 1 ); /* Disable INT */
        PDL_WRITE_REG_SYNC( pstcIcu->unICSC.stcField.u1ICP1C, 1 ); /* Clear possible pending request */

        /* Setup edge selection */
        pstcIcu->unICS.stcField.u2EG1 = enEdgeDetection ;

        /* Interrupt config */
        if ( pstcIcuInternData->pfnCallbackChannel1 != NULL &&
             enEdgeDetection != IcuNoEdge
           )
        {
            /* Enable channel 0 interrupt, if callback is provided AND edge
               detection is in use */
            pstcIcu->unICSS.stcField.u1ICE1S = 1;
        }
    } /* if ( u8Channel == 0 ) */

    return Ok ;
} /* Icu_EnableChannel */


/**
 *****************************************************************************
 ** \brief Stop Input Capture Unit channel
 **
 ** This function sets the edge detection of the channel to #IcuNoEdge. If
 ** none of the channels of the given ICU is active any longer, the ICU
 ** interrupt is disabled.
 **
 ** \param [in]  pstcIcu          Input Capture Unit instance
 ** \param [in]  u8Channel        ICU channel (0...ICU_CHANNEL_COUNT-1)
 **
 ** \retval Ok                    Channel is disabled.
 ** \retval ErrorInvalidParameter If one of the following conditions is met
 **         - pstcIcu == NULL
 **         - u8Channel >= ICU_CHANNEL_COUNT
 *****************************************************************************/
en_result_t Icu_DisableChannel( volatile stc_icun_t* pstcIcu, uint8_t u8Channel )
{
    /* Check for NULL pointer */
    if ( pstcIcu == NULL )
    {
        return ErrorInvalidParameter ;
    }

    /* Check channel range */
    if ( u8Channel >= ICU_CHANNEL_COUNT )
    {
        return ErrorInvalidParameter ;
    }

    /* Icu_EnableChannel with edge detection of type IcuNoEdge will disable
       the channel */
    return Icu_EnableChannel( pstcIcu, u8Channel, IcuNoEdge ) ;
} /* Icu_DisableChannel */


/**
 *****************************************************************************
 ** \brief Get a Capture data.
 **
 ** This function get a capture data value.
 **
 ** \param [in]   pstcIcu          ICU unit instance
 ** \param [out]  data             Capture data
 **
 ** \retval Ok                    Got a capture data.
 ** \retval ErrorInvalidParameter If pstcIcu == NULL.
 *****************************************************************************/
en_result_t Icu_GetCount( volatile stc_icun_t* pstcIcu,
                          uint8_t u8Channel,
                          uint32_t* data
                        )
{
    /* Check for NULL pointer */
    if ( pstcIcu == NULL )
    {
        return ErrorInvalidParameter;
    }

    /* Check channel range */
    if ( u8Channel >= ICU_CHANNEL_COUNT )
    {
        return ErrorInvalidParameter ;
    }

    /* Select channels */
    if ( u8Channel == 0 )
    {
        /* channel 0 */
        *data = pstcIcu->unIPCP0.u32Register;
    }
    else
    {
        /* channel 1 */
        *data = pstcIcu->unIPCP1.u32Register;
    }

    return Ok ;
} /* Icu_GetCount */


#endif /* PDL_PERIPHERAL_ICU_ACTIVE */
/*****************************************************************************/
/* EOF (not truncated)                                                       */
/*****************************************************************************/
