/* ======================================================================
 *   Copyright (c) 2022 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     Dio.c
 *
 */

/*  ----------------------------------------------------------------------------
*  FILE DESCRIPTION
*  -----------------------------------------------------------------------------
*         File:  Dio.c
*    Component:  AM263x MCAL driver
*       Module:  DioDriver
*    Generator:  -
*
*   Description: This DIO Driver provides services for
*                reading and writing to/from
*                        -   DIO Channels (Pins)
*                        -   DIO Ports
*                        -   DIO Channel Groups
*
*                This module works on pins and ports which are configured by the
*                PORT driver for this purpose.For this reason there is no 
*                configuration and initialization of this port structure in the 
*                DIO Driver.
*------------------------------------------------------------------------------*/
#define DIO_SOURCE

/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */

/* Requirements : SWS_Dio_00117 */
#include "Dio.h"
#include "Dio_priv.h"
/* Requirements : SWS_Dio_00117 */
#include "SchM_Dio.h"


/* Requirements : SWS_Dio_00117, SWS_Dio_00194 */
#if (DIO_CFG_DEV_ERROR_DETECT == STD_ON)
#include "Det.h"
#endif

#define DIO_START_SEC_CODE
/* Requirements : SWS_Dio_00117 */
 #include "Dio_MemMap.h"
#include "sys_common.h"
#define DIO_STOP_SEC_CODE
#include "Dio_MemMap.h"


/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */

/* AUTOSAR version information check has to match definition in header file */
#if ((DIO_AR_RELEASE_MAJOR_VERSION != (0x04U)) || \
    (DIO_AR_RELEASE_MINOR_VERSION != (0x03U)) ||  \
    (DIO_AR_RELEASE_REVISION_VERSION != (0x01U)))
  #error "AUTOSAR Version Numbers of Dio are different"
#endif

#if ((DIO_SW_MAJOR_VERSION != (0U)) || (DIO_SW_MINOR_VERSION != (9U)))
  #error "Version numbers of Dio.c and Dio.h are inconsistent!"
#endif

#if ((DIO_CFG_MAJOR_VERSION != (0U)) || (DIO_CFG_MINOR_VERSION != (9U)))
  #error "Version numbers of Dio.c and Dio_Cfg.h are inconsistent!"
#endif

#define DIO_READ_PORT_MASK              (~((uint32) 0U))

/* To Return 0 when Improper ID is passed to Read functions */
#define DIO_RETURN_ZERO                 (0U)

/* ========================================================================== */
/*                         Structure Declarations                             */
/* ========================================================================== */

/* None */

/* ========================================================================== */
/*                          Function Declarations                             */
/* ========================================================================== */

static Dio_LevelType Dio_pinRead(uint32 baseAddr,
                                 uint32 pinNumber);
static void Dio_getGPIORegInfo(Dio_ChannelType channelId,
                               uint32         *baseAddr,
                               uint32         *pinNumber);
static void Dio_ChkDirWritePin(Dio_ChannelType ChannelId, Dio_LevelType Level);
static void Dio_pinWrite(uint32        baseAdd,
                           uint32        pinNumber,
                           Dio_LevelType level);
#if defined(DIO_WRITE_CHANNEL_EVENT_ID ) && (STD_ON == \
                                                          DIO_CFG_SUPPORT_GIO)
static Dio_StatusType Dio_checkIO(Dio_ChannelType channelId,
                                  Dio_LevelType   level);
#endif
#if (STD_ON == DIO_CFG_SUPPORT_GIO)
static void DIO_gioGetDirection(Dio_ChannelType    channelId,
                                Dio_DirectionType *gioDirection);
#endif
#if (STD_ON == DIO_CFG_SUPPORT_GIO)
static void Dio_gioReadPort(uint32 portId, uint32 *portVal);
static void Dio_gioWritePort(uint32 portId, uint32 Level);
#endif

#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
static Std_ReturnType Dio_checkChannelValidity(Dio_ChannelType channelId);
static inline void Dio_reportDetError(uint8 apiId, uint8 errorId);
#endif  /* #if (STD_ON == DIO_CFG_DEV_ERROR_DETECT) */

#define DIO_START_SEC_CODE
#include "Dio_MemMap.h"



#define DIO_STOP_SEC_CODE
#include "Dio_MemMap.h"

/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */
/* None */

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */
#define DIO_START_SEC_CODE
#include "Dio_MemMap.h"

