/*==================================================================================================
*   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_IntOsc.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_IntOsc_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_IntOsc_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"

/*==================================================================================================
                               SOURCE FILE VERSION INFORMATION
==================================================================================================*/
#define CLOCK_IP_INTOSC_VENDOR_ID_C                      43
#define CLOCK_IP_INTOSC_AR_RELEASE_MAJOR_VERSION_C       4
#define CLOCK_IP_INTOSC_AR_RELEASE_MINOR_VERSION_C       4
#define CLOCK_IP_INTOSC_AR_RELEASE_REVISION_VERSION_C    0
#define CLOCK_IP_INTOSC_SW_MAJOR_VERSION_C               0
#define CLOCK_IP_INTOSC_SW_MINOR_VERSION_C               9
#define CLOCK_IP_INTOSC_SW_PATCH_VERSION_C               0

/*==================================================================================================
*                                     FILE VERSION CHECKS
==================================================================================================*/
/* Check if Clock_Ip_IntOsc.c file and Clock_Ip_Private.h file are of the same vendor */
#if (CLOCK_IP_INTOSC_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_IntOsc.c file and Clock_Ip_Private.h file are of the same Autosar version */
#if ((CLOCK_IP_INTOSC_AR_RELEASE_MAJOR_VERSION_C != CLOCK_IP_PRIVATE_AR_RELEASE_MAJOR_VERSION) || \
     (CLOCK_IP_INTOSC_AR_RELEASE_MINOR_VERSION_C != CLOCK_IP_PRIVATE_AR_RELEASE_MINOR_VERSION) || \
     (CLOCK_IP_INTOSC_AR_RELEASE_REVISION_VERSION_C != CLOCK_IP_PRIVATE_AR_RELEASE_REVISION_VERSION) \
    )
    #error "AutoSar Version Numbers of Clock_Ip_IntOsc.c and Clock_Ip_Private.h are different"
#endif

/* Check if Clock_Ip_IntOsc.c file and Clock_Ip_Private.h file are of the same Software version */
#if ((CLOCK_IP_INTOSC_SW_MAJOR_VERSION_C != CLOCK_IP_PRIVATE_SW_MAJOR_VERSION) || \
     (CLOCK_IP_INTOSC_SW_MINOR_VERSION_C != CLOCK_IP_PRIVATE_SW_MINOR_VERSION) || \
     (CLOCK_IP_INTOSC_SW_PATCH_VERSION_C != CLOCK_IP_PRIVATE_SW_PATCH_VERSION) \
    )
    #error "Software Version Numbers of Clock_Ip_IntOsc.c and Clock_Ip_Private.h are different"
#endif

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


static void InternalOscillatorEmpty(Clock_Ip_IrcoscConfigType const* config);
#ifdef FIRC_STDBY_ENABLE
static void FircStdbyEnable(Clock_Ip_IrcoscConfigType const* config);
#endif
#ifdef SIRC_STDBY_ENABLE
static void SircStdbyEnable(Clock_Ip_IrcoscConfigType const* config);
#endif
#ifdef SIRC_ENABLE
static status_t SircEnable(Clock_Ip_IrcoscConfigType const* config);
#endif
#ifdef SIRC_VLP_ENABLE
static SircVlpEnable(Clock_Ip_IrcoscConfigType const* config);
#endif
#ifdef SIRC_STOP_ENABLE
static SircStopEnable(Clock_Ip_IrcoscConfigType const* config);
#endif
#ifdef FIRC_ENABLE
static status_t FIRCEnable(Clock_Ip_IrcoscConfigType const* config);
#endif
#ifdef FIRC_VLP_ENABLE
static FIRCVlpEnable(Clock_Ip_IrcoscConfigType const* config);
#endif
#ifdef FIRC_STOP_ENABLE
static FIRCStopEnable(Clock_Ip_IrcoscConfigType const* config);
#endif

/* Clock stop section code */
#define MCU_STOP_SEC_CODE
/**
* @violates @ref Clock_Ip_IntOsc_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_IntOsc_c_REF_1 #include directives should only be preceded by preprocessor
* directives or comments.
*/
#include "Mcu_MemMap.h"

