Files
base/drivers/source/clk/sdrv_ckgen_dump.c
2025-11-07 09:57:14 +08:00

397 lines
10 KiB
C

/**
* @file sdrv_ckgen_dump.c
* @brief semidrive ckgen dump source file.
*
* @copyright Copyright (c) 2022 Semidrive Semiconductor.
* All rights reserved.
*/
#if CONFIG_CLK_DUMP
#include <sdrv_ckgen.h>
#include <compiler.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <debug.h>
#include <bits.h>
#include <regs_base.h>
#include "sdrv_ckgen_common.h"
#include "sdrv_ckgen_dump.h"
#include "sdrv_ckgen_hw_access.h"
#include "sdrv_pll_hw_access.h"
#include "sdrv_fs24m_hw_access.h"
#include "sdrv_fs32k_hw_access.h"
static struct list_node g_root_clk;
static sdrv_ckgen_clk_t g_ckgen_clk;
static sdrv_clk_t **sdrv_ckgen_get_store_info(sdrv_ckgen_node_t *node, uint32_t *size)
{
sdrv_clk_t **store_list = NULL;
uint32_t list_num = 0;
switch (node->type) {
case CKGEN_RC24M_TYPE:
case CKGEN_FS24M_TYPE:
case CKGEN_RC32K_TYPE:
case CKGEN_FS32K_TYPE:
store_list = &g_ckgen_clk.ref_clk[0];
list_num = SDRV_CKGEN_REF_MAX_NUM;
break;
case CKGEN_PLL_CTRL_TYPE:
store_list = &g_ckgen_clk.pll_ctrl[0];
list_num = SDRV_CKGEN_PLL_CTRL_MAX_NUM;
break;
case CKGEN_PLL_LVDS_TYPE:
store_list = &g_ckgen_clk.pll_lvds[0];
list_num = SDRV_CKGEN_PLL_LVDS_MAX_NUM;
break;
case CKGEN_SF_BUS_SLICE_TYPE:
store_list = &g_ckgen_clk.sf_bus[0];
list_num = SDRV_CKGEN_SF_BUS_MAX_NUM;
break;
case CKGEN_BUS_SLICE_TYPE:
store_list = &g_ckgen_clk.bus[0];
list_num = SDRV_CKGEN_BUS_MAX_NUM;
break;
case CKGEN_CORE_SLICE_TYPE:
store_list = &g_ckgen_clk.core[0];
list_num = SDRV_CKGEN_CORE_MAX_NUM;
break;
case CKGEN_IP_SLICE_TYPE:
if (node->base == APB_CKGEN_SF_BASE) {
store_list = &g_ckgen_clk.saf_ip[0];
list_num = SDRV_CKGEN_SAF_IP_MAX_NUM;
}
else {
store_list = &g_ckgen_clk.ap_ip[0];
list_num = SDRV_CKGEN_AP_IP_MAX_NUM;
}
break;
default:
break;
}
*size = list_num;
return store_list;
}
static status_t sdrv_ckgen_add_clknode_global(sdrv_clk_t *clk)
{
sdrv_clk_t **store_list;
uint32_t list_num;
store_list = sdrv_ckgen_get_store_info((sdrv_ckgen_node_t *)clk->ckgen_ref, &list_num);
if ((store_list) && (clk->ckgen_ref->id < list_num) && (!store_list[clk->ckgen_ref->id])) {
store_list[clk->ckgen_ref->id] = clk;
return SDRV_STATUS_OK;
}
else {
return SDRV_CKGEN_CONSTRUCT_CLKTREE_FAILED;
}
}
static sdrv_clk_t *sdrv_ckgen_get_clknode_from_global(sdrv_ckgen_node_t *node)
{
sdrv_clk_t **store_list;
uint32_t list_num;
store_list = sdrv_ckgen_get_store_info(node, &list_num);
if ((store_list) && (node->id < list_num) && (store_list[node->id])) {
return store_list[node->id];
}
else {
return NULL;
}
}
static sdrv_ckgen_node_t *sdrv_ckgen_get_mux_from_register(sdrv_ckgen_slice_node_t *slice)
{
uint8_t mux = -1;
if (slice->clk_node.type == CKGEN_IP_SLICE_TYPE) {
mux = sdrv_ckgen_ip_slice_get_mux(slice->clk_node.base, slice->clk_node.id);
}
else if (slice->clk_node.type == CKGEN_CORE_SLICE_TYPE) {
mux = sdrv_ckgen_core_slice_get_mux(slice->clk_node.base, slice->clk_node.id);
}
else if (slice->clk_node.type == CKGEN_SF_BUS_SLICE_TYPE ||
slice->clk_node.type == CKGEN_BUS_SLICE_TYPE) {
mux = sdrv_ckgen_bus_slice_get_pre_mux(slice->clk_node.base, slice->clk_node.id);
}
if (mux < slice->parents_num) {
return (sdrv_ckgen_node_t *)(slice->parents[mux]);
}
else {
return NULL;
}
}
static sdrv_ckgen_node_t *sdrv_ckgen_get_parent_ckgen_node(sdrv_ckgen_node_t *node)
{
sdrv_ckgen_node_t *ret = NULL;
sdrv_ckgen_slice_node_t *slice;
sdrv_pll_node_t *pll;
switch (node->type) {
case CKGEN_PLL_CTRL_TYPE:
case CKGEN_PLL_LVDS_TYPE:
pll = (sdrv_pll_node_t *)node;
ret = (sdrv_ckgen_node_t *)(pll->parent);
break;
case CKGEN_SF_BUS_SLICE_TYPE:
case CKGEN_BUS_SLICE_TYPE:
case CKGEN_CORE_SLICE_TYPE:
case CKGEN_IP_SLICE_TYPE:
slice = (sdrv_ckgen_slice_node_t *)node;
ret = sdrv_ckgen_get_mux_from_register(slice);
break;
default:
break;
}
return ret;
}
static void sdrv_ckgen_fs_source_check(sdrv_clk_t *clk)
{
uint32_t src;
if (clk->ckgen_ref->type == CKGEN_FS24M_TYPE) {
src = sdrv_fs24m_fs_sel_get(clk->ckgen_ref->base);
if (src == FS24M_FS_SEL_XTAL) {
clk->name = "fs24m(xtal)";
}
else {
clk->name = "fs24m(rc)";
}
}
else {
RTC_SS_ACCESS_START();
src = sdrv_fs32k_fs_sel_get(clk->ckgen_ref->base);
RTC_SS_ACCESS_END();
if (src == FS32K_FS_SEL_XTAL) {
clk->name = "fs32k(xtal)";
}
else {
clk->name = "fs32k(rc)";
}
}
}
static uint32_t sdrv_ckgen_node_get_rate(sdrv_ckgen_node_t *node)
{
uint32_t rate = 0;
switch (node->type) {
case CKGEN_RC24M_TYPE:
case CKGEN_FS24M_TYPE:
rate = 24000000;
break;
case CKGEN_RC32K_TYPE:
case CKGEN_FS32K_TYPE:
rate = 32768;
break;
case CKGEN_PLL_CTRL_TYPE:
case CKGEN_PLL_LVDS_TYPE:
rate = sdrv_pll_get_rate(node);
break;
case CKGEN_SF_BUS_SLICE_TYPE:
rate = sdrv_ckgen_bus_mon_get_rate(node->base, node->id, 0);
if (rate == 0) {
rate = sdrv_ckgen_bus_get_rate(node, CKGEN_BUS_CLK_OUT_M);
}
break;
case CKGEN_BUS_SLICE_TYPE:
rate = sdrv_ckgen_bus_mon_get_rate(node->base, node->id, 1);
if (rate == 0) {
rate = sdrv_ckgen_bus_get_rate(node, CKGEN_BUS_CLK_OUT_N);
}
break;
case CKGEN_CORE_SLICE_TYPE:
rate = sdrv_ckgen_core_mon_get_rate(node->base, node->id);
if (rate == 0) {
rate = sdrv_ckgen_get_rate(node);
}
break;
case CKGEN_IP_SLICE_TYPE:
rate = sdrv_ckgen_ip_mon_get_rate(node->base, node->id);
if (rate == 0) {
rate = sdrv_ckgen_get_rate(node);
}
break;
default:
break;
}
return rate;
}
static int calculate_maxdepths(sdrv_clk_t *clk, int depth)
{
int maxdepths = depth;
int namedepth;
int tmp;
if (!clk) {
sdrv_clk_t *child = NULL;
if (list_is_empty(&g_root_clk)) {
return 0;
}
list_for_every_entry(&g_root_clk, child, sdrv_clk_t, node) {
tmp = calculate_maxdepths(child, depth);
if (tmp > maxdepths) {
maxdepths = tmp;
}
}
return maxdepths;
}
namedepth = strlen(clk->name) / 8 + 1;
maxdepths = namedepth + depth;
if (!list_is_empty(&clk->child)) {
sdrv_clk_t *child = NULL;
list_for_every_entry(&clk->child, child, sdrv_clk_t, node) {
tmp = calculate_maxdepths(child, depth + 1);
if (tmp > maxdepths) {
maxdepths = tmp;
}
}
}
return maxdepths;
}
static void print_prefix(int depth, const char *s)
{
int i;
for (i = 0; i < depth; i++) {
ssdk_printf(0, "%s", s);
}
}
static void dump_clktree_internal(sdrv_clk_t *clk, int depth, const int maxdepth)
{
int slice_gate;
if (!clk) {
sdrv_clk_t *child = NULL;
if (list_is_empty(&g_root_clk)) {
return;
}
list_for_every_entry(&g_root_clk, child, sdrv_clk_t, node) {
dump_clktree_internal(child, depth, maxdepth);
}
return;
}
print_prefix(depth, "\t");
ssdk_printf(0, "%s", clk->name);
if ((maxdepth - depth) > (int)strlen(clk->name) / 8) {
print_prefix(maxdepth - depth - strlen(clk->name) / 8, "\t");
}
slice_gate = sdrv_ckgen_slice_gated((sdrv_ckgen_node_t *)clk->ckgen_ref);
ssdk_printf(0, "%-15u%s\r\n", sdrv_ckgen_node_get_rate((sdrv_ckgen_node_t *)clk->ckgen_ref),
(slice_gate >= 0) ? (slice_gate ? "Gated" : "Active") : "");
if (!list_is_empty(&clk->child)) {
sdrv_clk_t *child = NULL;
list_for_every_entry(&clk->child, child, sdrv_clk_t, node) {
dump_clktree_internal(child, depth + 1, maxdepth);
}
}
}
status_t sdrv_ckgen_construct_clktree(sdrv_clk_t *clk_nodes, uint32_t node_num)
{
sdrv_ckgen_node_t *parent;
sdrv_clk_t *parent_clk;
sdrv_clk_t *clk;
status_t ret;
list_initialize(&g_root_clk);
memset(&g_ckgen_clk, 0x0, sizeof(sdrv_ckgen_clk_t));
for (uint32_t i = 0; i < node_num; i++) {
clk = &clk_nodes[i];
ret = sdrv_ckgen_add_clknode_global(clk);
if (ret != SDRV_STATUS_OK) {
return ret;
}
list_clear_node(&clk->node);
list_initialize(&clk->child);
}
for (uint32_t i = 0; i < node_num; i++) {
clk = &clk_nodes[i];
parent = sdrv_ckgen_get_parent_ckgen_node((sdrv_ckgen_node_t *)clk->ckgen_ref);
if (parent) {
parent_clk = sdrv_ckgen_get_clknode_from_global(parent);
if (parent_clk) {
list_add_tail(&parent_clk->child, &clk->node);
}
else {
return SDRV_CKGEN_PARENT_NODE_NULL;
}
}
else {
list_add_tail(&g_root_clk, &clk->node);
if (clk->ckgen_ref->type == CKGEN_FS24M_TYPE ||
clk->ckgen_ref->type == CKGEN_FS32K_TYPE) {
sdrv_ckgen_fs_source_check(clk);
}
}
}
return SDRV_STATUS_OK;
}
status_t sdrv_ckgen_dump_clktree(sdrv_clk_t *clk_node)
{
int maxdepths;
maxdepths = calculate_maxdepths(clk_node, 0);
dump_clktree_internal(clk_node, 0, maxdepths);
return SDRV_STATUS_OK;
}
#endif