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

#include "fsl_clock.h"
/*******************************************************************************
 * Definitions
 ******************************************************************************/
/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.clock"
#endif

/*******************************************************************************
 * Variables
 ******************************************************************************/
/** External clock rate on the CLKIN pin in Hz. If not used,
    set this to 0. Otherwise, set it to the exact rate in Hz this pin is
    being driven at. */
volatile static uint32_t s_Ext_Clk_Freq = 16000000U;

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/* Get FRO 12M Clk */
static uint32_t CLOCK_GetFro12MFreq(void);
/* Get CLK 1M Clk */
static uint32_t CLOCK_GetClk1MFreq(void);
/* Get HF FRO Clk */
static uint32_t CLOCK_GetFroHfFreq(void);
/* Get CLK 48M Clk */
static uint32_t CLOCK_GetClk48MFreq(void);
/* Get CLK 16K Clk */
static uint32_t CLOCK_GetClk16KFreq(uint8_t id);
/* Get EXT OSC Clk */
static uint32_t CLOCK_GetExtClkFreq(void);
/* Get Main_Clk */
uint32_t CLOCK_GetMainClk(void);
/* Get FRO_16K */
static uint32_t CLOCK_GetFRO16KFreq(void);

/* Check if DIV is halt */
static inline bool CLOCK_IsDivHalt(uint32_t div_value)
{
    if (0U != (div_value & (1UL << 30U)))
    {
        return true;
    }
    else
    {
        return false;
    }
}

/*******************************************************************************
 * Code
 ******************************************************************************/

/* Clock Selection for IP */
/**
 * brief   Configure the clock selection muxes.
 * param   connection  : Clock to be configured.
 * return  Nothing
 */
void CLOCK_AttachClk(clock_attach_id_t connection)
{
    const uint32_t reg_offset = CLK_ATTACH_REG_OFFSET(connection);
    const uint32_t clk_sel    = CLK_ATTACH_CLK_SEL(connection);

    if (kNONE_to_NONE != connection)
    {
        CLOCK_SetClockSelect((clock_select_name_t)reg_offset, clk_sel);
    }
}

/* Return the actual clock attach id */
/**
 * brief   Get the actual clock attach id.
 * This fuction uses the offset in input attach id, then it reads the actual source value in
 * the register and combine the offset to obtain an actual attach id.
 * param   connection  : Clock attach id to get.
 * return  Clock source value.
 */
clock_attach_id_t CLOCK_GetClockAttachId(clock_attach_id_t connection)
{
    const uint32_t reg_offset = CLK_ATTACH_REG_OFFSET(connection);
    uint32_t actual_sel       = 0U;
    uint32_t clock_attach_id  = 0U;

    if (kNONE_to_NONE == connection)
    {
        return kNONE_to_NONE;
    }

    actual_sel      = CLOCK_GetClockSelect((clock_select_name_t)reg_offset);
    clock_attach_id = CLK_ATTACH_MUX(reg_offset, actual_sel);

    return (clock_attach_id_t)clock_attach_id;
}

/* Set the clock selection value */
void CLOCK_SetClockSelect(clock_select_name_t sel_name, uint32_t value)
{
    volatile uint32_t *pClkCtrl = (volatile uint32_t *)(MRCC0_BASE + (uint32_t)sel_name);
    assert(sel_name <= kCLOCK_SelMax);

    if (sel_name == kCLOCK_SelSCGSCS)
    {
        SCG0->RCCR = (SCG0->RCCR & ~(SCG_RCCR_SCS_MASK)) | SCG_RCCR_SCS(value);
        while ((SCG0->CSR & SCG_CSR_SCS_MASK) != SCG_CSR_SCS(value))
        {
        }
    }
    else
    {
        /* Unlock clock configuration */
        SYSCON->CLKUNLOCK &= ~SYSCON_CLKUNLOCK_UNLOCK_MASK;

        *pClkCtrl = value;

        /* Freeze clock configuration */
        SYSCON->CLKUNLOCK |= SYSCON_CLKUNLOCK_UNLOCK_MASK;
    }
}