const intOscCallback intOscCallbacks[IRCOSC_CALLBACKS_COUNT] =
{
    {
        InternalOscillatorEmpty,          /* Set */
    },
#ifdef FIRC_STDBY_ENABLE
    {
        FircStdbyEnable,                  /* Set */
    },
#endif
#ifdef SIRC_STDBY_ENABLE
    {
        SircStdbyEnable,                  /* Set */
    },
#endif

#ifdef SIRC_ENABLE
    {
        SircEnable,                       /* Set */
    },
#endif

#ifdef SIRC_VLP_ENABLE
    {
        SircVlpEnable,                    /* Set */
    },
#endif

#ifdef SIRC_STOP_ENABLE
    {
        SircStopEnable,                   /* Set */
    },
#endif

#ifdef FIRC_ENABLE
    {
        FIRCEnable,                       /* Set */
    },
#endif

#ifdef FIRC_VLP_ENABLE
    {
        FIRCVlpEnable,                    /* Set */
    },
#endif

#ifdef FIRC_STOP_ENABLE
    {
        FIRCStopEnable,                    /* Set */
    },
#endif
};


/* Clock stop constant section data */
#define MCU_STOP_SEC_CONST_UNSPECIFIED
/**
* @violates @ref Clock_Ip_IntOsc_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_IntOsc_c_REF_1 #include directives should only be preceded by preprocessor
* directives or comments.
*/
#include "Mcu_MemMap.h"

static void InternalOscillatorEmpty(Clock_Ip_IrcoscConfigType const* config)
{
    (void)config;
    /* No implementation */
}

#ifdef FIRC_STDBY_ENABLE
static void FircStdbyEnable(Clock_Ip_IrcoscConfigType const* config)
{
    if (config->enable != 0U)
    {
        FIRC->STDBY_ENABLE |= FIRC_STDBY_ENABLE_STDBY_EN_MASK;
        /* Update Clock state for FIRC_STANDBY_CLK to enable */
        UpdateClockState(config->name, ENABLED_CLOCK);
    }
    else
    {
        FIRC->STDBY_ENABLE &= ~FIRC_STDBY_ENABLE_STDBY_EN_MASK;
        /* Update Clock state for FIRC_STANDBY_CLK to disable */
        UpdateClockState(config->name, DISABLED_CLOCK);
    }

}
#endif
#ifdef SIRC_STDBY_ENABLE
static void SircStdbyEnable(Clock_Ip_IrcoscConfigType const* config)
{
    if (config->enable != 0U)
    {
        SIRC->MISCELLANEOUS_IN |= SIRC_MISCELLANEOUS_IN_STANDBY_ENABLE_MASK;
        /* Update Clock state for SIRC_STANDBY_CLK to enable */
        UpdateClockState(config->name, ENABLED_CLOCK);
    }
    else
    {
        SIRC->MISCELLANEOUS_IN &= ~SIRC_MISCELLANEOUS_IN_STANDBY_ENABLE_MASK;
        /* Update Clock state for SIRC_STANDBY_CLK to disable */
        UpdateClockState(config->name, DISABLED_CLOCK);
    }

}
#endif

#ifdef SIRC_ENABLE
static status_t SircEnable(Clock_Ip_IrcoscConfigType const* config)
{
    uint32 instance = clockFeatures[config->name][CLOCK_MODULE_INSTANCE];
    boolean TimeoutOccurred = FALSE;
    uint32 StartTime;
    uint32 ElapsedTime;
    uint32 TimeoutTicks;
    uint32 IrcoscStatus;
    Clock_Ip_IrcoscConfigType sircConfig;
    
    (void)instance;
    
    

    /* TODO config = NULL ??*/
    if (config == NULL)
    {
        config.name   = FIRC_CLK;
        config.range  = 1U;   /* 8MHz */
        config.enable = 1U;   /* enabled */
    }

    /* Clear LK bit field */
    SCG->SIRCCSR &= (uint32)(~(SCG_SIRCCSR_LK_MASK));

    /* Disable clock */
    SCG->SIRCCSR &= (uint32)(~(SCG_SIRCCSR_SIRCEN_MASK));

    /* Configure SIRC. */
    if (config->enable)
    {
        /* Step frequency range. */
        SCG->SIRCCFG = SCG_SIRCCFG_RANGE(config->range);

        /* Enable clock. */
        SCG->SIRCCSR |= SCG_SIRCCSR_SIRCEN(1U);      

        ClockStartTimeout(&StartTime, &ElapsedTime, &TimeoutTicks, CLOCK_TIMEOUT_VALUE_US);
        /* Wait until ircosc is locked */
        do
        {
            Clock_Ip_TimeDelay();
            IrcoscStatus = (((SCG->SIRCCSR & SCG_SIRCCSR_SIRCVLD_MASK) >> SCG_SIRCCSR_SIRCVLD_SHIFT));
            TimeoutOccurred = ClockTimeoutExpired(&StartTime, &ElapsedTime, TimeoutTicks);
        }
        while ((IrcoscStatus == 0U) && (FALSE == TimeoutOccurred));

        if (FALSE == TimeoutOccurred)
        {
            UpdateClockState(config->name, ENABLED_CLOCK);
        }
        else
        {
            /* Report timeout error */
            ReportClockErrors(CLOCK_IP_REPORT_TIMEOUT_ERROR, config->name);
        }
    }

    return status;
}
#endif

