/******************************************************************************
* $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 port.c
**
** A detailed description is available at
** @link PortGroup Port Module description @endlink
**
** History:
**   - 2014-05-16  0.01  HS  Initial version for Traveo
**   - 2014-10-17  0.02  CEy Added functions to configure several port pins
**                           and resource inputs, added GPIO Toggle function
*****************************************************************************/

/*****************************************************************************/
/* Include files                                                             */
/*****************************************************************************/
#include "common_include.h"

/*****************************************************************************/
/* Global pre-processor symbols/macros ('#define')                           */
/*****************************************************************************/
/**
*****************************************************************************
** \addtogroup PortGroup Port functions (Port)
**
*****************************************************************************/
/*!@{ */
    
    /*****************************************************************************/
    /* Local pre-processor symbols/macros ('#define')                            */
    /*****************************************************************************/
    /*! Maximum channel number of each port */
#define PORT_MAX_PORT_CHANNEL_COUNT     (32)
    
    /**
    *****************************************************************************
    ** \brief Write protected register of PPC
    **
    ** \note This macro contains IRQ mask processing for protected sequence.
    **
    ** \param [in]  reg                Register access symbol.
    ** \param [in]  data               Write data.
    *****************************************************************************/
#define PORT_WPREG_PPC(reg, data)                   \
    {                                                   \
        un_ppc_keycdr_t wk;                             \
        uint32_t adr = (uint32_t)&reg;                  \
        uint8_t size_key;                               \
        wk.au16Halfword[0] = adr & 0x7FFF;              \
        size_key = (sizeof(reg) >> 1) << 4;             \
        IRQ_DISABLE_LOCAL();                            \
        wk.au8Byte[3] = size_key | (0 << 6);            \
        PPC_KEYCDR = wk.u32Register;                    \
        wk.au8Byte[3] = size_key | (1 << 6);            \
        PPC_KEYCDR = wk.u32Register;                    \
        wk.au8Byte[3] = size_key | (2 << 6);            \
        PPC_KEYCDR = wk.u32Register;                    \
        wk.au8Byte[3] = size_key | (3 << 6);            \
        PPC_KEYCDR = wk.u32Register;                    \
        reg = data;                                     \
        IRQ_RESTORE();                                  \
    }
    
    /**
    *****************************************************************************
    ** \brief Write protected register of GPIO
    **
    ** \note This macro contains IRQ mask processing for protected sequence.
    **
    ** \param [in]  reg                Register access symbol.
    ** \param [in]  data               Write data.
    *****************************************************************************/
#define PORT_WPREG_GPIO(reg, data)                  \
    {                                                   \
        un_gpio_keycdr_t wk;                            \
        uint32_t adr = (uint32_t)&reg;                  \
        uint8_t size_key;                               \
        wk.au16Halfword[0] = adr & 0x7FFF;              \
        size_key = (sizeof(reg) >> 1) << 4;             \
        IRQ_DISABLE_LOCAL();                            \
        wk.au8Byte[3] = size_key | (0 << 6);            \
        GPIO_KEYCDR = wk.u32Register;                   \
        wk.au8Byte[3] = size_key | (1 << 6);            \
        GPIO_KEYCDR = wk.u32Register;                   \
        wk.au8Byte[3] = size_key | (2 << 6);            \
        GPIO_KEYCDR = wk.u32Register;                   \
        wk.au8Byte[3] = size_key | (3 << 6);            \
        GPIO_KEYCDR = wk.u32Register;                   \
        reg = data;                                     \
        IRQ_RESTORE();                                  \
    }
    
    /**
    *****************************************************************************
    ** \brief Write protected register of RIC
    **
    ** \note This macro contains IRQ mask processing for protected sequence.
    **
    ** \param [in]  reg                Register access symbol.
    ** \param [in]  data               Write data.
    *****************************************************************************/
#define PORT_WPREG_RIC(reg, data)                   \
    {                                                   \
        un_ric_keycdr_t wk;                             \
        uint32_t adr = (uint32_t)&reg;                  \
        uint8_t size_key;                               \
        wk.au16Halfword[0] = adr & 0x7FFF;              \
        size_key = (sizeof(reg) >> 1) << 4;             \
        IRQ_DISABLE_LOCAL();                            \
        wk.au8Byte[3] = size_key | (0 << 6);            \
        RIC_KEYCDR = wk.u32Register;                    \
        wk.au8Byte[3] = size_key | (1 << 6);            \
        RIC_KEYCDR = wk.u32Register;                    \
        wk.au8Byte[3] = size_key | (2 << 6);            \
        RIC_KEYCDR = wk.u32Register;                    \
        wk.au8Byte[3] = size_key | (3 << 6);            \
        RIC_KEYCDR = wk.u32Register;                    \
        reg = data;                                     \
        IRQ_RESTORE();                                  \
    }
    
    /**
    *****************************************************************************
    ** \brief Write protected register of ADC
    **
    ** \note This macro contains IRQ mask processing for protected sequence.
    **
    ** \param [in]  reg                Register access symbol.
    ** \param [in]  data               Write data.
    *****************************************************************************/