/* Get the clock selection value */
uint32_t CLOCK_GetClockSelect(clock_select_name_t sel_name)
{
    volatile uint32_t *pClkCtrl = (volatile uint32_t *)(MRCC0_BASE + (uint32_t)sel_name);
    uint32_t actual_sel         = 0U;
    assert(sel_name <= kCLOCK_SelMax);

    if (sel_name == kCLOCK_SelSCGSCS)
    {
        actual_sel = (uint32_t)((SCG0->RCCR & SCG_RCCR_SCS_MASK) >> SCG_RCCR_SCS_SHIFT);
    }
    else
    {
        actual_sel = *pClkCtrl;
    }

    return actual_sel;
}

/* Set the clock divider value */
void CLOCK_SetClockDiv(clock_div_name_t div_name, uint32_t value)
{
    volatile uint32_t *pDivCtrl = (volatile uint32_t *)(MRCC0_BASE + (uint32_t)div_name);
    assert(div_name <= kCLOCK_DivMax);

    /* Unlock clock configuration */
    SYSCON->CLKUNLOCK &= ~SYSCON_CLKUNLOCK_UNLOCK_MASK;

    /* halt and reset clock dividers */
    *pDivCtrl = 0x3UL << 29U;

    if (value == 0U) /*!<  halt */
    {
        *pDivCtrl |= (1UL << 30U);
    }
    else
    {
        *pDivCtrl = (value - 1U);
    }

    /* Freeze clock configuration */
    SYSCON->CLKUNLOCK |= SYSCON_CLKUNLOCK_UNLOCK_MASK;
}

/* Get the clock divider value */
uint32_t CLOCK_GetClockDiv(clock_div_name_t div_name)
{
    volatile uint32_t *pDivCtrl = (volatile uint32_t *)(MRCC0_BASE + (uint32_t)div_name);
    assert(div_name <= kCLOCK_DivMax);

    if (((*pDivCtrl) & (1UL << 30U)) != 0U)
    {
        return 0;
    }
    else
    {
        return ((*pDivCtrl & 0xFFU) + 1U);
    }
}

/* Halt the clock divider value */
void CLOCK_HaltClockDiv(clock_div_name_t div_name)
{
    volatile uint32_t *pDivCtrl = (volatile uint32_t *)(MRCC0_BASE + (uint32_t)div_name);
    assert(div_name <= kCLOCK_DivMax);

    /* Unlock clock configuration */
    SYSCON->CLKUNLOCK &= ~SYSCON_CLKUNLOCK_UNLOCK_MASK;

    *pDivCtrl |= (1UL << 30U);

    /* Freeze clock configuration */
    SYSCON->CLKUNLOCK |= SYSCON_CLKUNLOCK_UNLOCK_MASK;
}

/* Initialize the FROHF to given frequency (48,64,96,192) */
status_t CLOCK_SetupFROHFClocking(uint32_t iFreq)
{
    uint8_t freq_select = 0x0U;
    switch (iFreq)
    {
        case 48000000U:
            freq_select = 1U;
            break;
        case 64000000U:
            freq_select = 3U;
            break;
        case 96000000U:
            freq_select = 5U;
            break;
        case 192000000U:
            freq_select = 7U;
            break;
        default:
            freq_select = 0xFU;
            break;
    }

    if (0xFU == freq_select)
    {
        return kStatus_Fail;
    }

    /* Set FIRC frequency */
    SCG0->FIRCCFG = SCG_FIRCCFG_FREQ_SEL(freq_select);

    /* Unlock FIRCCSR */
    SCG0->FIRCCSR &= ~SCG_FIRCCSR_LK_MASK;

    /* Enable CLK 48 MHz clock for peripheral use */
    SCG0->FIRCCSR |= SCG_FIRCCSR_FIRC_SCLK_PERIPH_EN_MASK;
    /* Enable FIRC HF clock for peripheral use */
    SCG0->FIRCCSR |= SCG_FIRCCSR_FIRC_FCLK_PERIPH_EN_MASK;
    /* Enable FIRC */
    SCG0->FIRCCSR |= SCG_FIRCCSR_FIRCEN_MASK;

    /* Lock FIRCCSR */
    SCG0->FIRCCSR |= SCG_FIRCCSR_LK_MASK;

    /* Wait for FIRC clock to be valid. */
    while ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK) == 0U)
    {
    }

    return kStatus_Success;
}