#ifdef SIRC_VLP_ENABLE
static SircVlpEnable(Clock_Ip_IrcoscConfigType const* config)
{
    /* Clear LK bit field */
    SCG->SIRCCSR &= (uint32)(~(SCG_SIRCCSR_LK_MASK));

    /* Disable clock */
    SCG->SIRCCSR &= (uint32)(~(SCG_SIRCCSR_SIRCLPEN_MASK));

    /* Configure SIRC. */
    if (config->enable)
    {
        /* Enable clock in VLP mode */
        SCG->SIRCCSR |= SCG_SIRCCSR_SIRCLPEN(1U);      
    }
}
#endif

#ifdef SIRC_STOP_ENABLE
static SircStopEnable(Clock_Ip_IrcoscConfigType const* config)
{
    /* Clear LK bit field */
    SCG->SIRCCSR &= (uint32)(~(SCG_SIRCCSR_LK_MASK));

    /* Disable clock */
    SCG->SIRCCSR &= (uint32)(~(SCG_SIRCCSR_SIRCSTEN_MASK));

    /* Configure SIRC. */
    if (config->enable)
    {
        /* Enable clock in STOP mode */
        SCG->SIRCCSR |= SCG_SIRCCSR_SIRCSTEN(1U);      
    }
}
#endif


