Files
6CAR/drivers/source/clk/sdrv_ckgen.c
2026-04-18 09:16:58 +08:00

1597 lines
46 KiB
C

/**
* @file sdrv_ckgen.c
* @brief sdrv ckgen source file.
*
* @copyright Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#include <debug.h>
#include <common.h>
#include <udelay/udelay.h>
#include "sdrv_ckgen.h"
#include "sdrv_fs32k_hw_access.h"
#include "sdrv_fs24m_hw_access.h"
#include "sdrv_ckgen_hw_access.h"
#include "sdrv_pll_hw_access.h"
#include "sdrv_ckgen_common.h"
#if CONFIG_CLK_DUMP
#include "sdrv_ckgen_dump.h"
#endif
#define CKGEN_24M_RATE 24000000
#define CKGEN_SLICE_PARENT(ckgen, mux) (sdrv_ckgen_node_t *)(ckgen->parents[mux])
#ifndef CONFIG_CLK_CHECK
#define CONFIG_CLK_CHECK 0
#endif
/**
* @brief LVDS PLL ID.
*/
typedef enum sdrv_pll_id {
PLL_LVDS_CLK_0 = 0, /**< LVDS PLL CLK OUT0 */
PLL_LVDS_CLK_1 = 1, /**< LVDS PLL CLK OUT1 */
PLL_LVDS_CLK_2 = 2, /**< LVDS PLL CLK OUT2 */
PLL_LVDS_CKGEN = 3, /**< LVDS PLL CKGEN CLKIN */
} sdrv_pll_id_e;
static bool sdrv_ckgen_is_better_rate(uint32_t rate, uint32_t now, uint32_t best)
{
return ABS(now - rate) <= ABS(best - rate);
}
__UNUSED static uint32_t sdrv_ckgen_get_roundup_div(uint32_t f_ref, uint32_t freq)
{
uint32_t div;
if (0u == freq) {
return UINT32_MAX;
}
div = DIV_ROUND_UP(f_ref, freq);
/* The div greater than 1 and less than UINT32_MAX. */
return div;
}
static uint32_t sdrv_ckgen_get_closest_div(uint32_t f_ref, uint32_t freq)
{
uint32_t div;
if (0u == freq) {
return UINT32_MAX;
}
div = DIV_ROUND_CLOSEST(f_ref, freq);
/* The div greater than 1 and less than UINT32_MAX. */
return div;
}
static uint32_t sdrv_ckgen_freq_round(uint32_t freq)
{
freq /= 10U;
freq += 5U;
freq /= 10U;
freq *= 100U;
return freq;
}
#if CONFIG_CLK_CHECK
static status_t sdrv_ckgen_freq_check(uint32_t expect, uint32_t actual, uint32_t percent)
{
uint32_t err = expect / percent;
if (expect > CKGEN_24M_RATE) {
if ((actual >= (expect - err)) && (actual <= (expect + err))) {
return SDRV_STATUS_OK;
}
else {
return SDRV_CKGEN_FREQUENCY_INCORRECT;
}
}
else {
return SDRV_STATUS_OK;
}
}
#endif
static uint32_t sdrv_ckgen_get_parent_rate(sdrv_ckgen_node_t *parent)
{
uint32_t rate = 0;
if (parent->type == CKGEN_RC24M_TYPE || \
parent->type == CKGEN_FS24M_TYPE) {
rate = CKGEN_24M_RATE;
}
else if (parent->type == CKGEN_PLL_CTRL_TYPE || \
parent->type == CKGEN_PLL_LVDS_TYPE) {
rate = sdrv_pll_get_rate(parent);
}
else if (parent->type == CKGEN_IP_SLICE_TYPE || \
parent->type == CKGEN_CORE_SLICE_TYPE) {
rate = sdrv_ckgen_get_rate(parent);
}
else if (parent->type == CKGEN_BUS_SLICE_TYPE || \
parent->type == CKGEN_SF_BUS_SLICE_TYPE) {
rate = sdrv_ckgen_bus_get_rate(parent, CKGEN_BUS_CLK_OUT);
}
return rate;
}
static int sdrv_ckgen_ip_slice_select_parent(sdrv_ckgen_slice_node_t *ip_slice,
uint32_t rate, uint32_t *best_div)
{
sdrv_ckgen_node_t *parent;
uint32_t parent_rate;
uint32_t calc_rate, best_rate = 0;
uint32_t div;
int best_index = -1;
for (uint8_t i = 0; i < ip_slice->parents_num; i++) {
parent = (sdrv_ckgen_node_t *)ip_slice->parents[i];
if (!parent) {
continue;
}
parent_rate = sdrv_ckgen_get_parent_rate(parent);
parent_rate = sdrv_ckgen_freq_round(parent_rate);
if (parent_rate < rate) {
continue;
}
div = sdrv_ckgen_get_closest_div(parent_rate, rate);
if ((div - 1u) > CKGEN_IP_CTL_DIV_NUM_MASK) {
continue;
}
calc_rate = parent_rate / div;
if (sdrv_ckgen_is_better_rate(rate, calc_rate, best_rate)) {
best_rate = calc_rate;
best_index = i;
*best_div = div;
/* if choose xtal24m best, no need check pll */
if (rate == best_rate && i == 1) {
break;
}
}
}
return best_index;
}
static status_t sdrv_ckgen_ip_slice_set_rate(sdrv_ckgen_node_t *ckgen, uint32_t rate)
{
sdrv_ckgen_slice_node_t *ip_slice = (sdrv_ckgen_slice_node_t *)ckgen;
uint32_t div;
status_t ret;
int mux;
bool hw_gate;
/* select the suitable parent */
mux = sdrv_ckgen_ip_slice_select_parent(ip_slice, rate, &div);
if (mux < 0) {
return SDRV_CKGEN_SLICE_NO_SUITABLE_PARENT;
}
hw_gate = sdrv_ckgen_ip_slice_hardware_gated(ckgen->base, ckgen->id);
if (hw_gate) {
sdrv_ckgen_ip_slice_hardware_gate_enable(ckgen->base, ckgen->id, false);
}
/* set max divider frist avoid ip clock override */
ret = sdrv_ckgen_ip_slice_set_div(ckgen->base, ckgen->id, CKGEN_IP_CTL_DIV_NUM_MASK + 1U);
if (ret != SDRV_STATUS_OK) {
goto ip_exit;
}
ret = sdrv_ckgen_ip_slice_set_mux(ckgen->base, ckgen->id, mux);
if (ret != SDRV_STATUS_OK) {
goto ip_exit;
}
/* set div after change mux */
ret = sdrv_ckgen_ip_slice_set_div(ckgen->base, ckgen->id, div);
if (ret != SDRV_STATUS_OK) {
goto ip_exit;
}
#if CONFIG_CLK_CHECK
ret = sdrv_ckgen_freq_check(rate, sdrv_ckgen_ip_mon_get_rate(ckgen->base, ckgen->id), 200);
if (ret != SDRV_STATUS_OK) {
goto ip_exit;
}
#endif
ip_exit:
if (hw_gate) {
sdrv_ckgen_ip_slice_hardware_gate_enable(ckgen->base, ckgen->id, true);
}
return ret;
}
static uint32_t sdrv_ckgen_ip_slice_get_rate(sdrv_ckgen_node_t *ckgen)
{
#if !CONFIG_CLK_MONITOR
sdrv_ckgen_slice_node_t *ip_slice = (sdrv_ckgen_slice_node_t *)ckgen;
sdrv_ckgen_node_t *parent;
uint32_t parent_rate;
uint32_t div;
uint8_t mux;
mux = sdrv_ckgen_ip_slice_get_mux(ckgen->base, ckgen->id);
div = sdrv_ckgen_ip_slice_get_div(ckgen->base, ckgen->id);
parent = CKGEN_SLICE_PARENT(ip_slice, mux);
parent_rate = sdrv_ckgen_get_parent_rate(parent);
return parent_rate / (div + 1u);
#else
return sdrv_ckgen_ip_mon_get_rate(ckgen->base, ckgen->id);
#endif
}
static int sdrv_ckgen_core_slice_select_parent(sdrv_ckgen_slice_node_t *core_slice,
uint32_t rate, uint32_t *best_div)
{
sdrv_ckgen_node_t *parent;
uint32_t parent_rate;
uint32_t calc_rate, best_rate = 0;
uint32_t div;
int best_index = -1;
for (uint8_t i = 0; i < core_slice->parents_num; i++) {
parent = (sdrv_ckgen_node_t *)core_slice->parents[i];
if (!parent) {
continue;
}
parent_rate = sdrv_ckgen_get_parent_rate(parent);
parent_rate = sdrv_ckgen_freq_round(parent_rate);
if (parent_rate < rate) {
continue;
}
/* ckin4 no pre_div */
if (i == 4) {
div = 1;
}
else {
div = sdrv_ckgen_get_closest_div(parent_rate, rate);
}
if ((div - 1u) > CKGEN_CORE_CTL_DIV_NUM_MASK) {
continue;
}
calc_rate = parent_rate / div;
if (sdrv_ckgen_is_better_rate(rate, calc_rate, best_rate)) {
best_rate = calc_rate;
best_index = i;
*best_div = div;
/* if choose xtal24m best, no need check pll */
if (rate == best_rate && i == 1) {
break;
}
}
}
return best_index;
}
static status_t sdrv_ckgen_core_slice_set_rate(sdrv_ckgen_node_t *ckgen, uint32_t rate)
{
sdrv_ckgen_slice_node_t *core_slice = (sdrv_ckgen_slice_node_t *)ckgen;
uint32_t div;
status_t ret = SDRV_STATUS_OK;
int mux;
bool hw_gate;
/* select the suitable parent */
mux = sdrv_ckgen_core_slice_select_parent(core_slice, rate, &div);
if (mux < 0) {
return SDRV_CKGEN_SLICE_NO_SUITABLE_PARENT;
}
hw_gate = sdrv_ckgen_core_slice_hardware_gated(ckgen->base, ckgen->id);
if (hw_gate) {
sdrv_ckgen_core_slice_hardware_gate_enable(ckgen->base, ckgen->id, false);
}
/* if mux is 0~3, set pre div to avoid freq is too high first. */
if (mux != 4) {
ret = sdrv_ckgen_core_slice_ena_pre_div(ckgen->base, ckgen->id);
if (ret != SDRV_STATUS_OK) {
goto core_exit;
}
ret = sdrv_ckgen_core_slice_set_div(ckgen->base, ckgen->id, 2);
if (ret != SDRV_STATUS_OK) {
goto core_exit;
}
}
/* set pre mux */
ret = sdrv_ckgen_core_slice_set_mux(ckgen->base, ckgen->id, (uint8_t)mux);
if (ret != SDRV_STATUS_OK) {
goto core_exit;
}
/* if mux is 0~3, set pre div */
if (mux != 4) {
ret = sdrv_ckgen_core_slice_set_div(ckgen->base, ckgen->id, div);
if (ret != SDRV_STATUS_OK) {
goto core_exit;
}
}
#if CONFIG_CLK_CHECK
ret = sdrv_ckgen_freq_check(rate, sdrv_ckgen_core_mon_get_rate(ckgen->base, ckgen->id), 200);
if (ret != SDRV_STATUS_OK) {
goto core_exit;
}
#endif
core_exit:
if (hw_gate) {
sdrv_ckgen_core_slice_hardware_gate_enable(ckgen->base, ckgen->id, true);
}
sdrv_pmu_counter_init();
return ret;
}
static uint32_t sdrv_ckgen_core_slice_get_rate(sdrv_ckgen_node_t *ckgen)
{
#if !CONFIG_CLK_MONITOR
sdrv_ckgen_slice_node_t *core_slice = (sdrv_ckgen_slice_node_t *)ckgen;
sdrv_ckgen_node_t *parent;
uint32_t parent_rate;
uint32_t div;
uint8_t mux;
mux = sdrv_ckgen_core_slice_get_mux(ckgen->base, ckgen->id);
if (mux == 4) {
div = 1;
}
else {
div = sdrv_ckgen_core_slice_get_div(ckgen->base, ckgen->id) + 1;
}
parent = CKGEN_SLICE_PARENT(core_slice, mux);
parent_rate = sdrv_ckgen_get_parent_rate(parent);
return parent_rate / div;
#else
return sdrv_ckgen_core_mon_get_rate(ckgen->base, ckgen->id);
#endif
}
static int sdrv_ckgen_bus_slice_select_parent(sdrv_ckgen_slice_node_t *bus_slice, uint32_t expect_rate,
uint8_t *mux, uint32_t *div_pre, uint32_t *div_m)
{
uint32_t best_rate = 0;
for (uint8_t i = 0; i < bus_slice->parents_num; i++) {
sdrv_ckgen_node_t *parent;
uint32_t parent_rate;
uint32_t div, calc_rate;
parent = (sdrv_ckgen_node_t *)bus_slice->parents[i];
if (!parent) {
continue;
}
parent_rate = sdrv_ckgen_get_parent_rate(parent);
parent_rate = sdrv_ckgen_freq_round(parent_rate);
if (parent_rate < expect_rate) {
continue;
}
div = sdrv_ckgen_get_closest_div(parent_rate, expect_rate);
if (div == UINT32_MAX ||
(i == 4 && div > CKGEN_MAX_DIV_M) ||
(i != 4 && div > (CKGEN_MAX_PRE_DIV * CKGEN_MAX_DIV_M))) {
continue;
}
calc_rate = parent_rate / div;
if (sdrv_ckgen_is_better_rate(expect_rate, calc_rate, best_rate)) {
best_rate = calc_rate;
*mux = i;
if (i == 4) {
*div_pre = 1;
*div_m = div;
}
else if (div <= CKGEN_MAX_PRE_DIV) {
*div_pre = div;
*div_m = 1;
}
else {
*div_pre = CKGEN_MAX_PRE_DIV;
*div_m = div / CKGEN_MAX_PRE_DIV;
}
/* if choose xtal24m best, no need check pll */
if (expect_rate == best_rate && i == 1) {
break;
}
}
}
return (best_rate != 0) ? 0 : -1;
}
static uint32_t sdrv_ckgen_bus_slice_get_clk_out_rate(sdrv_ckgen_node_t *ckgen)
{
sdrv_ckgen_slice_node_t *bus_slice = (sdrv_ckgen_slice_node_t *)ckgen;
sdrv_ckgen_node_t *parent;
uint32_t parent_rate;
uint32_t pre_div;
uint8_t pre_mux;
/* get pre mux */
pre_mux = sdrv_ckgen_bus_slice_get_pre_mux(ckgen->base, ckgen->id);
/* get parent */
parent = CKGEN_SLICE_PARENT(bus_slice, pre_mux);
parent_rate = sdrv_ckgen_get_parent_rate(parent);
if (pre_mux == 4) {
return parent_rate;
}
else {
/* get pre div */
pre_div = sdrv_ckgen_bus_slice_get_pre_div(ckgen->base, ckgen->id);
return parent_rate / (pre_div + 1u);
}
}
static status_t sdrv_ckgen_bus_rate_check(sdrv_ckgen_node_t *ckgen, uint32_t rate,
sdrv_ckgen_bus_post_div_e post_div)
{
uint32_t axi_rate = 0;
if (ckgen->type == CKGEN_SF_BUS_SLICE_TYPE) {
axi_rate = (post_div == CKGEN_BUS_DIV_4_2_1) ? (rate / 2) : rate;
if (axi_rate > CKGEN_AXI_MAX_RATE) {
return SDRV_CKGEN_BEYOND_MAX_AXI_RATE;
}
}
return SDRV_STATUS_OK;
}
#if CONFIG_CLK_DUMP
/**
* @brief Dump system clock tree.
*
* @param [in] clk_config system total clock node list.
* @param [in] clk_node the root clock node begin to dump, if set NULL means dump all clock.
* @return SDRV_STATUS_OK or error code.
*/
status_t sdrv_clktree_dump(sdrv_clk_config_t *clk_config, sdrv_clk_t *clk_node)
{
status_t ret = SDRV_STATUS_INVALID_PARAM;
if (clk_config && clk_config->config_num) {
ret = sdrv_ckgen_construct_clktree(clk_config->config_nodes, clk_config->config_num);
if (ret == SDRV_STATUS_OK) {
ret = sdrv_ckgen_dump_clktree(clk_node);
}
}
return ret;
}
#endif /* CONFIG_CLK_DUMP */
/**
* @brief Config XTAL24M oscillator.
*
* This function enable or disable xtal24m oscillator.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS24M_TYPE.
* @param [in] enable true represents enable oscillator, false represents disable oscillator.
* @return true represents success, false represents fail.
*/
int sdrv_xtal24m_enable(sdrv_ckgen_node_t *ckgen, bool enable)
{
int ret = true;
if (ckgen->type == CKGEN_FS24M_TYPE) {
sdrv_fs24m_xtal_test_enable(ckgen->base, false);
sdrv_fs24m_xtal_enable(ckgen->base, enable);
if (enable) {
ret = sdrv_fs24m_wait_ready(ckgen->base, FS24M_FS_SEL_XTAL, 500000u);
}
return ret;
}
else {
return false;
}
}
/**
* @brief Config XTAL24M oscillator.
*
* This function config 24M clock from external active crystal.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS24M_TYPE.
* @return true represents success, false represents fail.
*/
int sdrv_xtal24m_from_active_crystal(sdrv_ckgen_node_t *ckgen)
{
if (ckgen->type == CKGEN_FS24M_TYPE) {
sdrv_fs24m_xtal_enable(ckgen->base, false);
sdrv_fs24m_xtal_test_enable(ckgen->base, true);
return sdrv_fs24m_wait_ready(ckgen->base, FS24M_FS_SEL_XTAL, 500000u);
}
else {
return false;
}
}
/**
* @brief Config FS24M clock source.
*
* This function select FS24M clock source, RC oscillator or XTAL oscillator.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS24M_TYPE.
* @param [in] src RC oscillator or XTAL oscillator
* @return true represents success, false represents fail.
*/
int sdrv_fs24m_change_src(sdrv_ckgen_node_t *ckgen, sdrv_fs_src_type_e src)
{
uint8_t hw_src;
if (ckgen->type == CKGEN_FS24M_TYPE) {
hw_src = (src == FS_SRC_RC) ? FS24M_FS_SEL_RC : FS24M_FS_SEL_XTAL;
sdrv_fs24m_fs_sel(ckgen->base, hw_src);
return sdrv_fs24m_wait_active(ckgen->base, hw_src, 500000u);
}
else {
return false;
}
}
/**
* @brief Get FS32K real output clock frequency.
*
* This function use FS24M to check FS32K clock frequency. So you must make sure
* FS24M is accurate. If you want check RC32K frequency, first change FS32K source
* to RC, then call this function. If you want check XTAL32K frequency, first change
* FS32K source to XTAL, then call this function.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS24M_TYPE.
* @return Real FS32K output frequency.
*/
uint32_t sdrv_ckgen_get_fs32k_real_frequency(sdrv_ckgen_node_t *ckgen)
{
uint32_t freq_cnt;
if (ckgen->type != CKGEN_FS24M_TYPE) {
return 0;
}
freq_cnt = sdrv_fs24m_get_32k_frequency_counter(ckgen->base);
if (freq_cnt) {
freq_cnt = CKGEN_24M_RATE / freq_cnt;
}
return freq_cnt;
}
/**
* @brief Config XTAL32K oscillator.
*
* This function enable or disable xtal32k oscillator.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS32K_TYPE.
* @param [in] enable true represents enable oscillator, false represents disable oscillator.
* @return true represents success, false represents fail.
*/
int sdrv_xtal32k_enable(sdrv_ckgen_node_t *ckgen, bool enable)
{
int ret = true;
if (ckgen->type == CKGEN_FS32K_TYPE) {
RTC_SS_ACCESS_START();
sdrv_fs32k_xtal_test_enable(ckgen->base, false);
sdrv_fs32k_xtal_enable(ckgen->base, enable);
if (enable) {
ret = sdrv_fs32k_wait_ready(ckgen->base, FS32K_FS_SEL_XTAL, 500000u);
}
FS32K_REG_DUMMY_READ(ckgen->base);
RTC_SS_ACCESS_END();
return ret;
}
else {
return false;
}
}
/**
* @brief Config XTAL32K oscillator.
*
* This function enable or disable xtal32k oscillator without wait ready status.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS32K_TYPE.
* @param [in] enable true represents enable oscillator, false represents disable oscillator.
* @return true represents success, false represents fail.
*/
int sdrv_xtal32k_enable_nowait(sdrv_ckgen_node_t *ckgen, bool enable)
{
if (ckgen->type == CKGEN_FS32K_TYPE) {
RTC_SS_ACCESS_START();
sdrv_fs32k_xtal_test_enable(ckgen->base, false);
sdrv_fs32k_xtal_enable(ckgen->base, enable);
FS32K_REG_DUMMY_READ(ckgen->base);
RTC_SS_ACCESS_END();
return true;
}
else {
return false;
}
}
/**
* @brief Get xtal32k ready status.
*
* This function get xtal32k ready status.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS32K_TYPE.
* @return true represents xtal32k ready, false represents xtal32 not ready.
*/
bool sdrv_xtal32k_get_ready_status(sdrv_ckgen_node_t *ckgen)
{
bool ret;
if (ckgen->type == CKGEN_FS32K_TYPE) {
RTC_SS_ACCESS_START();
ret = sdrv_fs32k_get_ready_status(ckgen->base, FS32K_FS_SEL_XTAL);
FS32K_REG_DUMMY_READ(ckgen->base);
RTC_SS_ACCESS_END();
return ret;
}
else {
return false;
}
}
/**
* @brief Config XTAL32K oscillator.
*
* This function config 32k clock from external active crystal.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS32K_TYPE.
* @return true represents success, false represents fail.
*/
int sdrv_xtal32k_from_active_crystal(sdrv_ckgen_node_t *ckgen)
{
if (ckgen->type == CKGEN_FS32K_TYPE) {
int ret;
RTC_SS_ACCESS_START();
sdrv_fs32k_xtal_enable(ckgen->base, false);
sdrv_fs32k_xtal_test_enable(ckgen->base, true);
ret = sdrv_fs32k_wait_ready(ckgen->base, FS32K_FS_SEL_XTAL, 500000u);
RTC_SS_ACCESS_END();
return ret;
}
else {
return false;
}
}
/**
* @brief Config FS32K clock source.
*
* This function select FS32K clock source, RC oscillator or XTAL oscillator.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS32K_TYPE.
* @param [in] src RC oscillator or XTAL oscillator
* @return true represents success, false represents fail.
*/
int sdrv_fs32k_change_src(sdrv_ckgen_node_t *ckgen, sdrv_fs_src_type_e src)
{
uint8_t hw_src;
int ret;
if (ckgen->type == CKGEN_FS32K_TYPE) {
hw_src = (src == FS_SRC_RC) ? FS32K_FS_SEL_RC : FS32K_FS_SEL_XTAL;
RTC_SS_ACCESS_START();
sdrv_fs32k_fs_sel(ckgen->base, hw_src);
ret = sdrv_fs32k_wait_active(ckgen->base, hw_src, 500000u);
RTC_SS_ACCESS_END();
return ret;
}
else {
return false;
}
}
/**
* @brief Config FS32K clock source without wait active status.
*
* This function select FS32K clock source, RC oscillator or XTAL oscillator.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS32K_TYPE.
* @param [in] src RC oscillator or XTAL oscillator
* @return true represents success, false represents fail.
*/
int sdrv_fs32k_change_src_nowait(sdrv_ckgen_node_t *ckgen, sdrv_fs_src_type_e src)
{
uint8_t hw_src;
if (ckgen->type == CKGEN_FS32K_TYPE) {
hw_src = (src == FS_SRC_RC) ? FS32K_FS_SEL_RC : FS32K_FS_SEL_XTAL;
RTC_SS_ACCESS_START();
sdrv_fs32k_fs_sel(ckgen->base, hw_src);
FS32K_REG_DUMMY_READ(ckgen->base);
RTC_SS_ACCESS_END();
return true;
}
else {
return false;
}
}
/**
* @brief Get FS32K clock source active status.
*
* This function get FS32K clock source, RC oscillator or XTAL oscillator, active status.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS32K_TYPE.
* @param [in] src RC oscillator or XTAL oscillator
* @return true represents clock source as FS32K
*/
bool sdrv_fs32k_get_src_active_status(sdrv_ckgen_node_t *ckgen, sdrv_fs_src_type_e src)
{
uint8_t hw_src;
bool ret;
if (ckgen->type == CKGEN_FS32K_TYPE) {
hw_src = (src == FS_SRC_RC) ? FS32K_FS_SEL_RC : FS32K_FS_SEL_XTAL;
RTC_SS_ACCESS_START();
ret = sdrv_fs32k_get_active_status(ckgen->base, hw_src);
FS32K_REG_DUMMY_READ(ckgen->base);
RTC_SS_ACCESS_END();
return ret;
}
else {
return false;
}
}
/**
* @brief Control low power voltage detector power on or down.
*
* @param [in] ckgen ckgen node type must be CKGEN_FS32K_TYPE.
* @param [in] power_on True or False.
* @return true represents success, false represents fail.
*/
int sdrv_fs32k_lpvd_power_ctrl(sdrv_ckgen_node_t *ckgen, bool power_on)
{
if (ckgen->type == CKGEN_FS32K_TYPE) {
RTC_SS_ACCESS_START();
sdrv_fs32k_lpvd_power_ctl(ckgen->base, power_on);
FS32K_REG_DUMMY_READ(ckgen->base);
RTC_SS_ACCESS_END();
return true;
}
else {
return false;
}
}
/**
* @brief Config PLL rate.
*
* This function config clock rate for specific PLL.
*
* @param [in] ckgen ckgen node type can be CKGEN_PLL_CTRL_TYPE or CKGEN_PLL_LVDS_TYPE.
* @param [in] rate clock rate to be set.
* @return 0 represents success, otherwise failed.
*/
status_t sdrv_pll_set_rate(sdrv_ckgen_node_t *ckgen, uint32_t rate)
{
return sdrv_pll_set_rate_with_dsm(ckgen, rate, false);
}
/**
* @brief Config PLL rate with delta-sigma modulator enable config.
*
* This function config clock rate for specific PLL, and when rate configed work as integer pll,
* it's up to user whether enable fractional.
*
* @param [in] ckgen ckgen node type can be CKGEN_PLL_CTRL_TYPE or CKGEN_PLL_LVDS_TYPE.
* @param [in] rate clock rate to be set.
* @param [in] dsm_en dsm enable or not.
* @return SDRV_STATUS_OK or error code.
*/
status_t sdrv_pll_set_rate_with_dsm(sdrv_ckgen_node_t *ckgen, uint32_t rate,
sdrv_ckgen_pll_ssc_config_t *ssc_cfg)
{
sdrv_pll_node_t *pll = (sdrv_pll_node_t *)ckgen;
uint32_t div;
status_t ret;
if (ckgen == NULL) {
return SDRV_CKGEN_POINTER_IS_NULL;
}
if (ckgen->type == CKGEN_PLL_CTRL_TYPE) {
ret = sdrv_pll_set_vco_rate(ckgen->base, CKGEN_24M_RATE, rate, ssc_cfg);
}
else if (ckgen->type == CKGEN_PLL_LVDS_TYPE) {
uint32_t parent_rate = sdrv_pll_get_rate((sdrv_ckgen_node_t *)pll->parent);
div = sdrv_ckgen_get_closest_div(parent_rate, rate);
if (div < PLL_LVDS_OUT_DIV_MIN || div > PLL_LVDS_OUT_DIV_MAX) {
return SDRV_CKGEN_BEYOND_MAX_DIVIDER;
}
if (ckgen->id == PLL_LVDS_CLK_1) {
ret = sdrv_pll_set_lvds_clk1_div(ckgen->base, div);
}
else if (ckgen->id == PLL_LVDS_CLK_2) {
ret = sdrv_pll_set_lvds_clk2_div(ckgen->base, div);
}
else if (ckgen->id == PLL_LVDS_CKGEN) {
if (div > PLL_LVDS_CKGEN_IN_DIV_MAX) {
return SDRV_CKGEN_BEYOND_MAX_DIVIDER;
}
ret = sdrv_pll_set_lvds_ckgen_div(ckgen->base, div);
}
else {
// no need, no div
ret = SDRV_STATUS_OK;
}
}
else {
ret = SDRV_CKGEN_SLICE_TYPE_ERROR;
}
return ret;
}
/**
* @brief Get PLL rate.
*
* This function get clock rate for specific PLL.
*
* @param [in] ckgen ckgen node type can be CKGEN_PLL_CTRL_TYPE or CKGEN_PLL_LVDS_TYPE.
* @return pll clock rate.
*/
uint32_t sdrv_pll_get_rate(sdrv_ckgen_node_t *ckgen)
{
sdrv_pll_node_t *pll = (sdrv_pll_node_t *)ckgen;
uint32_t rate = 0;
if (ckgen == NULL) {
return rate;
}
if (ckgen->type == CKGEN_PLL_CTRL_TYPE) {
rate = sdrv_pll_get_vco_rate(ckgen->base, CKGEN_24M_RATE);
}
else if (ckgen->type == CKGEN_PLL_LVDS_TYPE) {
uint32_t div;
uint32_t parent_rate = sdrv_pll_get_rate((sdrv_ckgen_node_t *)pll->parent);
if (ckgen->id == PLL_LVDS_CLK_0) {
rate = parent_rate;
}
else if (ckgen->id == PLL_LVDS_CLK_1) {
div = sdrv_pll_get_lvds_clk1_div(ckgen->base);
rate = (uint32_t)(parent_rate / (div + 1));
}
else if (ckgen->id == PLL_LVDS_CLK_2) {
div = sdrv_pll_get_lvds_clk2_div(ckgen->base);
rate = (uint32_t)(parent_rate / (div + 1));
}
else {
div = sdrv_pll_get_lvds_ckgen_div(ckgen->base);
rate = (uint32_t)(parent_rate / (div + 1));
}
}
return rate;
}
/**
* @brief Get PLL lock detector status.
*
* This function check whether pll is locked.
*
* @param [in] ckgen ckgen node type can be CKGEN_PLL_CTRL_TYPE.
* @return true represets locked, false represents unlocked.
*/
status_t sdrv_pll_is_locked(sdrv_ckgen_node_t *ckgen)
{
if (ckgen == NULL) {
return SDRV_CKGEN_POINTER_IS_NULL;
}
return sdrv_pll_get_lock_status(ckgen->base);
}
///**
// * @brief Set PLL spread amplitude.
// *
// * This function set SSC amplitude for specific pll.
// *
// * @param [in] ckgen ckgen node only can be CKGEN_PLL_CTRL_TYPE.
// * @param [in] amplitude SSC amplitude 0-31 represents 0.0% - 3.1%.
// * @return 0 represents success, otherwise failed.
// */
//status_t sdrv_pll_set_ssc_amplitude(sdrv_ckgen_node_t *ckgen, sdrv_ckgen_ssc_amplitude_e amplitude)
//{
// if (ckgen == NULL) {
// return SDRV_CKGEN_POINTER_IS_NULL;
// }
//
// if (ckgen->type == CKGEN_PLL_CTRL_TYPE) {
// sdrv_pll_set_dsm_ssc_dep(ckgen->base, amplitude);
// return SDRV_STATUS_OK;
// }
// else {
// return SDRV_CKGEN_SLICE_TYPE_ERROR;
// }
//}
//
///**
// * @brief Set PLL spread frequency.
// *
// * This function set SSC frequency for specific pll.
// *
// * @param [in] ckgen ckgen node only can be CKGEN_PLL_CTRL_TYPE.
// * @param [in] ssc_freq SSC modulation frequency.
// * @return 0 represents success, otherwise failed.
// */
//status_t sdrv_pll_set_ssc_frequency(sdrv_ckgen_node_t *ckgen, sdrv_ckgen_ssc_freq_e ssc_freq)
//{
// if (ckgen == NULL) {
// return SDRV_CKGEN_POINTER_IS_NULL;
// }
//
// if (ckgen->type == CKGEN_PLL_CTRL_TYPE) {
// sdrv_pll_set_dsm_ssc_freq(ckgen->base, ssc_freq);
// return SDRV_STATUS_OK;
// }
// else {
// return SDRV_CKGEN_SLICE_TYPE_ERROR;
// }
//}
//
///**
// * @brief Set PLL spread mode.
// *
// * This function set SSC mode for specific pll. This function will check PLL work mode first,
// * if pll config DSM_DISABLE, set spread mode will failed.
// *
// * @param [in] ckgen ckgen node only can be CKGEN_PLL_CTRL_TYPE.
// * @param [in] ssc_mode SSC mode.
// * @return 0 represents success, otherwise failed.
// */
//status_t sdrv_pll_set_ssc_mode(sdrv_ckgen_node_t *ckgen, sdrv_ckgen_ssc_mode_e ssc_mode)
//{
// if (ckgen == NULL) {
// return SDRV_CKGEN_POINTER_IS_NULL;
// }
//
// if (ckgen->type == CKGEN_PLL_CTRL_TYPE) {
// return sdrv_pll_set_dsm_ssc_mode(ckgen->base, ssc_mode);
// }
// else {
// return SDRV_CKGEN_SLICE_TYPE_ERROR;
// }
//}
/**
* @brief Config clock rate for Core/IP slice node.
*
* This function configures expected clock rate for specific ckgen node, it will
* search all his parent nodes, and select a closest clock rate at last.
*
* @param [in] ckgen ckgen node type can be CKGEN_IP_SLICE_TYPE or CKGEN_CORE_SLICE_TYPE.
* @param [in] rate expected clock rate.
* @return 0 represents success, otherwise failed.
*/
status_t sdrv_ckgen_set_rate(sdrv_ckgen_node_t *ckgen, uint32_t rate)
{
status_t ret;
if (ckgen == NULL) {
return SDRV_CKGEN_POINTER_IS_NULL;
}
switch (ckgen->type) {
case CKGEN_IP_SLICE_TYPE:
ret = sdrv_ckgen_ip_slice_set_rate(ckgen, rate);
break;
case CKGEN_CORE_SLICE_TYPE:
ret = sdrv_ckgen_core_slice_set_rate(ckgen, rate);
break;
default:
ret = SDRV_CKGEN_SLICE_TYPE_ERROR;
break;
}
return ret;
}
/**
* @brief Get clock rate for Core/IP slice node.
*
* This function get clock rate for CORE or IP slice node.
*
* @param [in] ckgen ckgen node type can be CKGEN_IP_SLICE_TYPE or CKGEN_CORE_SLICE_TYPE.
* @return CORE/IP clock rate.
*/
uint32_t sdrv_ckgen_get_rate(sdrv_ckgen_node_t *ckgen)
{
uint32_t rate = 0;
if (ckgen) {
switch (ckgen->type) {
case CKGEN_IP_SLICE_TYPE:
rate = sdrv_ckgen_ip_slice_get_rate(ckgen);
break;
case CKGEN_CORE_SLICE_TYPE:
rate = sdrv_ckgen_core_slice_get_rate(ckgen);
break;
default:
break;
}
}
return rate;
}
/**
* @brief Config clock rate for Bus slice node.
*
* This function configure expected clock rate for specific ckgen BUS slice node,
* since bus slice can output clock direct, or clock divide by m/n/p/q, and their ratio only
* has two option, one is 4:2:1, other is 2:2:1. Clk_out_m is for core clock rate, Clk_out_n
* is for AXI bus clock rate, Clk_out_p is for APB bus clock rate.
*
* @param [in] ckgen ckgen node type can be CKGEN_SF_BUS_SLICE_TYPE or CKGEN_BUS_SLICE_TYPE.
* @param [in] rate clock rate to be set for Clk_out_m.
* @param [in] div m/n/p,q ratio select.
* @return 0 represents success, otherwise failed.
*/
status_t sdrv_ckgen_bus_set_rate(sdrv_ckgen_node_t *ckgen, uint32_t rate,
sdrv_ckgen_bus_post_div_e post_div)
{
sdrv_ckgen_slice_node_t *bus_slice = (sdrv_ckgen_slice_node_t *)ckgen;
#if CONFIG_CLK_CHECK
sdrv_ckgen_bus_out_type_e bus_out;
#endif
uint32_t pre_div;
uint32_t m_div;
status_t ret;
uint8_t mux;
bool hw_gate;
ret = sdrv_ckgen_bus_rate_check(ckgen, rate, post_div);
if (ret != SDRV_STATUS_OK) {
return ret;
}
hw_gate = sdrv_ckgen_bus_slice_hardware_gated(ckgen->base, ckgen->id);
if (hw_gate) {
sdrv_ckgen_bus_slice_hardware_gate_enable(ckgen->base, ckgen->id, false);
}
/* force select post mux d0 */
ret = sdrv_ckgen_bus_slice_set_post_mux(ckgen->base, ckgen->id, 0);
if (ret != SDRV_STATUS_OK) {
goto bus_exit;
}
/* select best parent */
ret = sdrv_ckgen_bus_slice_select_parent(bus_slice, rate, &mux, &pre_div, &m_div);
if (ret) {
ret = SDRV_CKGEN_SLICE_NO_SUITABLE_PARENT;
goto bus_exit;
}
/* set div m/n/p to avoid freq is too high */
ret = sdrv_ckgen_bus_slice_set_mnpq_div_onetime(ckgen->base, ckgen->id, 2, 4, 8, 0);
if (ret != SDRV_STATUS_OK) {
goto bus_exit;
}
/* set pre mux */
ret = sdrv_ckgen_bus_slice_set_pre_mux(ckgen->base, ckgen->id, mux);
if (ret != SDRV_STATUS_OK) {
goto bus_exit;
}
/* if mux is 0~3, set pre div */
if (mux != 4) {
ret = sdrv_ckgen_bus_slice_set_pre_div(ckgen->base, ckgen->id, pre_div);
if (ret != SDRV_STATUS_OK) {
goto bus_exit;
}
}
if (ckgen->type == CKGEN_SF_BUS_SLICE_TYPE) {
ret = sdrv_ckgen_bus_slice_set_mnpq_div(ckgen->base, ckgen->id, CKGEN_HW_BUS_DIV_M, m_div);
if (ret != SDRV_STATUS_OK) {
goto bus_exit;
}
if (post_div == CKGEN_BUS_DIV_4_2_1) {
sdrv_ckgen_bus_slice_set_mnpq_div(ckgen->base, ckgen->id, CKGEN_HW_BUS_DIV_N, 2 * m_div);
sdrv_ckgen_bus_slice_set_mnpq_div(ckgen->base, ckgen->id, CKGEN_HW_BUS_DIV_P, 4 * m_div);
}
else {
sdrv_ckgen_bus_slice_set_mnpq_div(ckgen->base, ckgen->id, CKGEN_HW_BUS_DIV_N, 1 * m_div);
sdrv_ckgen_bus_slice_set_mnpq_div(ckgen->base, ckgen->id, CKGEN_HW_BUS_DIV_P, 2 * m_div);
}
/* set post div */
sdrv_ckgen_bus_slice_set_sf_post_div(ckgen->base, ckgen->id, post_div);
#if CONFIG_CLK_CHECK
bus_out = CKGEN_BUS_CLK_OUT_M;
#endif
}
else {
#if (CONFIG_E3 || CONFIG_D3)
if ((2 * m_div) > CKGEN_MAX_DIV_M) {
ret = SDRV_CKGEN_SLICE_NO_SUITABLE_PARENT;
goto bus_exit;
}
ret = sdrv_ckgen_bus_slice_set_mnpq_div_onetime(ckgen->base, ckgen->id, 0, m_div, 2 * m_div, 0);
#endif
#if CONFIG_E3L
ret = sdrv_ckgen_bus_slice_set_mnpq_div_onetime(ckgen->base, ckgen->id, 0, m_div, m_div, 0);
#endif
if (ret != SDRV_STATUS_OK) {
goto bus_exit;
}
#if CONFIG_CLK_CHECK
bus_out = CKGEN_BUS_CLK_OUT_N;
#endif
}
#if CONFIG_CLK_CHECK
ret = sdrv_ckgen_freq_check(rate, sdrv_ckgen_bus_mon_get_rate(ckgen->base,
ckgen->id, bus_out - CKGEN_BUS_CLK_OUT_M), 200);
if (ret != SDRV_STATUS_OK) {
goto bus_exit;
}
#endif
bus_exit:
if (hw_gate) {
sdrv_ckgen_bus_slice_hardware_gate_enable(ckgen->base, ckgen->id, true);
}
if (ckgen->type == CKGEN_SF_BUS_SLICE_TYPE) {
sdrv_pmu_counter_init();
}
return ret;
}
/**
* @brief Get clock rate for Bus slice node.
*
* This function can get Bus slice clock rate, it can get clk_in4 clock directly without divide, or
* Clk_out_m/Clk_out_n/Clk_out_p/Clk_out_q divided by m/n/p/q.
*
* @param [in] ckgen ckgen node type can be CKGEN_SF_BUS_SLICE_TYPE or CKGEN_BUS_SLICE_TYPE.
* @param [in] clk_out Clk_out/Clk_out_m/Clk_out_n/Clk_out_p/Clk_out_q.
* @return clock rate.
*/
uint32_t sdrv_ckgen_bus_get_rate(sdrv_ckgen_node_t *ckgen,
sdrv_ckgen_bus_out_type_e clk_out)
{
#if !CONFIG_CLK_MONITOR
uint32_t clk_out_rate;
uint32_t post_div;
/* get clk out rate */
clk_out_rate = sdrv_ckgen_bus_slice_get_clk_out_rate(ckgen);
/* get post div */
if (clk_out == CKGEN_BUS_CLK_OUT) {
post_div = 1;
}
else {
post_div = sdrv_ckgen_bus_slice_get_mnpq_div(ckgen->base, ckgen->id,
clk_out - CKGEN_BUS_CLK_OUT_M) + 1;
}
return clk_out_rate / post_div;
#else
return sdrv_ckgen_bus_mon_get_rate(ckgen->base, ckgen->id,
clk_out - CKGEN_BUS_CLK_OUT_M);
#endif
}
/**
* @brief Set CG NODE whether participate in low power handshake.
*
* This function config CG Node lowpower mask bit. If set to mask, cg status is ignored under low power
* handshake. Otherwize, cg status is considerd into low power handshake.
*
* @param [in] ckgen ckgen node type can be CG NODE.
* @param [in] mask true or false.
* @return 0 represents success, otherwise failed.
*/
status_t sdrv_ckgen_cg_mask(sdrv_ckgen_node_t *ckgen, bool mask)
{
status_t ret = SDRV_CKGEN_SLICE_TYPE_ERROR;
if (ckgen->type == CKGEN_PCG_TYPE) {
ret = sdrv_ckgen_xcg_lp_mask(ckgen->base, CKGEN_HW_PCG_TYPE,
ckgen->id, mask);
}
else if (ckgen->type == CKGEN_BCG_TYPE) {
ret = sdrv_ckgen_xcg_lp_mask(ckgen->base, CKGEN_HW_BCG_TYPE,
ckgen->id, mask);
}
else if (ckgen->type == CKGEN_CCG_TYPE) {
ret = sdrv_ckgen_xcg_lp_mask(ckgen->base, CKGEN_HW_CCG_TYPE,
ckgen->id, mask);
}
return ret;
}
/**
* @brief Config xcg status under run/sleep/hibernate mode.
*
* This function configure clock gating status in run/sleep/hibernate mode,
* when system enter run/sleep/hibernate mode, hardware auto enable or disable
* clock as configured.
*
* @param [in] ckgen ckgen node type can be CG NODE.
* @param [in] lp_mode Run/Sleep/Hibernate.
* @param [in] gating gating enable or disable.
* @return 0 represents success, otherwise failed.
*/
status_t sdrv_ckgen_set_gate(sdrv_ckgen_node_t *ckgen,
sdrv_ckgen_lp_mode_e lp_mode, bool gating)
{
status_t ret = SDRV_CKGEN_SLICE_TYPE_ERROR;
if (ckgen->type == CKGEN_PCG_TYPE) {
ret = sdrv_ckgen_xcg_set_gate(ckgen->base, CKGEN_HW_PCG_TYPE,
ckgen->id, lp_mode, gating);
}
else if (ckgen->type == CKGEN_BCG_TYPE) {
ret = sdrv_ckgen_xcg_set_gate(ckgen->base, CKGEN_HW_BCG_TYPE,
ckgen->id, lp_mode, gating);
}
else if (ckgen->type == CKGEN_CCG_TYPE) {
ret = sdrv_ckgen_xcg_set_gate(ckgen->base, CKGEN_HW_CCG_TYPE,
ckgen->id, lp_mode, gating);
}
else if (ckgen->type == CKGEN_PLL_CG_TYPE) {
ret = sdrv_ckgen_pll_set_gate(ckgen->base, ckgen->id, lp_mode, gating);
}
else if (ckgen->type == CKGEN_XTAL_CG_TYPE) {
ret = sdrv_ckgen_xtal_set_gate(ckgen->base, lp_mode, gating);
}
return ret;
}
/**
* @brief Config clock enable or disable in run mode.
*
* This function configure clock gating status in run mode.
*
* @param [in] ckgen ckgen node type can be CG NODE.
* @param [in] enable clock enable or disable.
* @return 0 represents success, otherwise failed.
*/
status_t sdrv_ckgen_clock_config(sdrv_ckgen_node_t *ckgen, bool enable)
{
uint32_t tout = 0;
status_t ret;
ret = sdrv_ckgen_set_gate(ckgen, (sdrv_ckgen_lp_mode_e)CKGEN_RUN_MODE, !enable);
if (ret) {
return ret;
}
while (!enable != sdrv_ckgen_is_gated(ckgen)) {
if (tout++ > 100) {
return SDRV_CKGEN_CLOCK_SET_TIMEOUT;
}
}
return SDRV_STATUS_OK;
}
/**
* @brief Config IP clock enable/disable in run/sleep/hibernate mode.
*
* This function config all xcg belongs to this IP in run/sleep/hibernate mode.
*
* @param [in] ckgen_ip CG Node list belongs to this IP.
* @param [in] mode Run/Sleep/Hibernate.
* @param [in] enable clock enable or disable.
* @return 0 represents success, otherwise failed.
*/
status_t sdrv_ckgen_ip_clock_enable(const sdrv_ckgen_node_t *ckgen_ip[],
sdrv_ckgen_lp_mode_e mode, bool enable)
{
sdrv_ckgen_node_t *node;
uint32_t i = 0;
status_t ret = SDRV_STATUS_OK;
while (ckgen_ip[i] && !ret) {
node = (sdrv_ckgen_node_t *)ckgen_ip[i];
ret = sdrv_ckgen_set_gate(node, mode, !enable);
i++;
}
return ret;
}
/**
* @brief Config PLL power down under run/sleep/hibernate mode.
*
* This function config enable or disable PLL power under run/sleep/hibernate mode.
*
* @param [in] ckgen ckgen node type can be CKGEN_PLL_CG_TYPE.
* @param [in] lp_mode Run/Sleep/Hibernate.
* @param [in] power_down power down enable or disable.
* @return 0 represents success, otherwise failed.
*/
status_t sdrv_ckgen_set_pll_power(sdrv_ckgen_node_t *ckgen,
sdrv_ckgen_lp_mode_e lp_mode, bool power_down)
{
status_t ret = SDRV_CKGEN_SLICE_TYPE_ERROR;
if (ckgen->type == CKGEN_PLL_CG_TYPE) {
ret = sdrv_ckgen_pll_set_pd(ckgen->base, ckgen->id, lp_mode, power_down);
}
return ret;
}
/**
* @brief Get ckgen xcg node gating status.
*
* This function check whether xcg node is gated.
*
* @param [in] ckgen ckgen node type can be CG NODE.
* @return true represents clock is gated, false represents clock is active.
*/
status_t sdrv_ckgen_is_gated(sdrv_ckgen_node_t *ckgen)
{
status_t gated = SDRV_CKGEN_SLICE_TYPE_ERROR;
#if !CONFIG_CLK_MONITOR
if (ckgen->type == CKGEN_PCG_TYPE) {
gated = sdrv_ckgen_xcg_is_gated(ckgen->base, CKGEN_HW_PCG_TYPE, ckgen->id);
}
else if (ckgen->type == CKGEN_BCG_TYPE) {
gated = sdrv_ckgen_xcg_is_gated(ckgen->base, CKGEN_HW_BCG_TYPE, ckgen->id);
}
else if (ckgen->type == CKGEN_CCG_TYPE) {
gated = sdrv_ckgen_xcg_is_gated(ckgen->base, CKGEN_HW_CCG_TYPE, ckgen->id);
}
#else
if (ckgen->type == CKGEN_PCG_TYPE) {
gated = sdrv_ckgen_mon_xcg_is_gated(ckgen->base, CKGEN_HW_PCG_TYPE, ckgen->id);
}
else if (ckgen->type == CKGEN_BCG_TYPE) {
gated = sdrv_ckgen_mon_xcg_is_gated(ckgen->base, CKGEN_HW_BCG_TYPE, ckgen->id);
}
else if (ckgen->type == CKGEN_CCG_TYPE) {
gated = sdrv_ckgen_mon_xcg_is_gated(ckgen->base, CKGEN_HW_CCG_TYPE, ckgen->id);
}
else if (ckgen->type == CKGEN_PLL_CG_TYPE) {
gated = sdrv_ckgen_mon_pll_is_gated(ckgen->base, ckgen->id);
}
else if (ckgen->type == CKGEN_XTAL_CG_TYPE) {
gated = sdrv_ckgen_mon_xtal_is_gated(ckgen->base);
}
#endif
return gated;
}
status_t sdrv_ckgen_slice_gated(sdrv_ckgen_node_t *ckgen)
{
status_t gated = SDRV_CKGEN_SLICE_TYPE_ERROR;
switch (ckgen->type) {
case CKGEN_SF_BUS_SLICE_TYPE:
case CKGEN_BUS_SLICE_TYPE:
gated = sdrv_ckgen_bus_slice_gated(ckgen->base, ckgen->id);
break;
case CKGEN_CORE_SLICE_TYPE:
gated = sdrv_ckgen_core_slice_gated(ckgen->base, ckgen->id);
break;
case CKGEN_IP_SLICE_TYPE:
gated = sdrv_ckgen_ip_slice_gated(ckgen->base, ckgen->id);
break;
default:
break;
}
return gated;
}
/**
* @brief Config All xcg gate or active.
*
* This function config PCG/BCG/CCG all gate or active.
*
* @param [in] xcg xcg info.
* @param [in] mode run mode.
* @param [in] gate true or false.
* @return SDRV_STATUS_OK or error code.
*/
status_t sdrv_ckgen_xcg_type_set(sdrv_ckgen_xcg_set_t *xcg, sdrv_ckgen_lp_mode_e mode, bool gate)
{
uint8_t hw_cg_type;
switch (xcg->cg_type) {
case CKGEN_PCG_TYPE:
hw_cg_type = CKGEN_HW_PCG_TYPE;
break;
case CKGEN_BCG_TYPE:
hw_cg_type = CKGEN_HW_BCG_TYPE;
break;
case CKGEN_CCG_TYPE:
hw_cg_type = CKGEN_HW_CCG_TYPE;
break;
default:
return SDRV_CKGEN_SLICE_TYPE_ERROR;
}
for (uint32_t idx = 0; idx < xcg->num; idx++) {
sdrv_ckgen_xcg_set_gate(xcg->base, hw_cg_type, idx, mode, gate);
}
return SDRV_STATUS_OK;
}
/**
* @brief System clock initialize.
*
* This function initialize all system clock as pre-defined. It will change core clock to
* 24M, then config PLL, after PLL is locked, config BUS and CORE to expect rate. After that
* config IP clock if defined, and enable or disable clock gate.
*
* @param [in] config pre-defined clock config list.
* @return 0 represents success, otherwise failed.
*/
status_t sdrv_ckgen_init(sdrv_ckgen_config_t *config)
{
uint32_t cnt;
status_t ret;
if (config == NULL) {
return SDRV_CKGEN_POINTER_IS_NULL;
}
/* bus config pre pll */
if (config->pre_bus_config) {
for (cnt = 0; cnt < config->pre_bus_config->config_num; cnt++) {
ret = sdrv_ckgen_bus_set_rate(config->pre_bus_config->config_nodes[cnt].clk_node,
config->pre_bus_config->config_nodes[cnt].rate,
config->pre_bus_config->config_nodes[cnt].post_div);
if (ret != SDRV_STATUS_OK) {
return ret;
}
}
}
/* pll config */
if (config->pll_config) {
for (cnt = 0; cnt < config->pll_config->config_num; cnt++) {
ret = sdrv_pll_set_rate_with_dsm(config->pll_config->config_nodes[cnt].clk_node,
config->pll_config->config_nodes[cnt].rate,
config->pll_config->config_nodes[cnt].node_cfg.pll);
if (ret != SDRV_STATUS_OK) {
return ret;
}
}
}
/* bus config */
if (config->bus_config) {
for (cnt = 0; cnt < config->bus_config->config_num; cnt++) {
ret = sdrv_ckgen_bus_set_rate(config->bus_config->config_nodes[cnt].clk_node,
config->bus_config->config_nodes[cnt].rate,
config->bus_config->config_nodes[cnt].post_div);
if (ret != SDRV_STATUS_OK) {
return ret;
}
}
}
/* core config */
if (config->core_config) {
for (cnt = 0; cnt < config->core_config->config_num; cnt++) {
ret = sdrv_ckgen_set_rate(config->core_config->config_nodes[cnt].clk_node,
config->core_config->config_nodes[cnt].rate);
if (ret != SDRV_STATUS_OK) {
return ret;
}
}
}
/* ip config */
if (config->ip_config) {
for (cnt = 0; cnt < config->ip_config->config_num; cnt++) {
ret = sdrv_ckgen_set_rate(config->ip_config->config_nodes[cnt].clk_node,
config->ip_config->config_nodes[cnt].rate);
if (ret != SDRV_STATUS_OK) {
return ret;
}
}
}
/* IP clock enable/disable config */
if (config->enable_config) {
for (cnt = 0; cnt < config->enable_config->config_num; cnt++) {
ret = sdrv_ckgen_ip_clock_enable(config->enable_config->config_nodes[cnt].ip_nodes,
config->enable_config->config_nodes[cnt].mode, config->enable_config->config_nodes[cnt].enable);
if (ret != SDRV_STATUS_OK) {
return ret;
}
}
}
/* gating config */
if (config->gating_config) {
for (cnt = 0; cnt < config->gating_config->config_num; cnt++) {
ret = sdrv_ckgen_set_gate(config->gating_config->config_nodes[cnt].clk_node,
config->gating_config->config_nodes[cnt].mode,
config->gating_config->config_nodes[cnt].gating);
if (ret != SDRV_STATUS_OK) {
return ret;
}
}
}
RTC_SS_ACCESS_START();
RTC_SS_ACCESS_END();
return SDRV_STATUS_OK;
}