xref: /optee_os/core/drivers/clk/clk.c (revision 9a3248fc031ef2b79a2eca0e5f364d3a494b44bf)
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