432 lines
14 KiB
C
432 lines
14 KiB
C
/**
|
|
* @file module_driver_pcc.c
|
|
* @author Flagchip
|
|
* @brief PCC driver type definition and API
|
|
* @version 2.0.0
|
|
* @date 2024-08-20
|
|
*
|
|
* SDK Version: 2.6.0
|
|
*
|
|
|
|
* @copyright Copyright (c) 2024 Flagchip Semiconductors Co., Ltd.
|
|
*
|
|
*/
|
|
|
|
/********************************************************************************
|
|
* Revision History:
|
|
*
|
|
* Version Date Initials CR# Descriptions
|
|
* --------- ---------- ------------ ---------- ---------------
|
|
* 2.0.0 2024-08-30 Flagchip055 N/A Update file structures
|
|
********************************************************************************/
|
|
|
|
#include "module_driver_pcc.h"
|
|
|
|
#if PCC_INSTANCE_COUNT > 0U
|
|
|
|
#include "module_driver_scg.h"
|
|
|
|
|
|
/***************** mcaro *********************/
|
|
#ifndef PCC_DEV_ERROR_REPORT
|
|
#define PCC_DEV_ERROR_REPORT STD_OFF
|
|
#endif
|
|
|
|
#if PCC_DEV_ERROR_REPORT == STD_ON
|
|
#define PCC_ReportDevError(func, error) ReportDevError(PCC_MODULE_ID, func, error)
|
|
#endif
|
|
|
|
|
|
/***************** Type define *********************/
|
|
/**
|
|
* @brief PCC clock attribution.
|
|
* @param u32RegOffset: the register offset base on PCC module base address 0x4002_4000h
|
|
* @param u8ClockProperty include clock domain and clock MUX information by bit filed setting.
|
|
*/
|
|
typedef struct
|
|
{
|
|
uint32_t u32RegOffset;
|
|
uint32_t u32ClockProperty;
|
|
} PCC_ClockMapType;
|
|
|
|
/**
|
|
* @brief PCC peripheral clock index map to SCG clock source
|
|
* @param ePccClkIndex: PCC peripheral clock index
|
|
* @param eScgClkIndex SCG clock source
|
|
*/
|
|
typedef struct
|
|
{
|
|
PCC_ClkGateSrcType ePccClkIndex;
|
|
SCG_ClkSrcType eScgClkIndex;
|
|
} PCC_ClockIndexMap;
|
|
|
|
/***************** Local Variables *********************/
|
|
/**
|
|
* @brief PCC clock index to SCG clock source map.
|
|
*/
|
|
static const PCC_ClockIndexMap s_tPccClocIndexkMap[PCC_MUX_MAX_NUMBER] = {
|
|
{PCC_CLKGATE_SRC_OFF, SCG_END_OF_CLOCKS},
|
|
|
|
{PCC_CLKGATE_SRC_FOSCDIV, SCG_FOSCDIVH_CLK},
|
|
|
|
{PCC_CLKGATE_SRC_SIRCDIV, SCG_SIRCDIVH_CLK},
|
|
|
|
{PCC_CLKGATE_SRC_FIRCDIV, SCG_FIRCDIVH_CLK},
|
|
|
|
#if PCC_PLLX_CLK1_SUPPORT
|
|
#if PCC_PLLX_CLK2_SUPPORT
|
|
{PCC_CLKGATE_SRC_PLL0CLK2, SCG_PLL0CLK2_RESERVE0},
|
|
#else
|
|
{PCC_CLKGATE_SRC_PLL1CLK1, SCG_PLL1CLK1_RESERVE0},
|
|
#endif /* PCC_PLLX_CLK2_SUPPORT */
|
|
#else
|
|
{PCC_CLKGATE_SRC_RESERVE0, SCG_END_OF_CLOCKS},
|
|
#endif /* PCC_PLLX_CLK1_SUPPORT */
|
|
|
|
#if PCC_PLL1_CLK_SUPPORT
|
|
{PCC_CLKGATE_SRC_PLL1DIV, SCG_PLL1DIVH_CLK},
|
|
#else
|
|
{PCC_CLKGATE_SRC_RESERVE1, SCG_END_OF_CLOCKS},
|
|
#endif /* PCC_PLL1_CLK_SUPPORT */
|
|
|
|
{PCC_CLKGATE_SRC_PLL0DIV, SCG_PLL0DIVH_CLK},
|
|
|
|
#if PCC_PLLX_CLK1_SUPPORT
|
|
{PCC_CLKGATE_SRC_PLL0CLK1, SCG_PLL0CLK1_RESERVE0}
|
|
#else
|
|
{PCC_CLKGATE_SRC_RESERVE2, SCG_END_OF_CLOCKS}
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
/**
|
|
* @brief clock attribution map.
|
|
*/
|
|
static const PCC_ClockMapType s_tPccClockMap[PCC_END_OF_CLOCKS] = PCC_CLOCK_MAP_INFO
|
|
|
|
/***************** Local Prototype Functions *********************/
|
|
|
|
/***************** Local Functions *********************/
|
|
|
|
/***************** Global Functions *********************/
|
|
|
|
/**
|
|
* @brief get PCC function clock status and value.
|
|
*
|
|
* @param pcc_ClkSrcType eClockName: used for choose PCC clock source to query.
|
|
*/
|
|
uint32_t PCC_GetPccFunctionClock(const PCC_ClkSrcType eClockName)
|
|
{
|
|
uint8_t u8DivVal;
|
|
PCC_ClkGateSrcType eSelVal;
|
|
uint32_t u32ScgClkIndex = 0U;
|
|
uint32_t u32FunctionFreqVal = 0U;
|
|
uint32_t u32RegVal;
|
|
uint32_t u32ScgClkDivIndex;
|
|
const PCC_ClockMapType *pAttributeVal;
|
|
|
|
#if PCC_DEV_ERROR_REPORT == STD_ON
|
|
if (eClockName >= PCC_END_OF_CLOCKS)
|
|
{
|
|
PCC_ReportDevError(PCC_GET_PCC_FUNCTION_CLOCK_ID, PCC_E_PARAM_OUT_RANGE);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
|
|
/* Get peripheral PCC register value */
|
|
u32RegVal = *((volatile uint32_t *)(PCC_BASE + s_tPccClockMap[eClockName].u32RegOffset));
|
|
pAttributeVal = &s_tPccClockMap[eClockName];
|
|
/* Check peripheral PCC is valid or not and peripheral have function clock or not */
|
|
if ((PCC_CGC_MASK == (u32RegVal & PCC_CGC_MASK)) && (0U != (pAttributeVal->u32ClockProperty & PCC_PROPERTY_MUXDIV_ALL_MASK)))
|
|
{
|
|
eSelVal = (PCC_ClkGateSrcType)((uint8_t)PCC_GetSEL(u32RegVal));
|
|
/* Get PCC divide value */
|
|
if (PCC_MOUDULE_DIV_USED == (pAttributeVal->u32ClockProperty & PCC_MOUDULE_DIV_USED))
|
|
{
|
|
u8DivVal = (uint8_t)(PCC_GetDIV(u32RegVal) + (uint8_t)1U);
|
|
}
|
|
else
|
|
{
|
|
u8DivVal = 1U;
|
|
}
|
|
|
|
/* Get peripheral function clock source which is from SCG or TCLK */
|
|
if (PCC_CLKGATE_SRC_OFF == eSelVal)
|
|
{
|
|
if (PCC_FUNCCLK_MUXDIVHPIN_USED == (pAttributeVal->u32ClockProperty & PCC_FUNCCLK_MUXDIVHPIN_USED))
|
|
{
|
|
u32FunctionFreqVal = PCC_FTU_TCLK_FREQ;
|
|
}
|
|
else
|
|
{
|
|
u32FunctionFreqVal = 0U;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PCC_FUNCCLK_MUXDIVH_USED == (pAttributeVal->u32ClockProperty & PCC_FUNCCLK_MUXDIVH_USED))
|
|
{
|
|
u32ScgClkDivIndex = 0U;
|
|
}
|
|
else if (PCC_FUNCCLK_MUXDIVM_USED == (pAttributeVal->u32ClockProperty & PCC_FUNCCLK_MUXDIVM_USED))
|
|
{
|
|
u32ScgClkDivIndex = 1U;
|
|
}
|
|
else if (PCC_FUNCCLK_MUXDIVL_USED == (pAttributeVal->u32ClockProperty & PCC_FUNCCLK_MUXDIVL_USED))
|
|
{
|
|
u32ScgClkDivIndex = 2U;
|
|
}
|
|
else
|
|
{
|
|
u32ScgClkDivIndex = 0U;
|
|
}
|
|
u32ScgClkIndex = (uint32_t)s_tPccClocIndexkMap[eSelVal].eScgClkIndex + u32ScgClkDivIndex;
|
|
}
|
|
|
|
/* If clock source is valid, calculate peripheral function clock */
|
|
if ((uint32_t)SCG_END_OF_CLOCKS != u32ScgClkIndex)
|
|
{
|
|
u32FunctionFreqVal = SCG_GetScgClockFreq((SCG_ClkSrcType)u32ScgClkIndex) / u8DivVal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
u32FunctionFreqVal = 0U;
|
|
}
|
|
|
|
#if PCC_DEV_ERROR_REPORT == STD_ON
|
|
}
|
|
#endif
|
|
|
|
return u32FunctionFreqVal;
|
|
}
|
|
|
|
/**
|
|
* @brief get PCC interface clock status and value.
|
|
*
|
|
* @param pcc_ClkSrcType eClockName: used for choose PCC clock source to query.
|
|
*/
|
|
uint32_t PCC_GetPccInterfaceClock(const PCC_ClkSrcType eClockName)
|
|
{
|
|
uint32_t u32InterfaceFreqVal = 0U;
|
|
uint32_t u32AttributeVal = 0U;
|
|
|
|
#if PCC_DEV_ERROR_REPORT == STD_ON
|
|
if (eClockName > PCC_END_OF_CLOCKS)
|
|
{
|
|
PCC_ReportDevError(PCC_GET_PCC_INTERFACE_CLOCK_ID, PCC_E_PARAM_OUT_RANGE);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
|
|
u32AttributeVal = s_tPccClockMap[eClockName].u32ClockProperty;
|
|
if (PCC_CLK_DOMAIN_BUS == (u32AttributeVal & PCC_CLK_DOMAIN_BUS))
|
|
{
|
|
u32InterfaceFreqVal = SCG_GetScgClockFreq(SCG_BUS_CLK);
|
|
}
|
|
else if (PCC_CLK_DOMAIN_SLOW == (u32AttributeVal & PCC_CLK_DOMAIN_SLOW))
|
|
{
|
|
u32InterfaceFreqVal = SCG_GetScgClockFreq(SCG_SLOW_CLK);
|
|
}
|
|
else
|
|
{
|
|
u32InterfaceFreqVal = 0U;
|
|
}
|
|
|
|
#if PCC_DEV_ERROR_REPORT == STD_ON
|
|
}
|
|
#endif
|
|
|
|
return u32InterfaceFreqVal;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief set PCC one peripheral clock configuration.
|
|
*
|
|
* @param PCC_CtrlType* pConfig: the PCC initialize value point set by user.
|
|
* @return PCC_StatusType pcc function status
|
|
*/
|
|
PCC_StatusType PCC_SetPcc(const PCC_CtrlType *const pConfig)
|
|
{
|
|
PCC_StatusType eStatus = PCC_STATUS_SUCCESS;
|
|
uint8_t u8DivVal;
|
|
uint32_t u32ScgClkIndex = 0U;
|
|
uint32_t u32FreqVal = 0U;
|
|
uint32_t u32FunctionFreqVal = 0U;
|
|
uint32_t u32RegVal = 0U;
|
|
uint32_t u32ScgClkDivIndex;
|
|
uint32_t u32ExceptRegVal;
|
|
const PCC_ClockMapType *pAttributeVal;
|
|
|
|
#if PCC_DEV_ERROR_REPORT == STD_ON
|
|
if (NULL == pConfig)
|
|
{
|
|
PCC_ReportDevError(PCC_SET_PCC_ID, PCC_E_PARAM_POINTER);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
|
|
pAttributeVal = &s_tPccClockMap[pConfig->eClockName];
|
|
/* check peripheral clock domain to calculate module clock */
|
|
if (PCC_CLK_DOMAIN_BUS == (pAttributeVal->u32ClockProperty & PCC_CLK_DOMAIN_BUS))
|
|
{
|
|
u32FreqVal = SCG_GetScgClockFreq(SCG_BUS_CLK);
|
|
}
|
|
else if (PCC_CLK_DOMAIN_SLOW == (pAttributeVal->u32ClockProperty & PCC_CLK_DOMAIN_SLOW))
|
|
{
|
|
u32FreqVal = SCG_GetScgClockFreq(SCG_SLOW_CLK);
|
|
}
|
|
else
|
|
{
|
|
/* Do nothing */
|
|
}
|
|
|
|
if (0U == u32FreqVal)
|
|
{
|
|
/* Please call SCG function first */
|
|
eStatus = PCC_STATUS_CLOCK_INVALID;
|
|
}
|
|
|
|
if (PCC_STATUS_SUCCESS == eStatus)
|
|
{
|
|
/* Disable PCC gate and Unlock PCC protection */
|
|
*((volatile uint32_t *)(PCC_BASE + pAttributeVal->u32RegOffset)) &= ~(uint32_t)(PCC_CGC_MASK | PCC_DWPLK_MASK);
|
|
|
|
if (pConfig->bEn)
|
|
{
|
|
if (PCC_MOUDULE_DIV_USED == (pAttributeVal->u32ClockProperty & PCC_MOUDULE_DIV_USED))
|
|
{
|
|
u8DivVal = (uint8_t)(pConfig->eDivider + (uint8_t)1U);
|
|
u32RegVal |= PCC_DIV(pConfig->eDivider);
|
|
}
|
|
else
|
|
{
|
|
u8DivVal = 1U;
|
|
}
|
|
|
|
if (0U != (pAttributeVal->u32ClockProperty & PCC_PROPERTY_MUXDIV_ALL_MASK))
|
|
{
|
|
u32RegVal |= PCC_SEL(pConfig->eClkSrc);
|
|
}
|
|
|
|
#if PCC_DWP_SUPPORT
|
|
if (PCC_DWP_SWR_AVAILABLE == (pAttributeVal->u32ClockProperty & PCC_DWP_SWR_AVAILABLE))
|
|
{
|
|
u32RegVal |= PCC_DWP(pConfig->eCtrlOwner);
|
|
}
|
|
#endif
|
|
|
|
if (PCC_CGC_AVAILABLE == (pAttributeVal->u32ClockProperty & PCC_CGC_AVAILABLE))
|
|
{
|
|
/* Enable peripheral clock */
|
|
u32RegVal |= PCC_CGC_MASK;
|
|
}
|
|
|
|
/* Configure PCC register */
|
|
*((volatile uint32_t *)(PCC_BASE + pAttributeVal->u32RegOffset)) = u32RegVal;
|
|
|
|
#if PCC_DWPLK_SUPPORT
|
|
/* Configure DWPLK */
|
|
if (pConfig->bLockCtrl)
|
|
{
|
|
u32RegVal |= PCC_DWPLK_MASK;
|
|
|
|
/* DWPLK need to be set after DWP */
|
|
*((volatile uint32_t *)(PCC_BASE + pAttributeVal->u32RegOffset)) = u32RegVal;
|
|
}
|
|
#endif
|
|
|
|
/* Get peripheral function clock source which is from SCG or TCLK */
|
|
if (PCC_CLKGATE_SRC_OFF == pConfig->eClkSrc)
|
|
{
|
|
if (PCC_FUNCCLK_MUXDIVHPIN_USED == (pAttributeVal->u32ClockProperty & PCC_FUNCCLK_MUXDIVHPIN_USED))
|
|
{
|
|
u32FunctionFreqVal = PCC_FTU_TCLK_FREQ;
|
|
}
|
|
else
|
|
{
|
|
u32FunctionFreqVal = 0U;
|
|
u8DivVal = 0U;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (PCC_FUNCCLK_MUXDIVH_USED == (pAttributeVal->u32ClockProperty & PCC_FUNCCLK_MUXDIVH_USED))
|
|
{
|
|
u32ScgClkDivIndex = 0U;
|
|
}
|
|
else if (PCC_FUNCCLK_MUXDIVM_USED == (pAttributeVal->u32ClockProperty & PCC_FUNCCLK_MUXDIVM_USED))
|
|
{
|
|
u32ScgClkDivIndex = 1U;
|
|
}
|
|
else if (PCC_FUNCCLK_MUXDIVL_USED == (pAttributeVal->u32ClockProperty & PCC_FUNCCLK_MUXDIVL_USED))
|
|
{
|
|
u32ScgClkDivIndex = 2U;
|
|
}
|
|
else
|
|
{
|
|
u32ScgClkDivIndex = 0U;
|
|
}
|
|
u32ScgClkIndex = (uint32_t)s_tPccClocIndexkMap[pConfig->eClkSrc].eScgClkIndex + u32ScgClkDivIndex;
|
|
}
|
|
|
|
/* Calculate peripheral function clock */
|
|
if (((uint32_t)SCG_END_OF_CLOCKS != u32ScgClkIndex) && (0U != u8DivVal))
|
|
{
|
|
u32FunctionFreqVal = SCG_GetScgClockFreq((SCG_ClkSrcType)u32ScgClkIndex) / u8DivVal;
|
|
}
|
|
u32ExceptRegVal = u32RegVal;
|
|
}
|
|
else
|
|
{
|
|
/* Clear PCC register */
|
|
*((volatile uint32_t *)(PCC_BASE + pAttributeVal->u32RegOffset)) = 0U;
|
|
u32ExceptRegVal = 0U;
|
|
u32FunctionFreqVal = 0U;
|
|
}
|
|
|
|
/* Check PCC register has been configured, If current CPU do not have permission to configure the PCC register, the except value
|
|
* will not match the actual value */
|
|
if (u32ExceptRegVal != *((volatile uint32_t *)(PCC_BASE + pAttributeVal->u32RegOffset)))
|
|
{
|
|
/* In this case, current core does not have permission to control the register */
|
|
eStatus = PCC_STATUS_CONFIGURED_NOT_SUPPORT;
|
|
}
|
|
else if (u32FunctionFreqVal > PCC_FUNCTION_CLOCK_MAX)
|
|
{
|
|
/* In this case, current function clock too high, must configured below 150M */
|
|
eStatus = PCC_STATUS_CONFIGURED_NOT_SUPPORT;
|
|
}
|
|
else
|
|
{
|
|
/* do nothing */
|
|
}
|
|
}
|
|
|
|
#if PCC_DEV_ERROR_REPORT == STD_ON
|
|
}
|
|
#endif
|
|
|
|
return eStatus;
|
|
}
|
|
|
|
/**
|
|
* @brief Generate peripheral reset
|
|
*
|
|
*/
|
|
void PCC_GenPeripheralReset(const PCC_ClkSrcType eClockName)
|
|
{
|
|
/* Close CGC */
|
|
*((volatile uint32_t *)(PCC_BASE + s_tPccClockMap[eClockName].u32RegOffset)) &= ~(uint32_t)PCC_CGC_MASK;
|
|
|
|
*((volatile uint32_t *)(PCC_BASE + s_tPccClockMap[eClockName].u32RegOffset)) |= (uint32_t)PCC_SWR_MASK;
|
|
*((volatile uint32_t *)(PCC_BASE + s_tPccClockMap[eClockName].u32RegOffset)) &= ~(uint32_t)PCC_SWR_MASK;
|
|
}
|
|
|
|
#endif /* #if PCC_INSTANCE_COUNT > 0U */
|