/*==================================================================================================
*   Project              : RTD AUTOSAR 4.4
*   Platform             : CORTEXM
*   Peripheral           : 
*   Dependencies         : none
*
*   Autosar Version      : 4.4.0
*   Autosar Revision     : ASR_REL_4_4_REV_0000
*   Autosar Conf.Variant :
*   SW Version           : 0.9.0
*   Build Version        : S32K3_RTD_0_9_0__ASR_REL_4_4_REV_0000_20210326
*
*   (c) Copyright 2020 - 2021 NXP Semiconductors
*   All Rights Reserved.
*
*   NXP Confidential. This software is owned or controlled by NXP and may only be
*   used strictly in accordance with the applicable license terms. By expressly
*   accepting such terms or by downloading, installing, activating and/or otherwise
*   using the software, you are agreeing that you have read, and that you agree to
*   comply with and are bound by, such license terms. If you do not agree to be
*   bound by the applicable license terms, then you may not retain, install,
*   activate or otherwise use the software.
==================================================================================================*/
/**
*   @file       Clock_Ip_Monitor.c
*   @version    0.9.0
*
*   @brief   CLOCK driver implementations.
*   @details CLOCK driver implementations.
*
*   @addtogroup CLOCK_DRIVER Clock Ip Driver
*   @{
*/

/**
* @page misra_violations MISRA-C:2012 violations
*
* @section Clock_Ip_Monitor_c_REF_1
* Violates MISRA 2012 Advisory Rule 20.1, #include directives should only be preceded by preprocessor
* directives or comments. AUTOSAR imposes the specification of the sections in which certain parts
* of the driver must be placed.
*
* @section Clock_Ip_Monitor_c_REF_2
* Violates MISRA 2012 Advisory Rule 4.8, This file includes the definition
* of types but does not use it. Header is common for all files
*
*/


#include "Clock_Ip_Private.h"
#include "SchM_Mcu.h"

/*==================================================================================================
                               SOURCE FILE VERSION INFORMATION
==================================================================================================*/
#define CLOCK_IP_MONITOR_VENDOR_ID_C                      43
#define CLOCK_IP_MONITOR_AR_RELEASE_MAJOR_VERSION_C       4
#define CLOCK_IP_MONITOR_AR_RELEASE_MINOR_VERSION_C       4
#define CLOCK_IP_MONITOR_AR_RELEASE_REVISION_VERSION_C    0
#define CLOCK_IP_MONITOR_SW_MAJOR_VERSION_C               0
#define CLOCK_IP_MONITOR_SW_MINOR_VERSION_C               9
#define CLOCK_IP_MONITOR_SW_PATCH_VERSION_C               0

/*==================================================================================================
*                                     FILE VERSION CHECKS
==================================================================================================*/
/* Check if Clock_Ip_Monitor.c file and Clock_Ip_Private.h file are of the same vendor */
#if (CLOCK_IP_MONITOR_VENDOR_ID_C != CLOCK_IP_PRIVATE_VENDOR_ID)
    #error "Clock_Ip_IntOsc.c and Clock_Ip_Private.h have different vendor ids"
#endif

/* Check if Clock_Ip_Monitor.c file and Clock_Ip_Private.h file are of the same Autosar version */
#if ((CLOCK_IP_MONITOR_AR_RELEASE_MAJOR_VERSION_C != CLOCK_IP_PRIVATE_AR_RELEASE_MAJOR_VERSION) || \
     (CLOCK_IP_MONITOR_AR_RELEASE_MINOR_VERSION_C != CLOCK_IP_PRIVATE_AR_RELEASE_MINOR_VERSION) || \
     (CLOCK_IP_MONITOR_AR_RELEASE_REVISION_VERSION_C != CLOCK_IP_PRIVATE_AR_RELEASE_REVISION_VERSION) \
    )
    #error "AutoSar Version Numbers of Clock_Ip_Monitor.c and Clock_Ip_Private.h are different"
#endif

