/* ======================================================================
 *   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     Wdg.c
 *
 *  \brief    This file contains implementation of the Internal Watchdog MCAL
 *            driver
 */
#define WDG_SOURCE

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

#include "Wdg.h"
#include "Dem.h"
#include "Wdg_priv.h"
#include "hw_ctrl_core.h"

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

/* AUTOSAR version information check has to match definition in header file */
#if ((WDG_AR_RELEASE_MAJOR_VERSION != (0x04U)) || \
    (WDG_AR_RELEASE_MINOR_VERSION != (0x03U)) ||  \
    (WDG_AR_RELEASE_REVISION_VERSION != (0x01U)))
  #error "WDG: AUTOSAR Version Numbers of WDG are different"
#endif

/* vendor specific version information is BCD coded */
#if ((WDG_SW_MAJOR_VERSION != (0U)) || (WDG_SW_MINOR_VERSION != (9U)))
  #error "Version numbers of Wdg.c and Wdg.h are inconsistent!"
#endif

#if ((WDG_CFG_MAJOR_VERSION != (0U)) || (WDG_CFG_MINOR_VERSION != (9U)))
   #error "Version numbers of Wdg.c and Wdg_Cfg.h are inconsistent!"
#endif

#define WDG_MHZ_TO_KHZ                  ((uint32) 1000)

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

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

/* None */

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

#define WDG_START_SEC_VAR_NO_INIT_UNSPECIFIED
#include "Wdg_MemMap.h"

static VAR(Wdg_DriverObjType, WDG_VAR_ZERO_INIT) gWdgDrvObj;

#define WDG_STOP_SEC_VAR_NO_INIT_UNSPECIFIED
#include "Wdg_MemMap.h"

#define WDG_START_SEC_VAR_INIT_UNSPECIFIED
#include "Wdg_MemMap.h"

#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
/** \brief WDG driver status */
volatile VAR(Wdg_StatusType, WDG_VAR_ZERO_INIT) gWdgDrvStatus = WDG_UNINIT;
#endif
#define WDG_STOP_SEC_VAR_INIT_UNSPECIFIED
#include "Wdg_MemMap.h"

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */
#define WDG_START_SEC_CODE
#include "Wdg_MemMap.h"

#if (STD_ON == WDG_CFG_GET_VERSION_INFO_API)
/* Design: WDG_DesignId_010*/
/* Requirements : SWS_Wdg_00109, SWS_Wdg_00174, SWS_Wdg_00111 */
FUNC(void, WDG_CODE) Wdg_GetVersionInfo(Std_VersionInfoType * versioninfo)
{
#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
    if (NULL_PTR == versioninfo)
    {
        (void) Wdg_reportDetError(WDG_API_GET_VERSION_INFO,
                                  WDG_E_PARAM_POINTER);
    }
    else
#endif
    {
        versioninfo->vendorID         = (WDG_VENDOR_ID);
        versioninfo->moduleID         = (WDG_MODULE_ID);
        versioninfo->sw_major_version = (uint8) (WDG_SW_MAJOR_VERSION);
        versioninfo->sw_minor_version = (uint8) (WDG_SW_MINOR_VERSION);
        versioninfo->sw_patch_version = (uint8) (WDG_SW_PATCH_VERSION);
    }

    return;
}
#endif  /* STD_ON == WDG_CFG_GET_VERSION_INFO_API */

/* Design: WDG_DesignId_007 */
/*
 * *Requirements : SWS_Wdg_00106, SWS_Wdg_00001, SWS_Wdg_00100, SWS_Wdg_00101,
 * SWS_Wdg_00025, SWS_Wdg_00173, SWS_Wdg_00090, SWS_Wdg_00019, SWS_Wdg_00086,
 * SWS_Wdg_00034, SWS_Wdg_00161
 */
FUNC(void, WDG_CODE) Wdg_Init(
    P2CONST(Wdg_ConfigType, AUTOMATIC, WDG_APPL_CONST) ConfigPtr)
{
    Std_ReturnType retVal;

#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
    if (NULL_PTR == ConfigPtr)
    {
        (void) Wdg_reportDetError(WDG_API_INIT, WDG_E_PARAM_POINTER);
    }
    else
#endif
    {
#if (WDG_CFG_DISABLE_ALLOWED != STD_ON)
        if (WDGIF_OFF_MODE == ConfigPtr->defaultMode)
        {
            Wdg_reportDemError(WDG_E_DISABLE_REJECTED,
                                  DEM_EVENT_STATUS_FAILED);
        }
        else
#endif
        {
            retVal = Wdg_platformInit(ConfigPtr);
            if (((Std_ReturnType) E_OK) != retVal)
            {
                /* Mode not supported */
                Wdg_reportDemError(WDG_E_MODE_FAILED,
                                      DEM_EVENT_STATUS_FAILED);
            }
            else
            {
                Wdg_reportDemError(WDG_E_MODE_FAILED,
                                      DEM_EVENT_STATUS_PASSED);

                gWdgDrvObj.baseAddr = Wdg_getWdgBaseAddr(
                    ConfigPtr->instanceId);
                /* Set Initial Trigger Timeout */
                gWdgDrvObj.timeOutCounter = ConfigPtr->initialTimeOut;
                /* Save initial watchdog counter value */
                gWdgDrvObj.counterRef =
                    Wdg_getCurrentDownCounter(gWdgDrvObj.baseAddr);
                /* Enable DWD */
                Wdg_counterEnable(gWdgDrvObj.baseAddr);
#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
                gWdgDrvStatus = WDG_IDLE;
#endif

            }
        }
    }

    return;
}

