12305544bSClément Léger // SPDX-License-Identifier: BSD-2-Clause 22305544bSClément Léger /* 32305544bSClément Léger * Copyright (c) 2021, Bootlin 4cd04d138SEtienne Carriere * Copyright (c) 2023, STMicroelectronics 52305544bSClément Léger */ 62305544bSClément Léger 7cd04d138SEtienne Carriere #include <config.h> 82305544bSClément Léger #include <drivers/clk.h> 92305544bSClément Léger #include <kernel/boot.h> 10*9a3248fcSEtienne Carriere #include <kernel/mutex_pm_aware.h> 112305544bSClément Léger #include <kernel/panic.h> 12*9a3248fcSEtienne Carriere #include <kernel/thread.h> 132305544bSClément Léger #include <malloc.h> 142305544bSClément Léger #include <stddef.h> 15cd04d138SEtienne Carriere #include <stdio.h> 162305544bSClément Léger 17*9a3248fcSEtienne Carriere /* Global clock tree access protection complying the power state transitions */ 18*9a3248fcSEtienne Carriere static struct mutex_pm_aware mu = MUTEX_PM_AWARE_INITIALIZER; 192305544bSClément Léger 20cd04d138SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE 212b13eca6SEtienne Carriere static SLIST_HEAD(, clk) clock_list = SLIST_HEAD_INITIALIZER(clock_list); 22cd04d138SEtienne Carriere #endif 23cd04d138SEtienne Carriere 24*9a3248fcSEtienne Carriere static void lock_clk(void) 25*9a3248fcSEtienne Carriere { 26*9a3248fcSEtienne Carriere mutex_pm_aware_lock(&mu); 27*9a3248fcSEtienne Carriere } 28*9a3248fcSEtienne Carriere 29*9a3248fcSEtienne Carriere static void unlock_clk(void) 30*9a3248fcSEtienne Carriere { 31*9a3248fcSEtienne Carriere mutex_pm_aware_unlock(&mu); 32*9a3248fcSEtienne Carriere } 33*9a3248fcSEtienne Carriere 342305544bSClément Léger struct clk *clk_alloc(const char *name, const struct clk_ops *ops, 352305544bSClément Léger struct clk **parent_clks, size_t parent_count) 362305544bSClément Léger { 372305544bSClément Léger struct clk *clk = NULL; 382305544bSClément Léger size_t parent = 0; 392305544bSClément Léger 402305544bSClément Léger clk = calloc(1, sizeof(*clk) + parent_count * sizeof(clk)); 412305544bSClément Léger if (!clk) 422305544bSClément Léger return NULL; 432305544bSClément Léger 442305544bSClément Léger clk->num_parents = parent_count; 452305544bSClément Léger for (parent = 0; parent < parent_count; parent++) 462305544bSClément Léger clk->parents[parent] = parent_clks[parent]; 472305544bSClément Léger 482305544bSClément Léger clk->name = name; 492305544bSClément Léger clk->ops = ops; 502305544bSClément Léger refcount_set(&clk->enabled_count, 0); 512305544bSClément Léger 522305544bSClément Léger return clk; 532305544bSClément Léger } 542305544bSClément Léger 552305544bSClément Léger void clk_free(struct clk *clk) 562305544bSClément Léger { 572305544bSClément Léger free(clk); 582305544bSClément Léger } 592305544bSClément Léger 602305544bSClément Léger static bool __maybe_unused clk_check(struct clk *clk) 612305544bSClément Léger { 62bff6a848SEtienne Carriere if (!clk || !clk->ops) 632305544bSClément Léger return false; 642305544bSClément Léger 652305544bSClément Léger if (clk->ops->set_parent && !clk->ops->get_parent) 662305544bSClément Léger return false; 672305544bSClément Léger 682305544bSClément Léger if (clk->num_parents > 1 && !clk->ops->get_parent) 692305544bSClément Léger return false; 702305544bSClément Léger 712305544bSClément Léger return true; 722305544bSClément Léger } 732305544bSClément Léger 742305544bSClément Léger static void clk_compute_rate_no_lock(struct clk *clk) 752305544bSClément Léger { 762305544bSClément Léger unsigned long parent_rate = 0; 772305544bSClément Léger 782305544bSClément Léger if (clk->parent) 792305544bSClément Léger parent_rate = clk->parent->rate; 802305544bSClément Léger 812305544bSClément Léger if (clk->ops->get_rate) 822305544bSClément Léger clk->rate = clk->ops->get_rate(clk, parent_rate); 832305544bSClément Léger else 842305544bSClément Léger clk->rate = parent_rate; 852305544bSClément Léger } 862305544bSClément Léger 872305544bSClément Léger struct clk *clk_get_parent_by_index(struct clk *clk, size_t pidx) 882305544bSClément Léger { 892305544bSClément Léger if (pidx >= clk->num_parents) 902305544bSClément Léger return NULL; 912305544bSClément Léger 922305544bSClément Léger return clk->parents[pidx]; 932305544bSClément Léger } 942305544bSClément Léger 952305544bSClément Léger static void clk_init_parent(struct clk *clk) 962305544bSClément Léger { 972305544bSClément Léger size_t pidx = 0; 982305544bSClément Léger 992305544bSClément Léger switch (clk->num_parents) { 1002305544bSClément Léger case 0: 1012305544bSClément Léger break; 1022305544bSClément Léger case 1: 1032305544bSClément Léger clk->parent = clk->parents[0]; 1042305544bSClément Léger break; 1052305544bSClément Léger default: 1062305544bSClément Léger pidx = clk->ops->get_parent(clk); 1072305544bSClément Léger assert(pidx < clk->num_parents); 1082305544bSClément Léger 1092305544bSClément Léger clk->parent = clk->parents[pidx]; 1102305544bSClément Léger break; 1112305544bSClément Léger } 1122305544bSClément Léger } 1132305544bSClément Léger 1142305544bSClément Léger TEE_Result clk_register(struct clk *clk) 1152305544bSClément Léger { 1162305544bSClément Léger assert(clk_check(clk)); 1172305544bSClément Léger 1182305544bSClément Léger clk_init_parent(clk); 1192305544bSClément Léger clk_compute_rate_no_lock(clk); 1202305544bSClément Léger 121cd04d138SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE 1222b13eca6SEtienne Carriere SLIST_INSERT_HEAD(&clock_list, clk, link); 123cd04d138SEtienne Carriere #endif 124cd04d138SEtienne Carriere 1252305544bSClément Léger DMSG("Registered clock %s, freq %lu", clk->name, clk_get_rate(clk)); 1262305544bSClément Léger 1272305544bSClément Léger return TEE_SUCCESS; 1282305544bSClément Léger } 1292305544bSClément Léger 1302305544bSClément Léger static bool clk_is_enabled_no_lock(struct clk *clk) 1312305544bSClément Léger { 1322305544bSClément Léger return refcount_val(&clk->enabled_count) != 0; 1332305544bSClément Léger } 1342305544bSClément Léger 1356c9ed842SEtienne Carriere bool clk_is_enabled(struct clk *clk) 1366c9ed842SEtienne Carriere { 1376c9ed842SEtienne Carriere return clk_is_enabled_no_lock(clk); 1386c9ed842SEtienne Carriere } 1396c9ed842SEtienne Carriere 1402305544bSClément Léger static void clk_disable_no_lock(struct clk *clk) 1412305544bSClément Léger { 1422305544bSClément Léger struct clk *parent = NULL; 1432305544bSClément Léger 1442305544bSClément Léger if (!refcount_dec(&clk->enabled_count)) 1452305544bSClément Léger return; 1462305544bSClément Léger 1472305544bSClément Léger if (clk->ops->disable) 1482305544bSClément Léger clk->ops->disable(clk); 1492305544bSClément Léger 1502305544bSClément Léger parent = clk_get_parent(clk); 1512305544bSClément Léger if (parent) 1522305544bSClément Léger clk_disable_no_lock(parent); 1532305544bSClément Léger } 1542305544bSClément Léger 1552305544bSClément Léger static TEE_Result clk_enable_no_lock(struct clk *clk) 1562305544bSClément Léger { 1572305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC; 1582305544bSClément Léger struct clk *parent = NULL; 1592305544bSClément Léger 1602305544bSClément Léger if (refcount_inc(&clk->enabled_count)) 1612305544bSClément Léger return TEE_SUCCESS; 1622305544bSClément Léger 1632305544bSClément Léger parent = clk_get_parent(clk); 1642305544bSClément Léger if (parent) { 1652305544bSClément Léger res = clk_enable_no_lock(parent); 1662305544bSClément Léger if (res) 1672305544bSClément Léger return res; 1682305544bSClément Léger } 1692305544bSClément Léger 1702305544bSClément Léger if (clk->ops->enable) { 1712305544bSClément Léger res = clk->ops->enable(clk); 1722305544bSClément Léger if (res) { 1732305544bSClément Léger if (parent) 1742305544bSClément Léger clk_disable_no_lock(parent); 1752305544bSClément Léger 1762305544bSClément Léger return res; 1772305544bSClément Léger } 1782305544bSClément Léger } 1792305544bSClément Léger 1802305544bSClément Léger refcount_set(&clk->enabled_count, 1); 1812305544bSClément Léger 1822305544bSClément Léger return TEE_SUCCESS; 1832305544bSClément Léger } 1842305544bSClément Léger 1852305544bSClément Léger TEE_Result clk_enable(struct clk *clk) 1862305544bSClément Léger { 1872305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC; 1882305544bSClément Léger 189*9a3248fcSEtienne Carriere lock_clk(); 1902305544bSClément Léger res = clk_enable_no_lock(clk); 191*9a3248fcSEtienne Carriere unlock_clk(); 1922305544bSClément Léger 1932305544bSClément Léger return res; 1942305544bSClément Léger } 1952305544bSClément Léger 1962305544bSClément Léger void clk_disable(struct clk *clk) 1972305544bSClément Léger { 198*9a3248fcSEtienne Carriere lock_clk(); 1992305544bSClément Léger clk_disable_no_lock(clk); 200*9a3248fcSEtienne Carriere unlock_clk(); 2012305544bSClément Léger } 2022305544bSClément Léger 2032305544bSClément Léger unsigned long clk_get_rate(struct clk *clk) 2042305544bSClément Léger { 2052305544bSClément Léger return clk->rate; 2062305544bSClément Léger } 2072305544bSClément Léger 2082305544bSClément Léger static TEE_Result clk_set_rate_no_lock(struct clk *clk, unsigned long rate) 2092305544bSClément Léger { 2102305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC; 2112305544bSClément Léger unsigned long parent_rate = 0; 2122305544bSClément Léger 2132305544bSClément Léger if (clk->parent) 2142305544bSClément Léger parent_rate = clk_get_rate(clk->parent); 2152305544bSClément Léger 2160ba7ae74SEtienne Carriere assert(!(clk->flags & CLK_SET_RATE_PARENT) || clk->parent); 2170ba7ae74SEtienne Carriere if (clk->flags & CLK_SET_RATE_PARENT) { 2180ba7ae74SEtienne Carriere res = clk_set_rate_no_lock(clk->parent, rate); 2190ba7ae74SEtienne Carriere if (res) 2200ba7ae74SEtienne Carriere return res; 2210ba7ae74SEtienne Carriere rate = clk_get_rate(clk->parent); 2220ba7ae74SEtienne Carriere } 2230ba7ae74SEtienne Carriere 2240ba7ae74SEtienne Carriere if (clk->ops->set_rate) { 22520f97d98SEtienne Carriere if (clk->flags & CLK_SET_RATE_UNGATE) { 22620f97d98SEtienne Carriere res = clk_enable_no_lock(clk); 22720f97d98SEtienne Carriere if (res) 22820f97d98SEtienne Carriere return res; 22920f97d98SEtienne Carriere } 23020f97d98SEtienne Carriere 2312305544bSClément Léger res = clk->ops->set_rate(clk, rate, parent_rate); 23220f97d98SEtienne Carriere 23320f97d98SEtienne Carriere if (clk->flags & CLK_SET_RATE_UNGATE) 23420f97d98SEtienne Carriere clk_disable_no_lock(clk); 23520f97d98SEtienne Carriere 2362305544bSClément Léger if (res) 2372305544bSClément Léger return res; 2380ba7ae74SEtienne Carriere } 2392305544bSClément Léger 2402305544bSClément Léger clk_compute_rate_no_lock(clk); 2412305544bSClément Léger 2422305544bSClément Léger return TEE_SUCCESS; 2432305544bSClément Léger } 2442305544bSClément Léger 2452305544bSClément Léger TEE_Result clk_set_rate(struct clk *clk, unsigned long rate) 2462305544bSClément Léger { 2472305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC; 2482305544bSClément Léger 249*9a3248fcSEtienne Carriere lock_clk(); 2502305544bSClément Léger 2512305544bSClément Léger if (clk->flags & CLK_SET_RATE_GATE && clk_is_enabled_no_lock(clk)) 2522305544bSClément Léger res = TEE_ERROR_BAD_STATE; 2532305544bSClément Léger else 2542305544bSClément Léger res = clk_set_rate_no_lock(clk, rate); 2552305544bSClément Léger 256*9a3248fcSEtienne Carriere unlock_clk(); 2572305544bSClément Léger 2582305544bSClément Léger return res; 2592305544bSClément Léger } 2602305544bSClément Léger 2612305544bSClément Léger struct clk *clk_get_parent(struct clk *clk) 2622305544bSClément Léger { 2632305544bSClément Léger return clk->parent; 2642305544bSClément Léger } 2652305544bSClément Léger 2662305544bSClément Léger static TEE_Result clk_get_parent_idx(struct clk *clk, struct clk *parent, 2672305544bSClément Léger size_t *pidx) 2682305544bSClément Léger { 2692305544bSClément Léger size_t i = 0; 2702305544bSClément Léger 2712305544bSClément Léger for (i = 0; i < clk_get_num_parents(clk); i++) { 2722305544bSClément Léger if (clk_get_parent_by_index(clk, i) == parent) { 2732305544bSClément Léger *pidx = i; 2742305544bSClément Léger return TEE_SUCCESS; 2752305544bSClément Léger } 2762305544bSClément Léger } 2772305544bSClément Léger EMSG("Clock %s is not a parent of clock %s", parent->name, clk->name); 2782305544bSClément Léger 2792305544bSClément Léger return TEE_ERROR_BAD_PARAMETERS; 2802305544bSClément Léger } 2812305544bSClément Léger 2822305544bSClément Léger static TEE_Result clk_set_parent_no_lock(struct clk *clk, struct clk *parent, 2832305544bSClément Léger size_t pidx) 2842305544bSClément Léger { 2852305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC; 2862305544bSClément Léger bool was_enabled = false; 2872305544bSClément Léger 2882305544bSClément Léger /* Requested parent is already the one set */ 2892305544bSClément Léger if (clk->parent == parent) 2902305544bSClément Léger return TEE_SUCCESS; 2912305544bSClément Léger 2922305544bSClément Léger was_enabled = clk_is_enabled_no_lock(clk); 2932305544bSClément Léger /* Call is needed to decrement refcount on current parent tree */ 2948baaac1cSEtienne Carriere if (was_enabled) { 2958baaac1cSEtienne Carriere if (clk->flags & CLK_SET_PARENT_PRE_ENABLE) { 2968baaac1cSEtienne Carriere res = clk_enable_no_lock(parent); 2978baaac1cSEtienne Carriere if (res) 2988baaac1cSEtienne Carriere return res; 2998baaac1cSEtienne Carriere } 3008baaac1cSEtienne Carriere 3012305544bSClément Léger clk_disable_no_lock(clk); 3028baaac1cSEtienne Carriere } 3032305544bSClément Léger 3042305544bSClément Léger res = clk->ops->set_parent(clk, pidx); 3052305544bSClément Léger if (res) 3062305544bSClément Léger goto out; 3072305544bSClément Léger 3082305544bSClément Léger clk->parent = parent; 3092305544bSClément Léger 3102305544bSClément Léger /* The parent changed and the rate might also have changed */ 3112305544bSClément Léger clk_compute_rate_no_lock(clk); 3122305544bSClément Léger 3132305544bSClément Léger out: 3142305544bSClément Léger /* Call is needed to increment refcount on the new parent tree */ 3152305544bSClément Léger if (was_enabled) { 3162305544bSClément Léger res = clk_enable_no_lock(clk); 3172305544bSClément Léger if (res) 3182305544bSClément Léger panic("Failed to re-enable clock after setting parent"); 3198baaac1cSEtienne Carriere 3208baaac1cSEtienne Carriere if (clk->flags & CLK_SET_PARENT_PRE_ENABLE) { 3218baaac1cSEtienne Carriere /* Balance refcount when new parent was pre-enabled */ 3228baaac1cSEtienne Carriere clk_disable_no_lock(parent); 3238baaac1cSEtienne Carriere } 3242305544bSClément Léger } 3252305544bSClément Léger 3262305544bSClément Léger return res; 3272305544bSClément Léger } 3282305544bSClément Léger 3292305544bSClément Léger TEE_Result clk_set_parent(struct clk *clk, struct clk *parent) 3302305544bSClément Léger { 3312305544bSClément Léger size_t pidx = 0; 3322305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC; 3332305544bSClément Léger 3342305544bSClément Léger if (clk_get_parent_idx(clk, parent, &pidx) || !clk->ops->set_parent) 3352305544bSClément Léger return TEE_ERROR_BAD_PARAMETERS; 3362305544bSClément Léger 337*9a3248fcSEtienne Carriere lock_clk(); 3382305544bSClément Léger if (clk->flags & CLK_SET_PARENT_GATE && clk_is_enabled_no_lock(clk)) { 3392305544bSClément Léger res = TEE_ERROR_BAD_STATE; 3402305544bSClément Léger goto out; 3412305544bSClément Léger } 3422305544bSClément Léger 3432305544bSClément Léger res = clk_set_parent_no_lock(clk, parent, pidx); 3442305544bSClément Léger out: 345*9a3248fcSEtienne Carriere unlock_clk(); 3462305544bSClément Léger 3472305544bSClément Léger return res; 3482305544bSClément Léger } 3495df61a5dSClément Léger 3505df61a5dSClément Léger TEE_Result clk_get_rates_array(struct clk *clk, size_t start_index, 3515df61a5dSClément Léger unsigned long *rates, size_t *nb_elts) 3525df61a5dSClément Léger { 3535df61a5dSClément Léger if (!clk->ops->get_rates_array) 3545df61a5dSClément Léger return TEE_ERROR_NOT_SUPPORTED; 3555df61a5dSClément Léger 3565df61a5dSClément Léger return clk->ops->get_rates_array(clk, start_index, rates, nb_elts); 3575df61a5dSClément Léger } 358cd04d138SEtienne Carriere 3598fbc0056SEtienne Carriere TEE_Result clk_get_rates_steps(struct clk *clk, unsigned long *min, 3608fbc0056SEtienne Carriere unsigned long *max, unsigned long *step) 3618fbc0056SEtienne Carriere { 3628fbc0056SEtienne Carriere if (!clk->ops->get_rates_steps) 3638fbc0056SEtienne Carriere return TEE_ERROR_NOT_SUPPORTED; 3648fbc0056SEtienne Carriere 3658fbc0056SEtienne Carriere return clk->ops->get_rates_steps(clk, min, max, step); 3668fbc0056SEtienne Carriere } 3678fbc0056SEtienne Carriere 36859db7f68SEtienne Carriere TEE_Result clk_get_duty_cycle(struct clk *clk, 36959db7f68SEtienne Carriere struct clk_duty_cycle *duty_cycle) 37059db7f68SEtienne Carriere { 37159db7f68SEtienne Carriere if (clk->ops->get_duty_cycle) 37259db7f68SEtienne Carriere return clk->ops->get_duty_cycle(clk, duty_cycle); 37359db7f68SEtienne Carriere 37405771552SEtienne Carriere if (clk->parent && (clk->flags & CLK_DUTY_CYCLE_PARENT)) 37505771552SEtienne Carriere return clk_get_duty_cycle(clk->parent, duty_cycle); 37605771552SEtienne Carriere 37759db7f68SEtienne Carriere /* Default set 50% duty cycle */ 37859db7f68SEtienne Carriere duty_cycle->num = 1; 37959db7f68SEtienne Carriere duty_cycle->den = 2; 38059db7f68SEtienne Carriere 38159db7f68SEtienne Carriere return TEE_SUCCESS; 38259db7f68SEtienne Carriere } 38359db7f68SEtienne Carriere 384cd04d138SEtienne Carriere /* Return updated message buffer position of NULL on failure */ 385cd04d138SEtienne Carriere static __printf(3, 4) char *add_msg(char *cur, char *end, const char *fmt, ...) 386cd04d138SEtienne Carriere { 387cd04d138SEtienne Carriere va_list ap = { }; 388cd04d138SEtienne Carriere int max_len = end - cur; 389cd04d138SEtienne Carriere int ret = 0; 390cd04d138SEtienne Carriere 391cd04d138SEtienne Carriere va_start(ap, fmt); 392cd04d138SEtienne Carriere ret = vsnprintf(cur, max_len, fmt, ap); 393cd04d138SEtienne Carriere va_end(ap); 394cd04d138SEtienne Carriere 395cd04d138SEtienne Carriere if (ret < 0 || ret >= max_len) 396cd04d138SEtienne Carriere return NULL; 397cd04d138SEtienne Carriere 398cd04d138SEtienne Carriere return cur + ret; 399cd04d138SEtienne Carriere } 400cd04d138SEtienne Carriere 401e64dede3SEtienne Carriere static struct clk *find_next_clk(struct clk *parent __maybe_unused, 402e64dede3SEtienne Carriere struct clk *sibling __maybe_unused) 403e64dede3SEtienne Carriere { 404e64dede3SEtienne Carriere struct clk *clk = NULL; 405e64dede3SEtienne Carriere 406e64dede3SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE 407e64dede3SEtienne Carriere if (sibling) 408e64dede3SEtienne Carriere clk = SLIST_NEXT(sibling, link); 409e64dede3SEtienne Carriere else 410e64dede3SEtienne Carriere clk = SLIST_FIRST(&clock_list); 411e64dede3SEtienne Carriere 412e64dede3SEtienne Carriere while (clk && clk->parent != parent) 413e64dede3SEtienne Carriere clk = SLIST_NEXT(clk, link); 414e64dede3SEtienne Carriere #endif 415e64dede3SEtienne Carriere 416e64dede3SEtienne Carriere return clk; 417e64dede3SEtienne Carriere } 418e64dede3SEtienne Carriere 419e64dede3SEtienne Carriere static bool clk_is_parent_last_child(struct clk *clk) 420e64dede3SEtienne Carriere { 421e64dede3SEtienne Carriere return !find_next_clk(clk->parent, clk); 422e64dede3SEtienne Carriere } 423e64dede3SEtienne Carriere 424e64dede3SEtienne Carriere static bool indent_last_node_already_found(struct clk *node_clk, 425e64dede3SEtienne Carriere int node_indent, int cur_indent) 426e64dede3SEtienne Carriere { 427e64dede3SEtienne Carriere struct clk *clk = node_clk; 428e64dede3SEtienne Carriere int n = 0; 429e64dede3SEtienne Carriere 430e64dede3SEtienne Carriere /* Find parent clock at level @node_indent - @cur_indent - 1 */ 431e64dede3SEtienne Carriere for (n = 0; n < node_indent - cur_indent - 1; n++) 432e64dede3SEtienne Carriere clk = clk->parent; 433e64dede3SEtienne Carriere 434e64dede3SEtienne Carriere return clk_is_parent_last_child(clk); 435e64dede3SEtienne Carriere } 436e64dede3SEtienne Carriere 437e64dede3SEtienne Carriere static void __maybe_unused print_clk(struct clk *clk, int indent) 438cd04d138SEtienne Carriere { 439cd04d138SEtienne Carriere static const char * const rate_unit[] = { "Hz", "kHz", "MHz", "GHz" }; 440cd04d138SEtienne Carriere int max_unit = ARRAY_SIZE(rate_unit); 441cd04d138SEtienne Carriere unsigned long rate = 0; 442cd04d138SEtienne Carriere char msg_buf[128] = { }; 443cd04d138SEtienne Carriere char *msg_end = msg_buf + sizeof(msg_buf); 444cd04d138SEtienne Carriere char *msg = msg_buf; 445cd04d138SEtienne Carriere int n = 0; 446cd04d138SEtienne Carriere 447cd04d138SEtienne Carriere /* 448cd04d138SEtienne Carriere * Currently prints the clock state based on the clock refcount. 449cd04d138SEtienne Carriere * A future change could print the hardware clock state when 450cd04d138SEtienne Carriere * related clock driver provides a struct clk_ops::is_enabled handler 451cd04d138SEtienne Carriere */ 452cd04d138SEtienne Carriere 453cd04d138SEtienne Carriere if (indent) { 454e64dede3SEtienne Carriere /* Indent for root clock level */ 455e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, " "); 456e64dede3SEtienne Carriere if (!msg) 457e64dede3SEtienne Carriere goto out; 458e64dede3SEtienne Carriere 459e64dede3SEtienne Carriere /* Indent for root parent to clock parent levels */ 460cd04d138SEtienne Carriere for (n = 0; n < indent - 1; n++) { 461e64dede3SEtienne Carriere if (indent_last_node_already_found(clk, indent, n)) 462e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, " "); 463e64dede3SEtienne Carriere else 464cd04d138SEtienne Carriere msg = add_msg(msg, msg_end, "| "); 465e64dede3SEtienne Carriere 466cd04d138SEtienne Carriere if (!msg) 467cd04d138SEtienne Carriere goto out; 468cd04d138SEtienne Carriere } 469cd04d138SEtienne Carriere 470e64dede3SEtienne Carriere /* Clock indentation */ 471e64dede3SEtienne Carriere if (clk_is_parent_last_child(clk)) 472e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, "`-- "); 473e64dede3SEtienne Carriere else 474e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, "|-- "); 475e64dede3SEtienne Carriere } else { 476e64dede3SEtienne Carriere /* Root clock indentation */ 477e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, "o- "); 478e64dede3SEtienne Carriere } 479cd04d138SEtienne Carriere if (!msg) 480cd04d138SEtienne Carriere goto out; 481cd04d138SEtienne Carriere 482cd04d138SEtienne Carriere rate = clk_get_rate(clk); 483cd04d138SEtienne Carriere for (n = 1; rate && !(rate % 1000) && n < max_unit; n++) 484cd04d138SEtienne Carriere rate /= 1000; 485cd04d138SEtienne Carriere 486cd04d138SEtienne Carriere msg = add_msg(msg, msg_end, "%s \t(%3s / refcnt %u / %ld %s)", 487cd04d138SEtienne Carriere clk_get_name(clk), 488cd04d138SEtienne Carriere refcount_val(&clk->enabled_count) ? "on " : "off", 489cd04d138SEtienne Carriere refcount_val(&clk->enabled_count), 490cd04d138SEtienne Carriere rate, rate_unit[n - 1]); 491cd04d138SEtienne Carriere if (!msg) 492cd04d138SEtienne Carriere goto out; 493cd04d138SEtienne Carriere 494cd04d138SEtienne Carriere out: 495cd04d138SEtienne Carriere if (!msg) 496cd04d138SEtienne Carriere snprintf(msg_end - 4, 4, "..."); 497cd04d138SEtienne Carriere 498aac2c716SEtienne Carriere DMSG("%s", msg_buf); 499cd04d138SEtienne Carriere } 500cd04d138SEtienne Carriere 501e64dede3SEtienne Carriere static void print_tree(void) 502cd04d138SEtienne Carriere { 503cd04d138SEtienne Carriere struct clk *clk = NULL; 504e64dede3SEtienne Carriere struct clk *parent = NULL; 505e64dede3SEtienne Carriere struct clk *next = NULL; 506e64dede3SEtienne Carriere int indent = -1; 507cd04d138SEtienne Carriere 508e64dede3SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE 509e64dede3SEtienne Carriere if (SLIST_EMPTY(&clock_list)) { 510aac2c716SEtienne Carriere DMSG("-- No registered clock"); 511e64dede3SEtienne Carriere return; 512cd04d138SEtienne Carriere } 513cd04d138SEtienne Carriere #endif 514e64dede3SEtienne Carriere 515e64dede3SEtienne Carriere while (true) { 516e64dede3SEtienne Carriere next = find_next_clk(parent, clk); 517e64dede3SEtienne Carriere if (next) { 518e64dede3SEtienne Carriere print_clk(next, indent + 1); 519e64dede3SEtienne Carriere /* Enter the subtree of the next clock */ 520e64dede3SEtienne Carriere parent = next; 521e64dede3SEtienne Carriere indent++; 522e64dede3SEtienne Carriere clk = NULL; 523e64dede3SEtienne Carriere } else { 524e64dede3SEtienne Carriere /* 525e64dede3SEtienne Carriere * We've processed all children at this level. 526e64dede3SEtienne Carriere * If parent is NULL we're at the top and are done. 527e64dede3SEtienne Carriere */ 528e64dede3SEtienne Carriere if (!parent) 529e64dede3SEtienne Carriere break; 530e64dede3SEtienne Carriere /* 531e64dede3SEtienne Carriere * Move up one level to resume with the next 532e64dede3SEtienne Carriere * child clock of the parent. 533e64dede3SEtienne Carriere */ 534e64dede3SEtienne Carriere clk = parent; 535e64dede3SEtienne Carriere parent = clk->parent; 536e64dede3SEtienne Carriere indent--; 537e64dede3SEtienne Carriere } 538e64dede3SEtienne Carriere } 539cd04d138SEtienne Carriere } 540cd04d138SEtienne Carriere 541cd04d138SEtienne Carriere void clk_print_tree(void) 542cd04d138SEtienne Carriere { 543aac2c716SEtienne Carriere if (IS_ENABLED(CFG_DRIVERS_CLK_PRINT_TREE) && 544aac2c716SEtienne Carriere TRACE_LEVEL >= TRACE_DEBUG) { 545aac2c716SEtienne Carriere DMSG("Clock tree summary (informative):"); 546e64dede3SEtienne Carriere print_tree(); 547cd04d138SEtienne Carriere } 548cd04d138SEtienne Carriere } 549