/* Check if Clock_Ip_Monitor.c file and Clock_Ip_Private.h file are of the same Software version */
#if ((CLOCK_IP_MONITOR_SW_MAJOR_VERSION_C != CLOCK_IP_PRIVATE_SW_MAJOR_VERSION) || \
     (CLOCK_IP_MONITOR_SW_MINOR_VERSION_C != CLOCK_IP_PRIVATE_SW_MINOR_VERSION) || \
     (CLOCK_IP_MONITOR_SW_PATCH_VERSION_C != CLOCK_IP_PRIVATE_SW_PATCH_VERSION) \
    )
    #error "Software Version Numbers of Clock_Ip_Monitor.c and Clock_Ip_Private.h are different"
#endif
#ifndef DISABLE_MCAL_INTERMODULE_ASR_CHECK
/* Check if Clock_Ip_Monitor.c file and SchM_Mcu.h file are of the same Autosar version */
#if ((CLOCK_IP_MONITOR_AR_RELEASE_MAJOR_VERSION_C != SCHM_MCU_AR_RELEASE_MAJOR_VERSION) || \
     (CLOCK_IP_MONITOR_AR_RELEASE_MINOR_VERSION_C != SCHM_MCU_AR_RELEASE_MINOR_VERSION))
    #error "AutoSar Version Numbers of Clock_Ip_Monitor.c and SchM_Mcu.h are different"
#endif
#endif
/* Clock start section code */
#define MCU_START_SEC_CODE
/**
* @violates @ref Clock_Ip_Monitor_c_REF_1 #include directives should only be preceded by preprocessor
* directives or comments.
*/
#include "Mcu_MemMap.h"

/*  TODO ARTD-738  Implement CMU in Clock_Ip driver    */

static void ClockMonitorEmpty(Clock_Ip_CmuConfigType const* config);
static void ClockMonitorEmpty_Disable(Clock_Ip_NameType name);
static void ClockMonitorEmpty_ClearStatus(Clock_Ip_NameType name);
static Clock_Ip_CmuStatusType ClockMonitorEmpty_GetStatus(Clock_Ip_NameType name);

#ifdef CMU_FC_FCE_REF_CNT_LFREF_HFREF
static void ResetCmuFcFceRefCntLfrefHfref(Clock_Ip_CmuConfigType const* config);
static void SetCmuFcFceRefCntLfrefHfref(Clock_Ip_CmuConfigType const* config);
static void DisableCmuFcFceRefCntLfrefHfref(Clock_Ip_NameType name);
static void ClearStatusCmuFcFceRefCntLfrefHfref(Clock_Ip_NameType name);
static Clock_Ip_CmuStatusType GetStatusCmuFcFceRefCntLfrefHfref(Clock_Ip_NameType name);
#endif

/* Clock stop section code */
#define MCU_STOP_SEC_CODE
/**
* @violates @ref Clock_Ip_Monitor_c_REF_1 #include directives should only be preceded by preprocessor
* directives or comments.
*/
#include "Mcu_MemMap.h"

/* Clock start constant section data */
#define MCU_START_SEC_CONST_UNSPECIFIED
/**
* @violates @ref Clock_Ip_Monitor_c_REF_1 #include directives should only be preceded by preprocessor
* directives or comments.
*/
#include "Mcu_MemMap.h"

const clockMonitorCallback cmuCallbacks[CMU_CALLBACKS_COUNT] =
{
    {
        ClockMonitorEmpty,                /* Reset */
        ClockMonitorEmpty,                /* Set */
        ClockMonitorEmpty_Disable,        /* Disable */
        ClockMonitorEmpty_ClearStatus,    /* Clear */
        ClockMonitorEmpty_GetStatus,      /* Get status */
    },
#ifdef CMU_FC_FCE_REF_CNT_LFREF_HFREF
    {
        ResetCmuFcFceRefCntLfrefHfref,          /* Reset */
        SetCmuFcFceRefCntLfrefHfref,            /* Set */
        DisableCmuFcFceRefCntLfrefHfref,        /* Disable */
        ClearStatusCmuFcFceRefCntLfrefHfref,    /* Clear */
        GetStatusCmuFcFceRefCntLfrefHfref,      /* Get status */
    },
#endif
};

