/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include "reset_drv.h"
#include "rcm_reg.h"
#include "pcc0_reg.h"
#include "pcc1_reg.h"
#include "../cpu/cpu_drv.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/

#define RESET_RCM_UNLOCK (0x52434D4Bu)
#define RESET_PCC_UNLOCK (0x55534552u)

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*******************************************************************************
 * the globals
 ******************************************************************************/

/*******************************************************************************
 * the functions
 ******************************************************************************/

void ResetDrv_Init(ResetDrvType *obj, struct _RcmRegType_ *rcmReg, struct _Pcc1RegType_ *pcc1Reg, struct _Pcc0RegType_ *pcc0Reg)
{
    obj->rcmReg  = rcmReg;
    obj->pcc1Reg = pcc1Reg;
    obj->pcc0Reg = pcc0Reg;
}

void ResetDrv_GetDefaultConfig(ResetDrv_ConfigType *userConfig)
{
    userConfig->enableExtFlt         = false;
    userConfig->extFltLen            = 16;
    userConfig->enableResetInterrupt = false;
    userConfig->delay                = RESETDRV_RESETDELAY_514LPOCYCLES;
}

void ResetDrv_Configure(ResetDrvType *obj, const ResetDrv_ConfigType *userConfig)
{
    if((userConfig->extFltLen > 32) || (userConfig->extFltLen < 1))
    {
        return;
    }
    if(userConfig->delay > RESETDRV_RESETDELAY_514LPOCYCLES)
    {
        return;
    }

    RcmReg_SetKey(obj->rcmReg, RESET_RCM_UNLOCK); /* Unlock RCM */

    RcmReg_SetExtRstFltEn(obj->rcmReg, userConfig->enableExtFlt);
    RcmReg_SetExtRstFltLen(obj->rcmReg, (uint8_t)(userConfig->extFltLen - 1));
    RcmReg_SetRstCtrlGlobalIE(obj->rcmReg, userConfig->enableResetInterrupt);
    RcmReg_SetRstCtrlRstDelay(obj->rcmReg, userConfig->delay);

    RcmReg_SetKey(obj->rcmReg, 0); /* Lock RCM again */
}

