/*
 * Copyright 2020-2022 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_cdog.h"

/*******************************************************************************
 * Definitions
 *******************************************************************************/

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.cdog"
#endif

/* Reset CONTROL mask */
#define RESERVED_CTRL_MASK 0x800u

#if defined(CDOG_IRQS)
/* Array of IRQs */
static const IRQn_Type s_CdogIrqs[] = CDOG_IRQS;
#endif /* CDOG_IRQS */

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Code
 ******************************************************************************/
#if defined(CDOG)
/*!
 * Weak implementation of CDOG IRQ, should be re-defined by user when using CDOG IRQ
 */
__WEAK void CDOG_DriverIRQHandler(void)
{
    /*    NVIC_DisableIRQ(CDOG_IRQn);
     *    CDOG_Stop(CDOG, s_start);
     *    CDOG->FLAGS = 0x0U;
     *    CDOG_Start(CDOG, 0xFFFFFFU, s_start);
     *    NVIC_EnableIRQ(CDOG_IRQn);
     */
}
#endif

#if defined(CDOG0)
/*!
 * Weak implementation of CDOG0 IRQ, should be re-defined by user when using CDOG IRQ
 */
__WEAK void CDOG0_DriverIRQHandler(void)
{
    /*    NVIC_DisableIRQ(CDOG0_IRQn);
     *    CDOG_Stop(CDOG0, s_start);
     *    CDOG0->FLAGS = 0x0U;
     *    CDOG_Start(CDOG0, 0xFFFFFFU, s_start);
     *    NVIC_EnableIRQ(CDOG0_IRQn);
     */
}
#endif

#if defined(CDOG1)
/*!
 * Weak implementation of CDOG1 IRQ, should be re-defined by user when using CDOG IRQ
 */
__WEAK void CDOG1_DriverIRQHandler(void)
{
    /*    NVIC_DisableIRQ(CDOG1_IRQn);
     *    CDOG_Stop(CDOG1, s_start);
     *    CDOG1->FLAGS = 0x0U;
     *    CDOG_Start(CDOG1, 0xFFFFFFU, s_start);
     *    NVIC_EnableIRQ(CDOG1_IRQn);
     */
}
#endif

/*!
 * brief Sets the default configuration of CDOG
 *
 * This function initialize CDOG config structure to default values.
 *
 * param conf CDOG configuration structure
 */
void CDOG_GetDefaultConfig(cdog_config_t *conf)
{
    /* Default configuration after reset */
    conf->lock       = (uint8_t)kCDOG_LockCtrl_Unlock;    /* Lock control */
    conf->timeout    = (uint8_t)kCDOG_FaultCtrl_NoAction; /* Timeout control */
    conf->miscompare = (uint8_t)kCDOG_FaultCtrl_NoAction; /* Miscompare control */
    conf->sequence   = (uint8_t)kCDOG_FaultCtrl_NoAction; /* Sequence control */
    conf->state      = (uint8_t)kCDOG_FaultCtrl_NoAction; /* State control */
    conf->address    = (uint8_t)kCDOG_FaultCtrl_NoAction; /* Address control */
    conf->irq_pause  = (uint8_t)kCDOG_IrqPauseCtrl_Run;   /* IRQ pause control */
    conf->debug_halt = (uint8_t)kCDOG_DebugHaltCtrl_Run;  /* Debug halt control */
    return;
}

/*!
 * brief Sets secure counter and instruction timer values
 *
 * This function sets value in RELOAD and START registers for instruction timer.
 *
 * param base CDOG peripheral base address
 * param reload reload value
 * param start start value
 */
void CDOG_Start(CDOG_Type *base, uint32_t reload, uint32_t start)
{
    base->RELOAD = reload;
    base->START  = start;
}

/*!
 * brief Stops secure counter and instruction timer
 *
 * This function stops instruction timer and secure counter.
 * This also change state of CDOG to IDLE.
 *
 * param base CDOG peripheral base address
 * param stop expected value which will be compared with value of secure counter
 */
void CDOG_Stop(CDOG_Type *base, uint32_t stop)
{
    base->STOP = stop;
}

/*!
 * brief Sets secure counter and instruction timer values
 *
 * This function sets value in STOP, RELOAD and START registers
 * for instruction timer and secure counter.
 *
 * param base CDOG peripheral base address
 * param stop expected value which will be compared with value of secure counter
 * param reload reload value for instruction timer
 * param start start value for secure timer
 */
void CDOG_Set(CDOG_Type *base, uint32_t stop, uint32_t reload, uint32_t start)
{
    base->STOP   = stop;
    base->RELOAD = reload;
    base->START  = start;
}

/*!
 * brief Add value to secure counter
 *
 * This function add specified value to secure counter.
 *
 * param base CDOG peripheral base address.
 * param add Value to be added.
 */