/* Clock stop constant section data */
#define MCU_STOP_SEC_CONST_UNSPECIFIED
/**
* @violates @ref Clock_Ip_Monitor_c_REF_1 #include directives should only be preceded by preprocessor
* directives or comments.
*/
#include "Mcu_MemMap.h"


/* Clock start section code */
#define MCU_START_SEC_CODE
/**
* @violates @ref Clock_Ip_Monitor_c_REF_1 #include directives should only be preceded by preprocessor
* directives or comments.
*/
#include "Mcu_MemMap.h"

static void ClockMonitorEmpty(Clock_Ip_CmuConfigType const* config)
{
    (void)config;
    /* No implementation */
}

static void ClockMonitorEmpty_Disable(Clock_Ip_NameType name)
{
    (void)name;
    /* No implementation */
}

static void ClockMonitorEmpty_ClearStatus(Clock_Ip_NameType name)
{
    (void)name;
    /* No implementation */
}

static Clock_Ip_CmuStatusType ClockMonitorEmpty_GetStatus(Clock_Ip_NameType name)
{
    (void)name;
    /* No implementation */
    return CLOCK_IP_CMU_STATUS_UNDEFINED;
}


#ifdef CMU_FC_FCE_REF_CNT_LFREF_HFREF
static void DisableCmuFcFceRefCntLfrefHfref(Clock_Ip_NameType name)
{
    uint32 instance      = clockFeatures[name][CMU_INSTANCE];

    boolean TimeoutOccurred = FALSE;
    uint32 StartTime;
    uint32 ElapsedTime;
    uint32 TimeoutTicks;
    uint32 FrequencyCheckStatus;

#if defined(S32K3XX)
    /* Check clock status for CMU */
    if (((MC_ME->PRTN1_COFB1_STAT & MC_ME_PRTN1_COFB1_STAT_BLOCK47_MASK) == 0U))
    {
        /* Enable clock for CMU device */
        MC_ME->PRTN1_COFB1_CLKEN |= MC_ME_PRTN1_COFB1_CLKEN_REQ47(1U);   /* REQ47: Clock monitor unit */
        MC_ME->PRTN1_PCONF       |= MC_ME_PRTN1_PCONF_PCE_MASK;          /* PCE=1: Enable the clock to Partition #1 */
        MC_ME->PRTN1_PUPD        |= MC_ME_PRTN1_PUPD_PCUD_MASK;          /* PCUD=1: Trigger the hardware process */
        McMeEnterKey();
        /* Wait until CMU clock is running */
        ClockStartTimeout(&StartTime, &ElapsedTime, &TimeoutTicks, CLOCK_TIMEOUT_VALUE_US);
        do
        {
            TimeoutOccurred = ClockTimeoutExpired(&StartTime, &ElapsedTime, TimeoutTicks);
        }
        while(((MC_ME->PRTN1_COFB1_STAT & MC_ME_PRTN1_COFB1_STAT_BLOCK47_MASK) == 0U) && (FALSE == TimeoutOccurred));
        /* timeout notification */
        if (TRUE == TimeoutOccurred)
        {
            /* Report timeout error */
            ReportClockErrors(CLOCK_IP_REPORT_TIMEOUT_ERROR, RESERVED_CLK);
        }
    }
#endif
    /* Enter critical region*/
    SchM_Enter_Mcu_MCU_EXCLUSIVE_AREA_01();
    /* Only disable frequency check if it is enabled */
    if ((cmu[instance]->GCR & CMU_FC_GCR_FCE_MASK) == CMU_FREQUENCY_CHECK_ENABLED)
    {
        ClockStartTimeout(&StartTime, &ElapsedTime, &TimeoutTicks, CLOCK_TIMEOUT_VALUE_US);
        /* Wait for frequency check to be running. */
        do
        {
            FrequencyCheckStatus = (cmu[instance]->SR & CMU_FC_SR_RS_MASK);
            TimeoutOccurred = ClockTimeoutExpired(&StartTime, &ElapsedTime, TimeoutTicks);
        }
        while((FrequencyCheckStatus == CMU_FREQUENCY_CHECK_STOPPED) && (!TimeoutOccurred));

        /* timeout notification */
        if (TimeoutOccurred)
        {
            /* Report timeout error */
            ReportClockErrors(CLOCK_IP_REPORT_TIMEOUT_ERROR, name);
        }
        else
        {
            /* Disable frequency check */
            cmu[instance]->GCR &= ~CMU_FC_GCR_FCE_MASK;
        }
    }

    /* Disable interupts */
    cmu[instance]->IER &= ~(CMU_FC_IER_FLLIE_MASK | CMU_FC_IER_FHHIE_MASK | CMU_FC_IER_FLLAIE_MASK | CMU_FC_IER_FHHAIE_MASK);

    /* Reset reference counter */
    cmu[instance]->RCCR = CMU_RESET_COUNTER_VALUE;

    /* Reset high limit */
    cmu[instance]->HTCR = CMU_RESET_HIGH_LIMIT;

    /* Reset high limit */
    cmu[instance]->LTCR = CMU_RESET_LOW_LIMIT;

    /* Clear flags */
    cmu[instance]->SR |= (CMU_FC_SR_FLL_MASK | CMU_FC_SR_FHH_MASK | CMU_FC_SR_RS_MASK);
    
    /* Exit critical region. */
    SchM_Exit_Mcu_MCU_EXCLUSIVE_AREA_01();
}