#ifdef FIRC_ENABLE
static status_t FIRCEnable(Clock_Ip_IrcoscConfigType const* config)
{
    uint32 instance = clockFeatures[config->name][CLOCK_MODULE_INSTANCE];
    boolean TimeoutOccurred = FALSE;
    uint32 StartTime;
    uint32 ElapsedTime;
    uint32 TimeoutTicks;
    uint32 IrcoscStatus;
    
    (void)instance;

    /* Clear LK bit field */
    SCG->FIRCCSR &= (uint32)(~(SCG_FIRCCSR_LK_MASK));

    /* Check that FIRC is used by system clock) */
    if ((SCG->FIRCSCR & SCG_FIRCCSR_FIRCSEL_MASK) != 0U)
    {
        /* Check whether FIRC is already configured as required */
        if (((config->range != (SCG->FIRCCFG & SCG_FIRCCFG_RANGE_MASK) >> SCG_FIRCCFG_RANGE_SHIFT) || 
            (config->regulator != SCG_FIRCCSR_FIRCREGOFF((regulator) ? 0UL : 1UL)))
        {
            /* Enable SIRC if it is disabled. */
            if ((SCG->SIRCCSR & SCG_SIRCCSR_SIRCEN_MASK) == 0U) 
            {
                SircEnable(NULL_PTR);
            }

            /* Switch to SIRC */
            uint32 regValue = scg->RCCR;
            regValue &= ~SCG_RCCR_SCS_MASK;
            regValue |= (SIRC_CLK_SOURCE << SCG_RCCR_SCS_SHIFT);
            scg->RCCR = regValue;
            
            timeut = 10U;
            while ((((SCG->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT) != ((uint32_t)SIRC_clock_source)) && (timeout > 0U));
            if (timeout == 0U)
            {
                retValue = STATUS_TIMEOUT;
            }
            
            /* Disable clock */
            SCG->FIRCCSR &= (uint32)(~(SCG_FIRCCSR_FIRCEN_MASK));

            /* Configure FIRC. */
            if (config->enable)
            {                
                /* Step frequency range. */
                SCG->FIRCCFG = SCG_FIRCCFG_RANGE(config->range) | SCG_FIRCCSR_FIRCREGOFF((config->regulator) ? 0UL : 1UL);

                /* Enable clock. */
                SCG->FIRCCSR |= SCG_FIRCCSR_FIRCEN(1U);      

                ClockStartTimeout(&StartTime, &ElapsedTime, &TimeoutTicks, CLOCK_TIMEOUT_VALUE_US);
                /* Wait until ircosc is locked */
                do
                {
                    Clock_Ip_TimeDelay();
                    IrcoscStatus = (((SCG->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK) >> SCG_FIRCCSR_FIRCVLD_SHIFT));
                    TimeoutOccurred = ClockTimeoutExpired(&StartTime, &ElapsedTime, TimeoutTicks);
                }
                while ((IrcoscStatus == 0U) && (FALSE == TimeoutOccurred));

                if (FALSE == TimeoutOccurred)
                {
                    UpdateClockState(config->name, ENABLED_CLOCK);
                }
                else
                {
                    /* Report timeout error */
                    ReportClockErrors(CLOCK_IP_REPORT_TIMEOUT_ERROR, config->name);
                }
                
                /* Switch back to FIRC */
                uint32 regValue = scg->RCCR;
                regValue &= ~SCG_RCCR_SCS_MASK;
                regValue |= (FIRC_CLK_SOURCE << SCG_RCCR_SCS_SHIFT);
                scg->RCCR = regValue;
                
                timeut = 10U;
                while ((((SCG->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT) != ((uint32_t)SIRC_clock_source)) && (timeout > 0U));
                if (timeout == 0U)
                {
                    retValue = STATUS_TIMEOUT;
                }

            }
            else
            {
                UpdateClockState(config->name, DISABLED_CLOCK);
            }                
        }
        else
        {
            /* Firc is used as system clock source and is configured as required */
            UpdateClockState(config->name, ENABLED_CLOCK);
        }
    }
    else
    {
        /* Configure FIRC. */
        
        /* Disable clock */
        SCG->FIRCCSR &= (uint32)(~(SCG_FIRCCSR_FIRCEN_MASK));
        
        if (config->enable)
        {                
            /* Step frequency range. */
            SCG->FIRCCFG = SCG_FIRCCFG_RANGE(config->range) | SCG_FIRCCSR_FIRCREGOFF((config->regulator) ? 0UL : 1UL);

            /* Enable clock. */
            SCG->FIRCCSR |= SCG_FIRCCSR_FIRCEN(1U);      

            ClockStartTimeout(&StartTime, &ElapsedTime, &TimeoutTicks, CLOCK_TIMEOUT_VALUE_US);
            /* Wait until ircosc is locked */
            do
            {
                Clock_Ip_TimeDelay();
                IrcoscStatus = (((SCG->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK) >> SCG_FIRCCSR_FIRCVLD_SHIFT));
                TimeoutOccurred = ClockTimeoutExpired(&StartTime, &ElapsedTime, TimeoutTicks);
            }
            while ((IrcoscStatus == 0U) && (FALSE == TimeoutOccurred));

            if (FALSE == TimeoutOccurred)
            {
                UpdateClockState(config->name, ENABLED_CLOCK);
            }
            else
            {
                /* Report timeout error */
                ReportClockErrors(CLOCK_IP_REPORT_TIMEOUT_ERROR, config->name);
            }
        }
        else
        {
            UpdateClockState(config->name, DISABLED_CLOCK);
        }    
    }

    return status;
}
#endif

#ifdef FIRC_VLP_ENABLE
static FIRCVlpEnable(Clock_Ip_IrcoscConfigType const* config)
{
    /* Clear LK bit field */
    SCG->FIRCCSR &= (uint32)(~(SCG_FIRCCSR_LK_MASK));

    /* Disable clock */
    SCG->FIRCCSR &= (uint32)(~(SCG_FIRCCSR_FIRCLPEN_MASK));

    /* Configure FIRC. */
    if (config->enable)
    {
        /* Enable clock in VLP mode */
        SCG->FIRCCSR |= SCG_FIRCCSR_FIRCLPEN(1U);      
    }
}
#endif

#ifdef FIRC_STOP_ENABLE
static FIRCStopEnable(Clock_Ip_IrcoscConfigType const* config)
{
    /* Clear LK bit field */
    SCG->FIRCCSR &= (uint32)(~(SCG_FIRCCSR_LK_MASK));

    /* Disable clock */
    SCG->FIRCCSR &= (uint32)(~(SCG_FIRCCSR_FIRCSTEN_MASK));

    /* Configure FIRC. */
    if (config->enable)
    {
        /* Enable clock in STOP mode */
        SCG->FIRCCSR |= SCG_FIRCCSR_FIRCSTEN(1U);      
    }
}
#endif

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

/*! @}*/

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