void CDOG_Add(CDOG_Type *base, uint32_t add)
{
    base->ADD = (secure_counter_t)add;
}

/*!
 * brief Add 1 to secure counter
 *
 * This function add 1 to secure counter.
 *
 * param base CDOG peripheral base address.
 * param add Value to be added.
 */
void CDOG_Add1(CDOG_Type *base)
{
    base->ADD1 = (secure_counter_t)0x1U;
}

/*!
 * brief Add 16 to secure counter
 *
 * This function add 16 to secure counter.
 *
 * param base CDOG peripheral base address.
 * param add Value to be added.
 */
void CDOG_Add16(CDOG_Type *base)
{
    base->ADD16 = (secure_counter_t)0x1U;
}

/*!
 * brief Add 256 to secure counter
 *
 * This function add 256 to secure counter.
 *
 * param base CDOG peripheral base address.
 * param add Value to be added.
 */
void CDOG_Add256(CDOG_Type *base)
{
    base->ADD256 = (secure_counter_t)0x1U;
}

/*!
 * brief Substract value to secure counter
 *
 * This function substract specified value to secure counter.
 *
 * param base CDOG peripheral base address.
 * param sub Value to be substracted.
 */
void CDOG_Sub(CDOG_Type *base, uint32_t sub)
{
    base->SUB = (secure_counter_t)sub;
}

/*!
 * brief Substract 1 from secure counter
 *
 * This function substract specified 1 from secure counter.
 *
 * param base CDOG peripheral base address.
 */
void CDOG_Sub1(CDOG_Type *base)
{
    base->SUB1 = (secure_counter_t)0x1U;
}

/*!
 * brief Substract 16 from secure counter
 *
 * This function substract specified 16 from secure counter.
 *
 * param base CDOG peripheral base address.
 */
void CDOG_Sub16(CDOG_Type *base)
{
    base->SUB16 = (secure_counter_t)0x1U;
}

/*!
 * brief Substract 256 from secure counter
 *
 * This function substract specified 256 from secure counter.
 *
 * param base CDOG peripheral base address.
 */
void CDOG_Sub256(CDOG_Type *base)
{
    base->SUB256 = (secure_counter_t)0x1U;
}

/*!
 * brief Checks secure counter.
 *
 * This function compares stop value with secure counter value
 * by writting to RELOAD refister.
 *
 * param base CDOG peripheral base address
 * param check expected (stop) value.
 */
void CDOG_Check(CDOG_Type *base, uint32_t check)
{
    base->RESTART = check;
}

/*!
 * brief Set the CDOG persistent word.
 *
 * param base CDOG peripheral base address.
 * param value The value to be written.
 */
void CDOG_WritePersistent(CDOG_Type *base, uint32_t value)
{
    base->PERSISTENT = value;
}

/*!
 * brief Get the CDOG persistent word.
 *
 * param base CDOG peripheral base address.
 * return The persistent word.
 */
uint32_t CDOG_ReadPersistent(CDOG_Type *base)
{
    return base->PERSISTENT;
}

/*!
 * brief Initialize CDOG
 *
 * This function initializes CDOG setting and enable all interrupts.
 *
 * param base CDOG peripheral base address
 * param conf CDOG configuration structure
 * return Status of the init operation
 */