static void ResetCmuFcFceRefCntLfrefHfref(Clock_Ip_CmuConfigType const* config)
{
    DisableCmuFcFceRefCntLfrefHfref(config->name);
}

static void SetCmuFcFceRefCntLfrefHfref(Clock_Ip_CmuConfigType const* config)
{
    uint32 instance      = clockFeatures[config->name][CMU_INSTANCE];
    uint32 swIndex       = clockFeatures[config->name][CMU_SW_INDEX];
    
#if defined(S32K3XX)
    boolean TimeoutOccurred = FALSE;
    uint32 StartTime;
    uint32 ElapsedTime;
    uint32 TimeoutTicks;
    /* Check clock status for CMU */
    if (((MC_ME->PRTN1_COFB1_STAT & MC_ME_PRTN1_COFB1_STAT_BLOCK47_MASK) == 0U))
    {
        /* Enable clock for CMU device */
        MC_ME->PRTN1_COFB1_CLKEN |= MC_ME_PRTN1_COFB1_CLKEN_REQ47(1U);   /* REQ47: Clock monitor unit */
        MC_ME->PRTN1_PCONF       |= MC_ME_PRTN1_PCONF_PCE_MASK;          /* PCE=1: Enable the clock to Partition #1 */
        MC_ME->PRTN1_PUPD        |= MC_ME_PRTN1_PUPD_PCUD_MASK;          /* PCUD=1: Trigger the hardware process */
        McMeEnterKey();
        /* Wait until CMU clock is running */
        ClockStartTimeout(&StartTime, &ElapsedTime, &TimeoutTicks, CLOCK_TIMEOUT_VALUE_US);
        do
        {
            TimeoutOccurred = ClockTimeoutExpired(&StartTime, &ElapsedTime, TimeoutTicks);
        }
        while(((MC_ME->PRTN1_COFB1_STAT & MC_ME_PRTN1_COFB1_STAT_BLOCK47_MASK) == 0U) && (FALSE == TimeoutOccurred));
        /* timeout notification */
        if (TRUE == TimeoutOccurred)
        {
            /* Report timeout error */
            ReportClockErrors(CLOCK_IP_REPORT_TIMEOUT_ERROR, RESERVED_CLK);
        }
    }
#endif

    /* Set reference counter */
    cmu[instance]->RCCR = cmuEntries[swIndex].refCount;

    /* Set high limit */
    cmu[instance]->HTCR = cmuEntries[swIndex].hfRef;

    /* Set high limit */
    cmu[instance]->LTCR = cmuEntries[swIndex].lfRef;

    /* Enable/disable interrupts */
    cmu[instance]->IER = config->interrupt;

    /* Enable cmu */
    if (config->enable != 0U)
    {
        cmu[instance]->GCR |= CMU_FC_GCR_FCE_MASK;
    }
    else
    {
        cmu[instance]->GCR &= ~CMU_FC_GCR_FCE_MASK;
    }
}