#define PORT_WPREG_ADC(reg, data)                   \
    {                                                   \
        un_ader_2_keycdr_t wk;                          \
        uint32_t adr = (uint32_t)&reg;                  \
        uint8_t size_key;                               \
        wk.au16Halfword[0] = adr & 0x7FFF;              \
        size_key = (sizeof(reg) >> 1) << 4;             \
        IRQ_DISABLE_LOCAL();                            \
        wk.au8Byte[3] = size_key | (0 << 6);            \
        ADER_KEYCDR = wk.u32Register;                   \
        wk.au8Byte[3] = size_key | (1 << 6);            \
        ADER_KEYCDR = wk.u32Register;                   \
        wk.au8Byte[3] = size_key | (2 << 6);            \
        ADER_KEYCDR = wk.u32Register;                   \
        wk.au8Byte[3] = size_key | (3 << 6);            \
        ADER_KEYCDR = wk.u32Register;                   \
        reg = data;                                     \
        IRQ_RESTORE();                                  \
    }
    
    /*****************************************************************************/
    /* Global variable definitions (declared in header file with 'extern')       */
    /*****************************************************************************/
    
    /*****************************************************************************/
    /* Local type definitions ('typedef')                                        */
    /*****************************************************************************/
    /*! Define special Set/Clear structure to allow indexed access to special regs */
    typedef struct stc_port_set_clear_group {
        un_gpio_posrn_t       unPOSR;
        un_gpio_pocrn_t       unPOCR;
        un_gpio_ddsrn_t       unDDSR;
        un_gpio_ddcrn_t       unDDCR;
    } stc_port_set_clear_group_t;
    
    /*! Define special Output structure to allow indexed access to special regs */
    typedef struct stc_port_output_group
    {
        un_gpio_podrn_t       unPODR;
        un_gpio_ddrn_t        unDDR;
    } stc_port_output_group_t;
    
    /*****************************************************************************/
    /* Local function prototypes ('static')                                      */
    /*****************************************************************************/
    
    /*****************************************************************************/
    /* Local variable definitions ('static')                                     */
    /*****************************************************************************/
    
    /*****************************************************************************/
    /* Function implementation - global ('extern') and local ('static')          */
    /*****************************************************************************/
    
    /**
    *****************************************************************************
    ** \brief Set the configuration of a single port pin within given port
    **
    ** \pre Must be run after Port_SetResourceInput().
    **
    ** \param [in]  u8PortIndex       Index of port to set.(e.g. "0" of the P011)
    ** \param [in]  u8ChNumber        Channel number to operate on. (e.g."11" of the P011)
    ** \param [in]  pstcPinConfig     Pointer to a port pin configuration structure
    **
    ** \retval Ok                     Configuration applied to specified port pin.
    ** \retval ErrorInvalidParameter If one of the following conditions are met:
    **          - u8ChNumber out of range (0 .. PORT_MAX_PORT_CHANNEL_COUNT-1)
    **          - pstcPinConfig is a NULL pointer.
    *****************************************************************************/
    en_result_t Port_SetPinConfig(uint8_t u8PortIndex,
                                  uint8_t u8ChNumber,
                                  const stc_port_pin_config_t* pstcPinConfig)
    {
        uint32_t u32PcfgrnIndex;
        uint32_t u32SetMask;
        un_ppc_pcfgrn_t  unPortSetting = {0};
        volatile stc_port_set_clear_group_t* pstcPortSetClrGrp =
        (volatile stc_port_set_clear_group_t*)&GPIO.unPOSR0;
        volatile un_ppc_pcfgrn_t* punPcfgrn = &PPC.unPCFGR000;
        
        /* Null check */
        if (NULL == pstcPinConfig)
        {
            return ErrorInvalidParameter;
        }
        
        /* Check for channel number out of range */
        if (u8ChNumber >= PORT_MAX_PORT_CHANNEL_COUNT)
        {
            return ErrorInvalidParameter;
        }
        
        u32PcfgrnIndex = u8PortIndex * PORT_MAX_PORT_CHANNEL_COUNT + u8ChNumber;
        
        unPortSetting.stcField.u3POF = pstcPinConfig->enOutputFunction;
        unPortSetting.stcField.u2ODR = pstcPinConfig->enOutputDrive;
        unPortSetting.stcField.u2PIL = pstcPinConfig->enInputLevel;
        unPortSetting.stcField.u1PIE =
        (pstcPinConfig->bInputEnable == FALSE) ? 0 : 1;
        unPortSetting.stcField.u1NFE =
        (pstcPinConfig->bNoiseFilterEnable == FALSE) ? 0 : 1;
        
        /* Set port pull up/down */
        if (pstcPinConfig->enPullResistor== PortPullResistorDown)
        {
            unPortSetting.stcField.u1PDE = 1;
            unPortSetting.stcField.u1PUE = 0;
        }
        else if (pstcPinConfig->enPullResistor== PortPullResistorUp)
        {
            unPortSetting.stcField.u1PDE = 0;
            unPortSetting.stcField.u1PUE = 1;
        }
        else
        {
            unPortSetting.stcField.u1PDE = 0;
            unPortSetting.stcField.u1PUE = 0;
        }
        
        /* Calculate corresponding bit position */
        u32SetMask = (uint32_t)(1 << u8ChNumber);
        
        /* Set Initial port output level */
        if (pstcPinConfig->enGpioInitOutputLevel == PortGpioHigh)
        {
            pstcPortSetClrGrp[u8PortIndex].unPOSR.u32Register = u32SetMask;
        }
        else
        {
            pstcPortSetClrGrp[u8PortIndex].unPOCR.u32Register = u32SetMask;
        }
        
        /* Set port functions */
        PORT_WPREG_PPC(punPcfgrn[u32PcfgrnIndex], unPortSetting);
        
        /* Set port direction */
        if (pstcPinConfig->enGpioDirection == PortGpioOutput)
        {
            PORT_WPREG_GPIO(pstcPortSetClrGrp[u8PortIndex].unDDSR.u32Register, u32SetMask);
        }
        else
        {
            PORT_WPREG_GPIO(pstcPortSetClrGrp[u8PortIndex].unDDCR.u32Register, u32SetMask);
        }
        
        return Ok;
    }
    
    /**
    *****************************************************************************
    ** \brief Select the input port configuration
    **
    ** \param [in]  punRicResinReg    Pointer of the Resource Input Setting Register (RIC_RESIN)
    ** \param [in]  enInputPort       see #en_port_input_port_t
    *****************************************************************************/
    void Port_SelectInputPort(volatile un_ric_resinn_t*  punRicResinReg,
                              en_port_input_port_t enInputPort)
    {
        PORT_WPREG_RIC(punRicResinReg->au8Byte[1], enInputPort);
    }
    
    /**
    *****************************************************************************
    ** \brief Select the input resource configuration
    **
    ** \param [in]  punRicResinReg    Pointer of the Resource Input Setting Register (RIC_RESIN)
    ** \param [in]  enInputPort       see #en_port_input_source_t
    *****************************************************************************/
    void Port_SelectInputSource(volatile un_ric_resinn_t*  punRicResinReg,
                                en_port_input_source_t enInputSource)
    {
        PORT_WPREG_RIC(punRicResinReg->au8Byte[0], enInputSource);
    }
    
    /**
    *****************************************************************************
    ** \brief Enable port input
    **
    ** \pre Must be run after all other port configurations are completed.
    *****************************************************************************/
    void Port_EnableInput(void)
    {
        PORT_WPREG_GPIO(GPIO_PORTEN, 0x00000001);
    }
    
    /**
    *****************************************************************************
    ** \brief Set single pin level within given port
    **
    ** \param [in]  u8PortIndex       Index of port to set.(e.g. "0" of the P011)
    ** \param [in]  u8ChNumber        Channel number to operate on. (e.g."11" of the P011)
    ** \param [in]  enLevel           Port level. see #en_port_gpio_level_t
    **
    ** \retval Ok                     Setting of the port level was successfully done.
    ** \retval ErrorInvalidParameter  If one of the following conditions are met:
    **          - u8ChNumber out of range (0 .. PORT_MAX_PORT_CHANNEL_COUNT-1)
    *****************************************************************************/
    en_result_t Port_SetPortPinLevel(uint8_t u8PortIndex,
                                     uint8_t u8ChNumber,
                                     en_port_gpio_level_t enLevel)
    {
        uint32_t u32SetMask;
        volatile stc_port_set_clear_group_t* pstcPortSetClrGrp =
        (volatile stc_port_set_clear_group_t*)&GPIO.unPOSR0;
        
        /* Check for channel number out of range */
        if (u8ChNumber >= PORT_MAX_PORT_CHANNEL_COUNT)
        {
            return ErrorInvalidParameter;
        }
        
        u32SetMask = (uint32_t)(1 << u8ChNumber);
        if (enLevel == PortGpioHigh)
        {
            pstcPortSetClrGrp[u8PortIndex].unPOSR.u32Register = u32SetMask;
        }
        else
        {
            pstcPortSetClrGrp[u8PortIndex].unPOCR.u32Register = u32SetMask;
        }
        return Ok;
    }
