/**
* @file    Port_Lld.c
*==================================================================================================
*   Project              : YTMicro AUTOSAR 4.4.0 MCAL
*   Platform             : ARM
*   Peripheral           : Port_Lld
*   Dependencies         : none
*
*   Autosar Version      : V4.4.0
*   Autosar Revision     : ASR_REL_4_4_REV_0000
*   SW Version           : V2.2.0
*
*   (c) Copyright 2020-2023 Yuntu Microelectronics co.,ltd.
*   All Rights Reserved.
==================================================================================================*/

#ifdef __cplusplus
extern "C"
{
#endif

/*==================================================================================================
 *                                          INCLUDE FILES
==================================================================================================*/
#include "Compiler.h"
#include "Port_Lld_Reg.h"
#include "Port_Lld.h"
#include "SchM_Port.h"
/*==================================================================================================
 *                                  SOURCE FILE VERSION INFORMATION
==================================================================================================*/
#define PORT_LLD_VENDOR_ID_C                    (180)
#define PORT_LLD_AR_REL_MAJOR_VER_C              (4)
#define PORT_LLD_AR_REL_MINOR_VER_C              (4)
#define PORT_LLD_AR_REL_REVISION_VER_C           (0)
#define PORT_LLD_SW_MAJOR_VER_C                  (2)
#define PORT_LLD_SW_MINOR_VER_C                  (2)
#define PORT_LLD_SW_PATCH_VER_C                  (0)
/*==================================================================================================
 *                                        FILE VERSION CHECKS
==================================================================================================*/
/* Check if source file and PORT_LLD header file are of the same vendor */
#if (PORT_LLD_VENDOR_ID_C != PORT_LLD_VENDOR_ID)
#error "Port_Lld.c and Port_Lld.h have different vendor ids"
#endif

/* Check if source file and PORT_LLD header file are of the same Autosar version */
#if (( PORT_LLD_AR_REL_MAJOR_VER_C != PORT_LLD_AR_REL_MAJOR_VER) || \
      ( PORT_LLD_AR_REL_MINOR_VER_C != PORT_LLD_AR_REL_MINOR_VER) || \
      ( PORT_LLD_AR_REL_REVISION_VER_C != PORT_LLD_AR_REL_REVISION_VER))
#error "AutoSar Version Numbers of Port_Lld.c and Port_Lld.h are different"
#endif

/* Check if source file and PORT_LLD header file are of the same Software version */
#if (( PORT_LLD_SW_MAJOR_VER_C != PORT_LLD_SW_MAJOR_VER) || \
      ( PORT_LLD_SW_MINOR_VER_C != PORT_LLD_SW_MINOR_VER) || \
      ( PORT_LLD_SW_PATCH_VER_C != PORT_LLD_SW_PATCH_VER))
#error "Software Version Numbers of Port_Lld.c and Port_Lld.h are different"
#endif

/*==================================================================================================
 *                                         GLOBAL VARIABLES
==================================================================================================*/
/*==================================================================================================
 *                                          LOCAL VARIABLES
==================================================================================================*/
/*==================================================================================================
 *                                          LOCAL CONSTANTS
==================================================================================================*/
/*==================================================================================================
 *                                           LOCAL MACROS
==================================================================================================*/
#define PORT_REG_ADDR32_READ(address) (*(volatile uint32 *)(address))
#define PORT_REG_ADDR32_WRITE(address, value) ((*(volatile uint32 *)(address)) = (value))
#define PORT_REG_ADDR32_AEARWRITE(address, mask, value) \
    (PORT_REG_ADDR32_WRITE((address),                   \
                           ((PORT_REG_ADDR32_READ(address) & ((uint32) ~(mask))) | (value))))

#define PORT_REG_ADDR32_SET_BITS(address, mask) ((*(volatile uint32 *)(address)) |= (mask))
#define PORT_REG_ADDR32_CLEAR_BITS(address, mask) ((*(volatile uint32 *)(address)) &= (~(mask)))
#define PORT_REG_ADDR32_INDEX_SET_BIT(address, bitindex) ((*(volatile uint32 *)(address)) |= ((uint32)1 << (bitindex)))
#define PORT_REG_ADDR32_INDEX_CLEAR_BIT(address, bitindex) ((*(volatile uint32 *)(address)) &= (~((uint32)1 << (bitindex))))