#if (STD_ON == DIO_CFG_VERSION_INFO_API)
/* Design : DIO_DesignId_012 */
/* Requirements : SWS_Dio_00139, SWS_Dio_00131 */
FUNC(void, DIO_CODE)  Dio_GetVersionInfo(
    P2VAR(Std_VersionInfoType, AUTOMATIC, DIO_APPL_DATA) versioninfo)
{
#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
    if (NULL_PTR == versioninfo)
    {
        /* Requirements : SWS_Dio_00189 */
        /* Sub Requirements ID : DIO188 */
        Dio_reportDetError(DIO_SID_GET_VERSION_INFO, DIO_E_PARAM_POINTER);
    }
    else
#endif
    {
        versioninfo->vendorID         = (uint16) DIO_VENDOR_ID;
        versioninfo->moduleID         = (uint16) DIO_MODULE_ID;
        versioninfo->sw_major_version = (uint8)  DIO_SW_MAJOR_VERSION;
        versioninfo->sw_minor_version = (uint8)  DIO_SW_MINOR_VERSION;
        versioninfo->sw_patch_version = (uint8)  DIO_SW_PATCH_VERSION;
    }

    return;
}
#endif  /*(STD_ON == DIO_CFG_VERSION_INFO_API)*/

/* Design : DIO_DesignId_005 */
/* Requirements : SWS_Dio_00133 */
FUNC(Dio_LevelType, DIO_CODE) Dio_ReadChannel(Dio_ChannelType ChannelId)
{
    uint32        baseAddr;
    uint32        pinNumber;
    /* Requirements : SWS_Dio_00118 */
    Dio_LevelType chLevelVal = (Dio_LevelType) DIO_RETURN_ZERO;

#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
    /* Requirements : SWS_Dio_00074 */
    if ((((Std_ReturnType) E_OK) != Dio_checkChannelValidity(ChannelId)))
    {
        Dio_reportDetError(
            DIO_SID_READ_CHANNEL, DIO_E_PARAM_INVALID_CHANNEL_ID);
    }
    else
#endif
    {
        Dio_getGPIORegInfo(ChannelId, &baseAddr, &pinNumber);
        /* Requirements : SWS_Dio_00128, SWS_Dio_00012, SWS_Dio_00011,
         * SWS_Dio_00083, SWS_Dio_00027 */
        chLevelVal = Dio_pinRead(baseAddr, pinNumber);
    }

    return (chLevelVal);
}

/* Design : DIO_DesignId_006 */
/* Requirements : SWS_Dio_00134, SWS_Dio_00109 */
FUNC(void, DIO_CODE) Dio_WriteChannel(Dio_ChannelType ChannelId,
                                      Dio_LevelType Level)
{
    Dio_DirectionType gioDirection;

#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
    /* Requirements : SWS_Dio_00074 */
    if ((((Std_ReturnType) E_OK) != Dio_checkChannelValidity(ChannelId)))
    {
        /* ChannelId is NOT valid */
        Dio_reportDetError(
            DIO_SID_WRITE_CHANNEL, DIO_E_PARAM_INVALID_CHANNEL_ID);
    }
    /* Requirements : SWS_Dio_00119 */
    else
#endif
    {
        Dio_ChkDirWritePin(ChannelId, Level);
#if defined(DIO_WRITE_CHANNEL_EVENT_ID)
        /*If the channel is not configured as input, IO check*/
        DIO_gioGetDirection(ChannelId, &gioDirection);
        if (DIO_DIR_OUTPUT == gioDirection)
        {
            if (((Std_ReturnType) E_NOT_OK) == Dio_checkIO(ChannelId, Level))
            {
                /* Requirements : SWS_Dio_00131, SWS_Dio_00140 */
                (void)Dem_SetEventStatus(
                    (Dem_EventIdType) DIO_WRITE_CHANNEL_EVENT_ID,
                    (uint8) DEM_EVENT_STATUS_FAILED);
            }
        }
#endif
    }

    return;
}


/* Design : DIO_DesignId_007 */
/* Requirements : SWS_Dio_00135, SWS_Dio_00104 */
FUNC(Dio_PortLevelType, DIO_CODE) Dio_ReadPort(Dio_PortType PortId)
{
    /* Requirements : SWS_Dio_00118, SWS_Dio_00031 */
    Dio_PortLevelType portVal = (Dio_PortLevelType) DIO_RETURN_ZERO;

#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
    /* Requirements : SWS_Dio_00075 */
    if (0U == (DIO_CFG_ENABLED_PORT_MASK & (1U << PortId)))
    {
        /* PortId is NOT valid */
        Dio_reportDetError(
            DIO_SID_READ_PORT, DIO_E_PARAM_INVALID_PORT_ID);
    }
    else
#endif
    {
        /* Requirements : SWS_Dio_00031 */
        Dio_gioReadPort(PortId, &portVal);
    }

    return (portVal);
}