/*-------------------------------------------------------------------------
* Function Name  : Port_GetOutPortPinLevel
* Description    : ??¡ì?¨¦?¡ì?¡ì??¡ìo?3?1?¡ì1??|¡§????
* Input          :
* Output         : None
* Return         : None
* onther         :
--------------------------------------------------------------------------*/
en_result_t Port_GetOutPortPinLevel(uint8_t u8PortIndex,uint8_t u8ChNumber,en_port_gpio_level_t * enLevel)
{
    uint32_t u32SetMask;
    volatile stc_port_output_group_t * unPidrn = (volatile stc_port_output_group_t*)&GPIO.unPODR0;
    
    /* Null check */
    if (enLevel == NULL)
    {
        return ErrorInvalidParameter;
    }
    /* Check for channel number out of range */
    if (u8ChNumber >= PORT_MAX_PORT_CHANNEL_COUNT)
    {
        return ErrorInvalidParameter;
    }
    
    u32SetMask = (uint32_t)(1 << u8ChNumber);
    if ((unPidrn[u8PortIndex].unPODR.u32Register & u32SetMask) != 0)
    {
        *enLevel = PortGpioHigh;
    }
    else
    {
        *enLevel = PortGpioLow;
    }
    
    return Ok;
}
    /**
    *****************************************************************************
    ** \brief Get single pin level within given port
    **
    ** \param [in]  u8PortIndex       Index of port to set.(e.g. "0" of the P011)
    ** \param [in]  u8ChNumber        Channel number to operate on. (e.g."11" of the P011)
    ** \param [out]  enLevel          Port level. see #en_port_gpio_level_t
    **
    ** \retval Ok                     Getting of the port level was successfully done.
    ** \retval ErrorInvalidParameter  If one of the following conditions are met:
    **          - u8ChNumber out of range (0 .. PORT_MAX_PORT_CHANNEL_COUNT-1)
    **          - enLevel is a NULL pointer.
    *****************************************************************************/
    en_result_t Port_GetPortPinLevel(uint8_t u8PortIndex,
                                     uint8_t u8ChNumber,
                                     en_port_gpio_level_t * enLevel)
    {
        uint32_t u32SetMask;
        volatile un_gpio_pidrn_t * unPidrn = &GPIO.unPIDR0;
        
        /* Null check */
        if (enLevel == NULL)
        {
            return ErrorInvalidParameter;
        }
        /* Check for channel number out of range */
        if (u8ChNumber >= PORT_MAX_PORT_CHANNEL_COUNT)
        {
            return ErrorInvalidParameter;
        }
        
        u32SetMask = (uint32_t)(1 << u8ChNumber);
        if ((unPidrn[u8PortIndex].u32Register & u32SetMask) != 0)
        {
            *enLevel = PortGpioHigh;
        }
        else
        {
            *enLevel = PortGpioLow;
        }
        
        return Ok;
    }
    
    
    /**
    *****************************************************************************
    ** \brief Toggles single pin level within given port
    **
    ** \param [in]  u8PortIndex       Index of port to set.(e.g. "0" of the P011)
    ** \param [in]  u8ChNumber        Channel number to operate on. (e.g."11" of the P011)
    **
    ** \retval Ok                     Toggling the port level was successfully done.
    ** \retval ErrorInvalidParameter  If one of the following conditions are met:
    **          - u8ChNumber out of range (0 .. PORT_MAX_PORT_CHANNEL_COUNT-1)
    *****************************************************************************/
    en_result_t Port_TogglePortPin(uint8_t u8PortIndex,
                                   uint8_t u8ChNumber)
    {
        en_port_gpio_level_t enLevel;
        en_result_t enResult;
        uint32_t u32SetMask;
        volatile stc_port_set_clear_group_t* pstcPortSetClrGrp =
        (volatile stc_port_set_clear_group_t*)&GPIO.unPOSR0;
        
        /* Check for channel number out of range */
        if (u8ChNumber >= PORT_MAX_PORT_CHANNEL_COUNT)
        {
            return ErrorInvalidParameter;
        }
        
        // Get actual level for requested pin
        enResult = Port_GetPortPinLevel( u8PortIndex, u8ChNumber, &enLevel ) ;
        if ( enResult != Ok )
        {
            return enResult;
        }
        
        u32SetMask = (uint32_t)(1 << u8ChNumber);
        if (enLevel == PortGpioLow)
        {
            pstcPortSetClrGrp[u8PortIndex].unPOSR.u32Register = u32SetMask;
        }
        else
        {
            pstcPortSetClrGrp[u8PortIndex].unPOCR.u32Register = u32SetMask;
        }
        return Ok;
    }
    
    
    
    /**
    *****************************************************************************
    ** \brief Set Port Pin configuration
    **
    ** This function sets the configuration for each port pin in the array
    **
    ** \pre Must be in privileged mode
    **
    ** \param [in]  astcPortPins[]     Struct with the individual settings
    **                                 for each Port Pin
    **
    ** \param [in]  u32Count           dimension of the astcPortPins[]
    *
    ** \retval Ok                      Set of Pin enable bits was successful
    ** \retval ErrorAccessRights If following conditions is met:
    **             - API is not in privileged mode.
    ** \retval ErrorInvalidParameter If one of the following conditions are met:
    **             - astcPortPins == NULL
    **             - u32Count == 0
    *****************************************************************************/
    en_result_t Port_ConfigurePortPins(const stc_port_pin_set_t astcPortPins[], uint32_t u32Count)
    {
        uint32_t u32Index = 0;
        en_result_t enCheck = Ok;
        
        if (astcPortPins == NULL)
        {
            return ErrorInvalidParameter;
        }
        
        //  Check number of port pins to configure
        if (u32Count == 0)
        {
            return ErrorInvalidParameter;
        }
        
        // Walk through port pin list to configure  pins
        for (u32Index = 0; u32Index < u32Count; u32Index++)
        {
            // If port number is in valid range
            if (astcPortPins[u32Index].u8PortIndex < MCU_MAX_PORT_COUNT)
            {
                // If pin number is in valid range
                if (astcPortPins[u32Index].u8PinNumber < MCU_MAX_PORT_PIN_COUNT)
                {
                    uint8_t u8Port = astcPortPins[u32Index].u8PortIndex;
                    uint8_t u8Pin  = astcPortPins[u32Index].u8PinNumber;
                    const stc_port_pin_set_t* pstcPortPinSet = &astcPortPins[u32Index];
                    
                    // Set port pin configuration
                    enCheck = Port_SetPinConfig(u8Port, u8Pin, &pstcPortPinSet->stcPortPinConfig);
                    if (enCheck != Ok)
                    {
                        return enCheck;
                    }
                }
            }
        }
        return Ok;
    } // Port_ConfigurePortPins
    
    
    /**
    *****************************************************************************
    ** \brief Set Port Pin input matrix and special input signals
    **
    ** \param [in]  astcResources[]    Struct with the pointer to
    **                                 RICFG.un???.stcField register and the
    **                                 Parameters to set the input matrix
    **
    ** \param [in]  u32Count           size of the astcResources[]
    **
    ** \retval Ok                      Set of Pin enable bits was successful
    **
    ** \retval ErrorInvalidParameter If one of the following conditions are met:
    **             - astcInputResources == NULL
    **             - u32Count = 0
    *****************************************************************************/
    en_result_t Port_ConfigureInputResources(const stc_input_resource_set_t astcInputResources[], uint32_t u32Count)
    {
        uint32_t u32Index = 0;
        
        if (astcInputResources == NULL)
        {
            return ErrorInvalidParameter;
        }
        
        if (u32Count == 0)
        {
            return ErrorInvalidParameter;
        }
        
        // Walk through resources list
        for (u32Index = 0; u32Index < u32Count; u32Index++)
        {
            
            // Create parameters
            volatile un_ric_resinn_t* punRicResinReg = astcInputResources[u32Index].punRicResinReg;
            en_port_input_port_t      enInputPort   = astcInputResources[u32Index].enInputPort;
            en_port_input_source_t    enInputSource = astcInputResources[u32Index].enInputSource;
            
            // Set configuration register
            Port_SelectInputPort  (punRicResinReg, enInputPort);
            Port_SelectInputSource(punRicResinReg, enInputSource);
        }
        
        // Enable all now configured input ports
        Port_EnableInput();
        return Ok;
    }   
/*! @} */

/*****************************************************************************/
/* EOF (not truncated)                                                       */
/*****************************************************************************/