#define Port_IsCorrectPortPinId(Port, Pin) (((Port) < PORT_TOTAL_NUMBER) && ((Pin) < PORT_PIN_TOTAL_NUMBER))

#if (PORT_FIXED_PB_CONFIG == STD_ON)
#define PORT_UNUSED_PARAM(parma) ((void)parma)
#endif
/**
 * MR12 RULE 2.2 VIOLATION: The operation here is redundant as the result's value is always that of
 * the left-hand operand. This line of code has been maintained for the sake of code clarity and readability.
 */
/* PRQA S 2985, 2986 EOF */
/*==================================================================================================
 *                                          LOCAL FUNCTIONS
==================================================================================================*/
#define PORT_START_SEC_CODE
#include "Port_MemMap.h"
/**
 * @brief This function is used to set the pin's pull configuration.
 *
 * @param PinPullConfig Target pull type
 * @param RegPCTRLPCRAddr PCTRL PCR register address
 */
PORT_FUNC LOCAL_INLINE void Port_Lld_InitPull(Port_PinPullConfigType PinPullConfig, uint32 RegPCTRLPCRAddr)
{
    switch (PinPullConfig)
    {
    case PORT_PIN_NO_PULL:
        PORT_REG_ADDR32_CLEAR_BITS(RegPCTRLPCRAddr, PCTRL_PCR_PE_MASK);
        PORT_REG_ADDR32_CLEAR_BITS(RegPCTRLPCRAddr, PCTRL_PCR_PS_MASK);
        break;
    case PORT_PIN_PULLDOWN:
        PORT_REG_ADDR32_SET_BITS(RegPCTRLPCRAddr, PCTRL_PCR_PE_MASK);
        PORT_REG_ADDR32_CLEAR_BITS(RegPCTRLPCRAddr, PCTRL_PCR_PS_MASK);
        break;
    case PORT_PIN_PULLUP:
        PORT_REG_ADDR32_SET_BITS(RegPCTRLPCRAddr, PCTRL_PCR_PE_MASK);
        PORT_REG_ADDR32_SET_BITS(RegPCTRLPCRAddr, PCTRL_PCR_PS_MASK);
        break;
    default:
        /* null */
        break;
    }
}


#if PORT_HAS_SLEW_RATE == STD_ON
/**
 * @brief This function is used to set the pin's slew rate.
 *
 * @param PinSlewRate The port pin slew rate.
 * @param RegPCTRLPCRAddr PCTRL PCR register address.
 */
PORT_FUNC LOCAL_INLINE void Port_Lld_InitSRE(Port_PinSlewRateType PinSlewRate, uint32 RegPCTRLPCRAddr)
{
    if (PORT_PIN_SLOW_SLEW_RATE == PinSlewRate)
    {
        PORT_REG_ADDR32_SET_BITS(RegPCTRLPCRAddr, PCTRL_PCR_SRE_MASK);
    }
    else
    {
        PORT_REG_ADDR32_CLEAR_BITS(RegPCTRLPCRAddr, PCTRL_PCR_SRE_MASK);
    }
}
#endif

/**
 * @brief This function sets the relative register for a pin in GPIO mode.
 *
 * @details This function sets the relative register according to the pin's direction specified in PinConfigPtr.
 *
 * @param PinConfigPtr The pin config type ready for initialization.
 * @param PinHwId Hardware pin ID
 * @param PortHwId Hardware port ID
 */

PORT_FUNC LOCAL_INLINE void Port_Lld_InitGPIOMode(const Port_PinConfigType *PinConfigPtr, Port_HwPinType PinHwId, Port_HwPortType PortHwId)
{
    if (PORT_PIN_IN == PinConfigPtr->Direction) /* Direction = PORT_PIN_IN */
    {
        /* Disable port pin output */
        PORT_REG_ADDR32_INDEX_CLEAR_BIT(GPIO_POER_ADDR32(PortHwId), PinHwId);
        /* Enalbe port pin input */
        PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_PIER_ADDR32(PortHwId), PinHwId);

        /* Config the passive filter */
        if (STD_ON == PinConfigPtr->PassiveFilter)
        {
            PORT_REG_ADDR32_SET_BITS(PCTRL_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId), PCTRL_PCR_PFE_MASK); /* Enable passive filter */
        }
        else
        {
            PORT_REG_ADDR32_CLEAR_BITS(PCTRL_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId), PCTRL_PCR_PFE_MASK); /* Disable passive filter */
        }

        uint32 RegGPIOPCRAddr = GPIO_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId);