/* Initialize the FRO12M. */
status_t CLOCK_SetupFRO12MClocking(void)
{
    /* Unlock SIRCCSR */
    SCG0->SIRCCSR &= ~SCG_SIRCCSR_LK_MASK;

    /* Enable FRO12M clock for peripheral use */
    SCG0->SIRCCSR |= SCG_SIRCCSR_SIRC_CLK_PERIPH_EN_MASK;

    /* Lock SIRCCSR */
    SCG0->SIRCCSR |= SCG_SIRCCSR_LK_MASK;

    /* Wait for SIRC clock to be valid. */
    while ((SCG0->SIRCCSR & SCG_SIRCCSR_SIRCVLD_MASK) == 0U)
    {
    }

    return kStatus_Success;
}

/*!
 * brief   Initialize the FRO16K.
 * This function turns on FRO16K.
 * return  returns success or fail status.
 */
status_t CLOCK_SetupFRO16KClocking(uint8_t clk_16k_enable_mask)
{
    VBAT0->FROCTLA |= VBAT_FROCTLA_FRO_EN_MASK;
    VBAT0->FROLCKA |= VBAT_FROLCKA_LOCK_MASK;

    /* enable clk_16k output clock to corresponding modules according to the enable_mask. */
    VBAT0->FROCLKE |= VBAT_FROCLKE_CLKE(((uint32_t)clk_16k_enable_mask));

    return kStatus_Success;
}

/*!
 * brief   Initialize the external osc clock to given frequency.
 * param   iFreq   : Desired frequency (must be equal to exact rate in Hz)
 * return  returns success or fail status.
 */
status_t CLOCK_SetupExtClocking(uint32_t iFreq)
{
    uint8_t range = 0U;

    if ((iFreq >= 8000000U) && (iFreq < 16000000U))
    {
        range = 0U;
    }
    else if ((iFreq >= 16000000U) && (iFreq < 25000000U))
    {
        range = 1U;
    }
    else if ((iFreq >= 25000000U) && (iFreq < 40000000U))
    {
        range = 2U;
    }
    else if ((iFreq >= 40000000U) && (iFreq <= 50000000U))
    {
        range = 3U;
    }
    else
    {
        return kStatus_InvalidArgument;
    }

    /* If configure register is locked, return error. */
    if ((SCG0->SOSCCSR & SCG_SOSCCSR_LK_MASK) != 0U)
    {
        return kStatus_ReadOnly;
    }

    /* De-initializes the SCG SOSC */
    SCG0->SOSCCSR = SCG_SOSCCSR_SOSCERR_MASK;

    /* Select SOSC source (internal crystal oscillator) and Configure SOSC range */
    SCG0->SOSCCFG = SCG_SOSCCFG_EREFS_MASK | SCG_SOSCCFG_RANGE(range);

    /* Unlock SOSCCSR */
    SCG0->SOSCCSR &= ~SCG_SOSCCSR_LK_MASK;

    /* Enable SOSC clock monitor and Enable SOSC */
    SCG0->SOSCCSR |= (SCG_SOSCCSR_SOSCCM_MASK | SCG_SOSCCSR_SOSCEN_MASK);

    /* Wait for SOSC clock to be valid. */
    while ((SCG0->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) == 0U)
    {
    }

    s_Ext_Clk_Freq = iFreq;

    return kStatus_Success;
}

/*!
 * @brief   Initialize the external reference clock to given frequency.
 * param   iFreq   : Desired frequency (must be equal to exact rate in Hz)
 * return  returns success or fail status.
 */
status_t CLOCK_SetupExtRefClocking(uint32_t iFreq)
{
    
    if (iFreq > 50000000U)
    {
        return kStatus_InvalidArgument;
    }

    /* If configure register is locked, return error. */
    if ((SCG0->SOSCCSR & SCG_SOSCCSR_LK_MASK) != 0U)
    {
        return kStatus_ReadOnly;
    }

    /* De-initializes the SCG SOSC */
    SCG0->SOSCCSR = SCG_SOSCCSR_SOSCERR_MASK;

    /* Select SOSC source (external reference clock)*/
    SCG0->SOSCCFG &= ~SCG_SOSCCFG_EREFS_MASK;

    /* Unlock SOSCCSR */
    SCG0->SOSCCSR &= ~SCG_SOSCCSR_LK_MASK;

    /* Enable SOSC clock monitor and Enable SOSC */
    SCG0->SOSCCSR |= (SCG_SOSCCSR_SOSCCM_MASK | SCG_SOSCCSR_SOSCEN_MASK);

    /* Wait for SOSC clock to be valid. */
    while ((SCG0->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) == 0U)
    {
    }

    s_Ext_Clk_Freq = iFreq;

    return kStatus_Success;
}

