/** * @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 #include #include #include #include #include #include #include #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