/* Design : DIO_DesignId_008 */
/* Requirements : SWS_Dio_00136 */
FUNC(void, DIO_CODE) Dio_WritePort(Dio_PortType PortId, Dio_PortLevelType Level)
{
#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
    /* Requirements : SWS_Dio_00075 */
    if (0U == (DIO_CFG_ENABLED_PORT_MASK & (1U << PortId)))
    {
        /* PortId is NOT valid  */
        Dio_reportDetError(
            DIO_SID_WRITE_PORT, DIO_E_PARAM_INVALID_PORT_ID);
    }
    /* Requirements : SWS_Dio_00119 */
    else
#endif
    {
        /* Requirements : SWS_Dio_00005, SWS_Dio_00060 */
        SchM_Enter_Dio_DIO_EXCLUSIVE_AREA_0();
        /* Requirements : SWS_Dio_00034 */
        Dio_gioWritePort(PortId, Level);
        /* Requirements : SWS_Dio_00005, SWS_Dio_00060 */
        SchM_Exit_Dio_DIO_EXCLUSIVE_AREA_0();
    }

    return;
}



/* Design : DIO_DesignId_009 */
/* Requirements : SWS_Dio_00137 */
FUNC(Dio_PortLevelType, DIO_CODE)
       Dio_ReadChannelGroup(Dio_ChannelGroupRefType ChannelGroupIdPtr)
{
    /* Requirements : SWS_Dio_00118 */
    Dio_PortLevelType portLevelVal = (Dio_PortLevelType) DIO_RETURN_ZERO;

#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
    /* Requirements : SWS_Dio_00114 */
    if (NULL_PTR == ChannelGroupIdPtr)
    {
        /* null pointer is referenced by the parameter */
        Dio_reportDetError(
            DIO_SID_READ_CHL_GROUP, DIO_E_PARAM_INVALID_GROUP);
    }
    else
#endif
    {
        /* Requirements : SWS_Dio_00014 */
        /* Requirements : SWS_Dio_00031 */
        Dio_gioReadPort(ChannelGroupIdPtr->port, &portLevelVal);
        /* Requirements : SWS_Dio_00037, SWS_Dio_00092 */
        portLevelVal &= ChannelGroupIdPtr->mask;
        /* Requirements : SWS_Dio_00093 */
        portLevelVal >>= ChannelGroupIdPtr->offset;
    }

    return (portLevelVal);
}

/* Design : DIO_DesignId_010 */
/* Requirements : SWS_Dio_00138 */
FUNC(void, DIO_CODE) Dio_WriteChannelGroup(
    Dio_ChannelGroupRefType ChannelGroupIdPtr, Dio_PortLevelType Level)
{
    uint32 portLevelVal;
    uint32 newValue;

#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
    /* Requirements : SWS_Dio_00114 */
    if (NULL_PTR == ChannelGroupIdPtr)
    {
        /* null pointer is referenced by the parameter */
        Dio_reportDetError(
            DIO_SID_WRITE_CHL_GROUP, DIO_E_PARAM_INVALID_GROUP);
    }
    /* Requirements : SWS_Dio_00119 */
    else
#endif
    {
        uint8 groupoffset;

        /* Requirements : SWS_Dio_00005, SWS_Dio_00060 */
        SchM_Enter_Dio_DIO_EXCLUSIVE_AREA_0();

        groupoffset = ChannelGroupIdPtr->offset;
        /* Requirements : SWS_Dio_00039, SWS_Dio_00091 */
        newValue = ((Level << groupoffset) & ChannelGroupIdPtr->mask);
        Dio_gioReadPort(ChannelGroupIdPtr->port, &portLevelVal);
        /* Requirements : SWS_Dio_00008, SWS_Dio_00090 */
        /* clear old value of this channel group and apply new levels */
        portLevelVal &= (~ChannelGroupIdPtr->mask);
        portLevelVal |= newValue;
        Dio_gioWritePort(ChannelGroupIdPtr->port, portLevelVal);

        /* Requirements : SWS_Dio_00005, SWS_Dio_00060 */
        SchM_Exit_Dio_DIO_EXCLUSIVE_AREA_0();
    }

    return;
}