/* Get IP Clk */
/*! brief  Return Frequency of selected clock
 *  return Frequency of selected clock
 */
uint32_t CLOCK_GetFreq(clock_name_t clockName)
{
    uint32_t freq = 0U;

    switch (clockName)
    {
        case kCLOCK_MainClk: /* MAIN_CLK */
            freq = CLOCK_GetMainClk();
            break;
        case kCLOCK_CoreSysClk: /* Core/system clock(CPU_CLK) */
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case kCLOCK_SYSTEM_CLK: /* AHB clock */
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case kCLOCK_BusClk: /* Bus clock (AHB clock) */
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case kCLOCK_ExtClk: /* External Clock */
            freq = CLOCK_GetExtClkFreq();
            break;
        case kCLOCK_FroHf: /* FROHF */
            freq = CLOCK_GetFroHfFreq();
            break;
        case kCLOCK_FroHfDiv: /* Divided by FROHF */
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case kCLOCK_Clk48M: /* CLK48M */
            freq = CLOCK_GetClk48MFreq();
            break;
        case kCLOCK_Fro12M: /* FRO12M */
            freq = CLOCK_GetFro12MFreq();
            break;
        case kCLOCK_Clk1M: /* CLK1M */
            freq = CLOCK_GetClk1MFreq();
            break;
        case kCLOCK_Fro16K: /* FRO16K */
            freq = CLOCK_GetFRO16KFreq();
            break;
        case kCLOCK_Clk16K0: /* CLK16K[0] */
            freq = CLOCK_GetClk16KFreq(0);
            break;
        case kCLOCK_Clk16K1: /* CLK16K[1] */
            freq = CLOCK_GetClk16KFreq(1);
            break;
        case kCLOCK_SLOW_CLK: /* SYSTEM_CLK divided by 4 */
            freq = CLOCK_GetCoreSysClkFreq() >> 2;
            break;
        default:
            freq = 0U;
            break;
    }
    return freq;
}

/* Get FRO 12M Clk */
/*! brief  Return Frequency of FRO 12MHz
 *  return Frequency of FRO 12MHz
 */
static uint32_t CLOCK_GetFro12MFreq(void)
{
    return ((SCG0->SIRCCSR & SCG_SIRCCSR_SIRC_CLK_PERIPH_EN_MASK) != 0U) ? 12000000U : 0U;
}

/* Get CLK 1M Clk */
/*! brief  Return Frequency of CLK 1MHz
 *  return Frequency of CLK 1MHz
 */
static uint32_t CLOCK_GetClk1MFreq(void)
{
    return 1000000U;
}

/* Get HF FRO Clk */
/*! brief  Return Frequency of High-Freq output of FRO
 *  return Frequency of High-Freq output of FRO
 */
static uint32_t CLOCK_GetFroHfFreq(void)
{
    uint32_t freq;

    if (((SCG0->FIRCCSR & SCG_FIRCCSR_FIRCEN_MASK) == 0U) ||
        ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRC_FCLK_PERIPH_EN_SHIFT) == 0U))
    {
        freq = 0U;
    }

    switch ((SCG0->FIRCCFG & SCG_FIRCCFG_FREQ_SEL_MASK) >> SCG_FIRCCFG_FREQ_SEL_SHIFT)
    {
        case 0U:
            freq = 36000000U;
            break;
        case 1U:
            freq = 48000000U;
            break;
        case 2U:
            freq = 48000000U;
            break;
        case 3U:
            freq = 64000000U;
            break;
        case 4U:
            freq = 72000000U;
            break;
        case 5U:
            freq = 96000000U;
            break;
        case 6U:
            freq = 144000000U;
            break;
        case 7U:
            freq = 192000000U;
            break;
        default:
            freq = 0U;
            break;
    }

    return freq;
}