static void ClearStatusCmuFcFceRefCntLfrefHfref(Clock_Ip_NameType name)
{
    uint32 instance      = clockFeatures[name][CMU_INSTANCE];
    uint32 cmuIsrValue;

#if defined(S32K3XX)
    boolean TimeoutOccurred = FALSE;
    uint32 StartTime;
    uint32 ElapsedTime;
    uint32 TimeoutTicks;
    /* Check clock status for CMU */
    if (((MC_ME->PRTN1_COFB1_STAT & MC_ME_PRTN1_COFB1_STAT_BLOCK47_MASK) == 0U))
    {
        /* Enable clock for CMU device */
        MC_ME->PRTN1_COFB1_CLKEN |= MC_ME_PRTN1_COFB1_CLKEN_REQ47(1U);   /* REQ47: Clock monitor unit */
        MC_ME->PRTN1_PCONF       |= MC_ME_PRTN1_PCONF_PCE_MASK;          /* PCE=1: Enable the clock to Partition #1 */
        MC_ME->PRTN1_PUPD        |= MC_ME_PRTN1_PUPD_PCUD_MASK;          /* PCUD=1: Trigger the hardware process */
        McMeEnterKey();
        /* Wait until CMU clock is running */
        ClockStartTimeout(&StartTime, &ElapsedTime, &TimeoutTicks, CLOCK_TIMEOUT_VALUE_US);
        do
        {
            TimeoutOccurred = ClockTimeoutExpired(&StartTime, &ElapsedTime, TimeoutTicks);
        }
        while(((MC_ME->PRTN1_COFB1_STAT & MC_ME_PRTN1_COFB1_STAT_BLOCK47_MASK) == 0U) && (FALSE == TimeoutOccurred));
        /* timeout notification */
        if (TRUE == TimeoutOccurred)
        {
            /* Report timeout error */
            ReportClockErrors(CLOCK_IP_REPORT_TIMEOUT_ERROR, RESERVED_CLK);
        }
    }
#endif

    /* Read flags */
    cmuIsrValue = cmu[instance]->SR & CMU_ISR_MASK;

    /* Clear flags */
    cmu[instance]->SR = cmuIsrValue;
}