bool ResetDrv_IsResetCaused(const ResetDrvType *obj, ResetDrv_SourceType source)
{
    bool retval = false;
    if(source >= RESETDRV_SOURCE_NUM)
    {
        return retval;
    }
    /* polyspace-begin DEFECT:DEAD_CODE [No action planned:low] "still keep default though it's not reachable" */
    switch(source)
    {
    case RESETDRV_SOURCE_WDG:
        retval = RcmReg_GetRstFlagWdg(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_SYS:
        retval = RcmReg_GetRstFlagSys(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_LOCKUP:
        retval = RcmReg_GetRstFlagLockup(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_PIN:
        retval = RcmReg_GetRstFlagPin(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_MDMAP:
        retval = RcmReg_GetRstFlagMdmap(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_PLL_LOSS:
        retval = RcmReg_GetRstFlagPllLoss(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_CMUP:
        retval = RcmReg_GetRstFlagCmuPll(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_CMUS:
        retval = RcmReg_GetRstFlagCmuSosc(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_FHU:
        retval = RcmReg_GetRstFlagFhu(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_STDBY_EXIT:
        retval = RcmReg_GetRstFlagStdbyExit(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_FLS_INITIAL:
        retval = RcmReg_GetRstFlagFlsInitial(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_LVR:
        retval = RcmReg_GetRstFlagLvr(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_POR:
        retval = RcmReg_GetRstFlagPor(obj->rcmReg) == 1;
        break;
    default:
        /* Should not get here */
        break;
    }
    /* polyspace-end DEFECT:DEAD_CODE [No action planned:low] "still keep default though it's not reachable" */
    return retval;
}

bool ResetDrv_IsResetSticky(const ResetDrvType *obj, ResetDrv_SourceType source)
{
    bool retval = false;
    if(source >= RESETDRV_SOURCE_NUM)
    {
        return retval;
    }
    /* polyspace-begin DEFECT:DEAD_CODE [No action planned:low] "still keep default though it's not reachable" */
    switch(source)
    {
    case RESETDRV_SOURCE_WDG:
        retval = RcmReg_GetRstSFlagWdg(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_SYS:
        retval = RcmReg_GetRstSFlagSys(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_LOCKUP:
        retval = RcmReg_GetRstSFlagLockup(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_PIN:
        retval = RcmReg_GetRstSFlagPin(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_MDMAP:
        retval = RcmReg_GetRstSFlagMdmap(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_PLL_LOSS:
        retval = RcmReg_GetRstSFlagPllLoss(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_CMUP:
        retval = RcmReg_GetRstSFlagCmuPll(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_CMUS:
        retval = RcmReg_GetRstSFlagCmuSosc(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_FHU:
        retval = RcmReg_GetRstSFlagFhu(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_STDBY_EXIT:
        retval = RcmReg_GetRstSFlagStdbyExit(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_FLS_INITIAL:
        retval = RcmReg_GetRstSFlagFlsInitial(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_LVR:
        retval = RcmReg_GetRstSFlagLvr(obj->rcmReg) == 1;
        break;
    case RESETDRV_SOURCE_POR:
        retval = RcmReg_GetRstSFlagPor(obj->rcmReg) == 1;
        break;
    default:
        /* Should not get here */
        break;
    }
    /* polyspace-end DEFECT:DEAD_CODE [No action planned:low] "still keep default though it's not reachable" */
    return retval;
}

void ResetDrv_ClrResetStickyFlag(ResetDrvType *obj, ResetDrv_SourceType source)
{
    if(source >= RESETDRV_SOURCE_NUM)
    {
        return;
    }
    RcmReg_SetKey(obj->rcmReg, RESET_RCM_UNLOCK); /* Unlock RCM */
    /* polyspace-begin DEFECT:DEAD_CODE [No action planned:low] "still keep default though it's not reachable" */
    switch(source)
    {
    case RESETDRV_SOURCE_WDG:
        RcmReg_SetRstSFlagWdg(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_SYS:
        RcmReg_SetRstSFlagSys(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_LOCKUP:
        RcmReg_SetRstSFlagLockup(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_PIN:
        RcmReg_SetRstSFlagPin(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_MDMAP:
        RcmReg_SetRstSFlagMdmap(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_PLL_LOSS:
        RcmReg_SetRstSFlagPllLoss(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_CMUP:
        RcmReg_SetRstSFlagCmuPll(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_CMUS:
        RcmReg_SetRstSFlagCmuSosc(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_FHU:
        RcmReg_SetRstSFlagFhu(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_STDBY_EXIT:
        RcmReg_SetRstSFlagStdbyExit(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_FLS_INITIAL:
        RcmReg_SetRstSFlagFlsInitial(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_LVR:
        RcmReg_SetRstSFlagLvr(obj->rcmReg, 1);
        break;
    case RESETDRV_SOURCE_POR:
        RcmReg_SetRstSFlagPor(obj->rcmReg, 1);
        break;
    default:
        /* Should not get here */
        break;
    }
    /* polyspace-end DEFECT:DEAD_CODE [No action planned:low] "still keep default though it's not reachable" */
    RcmReg_SetKey(obj->rcmReg, 0); /* Lock RCM again */
}

void ResetDrv_EnableResetInterrupt(ResetDrvType *obj, ResetDrv_ResetIntModuleType module, bool enable)
{
    RcmReg_SetKey(obj->rcmReg, RESET_RCM_UNLOCK); /* Unlock RCM */
    switch(module)
    {
    case RESETDRV_RESETINT_WDG:
        RcmReg_SetRstCtrlWdgRstIE(obj->rcmReg, enable ? 1 : 0);
        break;
    case RESETDRV_RESETINT_SYS:
        RcmReg_SetRstCtrlSysRstIE(obj->rcmReg, enable ? 1 : 0);
        break;
    case RESETDRV_RESETINT_PIN:
        RcmReg_SetRstCtrlPinRstIE(obj->rcmReg, enable ? 1 : 0);
        break;
    case RESETDRV_RESETINT_MDMAP:
        RcmReg_SetRstCtrlMdmapRstIE(obj->rcmReg, enable ? 1 : 0);
        break;
    case RESETDRV_RESETINT_PLL_LOSS:
        RcmReg_SetRstCtrlPllLossIE(obj->rcmReg, enable ? 1 : 0);
        break;
    case RESETDRV_RESETINT_CMUP:
        RcmReg_SetRstCtrlCmuPllIE(obj->rcmReg, enable ? 1 : 0);
        break;
    case RESETDRV_RESETINT_CMUS:
        RcmReg_SetRstCtrlCmuSoscIE(obj->rcmReg, enable ? 1 : 0);
        break;
    default:
        /* Should not get here */
        break;
    }
    RcmReg_SetKey(obj->rcmReg, 0); /* Lock RCM again */
}

bool ResetDrv_IsResetInterrupt(const ResetDrvType *obj, ResetDrv_ResetIntModuleType module)
{
    uint8_t retval = 0x0u;
    switch(module)
    {
    case RESETDRV_RESETINT_WDG:
        retval = RcmReg_GetRstIrqWdg(obj->rcmReg);
        break;
    case RESETDRV_RESETINT_SYS:
        retval = RcmReg_GetRstIrqSys(obj->rcmReg);
        break;
    case RESETDRV_RESETINT_PIN:
        retval = RcmReg_GetRstIrqPin(obj->rcmReg);
        break;
    case RESETDRV_RESETINT_MDMAP:
        retval = RcmReg_GetRstIrqMdmap(obj->rcmReg);
        break;
    case RESETDRV_RESETINT_PLL_LOSS:
        retval = RcmReg_GetRstIrqPllLoss(obj->rcmReg);
        break;
    case RESETDRV_RESETINT_CMUP:
        retval = RcmReg_GetRstIrqCmuPll(obj->rcmReg);
        break;
    case RESETDRV_RESETINT_CMUS:
        retval = RcmReg_GetRstIrqCmuSosc(obj->rcmReg);
        break;
    default:
        /* Should not get here */
        break;
    }
    return (retval == 0x1u);
}

void ResetDrv_EnableReset(ResetDrvType *obj, ResetDrv_ResetEnModuleType module, bool enable)
{
    RcmReg_SetKey(obj->rcmReg, RESET_RCM_UNLOCK); /* Unlock RCM */
    switch(module)
    {
    case RESETDRV_RESETEN_PLL_LOSS:
        RcmReg_SetRstCtrlPllLossEn(obj->rcmReg, enable ? 1 : 0);
        break;
    case RESETDRV_RESETEN_LOCKUP:
        RcmReg_SetRstCtrlLockupEn(obj->rcmReg, enable ? 1 : 0);
        break;
    default:
        /* Should not get here */
        break;
    }
    RcmReg_SetKey(obj->rcmReg, 0); /* Lock RCM again */
}

void ResetDrv_SoftwareResetModule(ResetDrvType *obj, ResetDrv_SwResetModuleType module)
{
    Pcc0Reg_SetKey(obj->pcc0Reg, RESET_PCC_UNLOCK); /* Unlock PCC */
    switch(module)
    {
    case RESETDRV_SWRESET_DMA:
        Pcc1Reg_SetDmaSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetDmaSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_MSCM:
        Pcc1Reg_SetMscmSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetMscmSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_CRC:
        Pcc1Reg_SetCrcSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetCrcSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_EIM:
        Pcc1Reg_SetEimSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetEimSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_ERM:
        Pcc1Reg_SetErmSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetErmSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_FHU:
        Pcc1Reg_SetFhuSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetFhuSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_SPI0:
        Pcc1Reg_SetSpi0SwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetSpi0SwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_SPI1:
        Pcc1Reg_SetSpi1SwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetSpi1SwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_UART0:
        Pcc1Reg_SetUart0SwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetUart0SwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_UART1:
        Pcc1Reg_SetUart1SwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetUart1SwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_CAN:
        Pcc1Reg_SetCanSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetCanSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_I2C:
        Pcc1Reg_SetI2CSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetI2CSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_PWMLITE0:
        Pcc1Reg_SetPwm0SwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetPwm0SwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_PWMLITE1:
        Pcc1Reg_SetPwm1SwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetPwm1SwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_MFT0:
        Pcc1Reg_SetMft0SwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetMft0SwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_MFT1:
        Pcc1Reg_SetMft1SwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetMft1SwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_GPIO:
        Pcc1Reg_SetGpioSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetGpioSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_CMUS:
        Pcc1Reg_SetCmusSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetCmusSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_CMUP:
        Pcc1Reg_SetCmupSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetCmupSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_FLS:
        Pcc1Reg_SetFlsCtlSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetFlsCtlSwR(obj->pcc1Reg, 0);
        break;
    case RESETDRV_SWRESET_MPU:
        Pcc1Reg_SetMpuSwR(obj->pcc1Reg, 1);
        Pcc1Reg_SetMpuSwR(obj->pcc1Reg, 0);
        break;
    /* always-on power domain related drivers */
    case RESETDRV_SWRESET_TOUCH:
        Pcc0Reg_SetTouchSwr(obj->pcc0Reg, 1);
        Pcc0Reg_SetTouchSwr(obj->pcc0Reg, 0);
        break;
    case RESETDRV_SWRESET_PORTA:
        Pcc0Reg_SetPortASwR(obj->pcc0Reg, 1);
        Pcc0Reg_SetPortASwR(obj->pcc0Reg, 0);
        break;
    case RESETDRV_SWRESET_PORTB:
        Pcc0Reg_SetPortBSwR(obj->pcc0Reg, 1);
        Pcc0Reg_SetPortBSwR(obj->pcc0Reg, 0);
        break;
    case RESETDRV_SWRESET_PORTC:
        Pcc0Reg_SetPortCSwR(obj->pcc0Reg, 1);
        Pcc0Reg_SetPortCSwR(obj->pcc0Reg, 0);
        break;
    case RESETDRV_SWRESET_LPTMR:
        Pcc0Reg_SetLptmrSwR(obj->pcc0Reg, 1);
        Pcc0Reg_SetLptmrSwR(obj->pcc0Reg, 0);
        break;
    case RESETDRV_SWRESET_LPIT:
        Pcc0Reg_SetLpitSwR(obj->pcc0Reg, 1);
        Pcc0Reg_SetLpitSwR(obj->pcc0Reg, 0);
        break;
    case RESETDRV_SWRESET_WDG:
        Pcc0Reg_SetWdgSwR(obj->pcc0Reg, 1);
        Pcc0Reg_SetWdgSwR(obj->pcc0Reg, 0);
        break;
    case RESETDRV_SWRESET_ADC:
        Pcc0Reg_SetAdcSwR(obj->pcc0Reg, 1);
        Pcc0Reg_SetAdcSwR(obj->pcc0Reg, 0);
        break;
    case RESETDRV_SWRESET_PDB:
        Pcc0Reg_SetPdbSwR(obj->pcc0Reg, 1);
        Pcc0Reg_SetPdbSwR(obj->pcc0Reg, 0);
        break;
    case RESETDRV_SWRESET_SYS:
        NVIC_SystemReset();
        break;
    default:
        /* Should not get here */
        break;
    }
    Pcc0Reg_SetKey(obj->pcc0Reg, 0); /* Lock Pcc0 again */
}
