1597 lines
46 KiB
C
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;
|
|
}
|