static Clock_Ip_CmuStatusType GetStatusCmuFcFceRefCntLfrefHfref(Clock_Ip_NameType name)
{
    uint32 cmuIerValue, cmuIsrValue;
    Clock_Ip_CmuStatusType status = CLOCK_IP_CMU_STATUS_UNDEFINED;
    uint32 instance      = clockFeatures[name][CMU_INSTANCE];

#if defined(S32K3XX)
    boolean TimeoutOccurred = FALSE;
    uint32 StartTime;
    uint32 ElapsedTime;
    uint32 TimeoutTicks;
    /* Check clock status for CMU */
    if (((MC_ME->PRTN1_COFB1_STAT & MC_ME_PRTN1_COFB1_STAT_BLOCK47_MASK) == 0U))
    {
        /* Enable clock for CMU device */
        MC_ME->PRTN1_COFB1_CLKEN |= MC_ME_PRTN1_COFB1_CLKEN_REQ47(1U);   /* REQ47: Clock monitor unit */
        MC_ME->PRTN1_PCONF       |= MC_ME_PRTN1_PCONF_PCE_MASK;          /* PCE=1: Enable the clock to Partition #1 */
        MC_ME->PRTN1_PUPD        |= MC_ME_PRTN1_PUPD_PCUD_MASK;          /* PCUD=1: Trigger the hardware process */
        McMeEnterKey();
        /* Wait until CMU clock is running */
        ClockStartTimeout(&StartTime, &ElapsedTime, &TimeoutTicks, CLOCK_TIMEOUT_VALUE_US);
        do
        {
            TimeoutOccurred = ClockTimeoutExpired(&StartTime, &ElapsedTime, TimeoutTicks);
        }
        while(((MC_ME->PRTN1_COFB1_STAT & MC_ME_PRTN1_COFB1_STAT_BLOCK47_MASK) == 0U) && (FALSE == TimeoutOccurred));
        /* timeout notification */
        if (TRUE == TimeoutOccurred)
        {
            /* Report timeout error */
            ReportClockErrors(CLOCK_IP_REPORT_TIMEOUT_ERROR, RESERVED_CLK);
        }
    }
#endif

    /* Read flags */
    cmuIsrValue = cmu[instance]->SR & CMU_ISR_MASK;

    /* Read interrupt enable */
    cmuIerValue = cmu[instance]->IER & CMU_ISR_MASK;

    cmuIsrValue = cmuIsrValue & cmuIerValue;


    /* Gheck flash if frequency check is enabled */
    if ((cmu[instance]->GCR & CMU_FC_GCR_FCE_MASK) == CMU_FREQUENCY_CHECK_ENABLED)
    {
        if ( ( CMU_FC_SR_FHH_MASK == (cmuIsrValue & CMU_FC_SR_FHH_MASK) ) )
        {
            status = CLOCK_IP_CMU_HIGH_FREQ;
        }
        else if ( CMU_FC_SR_FLL_MASK == (cmuIsrValue & CMU_FC_SR_FLL_MASK) )
        {
            status = CLOCK_IP_CMU_LOW_FREQ;
        }
        else
        {
            status = CLOCK_IP_CMU_IN_RANGE;
        }
    }

    return status;

}
#endif

/**
* @brief        This function clear the CMU interrupt flag from CMU module.
* @details      Called by RGM ISR routine when a user notification for CMU FCCU events is configured
*
* @return       void
*
* @implements Mcu_CMU_ClockFailInt_Activity
*
*/
void Mcu_CMU_ClockFailInt(void)
{
    uint32 cmuIerValue, cmuIsrValue, instance, indexCmuEntry;
    Clock_Ip_NameType name;

    for (indexCmuEntry = 0U; indexCmuEntry < CMU_ENTRIES_NO; indexCmuEntry++)
    {
        name       = cmuEntries[indexCmuEntry].name;
        instance   = clockFeatures[name][CMU_INSTANCE];

        /* Read flags */
        cmuIsrValue = cmu[instance]->SR & CMU_ISR_MASK;

        /* Clear status flag */
        cmu[instance]->SR = cmuIsrValue;

        /* Check whether driver is initialized */
        if(NULL_PTR != clockConfig)
        {
            /* Read interrupt enable */
            cmuIerValue = cmu[instance]->IER & CMU_ISR_MASK;

            /* Filter all interrupts that are not enabled from cmuIsrValue */
            cmuIsrValue = cmuIsrValue & cmuIerValue;

            /* If at least one interrupt has been triggered */
            if (cmuIsrValue != 0U)
            {
                ReportClockErrors(CLOCK_IP_CMU_NOTIFICATION, name);
            }
        }
    }
}

/* Clock stop section code */
#define MCU_STOP_SEC_CODE
/**
* @violates @ref Clock_Ip_Monitor_c_REF_1 #include directives should only be preceded by preprocessor
* directives or comments.
*/
#include "Mcu_MemMap.h"

/*! @}*/

/*******************************************************************************
 * EOF
 ******************************************************************************/