/* Get CLK 48M Clk */
/*! brief  Return Frequency of CLK 48MHz
 *  return Frequency of CLK 48MHz
 */
static uint32_t CLOCK_GetClk48MFreq(void)
{
    return (((SCG0->FIRCCSR & SCG_FIRCCSR_FIRC_SCLK_PERIPH_EN_MASK) != 0U) ||
            ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRC_SCLK_PERIPH_EN_SHIFT) == 0U)) ?
               48000000U :
               0U;
}

/*! brief  Return Frequency of FRO16K
 *  return Frequency of FRO_16K
 */
static uint32_t CLOCK_GetFRO16KFreq(void)
{
    return ((VBAT0->FROCTLA & VBAT_FROCTLA_FRO_EN_MASK) != 0U) ? 16000U : 0U;
}
/* Get CLK 16K Clk */
/*! brief  Return Frequency of CLK 16KHz
 *  return Frequency of CLK 16KHz
 */
static uint32_t CLOCK_GetClk16KFreq(uint8_t id)
{
    return (((VBAT0->FROCTLA & VBAT_FROCTLA_FRO_EN_MASK) != 0U) &&
            ((VBAT0->FROCLKE & VBAT_FROCLKE_CLKE((((uint32_t)id) << 1U))) != 0U)) ?
               16000U :
               0U;
}

/* Get EXT OSC Clk */
/*! brief  Return Frequency of External Clock
 *  return Frequency of External Clock. If no external clock is used returns 0.
 */
static uint32_t CLOCK_GetExtClkFreq(void)
{
    return ((SCG0->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK) != 0U) ? s_Ext_Clk_Freq : 0U;
}

/* Get MAIN Clk */
/*! brief  Return Frequency of Core System
 *  return Frequency of Core System
 */