#if PORT_HAS_DIGITAL_FILTER == STD_ON
        /* Set up Digital filter*/
        if (STD_ON == PinConfigPtr->DigitalFilter)
        {
#if defined(CPU_YTM32B1HA0)||defined(CPU_YTM32B1MC0)
            //TODO: seems HA0 any port can setup digital filter feature, need check and change 
            PORT_REG_ADDR32_SET_BITS(PCTRL_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId), PCTRL_PCR_DFE_MASK); /* Enable digital filter */
#else
            PORT_REG_ADDR32_SET_BITS(RegGPIOPCRAddr, GPIO_PCR_DFE_MASK); /* Enable digital filter */
#endif
        }
        else
        {
#if defined(CPU_YTM32B1HA0)||defined(CPU_YTM32B1MC0)
            PORT_REG_ADDR32_CLEAR_BITS(PCTRL_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId), PCTRL_PCR_DFE_MASK); /* Enable digital filter */
#else
            PORT_REG_ADDR32_CLEAR_BITS(RegGPIOPCRAddr, GPIO_PCR_DFE_MASK); /* Disable digital filter */
#endif
        }
#endif

        /* setup invert */
        if (STD_ON == PinConfigPtr->InvertEnable)
        {
            PORT_REG_ADDR32_SET_BITS(RegGPIOPCRAddr, GPIO_PCR_INVE_MASK); /* Enable invert */
        }
        else
        {
            PORT_REG_ADDR32_CLEAR_BITS(RegGPIOPCRAddr, GPIO_PCR_INVE_MASK); /* Disable invert */
        }
#if PORT_HAS_DIGITAL_FILTER == STD_ON
        /* setup width */
#if defined(CPU_YTM32B1HA0)||defined(CPU_YTM32B1MC0)
        PORT_REG_ADDR32_CLEAR_BITS(PCTRL_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId), PCTRL_PCR_DFW(PinConfigPtr->DigitalFilterWidth)); /* Enable digital filter */
#else
        PORT_REG_ADDR32_AEARWRITE(RegGPIOPCRAddr, GPIO_PCR_DFW_MASK, GPIO_PCR_DFW(PinConfigPtr->DigitalFilterWidth));
#endif
#endif
    }
    else /* Direction = PORT_PIN_OUT */
    {
        /** [SWS_Port_00055]
         *  Set the port pin output latch to a default level (defined during configuration)
         *  before setting the port pin direction to output.
         */
        /* Config the init level */
        if (PORT_PIN_LEVEL_LOW == PinConfigPtr->InitLevel)
        {
            PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_PCOR_ADDR32(PortHwId), PinHwId);
        }
        else
        {
            PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_PSOR_ADDR32(PortHwId), PinHwId);
        }
#if (PORT_READBACK_ENABLE == STD_ON)
        PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_PIER_ADDR32(PortHwId), PinHwId);
#else
        /* Disable port pin input */
        PORT_REG_ADDR32_INDEX_CLEAR_BIT(GPIO_PIER_ADDR32(PortHwId), PinHwId);
#endif
        /* Enalbe port pin output */
        PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_POER_ADDR32(PortHwId), PinHwId);
    }
}

/**
 * @brief This function sets the mode of a pin.
 *
 * @param PinConfigPtr Pointer to pin configuration.
 * @param PinHwId Hardware pin ID.
 * @param PortHwId Hardware port ID.
 *
 * @note If the pin's mode type is GPIO, the function `Port_Lld_InitGPIOMode()` will be called to set the GPIO relative register.
 */