#if (STD_ON == DIO_CFG_FLIP_CHANNEL_API)
/* Design : DIO_DesignId_011 */
/* Requirements : SWS_Dio_00190, SWS_Dio_00192, SWS_Dio_00193 */
FUNC(Dio_LevelType, DIO_CODE) Dio_FlipChannel(Dio_ChannelType ChannelId)
{
    Dio_LevelType channelVal = (Dio_LevelType) DIO_RETURN_ZERO;

#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
    if ((((Std_ReturnType) E_OK) != Dio_checkChannelValidity(ChannelId)))
    {
        /* ChannelId is NOT valid */
        Dio_reportDetError(
            DIO_SID_FLIP_CHANNEL, DIO_E_PARAM_INVALID_CHANNEL_ID);
    }
    /* Requirements : SWS_Dio_00119 */
    else
#endif
    {
        Dio_DirectionType gioDirection;
        uint32 baseAddr, pinNumber;

        Dio_getGPIORegInfo(ChannelId, &baseAddr, &pinNumber);
        /* Requirements : SWS_Dio_00192 */
        DIO_gioGetDirection(ChannelId, &gioDirection);
        if (DIO_DIR_OUTPUT == gioDirection)
        {
            Dio_GpioBitToggle((gpioPORT_t*)baseAddr, pinNumber);
            channelVal = Dio_pinRead(baseAddr, pinNumber);
        }
        else
        {
            Dio_LevelType     flipValue;
            /* Requirements : SWS_Dio_00191, SWS_Dio_00128, SWS_Dio_00012,
             * SWS_Dio_00011, SWS_Dio_00083, SWS_Dio_00027 */
            /*Read the channel value*/
            flipValue = Dio_pinRead(baseAddr, pinNumber);
            /* Input channel can't change value */
            /* Requirements : SWS_Dio_00193 */
            channelVal = flipValue;
        }
    }
    return (channelVal);
}
#endif /*(STD_ON == DIO_CFG_FLIP_CHANNEL_API)*/


/* Requirements : SWS_Dio_00105 */
static void Dio_gioWritePort(uint32 portId, uint32 Level)
{
    uint32 baseAddr;
    uint32 retVal = 0U;
    baseAddr      = Dio_getGPIOPortAddr((uint8) portId);
    /* Requirements : SWS_Dio_00004, SWS_Dio_00035, SWS_Dio_00108  SWS_Dio_00007*/
    /* Requirements : SWS_Dio_00040 */
    /* Requirements : SWS_Dio_00024, SWS_Dio_00108,  SWS_Dio_00007*/
    retVal = Dio_GpioPortWrite((gpioPORT_t *)baseAddr, Level);

    if (retVal == 0U)
    {
#if defined(DIO_WRITE_PORT_EVENT_ID)
        Dio_PortLevelType portVal;
        uint32 portDir;

        Dio_gioReadPort(portId, &portVal);
        portDir = Dio_GpioGetPortDirection((const gpioPORT_t *)baseAddr);
        if ((Level & portDir) != (portVal & portDir))
        {
            /* Requirements : SWS_Dio_00131, SWS_Dio_00140 */
            (void)Dem_SetEventStatus((Dem_EventIdType) DIO_WRITE_PORT_EVENT_ID,
                                  (uint8) DEM_EVENT_STATUS_FAILED);
        }
#endif
    }
}



static void Dio_gioReadPort(uint32 portId, uint32 *portVal)
{
    uint32 baseAddr;

    baseAddr = Dio_getGPIOPortAddr((uint8) portId);
    /* Requirements : SWS_Dio_00013 */
    *portVal = (uint32)Dio_GpioGetPort((const gpioPORT_t *)baseAddr);
}

/* Requirements : SWS_Dio_00051 */
static void Dio_pinWrite(uint32        baseAdd,
                          uint32        pinNumber,
                          Dio_LevelType level)
{
    /* Requirements : SWS_Dio_00089, SWS_Dio_00006 */
    if (((Dio_LevelType) STD_HIGH) == level)
    {
        Dio_GpioSetBit(( gpioPORT_t *)baseAdd, pinNumber,STD_HIGH );
    }
    else
    {
        Dio_GpioSetBit(( gpioPORT_t *)baseAdd, pinNumber,STD_LOW );
    }

    return;
}
#if defined(DIO_WRITE_CHANNEL_EVENT_ID ) \
    && (STD_ON == DIO_CFG_SUPPORT_GIO)
static Dio_StatusType Dio_checkIO(Dio_ChannelType channelId,
                                  Dio_LevelType   level)
{
    Dio_LevelType  newLevel;
    Dio_StatusType Status = (Std_ReturnType) E_OK;

    newLevel = Dio_ReadChannel(channelId);
    if (level != newLevel)
    {
        Status = (Std_ReturnType) E_NOT_OK;
    }

    return (Status);
}