/* Design: WDG_DesignId_008, WDG_DesignId_004 */
/*
 * *Requirements : SWS_Wdg_00107, SWS_Wdg_00051,
 * SWS_Wdg_00103, SWS_Wdg_00016, SWS_Wdg_00026, SWS_Wdg_00091, SWS_Wdg_00092,
 * SWS_Wdg_00017, SWS_Wdg_00018, SWS_Wdg_00180, SWS_Wdg_00181, SWS_Wdg_00182,
 */
 
FUNC(Std_ReturnType, WDG_CODE) Wdg_SetMode(WdgIf_ModeType Mode)
{
    Std_ReturnType retVal = (Std_ReturnType) E_NOT_OK;

#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
    if (WDG_IDLE != gWdgDrvStatus)
    {
        (void) Wdg_reportDetError(WDG_API_SET_MODE, WDG_E_DRIVER_STATE);
    }
    else if (Mode > WDGIF_FAST_MODE)
    {
        (void) Wdg_reportDetError(WDG_API_SET_MODE, WDG_E_PARAM_MODE);
    }
    else
#endif
    {
#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
        /* Set driver status as busy */
        gWdgDrvStatus = WDG_BUSY;
#endif
#if (WDG_CFG_DISABLE_ALLOWED != STD_ON)
        if (WDGIF_OFF_MODE == Mode)
        {
            Wdg_reportDemError(WDG_E_DISABLE_REJECTED,
                                  DEM_EVENT_STATUS_FAILED);
        }
        else
#endif
        {
            /* WDG dynamic mode switch not supported */
            Wdg_reportDemError(WDG_E_MODE_FAILED, DEM_EVENT_STATUS_FAILED);
        }

#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
        /* Set driver status as idle */
        gWdgDrvStatus = WDG_IDLE;
#endif
    }

    return (retVal);
}

/* Design: WDG_DesignId_009 */
/* Requirements : SWS_Wdg_00155, SWS_Wdg_00136, SWS_Wdg_00138, SWS_Wdg_00139,
 * SWS_Wdg_00140, SWS_Wdg_00146 */
FUNC(void, WDG_CODE) Wdg_SetTriggerCondition(uint16 timeout)
{
#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
    if ((uint16)WDG_CFG_MAX_TIMEOUT < (uint16)timeout)
    {
        /* API is being called with an invalid timeout value */
        (void) Wdg_reportDetError(WDG_SET_TRIGGER_CONDITION,
                                  WDG_E_PARAM_TIMEOUT);
    }
    else
#endif
    {
        /* Update trigger condition */
        gWdgDrvObj.timeOutCounter = timeout;
        if (0U == timeout)
        {
            /* Cause a WD reset if timeout is set as 0 */
            Wdg_generateSysReset(gWdgDrvObj.baseAddr);
        }
        else
        {
            /* Save current watchdog counter value */
            gWdgDrvObj.counterRef =
                Wdg_getCurrentDownCounter(gWdgDrvObj.baseAddr);
        }
    }

    return;
}

/* Design: WDG_DesignId_012 */
/*
 * Requirements : SWS_Wdg_00144, SWS_Wdg_00134, SWS_Wdg_00135, SWS_Wdg_00093,
 * SWS_Wdg_00094, SWS_Wdg_00095, SWS_Wdg_00035, SWS_Wdg_00052
 */