PORT_FUNC LOCAL_INLINE void Port_Lld_InitMode(const Port_PinConfigType *PinConfigPtr, Port_HwPinType PinHwId, Port_HwPortType PortHwId)
{
    if (PORT_PIN_MODE_GPIO == PinConfigPtr->InitMode)
    {
        /* Config the relative register of GPIO mode */
        Port_Lld_InitGPIOMode(PinConfigPtr, PinHwId, PortHwId);
        /* Assign the PCR_MUX register */
        PORT_REG_ADDR32_AEARWRITE(PCTRL_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId),
                                  PCTRL_PCR_MUX_MASK,
                                  (uint32)PORT_PIN_MODE_GPIO << PCTRL_PCR_MUX_SHIFT);
    }
    else
    {
        /** [SWS_Port_00055]
         *  Set the port pin output latch to a default level (defined during configuration)
         *  before setting the port pin direction to output.
         */
        /* Config the init output level to low */
        PORT_REG_ADDR32_INDEX_CLEAR_BIT(GPIO_PDOR_ADDR32(PortHwId), PinHwId);
        /* Disable port pin input */
        PORT_REG_ADDR32_INDEX_CLEAR_BIT(GPIO_PIER_ADDR32(PortHwId), PinHwId);
        /* Disable port pin output */
        PORT_REG_ADDR32_INDEX_CLEAR_BIT(GPIO_POER_ADDR32(PortHwId), PinHwId);
        /* Assign the PCR_MUX register */
        PORT_REG_ADDR32_AEARWRITE(PCTRL_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId),
                                  PCTRL_PCR_MUX_MASK,
                                  (uint32)(PinConfigPtr->InitMode) << PCTRL_PCR_MUX_SHIFT);
    }
}

/*==================================================================================================
 *                                         GLOBAL FUNCTIONS
==================================================================================================*/
PORT_FUNC void Port_Lld_Init(const Port_ConfigType *ConfigPtr)
{
    Port_HwPortType PortHwId;
    Port_HwPinType PinHwId;
    uint8 PortAvailablePinsNumber;
    const Port_PinConfigType *PinConfigPtr;

    for (uint8 Idx = 0U; Idx < ConfigPtr->NumberOfGroup; ++Idx)
    {
        PortHwId = ConfigPtr->PortGroupConfigPtr[Idx].HwPortId;
        PinConfigPtr = ConfigPtr->PortGroupConfigPtr[Idx].PortPinConfigPtr;
        PortAvailablePinsNumber = ConfigPtr->PortGroupConfigPtr[Idx].NumberOfPortPins;
        for (uint8 PinConfigIndex = 0U; PinConfigIndex < PortAvailablePinsNumber; ++PinConfigIndex)
        {
            PinHwId = PinConfigPtr->HwPinId;
            /* Config the pull selection */
            Port_Lld_InitPull(PinConfigPtr->PullConfig, PCTRL_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId));
#if PORT_HAS_SLEW_RATE == STD_ON
            /* Config the slew rate */
            Port_Lld_InitSRE(PinConfigPtr->SlewRate, PCTRL_PCR_ADDR32((uint32)PortHwId, (uint32)PinHwId));
#endif
            /* Config the pin mode */
            Port_Lld_InitMode(PinConfigPtr, PinHwId, PortHwId);
            ++PinConfigPtr;
        }
    }
}

PORT_FUNC void Port_Lld_RefreshPortDirection(const Port_ConfigType *ConfigPtr)
{
    Port_HwPinType PinHwId;
    uint8 PortAvailablePinsNumber;
    const Port_PinConfigType *PinConfigPtr;

    for (Port_HwPortType PortHwId = 0;  PortHwId < ConfigPtr->NumberOfGroup; ++PortHwId)
    {
        PinConfigPtr = ConfigPtr->PortGroupConfigPtr[PortHwId].PortPinConfigPtr;
        PortAvailablePinsNumber = ConfigPtr->PortGroupConfigPtr[PortHwId].NumberOfPortPins;
        for (uint8 PinConfigIndex = 0; PinConfigIndex < PortAvailablePinsNumber; ++PinConfigIndex)
        {
            /* [SWS_Port_00061]*/
            if (PinConfigPtr->IsDirectionChangeable == PORT_PIN_DIRECTION_UNCHANGEABLE)
            {
                PinHwId = PinConfigPtr->HwPinId;
                /* Ensure the port pin not masked */
                SchM_Enter_Port_PORT_EXCLUSIVE_AREA_02();
                /* Config the direction */
                if (PORT_PIN_IN == PinConfigPtr->Direction) /* Direction = PORT_PIN_IN */
                {
                    /* Disable port pin output */
                    PORT_REG_ADDR32_INDEX_CLEAR_BIT(GPIO_POER_ADDR32(PortHwId), PinHwId);
                    /* Enalbe port pin input */
                    PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_PIER_ADDR32(PortHwId), PinHwId);
                }
                else
                {
#if (PORT_READBACK_ENABLE == STD_ON)
                    PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_PIER_ADDR32(PortHwId), PinHwId);
#else
                    /* Disable port pin input */
                    PORT_REG_ADDR32_INDEX_CLEAR_BIT(GPIO_PIER_ADDR32(PortHwId), PinHwId);
#endif
                    /* Enalbe port pin output */
                    PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_POER_ADDR32(PortHwId), PinHwId);
                }
                SchM_Exit_Port_PORT_EXCLUSIVE_AREA_02();
            }
            ++PinConfigPtr;
        }
    }
}

