PeripheralDriver_Flagchip_F.../Src/module_driver_pcc.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 */