uint32_t CLOCK_GetMainClk(void)
{
    uint32_t freq = 0U;

    switch ((SCG0->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT)
    {
        case 1U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 2U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 3U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 4U:
            freq = CLOCK_GetClk16KFreq(1);
            break;
        default:
            freq = 0U;
            break;
    }

    return freq;
}

/*! brief  Return Frequency of core
 *  return Frequency of the core
 */
uint32_t CLOCK_GetCoreSysClkFreq(void)
{
    return CLOCK_GetMainClk() / ((SYSCON->AHBCLKDIV & 0xFFU) + 1U);
}

/* Get I3C Clk */
/*! brief  Return Frequency of I3C Clock
 *  return Frequency of I3C Clock
 */
uint32_t CLOCK_GetI3CFClkFreq(void)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (MRCC0->MRCC_I3C0_FCLK_CLKSEL);
    uint32_t clkdiv = (MRCC0->MRCC_I3C0_FCLK_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/* Get CTimer Clk */
/*! brief  Return Frequency of CTimer functional Clock
 *  return Frequency of CTimer functional Clock
 */
uint32_t CLOCK_GetCTimerClkFreq(uint32_t id)
{
    uint32_t freq   = 0U;
    uint32_t clksel = 0U;
    uint32_t clkdiv = 0U;

    switch (id)
    {
        case 0U:
            clksel = MRCC0->MRCC_CTIMER0_CLKSEL;
            clkdiv = MRCC0->MRCC_CTIMER0_CLKDIV;
            break;
        case 1U:
            clksel = MRCC0->MRCC_CTIMER1_CLKSEL;
            clkdiv = MRCC0->MRCC_CTIMER1_CLKDIV;
            break;
        case 2U:
            clksel = MRCC0->MRCC_CTIMER2_CLKSEL;
            clkdiv = MRCC0->MRCC_CTIMER2_CLKDIV;
            break;
        case 3U:
            clksel = MRCC0->MRCC_CTIMER3_CLKSEL;
            clkdiv = MRCC0->MRCC_CTIMER3_CLKDIV;
            break;
        case 4U:
            clksel = MRCC0->MRCC_CTIMER4_CLKSEL;
            clkdiv = MRCC0->MRCC_CTIMER4_CLKDIV;
            break;
        default:
            clksel = MRCC0->MRCC_CTIMER0_CLKSEL;
            clkdiv = MRCC0->MRCC_CTIMER0_CLKDIV;
            break;
    }

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 1U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 4U:
            freq = CLOCK_GetClk16KFreq(1);
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/* Get LPI2C Clk */
/*! brief  Return Frequency of LPI2C functional Clock
 *  return Frequency of LPI2C functional Clock
 */
uint32_t CLOCK_GetLpi2cClkFreq(uint32_t id)
{
    uint32_t freq = 0U;

    uint32_t clksel = 0U;
    uint32_t clkdiv = 0U;

    switch (id)
    {
        case 0U:
            clksel = MRCC0->MRCC_LPI2C0_CLKSEL;
            clkdiv = MRCC0->MRCC_LPI2C0_CLKDIV;
            break;
        case 1U:
            clksel = MRCC0->MRCC_LPI2C1_CLKSEL;
            clkdiv = MRCC0->MRCC_LPI2C1_CLKDIV;
            break;
        case 2U:
            clksel = MRCC0->MRCC_LPI2C2_CLKSEL;
            clkdiv = MRCC0->MRCC_LPI2C2_CLKDIV;
            break;
        case 3U:
            clksel = MRCC0->MRCC_LPI2C3_CLKSEL;
            clkdiv = MRCC0->MRCC_LPI2C3_CLKDIV;
            break;
        default:
            clksel = MRCC0->MRCC_LPI2C0_CLKSEL;
            clkdiv = MRCC0->MRCC_LPI2C0_CLKDIV;
            break;
    }

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of LPSPI Clock
 *  return Frequency of LPSPI Clock
 */
uint32_t CLOCK_GetLpspiClkFreq(uint32_t id)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (0U == id) ? (MRCC0->MRCC_LPSPI0_CLKSEL) : (MRCC0->MRCC_LPSPI1_CLKSEL);
    uint32_t clkdiv = (0U == id) ? (MRCC0->MRCC_LPSPI0_CLKDIV) : (MRCC0->MRCC_LPSPI1_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of LPUART Clock
 *  return Frequency of LPUART Clock
 */
uint32_t CLOCK_GetLpuartClkFreq(uint32_t id)
{
    uint32_t freq   = 0U;
    uint32_t clksel = 0U;
    uint32_t clkdiv = 0U;

    switch (id)
    {
        case 0U:
            clksel = MRCC0->MRCC_LPUART0_CLKSEL;
            clkdiv = MRCC0->MRCC_LPUART0_CLKDIV;
            break;
        case 1U:
            clksel = MRCC0->MRCC_LPUART1_CLKSEL;
            clkdiv = MRCC0->MRCC_LPUART1_CLKDIV;
            break;
        case 2U:
            clksel = MRCC0->MRCC_LPUART2_CLKSEL;
            clkdiv = MRCC0->MRCC_LPUART2_CLKDIV;
            break;
        case 3U:
            clksel = MRCC0->MRCC_LPUART3_CLKSEL;
            clkdiv = MRCC0->MRCC_LPUART3_CLKDIV;
            break;
        case 4U:
            clksel = MRCC0->MRCC_LPUART4_CLKSEL;
            clkdiv = MRCC0->MRCC_LPUART4_CLKDIV;
            break;
        default:
            clksel = MRCC0->MRCC_LPUART0_CLKSEL;
            clkdiv = MRCC0->MRCC_LPUART0_CLKDIV;
            break;
    }
    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 4U:
            freq = CLOCK_GetClk16KFreq(1);
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of LPTMR Clock
 *  return Frequency of LPTMR Clock
 */
uint32_t CLOCK_GetLptmrClkFreq(void)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (MRCC0->MRCC_LPTMR0_CLKSEL);
    uint32_t clkdiv = (MRCC0->MRCC_LPTMR0_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of OSTIMER
 *  return Frequency of OSTIMER Clock
 */
uint32_t CLOCK_GetOstimerClkFreq(void)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (MRCC0->MRCC_OSTIMER0_CLKSEL);

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetClk16KFreq(1);
            break;
        case 2U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq;
}

/*! brief  Return Frequency of Adc Clock
 *  return Frequency of Adc.
 */
uint32_t CLOCK_GetAdcClkFreq(uint32_t id)
{
    uint32_t freq = 0U;

    uint32_t clksel = (0U == id) ? (MRCC0->MRCC_ADC0_CLKSEL) : (MRCC0->MRCC_ADC1_CLKSEL);
    uint32_t clkdiv = (0U == id) ? (MRCC0->MRCC_ADC0_CLKDIV) : (MRCC0->MRCC_ADC1_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 1U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of Dac Clock
 *  return Frequency of Dac.
 */
uint32_t CLOCK_GetDacClkFreq(void)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (MRCC0->MRCC_DAC0_CLKSEL);
    uint32_t clkdiv = (MRCC0->MRCC_DAC0_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 1U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of CMP Function Clock
 *  return Frequency of CMP Function.
 */
uint32_t CLOCK_GetCmpFClkFreq(uint32_t id)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (0U == id) ? (MRCC0->MRCC_CMP0_RR_CLKSEL) : (MRCC0->MRCC_CMP1_RR_CLKSEL);
    uint32_t clkdiv = (0U == id) ? (MRCC0->MRCC_CMP0_FUNC_CLKDIV) : (MRCC0->MRCC_CMP1_FUNC_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of CMP Round Robin Clock
 *  return Frequency of CMP Round Robin.
 */
uint32_t CLOCK_GetCmpRRClkFreq(uint32_t id)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (0U == id) ? (MRCC0->MRCC_CMP0_RR_CLKSEL) : (MRCC0->MRCC_CMP1_RR_CLKSEL);
    uint32_t clkdiv = (0U == id) ? (MRCC0->MRCC_CMP0_RR_CLKDIV) : (MRCC0->MRCC_CMP1_RR_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 2U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of Trace Clock
 *  return Frequency of Trace.
 */
uint32_t CLOCK_GetTraceClkFreq(void)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (MRCC0->MRCC_DBG_TRACE_CLKSEL);
    uint32_t clkdiv = (MRCC0->MRCC_DBG_TRACE_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetClk1MFreq();
            break;
        case 2U:
            freq = CLOCK_GetClk16KFreq(1);
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of CLKOUT Clock
 *  return Frequency of CLKOUT.
 */
uint32_t CLOCK_GetClkoutClkFreq(void)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (MRCC0->MRCC_CLKOUT_CLKSEL);
    uint32_t clkdiv = (MRCC0->MRCC_CLKOUT_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 1U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 2U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 3U:
            freq = CLOCK_GetClk16KFreq(1);
            break;
        case 6U:
            freq = CLOCK_GetCoreSysClkFreq() >> 2;
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of Systick Clock
 *  return Frequency of Systick.
 */
uint32_t CLOCK_GetSystickClkFreq(void)
{
    uint32_t freq   = 0U;
    uint32_t clksel = (MRCC0->MRCC_SYSTICK_CLKSEL);
    uint32_t clkdiv = (MRCC0->MRCC_SYSTICK_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetCoreSysClkFreq();
            break;
        case 1U:
            freq = CLOCK_GetClk1MFreq();
            break;
        case 2U:
            freq = CLOCK_GetClk16KFreq(1);
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of Systick Clock
 *  return Frequency of Systick.
 */
uint32_t CLOCK_GetWwdtClkFreq(void)
{
    uint32_t freq   = 0U;
    uint32_t clkdiv = (MRCC0->MRCC_WWDT0_CLKDIV);

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    freq = CLOCK_GetClk1MFreq();

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of FLEXIO FCLK
 *  return Frequency of FLEXIO FCLK.
 */
uint32_t CLOCK_GetFlexioClkFreq(void)
{
    uint32_t freq = 0U;

    uint32_t clksel = MRCC0->MRCC_FLEXIO0_CLKSEL;
    uint32_t clkdiv = MRCC0->MRCC_FLEXIO0_CLKDIV;

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 0U:
            freq = CLOCK_GetFro12MFreq();
            break;
        case 1U:
            freq = CLOCK_GetFroHfFreq();
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        case 5U:
            freq = CLOCK_GetClk1MFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/*! brief  Return Frequency of FlexCAN FCLK
 *  return Frequency of FlexCAN FCLK.
 */
uint32_t CLOCK_GetFlexcanClkFreq(void)
{
    uint32_t freq = 0U;

    uint32_t clksel = MRCC0->MRCC_FLEXCAN0_CLKSEL;
    uint32_t clkdiv = MRCC0->MRCC_FLEXCAN0_CLKDIV;

    if (true == CLOCK_IsDivHalt(clkdiv))
    {
        return 0;
    }

    switch (clksel)
    {
        case 2U:
            freq = CLOCK_GetFroHfFreq() / ((MRCC0->MRCC_FRO_HF_DIV_CLKDIV & 0xfU) + 1U);
            break;
        case 3U:
            freq = CLOCK_GetExtClkFreq();
            break;
        default:
            freq = 0U;
            break;
    }

    return freq / ((clkdiv & 0xFFU) + 1U);
}

/**
 * @brief   Setup FROHF trim.
 * @param   config   : FROHF trim value
 * @return  returns success or fail status.
 */
status_t CLOCK_FROHFTrimConfig(firc_trim_config_t config)
{
    SCG0->FIRCTCFG = SCG_FIRCTCFG_TRIMDIV(config.trimDiv) | SCG_FIRCTCFG_TRIMSRC(config.trimSrc);

    if (kSCG_FircTrimNonUpdate == config.trimMode)
    {
        SCG0->FIRCSTAT = SCG_FIRCSTAT_TRIMFINE(config.trimFine);
    }

    /* Set trim mode. */
    SCG0->FIRCCSR = (uint32_t)config.trimMode;

    if ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRCERR_MASK) == SCG_FIRCCSR_FIRCERR_MASK)
    {
        return (status_t)kStatus_Fail;
    }

    return (status_t)kStatus_Success;
}

/**
 * @brief   Setup FRO 12M trim.
 * @param   config   : FRO 12M trim value
 * @return  returns success or fail status.
 */
status_t CLOCK_FRO12MTrimConfig(sirc_trim_config_t config)
{
    SCG0->SIRCTCFG = SCG_SIRCTCFG_TRIMDIV(config.trimDiv) | SCG_SIRCTCFG_TRIMSRC(config.trimSrc);

    if (kSCG_SircTrimNonUpdate == config.trimMode)
    {
        SCG0->SIRCSTAT = SCG_SIRCSTAT_CCOTRIM(config.cltrim);
        SCG0->SIRCSTAT = SCG_SIRCSTAT_CCOTRIM(config.ccotrim);
    }

    /* Set trim mode. */
    SCG0->SIRCCSR = (uint32_t)config.trimMode;

    if ((SCG0->SIRCCSR & SCG_SIRCCSR_SIRCERR_MASK) == SCG_SIRCCSR_SIRCERR_MASK)
    {
        return (status_t)kStatus_Fail;
    }

    return (status_t)kStatus_Success;
}

/*!
 * @brief Sets the system OSC monitor mode.
 *
 * This function sets the system OSC monitor mode. The mode can be disabled,
 * it can generate an interrupt when the error is disabled, or reset when the error is detected.
 *
 * @param mode Monitor mode to set.
 */
void CLOCK_SetSysOscMonitorMode(scg_sosc_monitor_mode_t mode)
{
    uint32_t reg = SCG0->SOSCCSR;

    reg &= ~(SCG_SOSCCSR_SOSCCM_MASK | SCG_SOSCCSR_SOSCCMRE_MASK);

    reg |= (uint32_t)mode;

    SCG0->SOSCCSR = reg;
}

/*! brief Enable USB FS clock.
 * Enable USB Full Speed clock.
 */
bool CLOCK_EnableUsbfsClock(void)
{
    /* Enable USB clock */
    CLOCK_EnableClock(kCLOCK_GateUSB0);

    /* Enable FROHF with 48MHZ if it is disabled */
    if (0U == (SCG0->FIRCCSR & SCG_FIRCCSR_FIRCEN_MASK))
    {
        if (kStatus_Success != CLOCK_SetupFROHFClocking(48000000U))
        {
            return false;
        }
    }

    /* Enable CLK_48 MHz clock for peripheral use */
    SCG0->FIRCCSR |= SCG_FIRCCSR_FIRC_SCLK_PERIPH_EN_MASK;

    /* Use clk_48M for USB FS */
    CLOCK_AttachClk(kCLK_48M_to_USB0);

    return true;
}