PORT_FUNC boolean Port_Lld_IsPinDireChangeable(const Port_ConfigType *ConfigPtr, Port_HwPortType Port, Port_HwPinType Pin)
{
    const Port_PinConfigType *PinConfigPtr;
    uint8 PortAvailablePinsNumber;
    boolean ReturnValue = FALSE;

    PinConfigPtr = ConfigPtr->PortGroupConfigPtr[Port].PortPinConfigPtr;
    PortAvailablePinsNumber =  ConfigPtr->PortGroupConfigPtr[Port].NumberOfPortPins;

    /* Find the port pin config ,ensure whether the port pin direction changeable */
    for (uint8 PinConfigIndex = 0; (PinConfigIndex < PortAvailablePinsNumber) && (Pin != PinConfigPtr->HwPinId); ++PinConfigIndex)
    {
        ++PinConfigPtr;
    }
    if (PORT_PIN_DIRECTION_CHANGEABLE == PinConfigPtr->IsDirectionChangeable)
    {
        ReturnValue = TRUE;
    }
    return ReturnValue;
}

PORT_FUNC boolean Port_Lld_IsPinModeChangeable(const Port_ConfigType *ConfigPtr, Port_HwPortType Port, Port_HwPinType Pin)
{
    const Port_PinConfigType *PinConfigPtr;
    uint8 PortAvailablePinsNumber;
    boolean ReturnValue = FALSE;

    PinConfigPtr = ConfigPtr->PortGroupConfigPtr[Port].PortPinConfigPtr;
    PortAvailablePinsNumber =  ConfigPtr->PortGroupConfigPtr[Port].NumberOfPortPins;

    /* Find the port pin config ,ensure whether the port pin direction changeable */
    for (uint8 PinConfigIndex = 0U; (PinConfigIndex < PortAvailablePinsNumber) && (Pin != PinConfigPtr->HwPinId); ++PinConfigIndex)
    {
        ++PinConfigPtr;
    }
    if (PORT_PIN_MODE_CHANGEABLE == PinConfigPtr->IsModeChangeable)
    {
        ReturnValue = TRUE;
    }
    return ReturnValue;
}

PORT_FUNC void Port_Lld_SetPinDirection(Port_HwPortType Port, Port_HwPinType Pin, Port_PinDirectionType Direction)
{
    if (Port_IsCorrectPortPinId(Port, Pin))
    {
        SchM_Enter_Port_PORT_EXCLUSIVE_AREA_00();
        if (PORT_PIN_IN == Direction)
        {
            /* Disable port pin output */
            PORT_REG_ADDR32_INDEX_CLEAR_BIT(GPIO_POER_ADDR32(Port), Pin);
            /* Enable port pin input */
            PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_PIER_ADDR32(Port), Pin);
        }
        else
        {
#if (PORT_READBACK_ENABLE == STD_ON)
            PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_PIER_ADDR32(Port), Pin);
#else
            /* Disable port pin input */
            PORT_REG_ADDR32_INDEX_CLEAR_BIT(GPIO_PIER_ADDR32(Port), Pin);
#endif
            /* Enable port pin output */
            PORT_REG_ADDR32_INDEX_SET_BIT(GPIO_POER_ADDR32(Port), Pin);
        }
        SchM_Exit_Port_PORT_EXCLUSIVE_AREA_00();
    }
}

PORT_FUNC void Port_Lld_SetPinMode(Port_HwPortType Port, Port_HwPinType Pin, Port_PinModeType Mode)
{
    SchM_Enter_Port_PORT_EXCLUSIVE_AREA_01();
    PORT_REG_ADDR32_AEARWRITE(PCTRL_PCR_ADDR32((uint32)Port, (uint32)Pin), PCTRL_PCR_MUX_MASK, PCTRL_PCR_MUX((uint32)Mode));
    SchM_Exit_Port_PORT_EXCLUSIVE_AREA_01();
}

#define PORT_STOP_SEC_CODE
#include "Port_MemMap.h"

#ifdef __cplusplus
}
#endif

/* End of file Port_Lld.c */