FUNC(void, WDG_CODE) Wdg_Trigger(void)
{
    uint32 elapsedTime;

#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
    /* Check if driver already initialized */
    if (WDG_IDLE != gWdgDrvStatus)
    {
        (void) Wdg_reportDetError(WDG_API_TRIGGER, WDG_E_DRIVER_STATE);
    }
    else
#endif
    {
#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
        /* Set driver status as busy */
        gWdgDrvStatus = WDG_BUSY;
#endif

        /* If Timeout counter hasn't expired, continue trigger routine */
        if (0U < gWdgDrvObj.timeOutCounter)
        {
            /* Counter reference shall not be smaller than current WDW
             * counter */
            elapsedTime =
                Wdg_getCurrentDownCounter(gWdgDrvObj.baseAddr);
            if (gWdgDrvObj.counterRef > elapsedTime)
            {
                /* Calculate elapsed time since last trigger invocation */
                elapsedTime =
                    gWdgDrvObj.counterRef - elapsedTime;
                /* Convert elapsed time to milliseconds */
                elapsedTime =
                    (elapsedTime / (WDG_CFG_RTI_FREQUENCY * WDG_MHZ_TO_KHZ));
                /* Elapsed time is smaller than the last received trigger
                 * condition */
                if (gWdgDrvObj.timeOutCounter > elapsedTime)
                {
                    /* Write key sequence to reload the DWD */
                    Wdg_service(gWdgDrvObj.baseAddr);
                    /* Decrement Counter */
                    gWdgDrvObj.timeOutCounter =
                        (gWdgDrvObj.timeOutCounter - elapsedTime);
                    /* update the counter reference */
                    gWdgDrvObj.counterRef = Wdg_getReloadValue(gWdgDrvObj.baseAddr);
                }
                else
                {
                    /* Elapsed time is larger than last received trigger
                     * condition, stop servicing watchdog */
                    gWdgDrvObj.timeOutCounter = 0U;
                }
            }
        }
#if (STD_ON == WDG_CFG_DEV_ERROR_DETECT)
        /* Set driver status as idle */
        gWdgDrvStatus = WDG_IDLE;
#endif
    }
}
#if (STD_ON == WDG_CFG_REGISTER_READBACK_API)
/******************************************************************************
 *  Wdg_RegisterReadback
 ******************************************************************************/
/*! \brief      This method is Wdg_RegisterReadback Api.
 *  \param[in]  Wdg_RegisterReadbackType       RegisterReadbackPtr
 *  \param[out] none
 *  \return     E_OK, E_NOT_OK
 *  \context    Function could be called from from task level
 ******************************************************************************/
/* DesignId : WDG_DesignId_011 */
/* Requirements : AUTORADAR_MCAL-239 */
FUNC(Std_ReturnType, WDG_CODE) Wdg_RegisterReadback
(
    P2VAR(Wdg_RegisterReadbackType, AUTOMATIC, WDG_APPL_DATA) RegisterReadbackPtr
)
{
   Std_ReturnType register_readback_return = E_NOT_OK;

   if (NULL_PTR == RegisterReadbackPtr)
   {
      /* Do nothing. Incorrect pointer. */
   }
   else
   {
      RegisterReadbackPtr->Wdg_RtiDwdCtrl     = ((rtiBASE_t*)gWdgDrvObj.baseAddr)->DWDCTRL;
      RegisterReadbackPtr->Wdg_RtiDwdprld     = ((rtiBASE_t*)gWdgDrvObj.baseAddr)->DWDPRLD;
      RegisterReadbackPtr->Wdg_RtiWdStatus    = ((rtiBASE_t*)gWdgDrvObj.baseAddr)->WDSTATUS;
      RegisterReadbackPtr->Wdg_RtiWdKey       = ((rtiBASE_t*)gWdgDrvObj.baseAddr)->WDKEY;
      RegisterReadbackPtr->Wdg_RtiWwdRxnCtrl  = ((rtiBASE_t*)gWdgDrvObj.baseAddr)->WWDRXNCTRL;
      RegisterReadbackPtr->Wdg_RtiWwdSizeCtrl = ((rtiBASE_t*)gWdgDrvObj.baseAddr)->WWDSIZECTRL;

      register_readback_return = E_OK;
   }

   return(register_readback_return);
}
#endif /*STD_ON == WDG_REGISTER_READBACK_API*/


void Wdg_reportDemError(Dem_EventIdType     eventId,
                        Dem_EventStatusType eventStatus)
{
    
#ifdef MCAL_DYNAMIC_BUILD
    Dem_ReportErrorStatus(eventId,eventStatus);
#else                                   
    if(eventId == WDG_E_DISABLE_REJECTED){

        if (WDG_E_DISABLE_REJECTED != WDG_DEM_NO_EVENT )
        {
           /* WDG can't be disabled */
             (void)Dem_SetEventStatus(eventId, eventStatus);
        }
        else
        {
        /* Disable Rejected error not assigned to any DEM event */
        }

    }

    if(eventId == WDG_E_MODE_FAILED){
      if (WDG_E_MODE_FAILED != WDG_DEM_NO_EVENT){
          /* WDG can't be disabled */
          (void)Dem_SetEventStatus(eventId, eventStatus);
      }
      else{
        /* Disable mode failed error not assigned to any DEM event */
      }
    }
#endif     

    return;
}

#define WDG_STOP_SEC_CODE
#include "Wdg_MemMap.h"