status_t CDOG_Init(CDOG_Type *base, cdog_config_t *conf)
{
    /* Ungate clock to CDOG engine and reset it */
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
#ifdef CDOG_CLOCKS
    CLOCK_EnableClock(kCLOCK_Cdog);
#endif /* CDOG_CLOCKS */
#endif /* !FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

#if !(defined(FSL_FEATURE_CDOG_HAS_NO_RESET) && FSL_FEATURE_CDOG_HAS_NO_RESET)
    RESET_PeripheralReset(kCDOG_RST_SHIFT_RSTn);
#endif /* !FSL_FEATURE_CDOG_HAS_NO_RESET */

    if (base->CONTROL == 0x0U)
    {
        /* CDOG is not in IDLE mode, which may be cause after SW reset. */
        /* Writing to CONTROL register will trigger fault. */
        return kStatus_Fail;
    }

    /* Clear pending errors, otherwise the device will reset */
    /* itself immediately after enable Code Watchdog */
    if ((uint32_t)kCDOG_LockCtrl_Lock ==
        ((base->CONTROL & CDOG_CONTROL_LOCK_CTRL_MASK) >> CDOG_CONTROL_LOCK_CTRL_SHIFT))

    {
        base->FLAGS = CDOG_FLAGS_TO_FLAG(1U) | CDOG_FLAGS_MISCOM_FLAG(1U) | CDOG_FLAGS_SEQ_FLAG(1U) |
                      CDOG_FLAGS_CNT_FLAG(1U) | CDOG_FLAGS_STATE_FLAG(1U) | CDOG_FLAGS_ADDR_FLAG(1U) |
                      CDOG_FLAGS_POR_FLAG(1U);
    }
    else
    {
/* load default values for CDOG->CONTROL before flags clear */
#if defined(FSL_FEATURE_CDOG_NEED_LOAD_DEFAULT_CONF) && (FSL_FEATURE_CDOG_NEED_LOAD_DEFAULT_CONF > 0)
        cdog_config_t default_conf;

        /* Initialize CDOG */
        CDOG_GetDefaultConfig(&default_conf);

        /* Write default value to CDOG->CONTROL*/
        base->CONTROL = 
            CDOG_CONTROL_TIMEOUT_CTRL(default_conf.timeout) |       /* Action if the timeout event is triggered  */
            CDOG_CONTROL_MISCOMPARE_CTRL(default_conf.miscompare) | /* Action if the miscompare error event is triggered  */
            CDOG_CONTROL_SEQUENCE_CTRL(default_conf.sequence) |     /* Action if the sequence error event is triggered  */
            CDOG_CONTROL_STATE_CTRL(default_conf.state) |           /* Action if the state error event is triggered  */
            CDOG_CONTROL_ADDRESS_CTRL(default_conf.address) |       /* Action if the address error event is triggered */
            CDOG_CONTROL_IRQ_PAUSE(default_conf.irq_pause) |        /* Pause running during interrupts setup */
            CDOG_CONTROL_DEBUG_HALT_CTRL(default_conf.debug_halt) | /* Halt CDOG timer during debug */
            CDOG_CONTROL_LOCK_CTRL(default_conf.lock) | RESERVED_CTRL_MASK; /* Lock control register, RESERVED */
#endif /* FSL_FEATURE_CDOG_NEED_LOAD_DEFAULT_CONF */

        base->FLAGS = CDOG_FLAGS_TO_FLAG(0U) | CDOG_FLAGS_MISCOM_FLAG(0U) | CDOG_FLAGS_SEQ_FLAG(0U) |
                      CDOG_FLAGS_CNT_FLAG(0U) | CDOG_FLAGS_STATE_FLAG(0U) | CDOG_FLAGS_ADDR_FLAG(0U) |
                      CDOG_FLAGS_POR_FLAG(0U);
    }

    base->CONTROL =
        CDOG_CONTROL_TIMEOUT_CTRL(conf->timeout) |       /* Action if the timeout event is triggered  */
        CDOG_CONTROL_MISCOMPARE_CTRL(conf->miscompare) | /* Action if the miscompare error event is triggered  */
        CDOG_CONTROL_SEQUENCE_CTRL(conf->sequence) |     /* Action if the sequence error event is triggered  */
        CDOG_CONTROL_STATE_CTRL(conf->state) |           /* Action if the state error event is triggered  */
        CDOG_CONTROL_ADDRESS_CTRL(conf->address) |       /* Action if the address error event is triggered */
        CDOG_CONTROL_IRQ_PAUSE(conf->irq_pause) |        /* Pause running during interrupts setup */
        CDOG_CONTROL_DEBUG_HALT_CTRL(conf->debug_halt) | /* Halt CDOG timer during debug */
        CDOG_CONTROL_LOCK_CTRL(conf->lock) | RESERVED_CTRL_MASK; /* Lock control register, RESERVED */

#if defined(CDOG_IRQS)
    /* Enable peripheral IRQs, if defined in array */
    for (uint32_t i = 0; i < ARRAY_SIZE(s_CdogIrqs); i++)
    {
        NVIC_EnableIRQ(s_CdogIrqs[i]);
    }
#endif /* CDOG_IRQS */

    return kStatus_Success;
}

/*!
 * brief Deinitialize CDOG
 *
 * This function stops CDOG secure counter.
 *
 * param base CDOG peripheral base address
 */
void CDOG_Deinit(CDOG_Type *base)
{
#if defined(CDOG_IRQS)
    /* Enable peripheral IRQs, if defined in array */
    for (uint32_t i = 0; i < ARRAY_SIZE(s_CdogIrqs); i++)
    {
        NVIC_DisableIRQ(s_CdogIrqs[i]);
    }
#endif /* CDOG_IRQS */

#if !(defined(FSL_FEATURE_CDOG_HAS_NO_RESET) && FSL_FEATURE_CDOG_HAS_NO_RESET)
    RESET_SetPeripheralReset(kCDOG_RST_SHIFT_RSTn);
#endif /* !FSL_FEATURE_CDOG_HAS_NO_RESET */

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
#ifdef CDOG_CLOCKS
    CLOCK_DisableClock(kCLOCK_Cdog);
#endif /* CDOG_CLOCKS */
#endif /* !FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}