#endif


#if (STD_ON == DIO_CFG_SUPPORT_GIO)
static void DIO_gioGetDirection(Dio_ChannelType    channelId,
                                Dio_DirectionType *gioDirection)
{
    uint32 baseAddr;
    uint32 pinNumber;
    uint32 regval;

    Dio_getGPIORegInfo(channelId, &baseAddr, &pinNumber);

    regval = Dio_GpioGetDirection((const gpioPORT_t*)baseAddr, pinNumber);
    if ((uint32) 1U != regval)
    {
        *gioDirection = DIO_DIR_INPUT ;
    }
    else
    {
        *gioDirection = DIO_DIR_OUTPUT;
    }
}

#endif


/**
 *
 */
static Dio_LevelType Dio_pinRead(uint32 baseAddr,
                                 uint32 pinNumber)
{
    uint32        logiclvl;
    Dio_LevelType lvlret;

    /* Requirements : SWS_Dio_00051, SWS_Dio_00089 */

    logiclvl = Dio_GpioGetLogicLvl((const gpioPORT_t *) baseAddr, pinNumber);

    /* Requirements : SWS_Dio_00023 */
    if (logiclvl != 0U)
    {
        lvlret = (Dio_LevelType) STD_HIGH;
    }
    else
    {
        lvlret = (Dio_LevelType) STD_LOW;
    }

    return (lvlret);
}


/**
 * @fn
 */

static void Dio_getGPIORegInfo(Dio_ChannelType channelId,
                               uint32         *baseAddr,
                               uint32         *pinNumber)
{
    uint8 gpioRegNum;
    uint8 gpioPinNum;

    gpioRegNum = (uint8) (channelId / DIO_PORT_WIDTH);
    gpioPinNum = (uint8) (channelId % DIO_PORT_WIDTH);
    *baseAddr  = Dio_getGPIOPortAddr(gpioRegNum);
    *pinNumber = gpioPinNum;
}



#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)

static Std_ReturnType Dio_checkChannelValidity(Dio_ChannelType channelId)
{
    Std_ReturnType retVal = (Std_ReturnType) E_NOT_OK;
    uint32          gpioRegNum, gpioPinNum;
    boolean        isChannelEnabled = (boolean) FALSE;

    gpioRegNum = (uint32) (channelId / DIO_PORT_WIDTH);
    gpioPinNum = (uint32) (channelId % DIO_PORT_WIDTH);
    if (gpioRegNum < DIO_CFG_MAX_NO_OF_PORTS)
    {
        if (0U != ((uint32)(gDioConfigValidChannelMask[gpioRegNum])&(1U << gpioPinNum)))
        {
            isChannelEnabled = (boolean) TRUE;
        }

        if ((((boolean) TRUE) == isChannelEnabled))
        {
            retVal = (Std_ReturnType) E_OK;
        }
    }
    return (retVal);
}

#endif

static void Dio_ChkDirWritePin(Dio_ChannelType ChannelId, Dio_LevelType Level)
{
    uint32 baseAddr;
    uint32 pinNumber;
    Dio_DirectionType gioDirection;

    Dio_getGPIORegInfo(ChannelId, &baseAddr, &pinNumber);
    /* Requirements : SWS_Dio_00005,SWS_Dio_00060 */
    SchM_Enter_Dio_DIO_EXCLUSIVE_AREA_0();

    /* Requirements : SWS_Dio_00128, SWS_Dio_00064, SWS_Dio_00070 */
    /* Requirements : SWS_Dio_00028 */
    DIO_gioGetDirection(ChannelId, &gioDirection);
    if (DIO_DIR_OUTPUT == gioDirection)
    {
        /* Write to Channel if direction is OUTPUT. */
        Dio_pinWrite(baseAddr, pinNumber, Level);
    }
    /* Requirements : SWS_Dio_00029, SWS_Dio_00079 */
    /* else case for input channel : Do Nothing */
    /* Requirements : SWS_Dio_00005 */
    SchM_Exit_Dio_DIO_EXCLUSIVE_AREA_0();

    return ;
}

#if (STD_ON == DIO_CFG_DEV_ERROR_DETECT)
static inline void Dio_reportDetError(uint8 apiId, uint8 errorId)
{
    /* Requirements : SWS_Dio_00140 */
    (void) Det_ReportError(DIO_MODULE_ID, 0, apiId, errorId);
    return;
}
#endif  /* #if (STD_ON == DIO_CFG_DEV_ERROR_DETECT) */


#define DIO_STOP_SEC_CODE
#include "Dio_MemMap.h"



