397 lines
10 KiB
C
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
|