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>
109a3248fcSEtienne Carriere #include <kernel/mutex_pm_aware.h>
112305544bSClément Léger #include <kernel/panic.h>
129a3248fcSEtienne 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
179a3248fcSEtienne Carriere /* Global clock tree access protection complying the power state transitions */
189a3248fcSEtienne 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
lock_clk(void)249a3248fcSEtienne Carriere static void lock_clk(void)
259a3248fcSEtienne Carriere {
269a3248fcSEtienne Carriere mutex_pm_aware_lock(&mu);
279a3248fcSEtienne Carriere }
289a3248fcSEtienne Carriere
unlock_clk(void)299a3248fcSEtienne Carriere static void unlock_clk(void)
309a3248fcSEtienne Carriere {
319a3248fcSEtienne Carriere mutex_pm_aware_unlock(&mu);
329a3248fcSEtienne Carriere }
339a3248fcSEtienne Carriere
clk_alloc(const char * name,const struct clk_ops * ops,struct clk ** parent_clks,size_t parent_count)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
clk_free(struct clk * clk)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
clk_check(struct clk * clk)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
clk_compute_rate_no_lock(struct clk * clk)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
78*4b6058e4SGabriel Fernandez if (clk->parent) {
79*4b6058e4SGabriel Fernandez clk_compute_rate_no_lock(clk->parent);
802305544bSClément Léger parent_rate = clk->parent->rate;
81*4b6058e4SGabriel Fernandez }
822305544bSClément Léger
832305544bSClément Léger if (clk->ops->get_rate)
842305544bSClément Léger clk->rate = clk->ops->get_rate(clk, parent_rate);
852305544bSClément Léger else
862305544bSClément Léger clk->rate = parent_rate;
872305544bSClément Léger }
882305544bSClément Léger
clk_get_parent_by_index(struct clk * clk,size_t pidx)892305544bSClément Léger struct clk *clk_get_parent_by_index(struct clk *clk, size_t pidx)
902305544bSClément Léger {
912305544bSClément Léger if (pidx >= clk->num_parents)
922305544bSClément Léger return NULL;
932305544bSClément Léger
942305544bSClément Léger return clk->parents[pidx];
952305544bSClément Léger }
962305544bSClément Léger
clk_init_parent(struct clk * clk)972305544bSClément Léger static void clk_init_parent(struct clk *clk)
982305544bSClément Léger {
992305544bSClément Léger size_t pidx = 0;
1002305544bSClément Léger
1012305544bSClément Léger switch (clk->num_parents) {
1022305544bSClément Léger case 0:
1032305544bSClément Léger break;
1042305544bSClément Léger case 1:
1052305544bSClément Léger clk->parent = clk->parents[0];
1062305544bSClément Léger break;
1072305544bSClément Léger default:
1082305544bSClément Léger pidx = clk->ops->get_parent(clk);
1092305544bSClément Léger assert(pidx < clk->num_parents);
1102305544bSClément Léger
1112305544bSClément Léger clk->parent = clk->parents[pidx];
1122305544bSClément Léger break;
1132305544bSClément Léger }
1142305544bSClément Léger }
1152305544bSClément Léger
clk_register(struct clk * clk)1162305544bSClément Léger TEE_Result clk_register(struct clk *clk)
1172305544bSClément Léger {
1182305544bSClément Léger assert(clk_check(clk));
1192305544bSClément Léger
1202305544bSClément Léger clk_init_parent(clk);
1212305544bSClément Léger clk_compute_rate_no_lock(clk);
1222305544bSClément Léger
123cd04d138SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE
1242b13eca6SEtienne Carriere SLIST_INSERT_HEAD(&clock_list, clk, link);
125cd04d138SEtienne Carriere #endif
126cd04d138SEtienne Carriere
1272305544bSClément Léger DMSG("Registered clock %s, freq %lu", clk->name, clk_get_rate(clk));
1282305544bSClément Léger
1292305544bSClément Léger return TEE_SUCCESS;
1302305544bSClément Léger }
1312305544bSClément Léger
clk_is_enabled_no_lock(struct clk * clk)1322305544bSClément Léger static bool clk_is_enabled_no_lock(struct clk *clk)
1332305544bSClément Léger {
1342305544bSClément Léger return refcount_val(&clk->enabled_count) != 0;
1352305544bSClément Léger }
1362305544bSClément Léger
clk_is_enabled(struct clk * clk)1376c9ed842SEtienne Carriere bool clk_is_enabled(struct clk *clk)
1386c9ed842SEtienne Carriere {
1396c9ed842SEtienne Carriere return clk_is_enabled_no_lock(clk);
1406c9ed842SEtienne Carriere }
1416c9ed842SEtienne Carriere
clk_disable_no_lock(struct clk * clk)1422305544bSClément Léger static void clk_disable_no_lock(struct clk *clk)
1432305544bSClément Léger {
1442305544bSClément Léger struct clk *parent = NULL;
1452305544bSClément Léger
1462305544bSClément Léger if (!refcount_dec(&clk->enabled_count))
1472305544bSClément Léger return;
1482305544bSClément Léger
1492305544bSClément Léger if (clk->ops->disable)
1502305544bSClément Léger clk->ops->disable(clk);
1512305544bSClément Léger
1522305544bSClément Léger parent = clk_get_parent(clk);
1532305544bSClément Léger if (parent)
1542305544bSClément Léger clk_disable_no_lock(parent);
1552305544bSClément Léger }
1562305544bSClément Léger
clk_enable_no_lock(struct clk * clk)1572305544bSClément Léger static TEE_Result clk_enable_no_lock(struct clk *clk)
1582305544bSClément Léger {
1592305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC;
1602305544bSClément Léger struct clk *parent = NULL;
1612305544bSClément Léger
1622305544bSClément Léger if (refcount_inc(&clk->enabled_count))
1632305544bSClément Léger return TEE_SUCCESS;
1642305544bSClément Léger
1652305544bSClément Léger parent = clk_get_parent(clk);
1662305544bSClément Léger if (parent) {
1672305544bSClément Léger res = clk_enable_no_lock(parent);
1682305544bSClément Léger if (res)
1692305544bSClément Léger return res;
1702305544bSClément Léger }
1712305544bSClément Léger
1722305544bSClément Léger if (clk->ops->enable) {
1732305544bSClément Léger res = clk->ops->enable(clk);
1742305544bSClément Léger if (res) {
1752305544bSClément Léger if (parent)
1762305544bSClément Léger clk_disable_no_lock(parent);
1772305544bSClément Léger
1782305544bSClément Léger return res;
1792305544bSClément Léger }
1802305544bSClément Léger }
1812305544bSClément Léger
1822305544bSClément Léger refcount_set(&clk->enabled_count, 1);
1832305544bSClément Léger
1842305544bSClément Léger return TEE_SUCCESS;
1852305544bSClément Léger }
1862305544bSClément Léger
clk_enable(struct clk * clk)1872305544bSClément Léger TEE_Result clk_enable(struct clk *clk)
1882305544bSClément Léger {
1892305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC;
1902305544bSClément Léger
1919a3248fcSEtienne Carriere lock_clk();
1922305544bSClément Léger res = clk_enable_no_lock(clk);
1939a3248fcSEtienne Carriere unlock_clk();
1942305544bSClément Léger
1952305544bSClément Léger return res;
1962305544bSClément Léger }
1972305544bSClément Léger
clk_disable(struct clk * clk)1982305544bSClément Léger void clk_disable(struct clk *clk)
1992305544bSClément Léger {
2009a3248fcSEtienne Carriere lock_clk();
2012305544bSClément Léger clk_disable_no_lock(clk);
2029a3248fcSEtienne Carriere unlock_clk();
2032305544bSClément Léger }
2042305544bSClément Léger
clk_get_rate(struct clk * clk)2052305544bSClément Léger unsigned long clk_get_rate(struct clk *clk)
2062305544bSClément Léger {
207*4b6058e4SGabriel Fernandez clk_compute_rate_no_lock(clk);
208*4b6058e4SGabriel Fernandez
2092305544bSClément Léger return clk->rate;
2102305544bSClément Léger }
2112305544bSClément Léger
clk_set_rate_no_lock(struct clk * clk,unsigned long rate)2122305544bSClément Léger static TEE_Result clk_set_rate_no_lock(struct clk *clk, unsigned long rate)
2132305544bSClément Léger {
2142305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC;
2152305544bSClément Léger unsigned long parent_rate = 0;
2162305544bSClément Léger
2172305544bSClément Léger if (clk->parent)
2182305544bSClément Léger parent_rate = clk_get_rate(clk->parent);
2192305544bSClément Léger
2200ba7ae74SEtienne Carriere assert(!(clk->flags & CLK_SET_RATE_PARENT) || clk->parent);
2210ba7ae74SEtienne Carriere if (clk->flags & CLK_SET_RATE_PARENT) {
2220ba7ae74SEtienne Carriere res = clk_set_rate_no_lock(clk->parent, rate);
2230ba7ae74SEtienne Carriere if (res)
2240ba7ae74SEtienne Carriere return res;
2250ba7ae74SEtienne Carriere rate = clk_get_rate(clk->parent);
2260ba7ae74SEtienne Carriere }
2270ba7ae74SEtienne Carriere
2280ba7ae74SEtienne Carriere if (clk->ops->set_rate) {
22920f97d98SEtienne Carriere if (clk->flags & CLK_SET_RATE_UNGATE) {
23020f97d98SEtienne Carriere res = clk_enable_no_lock(clk);
23120f97d98SEtienne Carriere if (res)
23220f97d98SEtienne Carriere return res;
23320f97d98SEtienne Carriere }
23420f97d98SEtienne Carriere
2352305544bSClément Léger res = clk->ops->set_rate(clk, rate, parent_rate);
23620f97d98SEtienne Carriere
23720f97d98SEtienne Carriere if (clk->flags & CLK_SET_RATE_UNGATE)
23820f97d98SEtienne Carriere clk_disable_no_lock(clk);
23920f97d98SEtienne Carriere
2402305544bSClément Léger if (res)
2412305544bSClément Léger return res;
2420ba7ae74SEtienne Carriere }
2432305544bSClément Léger
2442305544bSClément Léger clk_compute_rate_no_lock(clk);
2452305544bSClément Léger
2462305544bSClément Léger return TEE_SUCCESS;
2472305544bSClément Léger }
2482305544bSClément Léger
clk_set_rate(struct clk * clk,unsigned long rate)2492305544bSClément Léger TEE_Result clk_set_rate(struct clk *clk, unsigned long rate)
2502305544bSClément Léger {
2512305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC;
2522305544bSClément Léger
2539a3248fcSEtienne Carriere lock_clk();
2542305544bSClément Léger
2552305544bSClément Léger if (clk->flags & CLK_SET_RATE_GATE && clk_is_enabled_no_lock(clk))
2562305544bSClément Léger res = TEE_ERROR_BAD_STATE;
2572305544bSClément Léger else
2582305544bSClément Léger res = clk_set_rate_no_lock(clk, rate);
2592305544bSClément Léger
2609a3248fcSEtienne Carriere unlock_clk();
2612305544bSClément Léger
2622305544bSClément Léger return res;
2632305544bSClément Léger }
2642305544bSClément Léger
clk_get_parent(struct clk * clk)2652305544bSClément Léger struct clk *clk_get_parent(struct clk *clk)
2662305544bSClément Léger {
2672305544bSClément Léger return clk->parent;
2682305544bSClément Léger }
2692305544bSClément Léger
clk_get_parent_idx(struct clk * clk,struct clk * parent,size_t * pidx)2702305544bSClément Léger static TEE_Result clk_get_parent_idx(struct clk *clk, struct clk *parent,
2712305544bSClément Léger size_t *pidx)
2722305544bSClément Léger {
2732305544bSClément Léger size_t i = 0;
2742305544bSClément Léger
2752305544bSClément Léger for (i = 0; i < clk_get_num_parents(clk); i++) {
2762305544bSClément Léger if (clk_get_parent_by_index(clk, i) == parent) {
2772305544bSClément Léger *pidx = i;
2782305544bSClément Léger return TEE_SUCCESS;
2792305544bSClément Léger }
2802305544bSClément Léger }
2812305544bSClément Léger EMSG("Clock %s is not a parent of clock %s", parent->name, clk->name);
2822305544bSClément Léger
2832305544bSClément Léger return TEE_ERROR_BAD_PARAMETERS;
2842305544bSClément Léger }
2852305544bSClément Léger
clk_set_parent_no_lock(struct clk * clk,struct clk * parent,size_t pidx)2862305544bSClément Léger static TEE_Result clk_set_parent_no_lock(struct clk *clk, struct clk *parent,
2872305544bSClément Léger size_t pidx)
2882305544bSClément Léger {
2892305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC;
2902305544bSClément Léger bool was_enabled = false;
2912305544bSClément Léger
2922305544bSClément Léger /* Requested parent is already the one set */
2932305544bSClément Léger if (clk->parent == parent)
2942305544bSClément Léger return TEE_SUCCESS;
2952305544bSClément Léger
2962305544bSClément Léger was_enabled = clk_is_enabled_no_lock(clk);
2972305544bSClément Léger /* Call is needed to decrement refcount on current parent tree */
2988baaac1cSEtienne Carriere if (was_enabled) {
2998baaac1cSEtienne Carriere if (clk->flags & CLK_SET_PARENT_PRE_ENABLE) {
3008baaac1cSEtienne Carriere res = clk_enable_no_lock(parent);
3018baaac1cSEtienne Carriere if (res)
3028baaac1cSEtienne Carriere return res;
3038baaac1cSEtienne Carriere }
3048baaac1cSEtienne Carriere
3052305544bSClément Léger clk_disable_no_lock(clk);
3068baaac1cSEtienne Carriere }
3072305544bSClément Léger
3082305544bSClément Léger res = clk->ops->set_parent(clk, pidx);
3092305544bSClément Léger if (res)
3102305544bSClément Léger goto out;
3112305544bSClément Léger
3122305544bSClément Léger clk->parent = parent;
3132305544bSClément Léger
3142305544bSClément Léger /* The parent changed and the rate might also have changed */
3152305544bSClément Léger clk_compute_rate_no_lock(clk);
3162305544bSClément Léger
3172305544bSClément Léger out:
3182305544bSClément Léger /* Call is needed to increment refcount on the new parent tree */
3192305544bSClément Léger if (was_enabled) {
3202305544bSClément Léger res = clk_enable_no_lock(clk);
3212305544bSClément Léger if (res)
3222305544bSClément Léger panic("Failed to re-enable clock after setting parent");
3238baaac1cSEtienne Carriere
3248baaac1cSEtienne Carriere if (clk->flags & CLK_SET_PARENT_PRE_ENABLE) {
3258baaac1cSEtienne Carriere /* Balance refcount when new parent was pre-enabled */
3268baaac1cSEtienne Carriere clk_disable_no_lock(parent);
3278baaac1cSEtienne Carriere }
3282305544bSClément Léger }
3292305544bSClément Léger
3302305544bSClément Léger return res;
3312305544bSClément Léger }
3322305544bSClément Léger
clk_set_parent(struct clk * clk,struct clk * parent)3332305544bSClément Léger TEE_Result clk_set_parent(struct clk *clk, struct clk *parent)
3342305544bSClément Léger {
3352305544bSClément Léger size_t pidx = 0;
3362305544bSClément Léger TEE_Result res = TEE_ERROR_GENERIC;
3372305544bSClément Léger
3382305544bSClément Léger if (clk_get_parent_idx(clk, parent, &pidx) || !clk->ops->set_parent)
3392305544bSClément Léger return TEE_ERROR_BAD_PARAMETERS;
3402305544bSClément Léger
3419a3248fcSEtienne Carriere lock_clk();
3422305544bSClément Léger if (clk->flags & CLK_SET_PARENT_GATE && clk_is_enabled_no_lock(clk)) {
3432305544bSClément Léger res = TEE_ERROR_BAD_STATE;
3442305544bSClément Léger goto out;
3452305544bSClément Léger }
3462305544bSClément Léger
3472305544bSClément Léger res = clk_set_parent_no_lock(clk, parent, pidx);
3482305544bSClément Léger out:
3499a3248fcSEtienne Carriere unlock_clk();
3502305544bSClément Léger
3512305544bSClément Léger return res;
3522305544bSClément Léger }
3535df61a5dSClément Léger
clk_get_rates_array(struct clk * clk,size_t start_index,unsigned long * rates,size_t * nb_elts)3545df61a5dSClément Léger TEE_Result clk_get_rates_array(struct clk *clk, size_t start_index,
3555df61a5dSClément Léger unsigned long *rates, size_t *nb_elts)
3565df61a5dSClément Léger {
3575df61a5dSClément Léger if (!clk->ops->get_rates_array)
3585df61a5dSClément Léger return TEE_ERROR_NOT_SUPPORTED;
3595df61a5dSClément Léger
3605df61a5dSClément Léger return clk->ops->get_rates_array(clk, start_index, rates, nb_elts);
3615df61a5dSClément Léger }
362cd04d138SEtienne Carriere
clk_get_rates_steps(struct clk * clk,unsigned long * min,unsigned long * max,unsigned long * step)3638fbc0056SEtienne Carriere TEE_Result clk_get_rates_steps(struct clk *clk, unsigned long *min,
3648fbc0056SEtienne Carriere unsigned long *max, unsigned long *step)
3658fbc0056SEtienne Carriere {
3668fbc0056SEtienne Carriere if (!clk->ops->get_rates_steps)
3678fbc0056SEtienne Carriere return TEE_ERROR_NOT_SUPPORTED;
3688fbc0056SEtienne Carriere
3698fbc0056SEtienne Carriere return clk->ops->get_rates_steps(clk, min, max, step);
3708fbc0056SEtienne Carriere }
3718fbc0056SEtienne Carriere
clk_get_duty_cycle(struct clk * clk,struct clk_duty_cycle * duty_cycle)37259db7f68SEtienne Carriere TEE_Result clk_get_duty_cycle(struct clk *clk,
37359db7f68SEtienne Carriere struct clk_duty_cycle *duty_cycle)
37459db7f68SEtienne Carriere {
37559db7f68SEtienne Carriere if (clk->ops->get_duty_cycle)
37659db7f68SEtienne Carriere return clk->ops->get_duty_cycle(clk, duty_cycle);
37759db7f68SEtienne Carriere
37805771552SEtienne Carriere if (clk->parent && (clk->flags & CLK_DUTY_CYCLE_PARENT))
37905771552SEtienne Carriere return clk_get_duty_cycle(clk->parent, duty_cycle);
38005771552SEtienne Carriere
38159db7f68SEtienne Carriere /* Default set 50% duty cycle */
38259db7f68SEtienne Carriere duty_cycle->num = 1;
38359db7f68SEtienne Carriere duty_cycle->den = 2;
38459db7f68SEtienne Carriere
38559db7f68SEtienne Carriere return TEE_SUCCESS;
38659db7f68SEtienne Carriere }
38759db7f68SEtienne Carriere
388cd04d138SEtienne Carriere /* Return updated message buffer position of NULL on failure */
add_msg(char * cur,char * end,const char * fmt,...)389cd04d138SEtienne Carriere static __printf(3, 4) char *add_msg(char *cur, char *end, const char *fmt, ...)
390cd04d138SEtienne Carriere {
391cd04d138SEtienne Carriere va_list ap = { };
392cd04d138SEtienne Carriere int max_len = end - cur;
393cd04d138SEtienne Carriere int ret = 0;
394cd04d138SEtienne Carriere
395cd04d138SEtienne Carriere va_start(ap, fmt);
396cd04d138SEtienne Carriere ret = vsnprintf(cur, max_len, fmt, ap);
397cd04d138SEtienne Carriere va_end(ap);
398cd04d138SEtienne Carriere
399cd04d138SEtienne Carriere if (ret < 0 || ret >= max_len)
400cd04d138SEtienne Carriere return NULL;
401cd04d138SEtienne Carriere
402cd04d138SEtienne Carriere return cur + ret;
403cd04d138SEtienne Carriere }
404cd04d138SEtienne Carriere
find_next_clk(struct clk * parent __maybe_unused,struct clk * sibling __maybe_unused)405e64dede3SEtienne Carriere static struct clk *find_next_clk(struct clk *parent __maybe_unused,
406e64dede3SEtienne Carriere struct clk *sibling __maybe_unused)
407e64dede3SEtienne Carriere {
408e64dede3SEtienne Carriere struct clk *clk = NULL;
409e64dede3SEtienne Carriere
410e64dede3SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE
411e64dede3SEtienne Carriere if (sibling)
412e64dede3SEtienne Carriere clk = SLIST_NEXT(sibling, link);
413e64dede3SEtienne Carriere else
414e64dede3SEtienne Carriere clk = SLIST_FIRST(&clock_list);
415e64dede3SEtienne Carriere
416e64dede3SEtienne Carriere while (clk && clk->parent != parent)
417e64dede3SEtienne Carriere clk = SLIST_NEXT(clk, link);
418e64dede3SEtienne Carriere #endif
419e64dede3SEtienne Carriere
420e64dede3SEtienne Carriere return clk;
421e64dede3SEtienne Carriere }
422e64dede3SEtienne Carriere
clk_is_parent_last_child(struct clk * clk)423e64dede3SEtienne Carriere static bool clk_is_parent_last_child(struct clk *clk)
424e64dede3SEtienne Carriere {
425e64dede3SEtienne Carriere return !find_next_clk(clk->parent, clk);
426e64dede3SEtienne Carriere }
427e64dede3SEtienne Carriere
indent_last_node_already_found(struct clk * node_clk,int node_indent,int cur_indent)428e64dede3SEtienne Carriere static bool indent_last_node_already_found(struct clk *node_clk,
429e64dede3SEtienne Carriere int node_indent, int cur_indent)
430e64dede3SEtienne Carriere {
431e64dede3SEtienne Carriere struct clk *clk = node_clk;
432e64dede3SEtienne Carriere int n = 0;
433e64dede3SEtienne Carriere
434e64dede3SEtienne Carriere /* Find parent clock at level @node_indent - @cur_indent - 1 */
435e64dede3SEtienne Carriere for (n = 0; n < node_indent - cur_indent - 1; n++)
436e64dede3SEtienne Carriere clk = clk->parent;
437e64dede3SEtienne Carriere
438e64dede3SEtienne Carriere return clk_is_parent_last_child(clk);
439e64dede3SEtienne Carriere }
440e64dede3SEtienne Carriere
print_clk(struct clk * clk,int indent)441e64dede3SEtienne Carriere static void __maybe_unused print_clk(struct clk *clk, int indent)
442cd04d138SEtienne Carriere {
443cd04d138SEtienne Carriere static const char * const rate_unit[] = { "Hz", "kHz", "MHz", "GHz" };
444cd04d138SEtienne Carriere int max_unit = ARRAY_SIZE(rate_unit);
445cd04d138SEtienne Carriere unsigned long rate = 0;
446cd04d138SEtienne Carriere char msg_buf[128] = { };
447cd04d138SEtienne Carriere char *msg_end = msg_buf + sizeof(msg_buf);
448cd04d138SEtienne Carriere char *msg = msg_buf;
449cd04d138SEtienne Carriere int n = 0;
450cd04d138SEtienne Carriere
451cd04d138SEtienne Carriere /*
452cd04d138SEtienne Carriere * Currently prints the clock state based on the clock refcount.
453cd04d138SEtienne Carriere * A future change could print the hardware clock state when
454cd04d138SEtienne Carriere * related clock driver provides a struct clk_ops::is_enabled handler
455cd04d138SEtienne Carriere */
456cd04d138SEtienne Carriere
457cd04d138SEtienne Carriere if (indent) {
458e64dede3SEtienne Carriere /* Indent for root clock level */
459e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, " ");
460e64dede3SEtienne Carriere if (!msg)
461e64dede3SEtienne Carriere goto out;
462e64dede3SEtienne Carriere
463e64dede3SEtienne Carriere /* Indent for root parent to clock parent levels */
464cd04d138SEtienne Carriere for (n = 0; n < indent - 1; n++) {
465e64dede3SEtienne Carriere if (indent_last_node_already_found(clk, indent, n))
466e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, " ");
467e64dede3SEtienne Carriere else
468cd04d138SEtienne Carriere msg = add_msg(msg, msg_end, "| ");
469e64dede3SEtienne Carriere
470cd04d138SEtienne Carriere if (!msg)
471cd04d138SEtienne Carriere goto out;
472cd04d138SEtienne Carriere }
473cd04d138SEtienne Carriere
474e64dede3SEtienne Carriere /* Clock indentation */
475e64dede3SEtienne Carriere if (clk_is_parent_last_child(clk))
476e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, "`-- ");
477e64dede3SEtienne Carriere else
478e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, "|-- ");
479e64dede3SEtienne Carriere } else {
480e64dede3SEtienne Carriere /* Root clock indentation */
481e64dede3SEtienne Carriere msg = add_msg(msg, msg_end, "o- ");
482e64dede3SEtienne Carriere }
483cd04d138SEtienne Carriere if (!msg)
484cd04d138SEtienne Carriere goto out;
485cd04d138SEtienne Carriere
486cd04d138SEtienne Carriere rate = clk_get_rate(clk);
487cd04d138SEtienne Carriere for (n = 1; rate && !(rate % 1000) && n < max_unit; n++)
488cd04d138SEtienne Carriere rate /= 1000;
489cd04d138SEtienne Carriere
490cd04d138SEtienne Carriere msg = add_msg(msg, msg_end, "%s \t(%3s / refcnt %u / %ld %s)",
491cd04d138SEtienne Carriere clk_get_name(clk),
492cd04d138SEtienne Carriere refcount_val(&clk->enabled_count) ? "on " : "off",
493cd04d138SEtienne Carriere refcount_val(&clk->enabled_count),
494cd04d138SEtienne Carriere rate, rate_unit[n - 1]);
495cd04d138SEtienne Carriere if (!msg)
496cd04d138SEtienne Carriere goto out;
497cd04d138SEtienne Carriere
498cd04d138SEtienne Carriere out:
499cd04d138SEtienne Carriere if (!msg)
500cd04d138SEtienne Carriere snprintf(msg_end - 4, 4, "...");
501cd04d138SEtienne Carriere
5026226e120SPascal Paillet IMSG("%s", msg_buf);
503cd04d138SEtienne Carriere }
504cd04d138SEtienne Carriere
print_tree(void)505e64dede3SEtienne Carriere static void print_tree(void)
506cd04d138SEtienne Carriere {
507cd04d138SEtienne Carriere struct clk *clk = NULL;
508e64dede3SEtienne Carriere struct clk *parent = NULL;
509e64dede3SEtienne Carriere struct clk *next = NULL;
510e64dede3SEtienne Carriere int indent = -1;
511cd04d138SEtienne Carriere
512e64dede3SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE
513e64dede3SEtienne Carriere if (SLIST_EMPTY(&clock_list)) {
5146226e120SPascal Paillet IMSG("-- No registered clock");
515e64dede3SEtienne Carriere return;
516cd04d138SEtienne Carriere }
517cd04d138SEtienne Carriere #endif
518e64dede3SEtienne Carriere
519e64dede3SEtienne Carriere while (true) {
520e64dede3SEtienne Carriere next = find_next_clk(parent, clk);
521e64dede3SEtienne Carriere if (next) {
522e64dede3SEtienne Carriere print_clk(next, indent + 1);
523e64dede3SEtienne Carriere /* Enter the subtree of the next clock */
524e64dede3SEtienne Carriere parent = next;
525e64dede3SEtienne Carriere indent++;
526e64dede3SEtienne Carriere clk = NULL;
527e64dede3SEtienne Carriere } else {
528e64dede3SEtienne Carriere /*
529e64dede3SEtienne Carriere * We've processed all children at this level.
530e64dede3SEtienne Carriere * If parent is NULL we're at the top and are done.
531e64dede3SEtienne Carriere */
532e64dede3SEtienne Carriere if (!parent)
533e64dede3SEtienne Carriere break;
534e64dede3SEtienne Carriere /*
535e64dede3SEtienne Carriere * Move up one level to resume with the next
536e64dede3SEtienne Carriere * child clock of the parent.
537e64dede3SEtienne Carriere */
538e64dede3SEtienne Carriere clk = parent;
539e64dede3SEtienne Carriere parent = clk->parent;
540e64dede3SEtienne Carriere indent--;
541e64dede3SEtienne Carriere }
542e64dede3SEtienne Carriere }
543cd04d138SEtienne Carriere }
544cd04d138SEtienne Carriere
clk_print_tree(void)545cd04d138SEtienne Carriere void clk_print_tree(void)
546cd04d138SEtienne Carriere {
547aac2c716SEtienne Carriere if (IS_ENABLED(CFG_DRIVERS_CLK_PRINT_TREE) &&
5486226e120SPascal Paillet TRACE_LEVEL >= TRACE_INFO) {
5496226e120SPascal Paillet IMSG("Clock tree summary (informative):");
550e64dede3SEtienne Carriere print_tree();
551cd04d138SEtienne Carriere }
552cd04d138SEtienne Carriere }
553