xref: /optee_os/core/drivers/clk/clk.c (revision cd04d138b5951f0ce2858df1e940247c032c87dd)
12305544bSClément Léger // SPDX-License-Identifier: BSD-2-Clause
22305544bSClément Léger /*
32305544bSClément Léger  * Copyright (c) 2021, Bootlin
4*cd04d138SEtienne Carriere  * Copyright (c) 2023, STMicroelectronics
52305544bSClément Léger  */
62305544bSClément Léger 
7*cd04d138SEtienne Carriere #include <config.h>
82305544bSClément Léger #include <drivers/clk.h>
92305544bSClément Léger #include <kernel/boot.h>
102305544bSClément Léger #include <kernel/panic.h>
112305544bSClément Léger #include <kernel/spinlock.h>
122305544bSClément Léger #include <libfdt.h>
132305544bSClément Léger #include <malloc.h>
142305544bSClément Léger #include <stddef.h>
15*cd04d138SEtienne Carriere #include <stdio.h>
162305544bSClément Léger 
172305544bSClément Léger /* Global clock tree lock */
182305544bSClément Léger static unsigned int clk_lock = SPINLOCK_UNLOCK;
192305544bSClément Léger 
20*cd04d138SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE
21*cd04d138SEtienne Carriere static STAILQ_HEAD(, clk) clock_list = STAILQ_HEAD_INITIALIZER(clock_list);
22*cd04d138SEtienne Carriere #endif
23*cd04d138SEtienne Carriere 
242305544bSClément Léger struct clk *clk_alloc(const char *name, const struct clk_ops *ops,
252305544bSClément Léger 		      struct clk **parent_clks, size_t parent_count)
262305544bSClément Léger {
272305544bSClément Léger 	struct clk *clk = NULL;
282305544bSClément Léger 	size_t parent = 0;
292305544bSClément Léger 
302305544bSClément Léger 	clk = calloc(1, sizeof(*clk) + parent_count * sizeof(clk));
312305544bSClément Léger 	if (!clk)
322305544bSClément Léger 		return NULL;
332305544bSClément Léger 
342305544bSClément Léger 	clk->num_parents = parent_count;
352305544bSClément Léger 	for (parent = 0; parent < parent_count; parent++)
362305544bSClément Léger 		clk->parents[parent] = parent_clks[parent];
372305544bSClément Léger 
382305544bSClément Léger 	clk->name = name;
392305544bSClément Léger 	clk->ops = ops;
402305544bSClément Léger 	refcount_set(&clk->enabled_count, 0);
412305544bSClément Léger 
422305544bSClément Léger 	return clk;
432305544bSClément Léger }
442305544bSClément Léger 
452305544bSClément Léger void clk_free(struct clk *clk)
462305544bSClément Léger {
472305544bSClément Léger 	free(clk);
482305544bSClément Léger }
492305544bSClément Léger 
502305544bSClément Léger static bool __maybe_unused clk_check(struct clk *clk)
512305544bSClément Léger {
522305544bSClément Léger 	if (!clk->ops)
532305544bSClément Léger 		return false;
542305544bSClément Léger 
552305544bSClément Léger 	if (clk->ops->set_parent && !clk->ops->get_parent)
562305544bSClément Léger 		return false;
572305544bSClément Léger 
582305544bSClément Léger 	if (clk->num_parents > 1 && !clk->ops->get_parent)
592305544bSClément Léger 		return false;
602305544bSClément Léger 
612305544bSClément Léger 	return true;
622305544bSClément Léger }
632305544bSClément Léger 
642305544bSClément Léger static void clk_compute_rate_no_lock(struct clk *clk)
652305544bSClément Léger {
662305544bSClément Léger 	unsigned long parent_rate = 0;
672305544bSClément Léger 
682305544bSClément Léger 	if (clk->parent)
692305544bSClément Léger 		parent_rate = clk->parent->rate;
702305544bSClément Léger 
712305544bSClément Léger 	if (clk->ops->get_rate)
722305544bSClément Léger 		clk->rate = clk->ops->get_rate(clk, parent_rate);
732305544bSClément Léger 	else
742305544bSClément Léger 		clk->rate = parent_rate;
752305544bSClément Léger }
762305544bSClément Léger 
772305544bSClément Léger struct clk *clk_get_parent_by_index(struct clk *clk, size_t pidx)
782305544bSClément Léger {
792305544bSClément Léger 	if (pidx >= clk->num_parents)
802305544bSClément Léger 		return NULL;
812305544bSClément Léger 
822305544bSClément Léger 	return clk->parents[pidx];
832305544bSClément Léger }
842305544bSClément Léger 
852305544bSClément Léger static void clk_init_parent(struct clk *clk)
862305544bSClément Léger {
872305544bSClément Léger 	size_t pidx = 0;
882305544bSClément Léger 
892305544bSClément Léger 	switch (clk->num_parents) {
902305544bSClément Léger 	case 0:
912305544bSClément Léger 		break;
922305544bSClément Léger 	case 1:
932305544bSClément Léger 		clk->parent = clk->parents[0];
942305544bSClément Léger 		break;
952305544bSClément Léger 	default:
962305544bSClément Léger 		pidx = clk->ops->get_parent(clk);
972305544bSClément Léger 		assert(pidx < clk->num_parents);
982305544bSClément Léger 
992305544bSClément Léger 		clk->parent = clk->parents[pidx];
1002305544bSClément Léger 		break;
1012305544bSClément Léger 	}
1022305544bSClément Léger }
1032305544bSClément Léger 
1042305544bSClément Léger TEE_Result clk_register(struct clk *clk)
1052305544bSClément Léger {
1062305544bSClément Léger 	assert(clk_check(clk));
1072305544bSClément Léger 
1082305544bSClément Léger 	clk_init_parent(clk);
1092305544bSClément Léger 	clk_compute_rate_no_lock(clk);
1102305544bSClément Léger 
111*cd04d138SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE
112*cd04d138SEtienne Carriere 	STAILQ_INSERT_TAIL(&clock_list, clk, link);
113*cd04d138SEtienne Carriere #endif
114*cd04d138SEtienne Carriere 
1152305544bSClément Léger 	DMSG("Registered clock %s, freq %lu", clk->name, clk_get_rate(clk));
1162305544bSClément Léger 
1172305544bSClément Léger 	return TEE_SUCCESS;
1182305544bSClément Léger }
1192305544bSClément Léger 
1202305544bSClément Léger static bool clk_is_enabled_no_lock(struct clk *clk)
1212305544bSClément Léger {
1222305544bSClément Léger 	return refcount_val(&clk->enabled_count) != 0;
1232305544bSClément Léger }
1242305544bSClément Léger 
1256c9ed842SEtienne Carriere bool clk_is_enabled(struct clk *clk)
1266c9ed842SEtienne Carriere {
1276c9ed842SEtienne Carriere 	return clk_is_enabled_no_lock(clk);
1286c9ed842SEtienne Carriere }
1296c9ed842SEtienne Carriere 
1302305544bSClément Léger static void clk_disable_no_lock(struct clk *clk)
1312305544bSClément Léger {
1322305544bSClément Léger 	struct clk *parent = NULL;
1332305544bSClément Léger 
1342305544bSClément Léger 	if (!refcount_dec(&clk->enabled_count))
1352305544bSClément Léger 		return;
1362305544bSClément Léger 
1372305544bSClément Léger 	if (clk->ops->disable)
1382305544bSClément Léger 		clk->ops->disable(clk);
1392305544bSClément Léger 
1402305544bSClément Léger 	parent = clk_get_parent(clk);
1412305544bSClément Léger 	if (parent)
1422305544bSClément Léger 		clk_disable_no_lock(parent);
1432305544bSClément Léger }
1442305544bSClément Léger 
1452305544bSClément Léger static TEE_Result clk_enable_no_lock(struct clk *clk)
1462305544bSClément Léger {
1472305544bSClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
1482305544bSClément Léger 	struct clk *parent = NULL;
1492305544bSClément Léger 
1502305544bSClément Léger 	if (refcount_inc(&clk->enabled_count))
1512305544bSClément Léger 		return TEE_SUCCESS;
1522305544bSClément Léger 
1532305544bSClément Léger 	parent = clk_get_parent(clk);
1542305544bSClément Léger 	if (parent) {
1552305544bSClément Léger 		res = clk_enable_no_lock(parent);
1562305544bSClément Léger 		if (res)
1572305544bSClément Léger 			return res;
1582305544bSClément Léger 	}
1592305544bSClément Léger 
1602305544bSClément Léger 	if (clk->ops->enable) {
1612305544bSClément Léger 		res = clk->ops->enable(clk);
1622305544bSClément Léger 		if (res) {
1632305544bSClément Léger 			if (parent)
1642305544bSClément Léger 				clk_disable_no_lock(parent);
1652305544bSClément Léger 
1662305544bSClément Léger 			return res;
1672305544bSClément Léger 		}
1682305544bSClément Léger 	}
1692305544bSClément Léger 
1702305544bSClément Léger 	refcount_set(&clk->enabled_count, 1);
1712305544bSClément Léger 
1722305544bSClément Léger 	return TEE_SUCCESS;
1732305544bSClément Léger }
1742305544bSClément Léger 
1752305544bSClément Léger TEE_Result clk_enable(struct clk *clk)
1762305544bSClément Léger {
1772305544bSClément Léger 	uint32_t exceptions = 0;
1782305544bSClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
1792305544bSClément Léger 
1802305544bSClément Léger 	exceptions = cpu_spin_lock_xsave(&clk_lock);
1812305544bSClément Léger 	res = clk_enable_no_lock(clk);
1822305544bSClément Léger 	cpu_spin_unlock_xrestore(&clk_lock, exceptions);
1832305544bSClément Léger 
1842305544bSClément Léger 	return res;
1852305544bSClément Léger }
1862305544bSClément Léger 
1872305544bSClément Léger void clk_disable(struct clk *clk)
1882305544bSClément Léger {
1892305544bSClément Léger 	uint32_t exceptions = 0;
1902305544bSClément Léger 
1912305544bSClément Léger 	exceptions = cpu_spin_lock_xsave(&clk_lock);
1922305544bSClément Léger 	clk_disable_no_lock(clk);
1932305544bSClément Léger 	cpu_spin_unlock_xrestore(&clk_lock, exceptions);
1942305544bSClément Léger }
1952305544bSClément Léger 
1962305544bSClément Léger unsigned long clk_get_rate(struct clk *clk)
1972305544bSClément Léger {
1982305544bSClément Léger 	return clk->rate;
1992305544bSClément Léger }
2002305544bSClément Léger 
2012305544bSClément Léger static TEE_Result clk_set_rate_no_lock(struct clk *clk, unsigned long rate)
2022305544bSClément Léger {
2032305544bSClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
2042305544bSClément Léger 	unsigned long parent_rate = 0;
2052305544bSClément Léger 
2062305544bSClément Léger 	if (clk->parent)
2072305544bSClément Léger 		parent_rate = clk_get_rate(clk->parent);
2082305544bSClément Léger 
2092305544bSClément Léger 	res = clk->ops->set_rate(clk, rate, parent_rate);
2102305544bSClément Léger 	if (res)
2112305544bSClément Léger 		return res;
2122305544bSClément Léger 
2132305544bSClément Léger 	clk_compute_rate_no_lock(clk);
2142305544bSClément Léger 
2152305544bSClément Léger 	return TEE_SUCCESS;
2162305544bSClément Léger }
2172305544bSClément Léger 
2182305544bSClément Léger TEE_Result clk_set_rate(struct clk *clk, unsigned long rate)
2192305544bSClément Léger {
2202305544bSClément Léger 	uint32_t exceptions = 0;
2212305544bSClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
2222305544bSClément Léger 
2232305544bSClément Léger 	if (!clk->ops->set_rate)
2242305544bSClément Léger 		return TEE_ERROR_NOT_SUPPORTED;
2252305544bSClément Léger 
2262305544bSClément Léger 	exceptions =  cpu_spin_lock_xsave(&clk_lock);
2272305544bSClément Léger 
2282305544bSClément Léger 	if (clk->flags & CLK_SET_RATE_GATE && clk_is_enabled_no_lock(clk))
2292305544bSClément Léger 		res = TEE_ERROR_BAD_STATE;
2302305544bSClément Léger 	else
2312305544bSClément Léger 		res = clk_set_rate_no_lock(clk, rate);
2322305544bSClément Léger 
2332305544bSClément Léger 	cpu_spin_unlock_xrestore(&clk_lock, exceptions);
2342305544bSClément Léger 
2352305544bSClément Léger 	return res;
2362305544bSClément Léger }
2372305544bSClément Léger 
2382305544bSClément Léger struct clk *clk_get_parent(struct clk *clk)
2392305544bSClément Léger {
2402305544bSClément Léger 	return clk->parent;
2412305544bSClément Léger }
2422305544bSClément Léger 
2432305544bSClément Léger static TEE_Result clk_get_parent_idx(struct clk *clk, struct clk *parent,
2442305544bSClément Léger 				     size_t *pidx)
2452305544bSClément Léger {
2462305544bSClément Léger 	size_t i = 0;
2472305544bSClément Léger 
2482305544bSClément Léger 	for (i = 0; i < clk_get_num_parents(clk); i++) {
2492305544bSClément Léger 		if (clk_get_parent_by_index(clk, i) == parent) {
2502305544bSClément Léger 			*pidx = i;
2512305544bSClément Léger 			return TEE_SUCCESS;
2522305544bSClément Léger 		}
2532305544bSClément Léger 	}
2542305544bSClément Léger 	EMSG("Clock %s is not a parent of clock %s", parent->name, clk->name);
2552305544bSClément Léger 
2562305544bSClément Léger 	return TEE_ERROR_BAD_PARAMETERS;
2572305544bSClément Léger }
2582305544bSClément Léger 
2592305544bSClément Léger static TEE_Result clk_set_parent_no_lock(struct clk *clk, struct clk *parent,
2602305544bSClément Léger 					 size_t pidx)
2612305544bSClément Léger {
2622305544bSClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
2632305544bSClément Léger 	bool was_enabled = false;
2642305544bSClément Léger 
2652305544bSClément Léger 	/* Requested parent is already the one set */
2662305544bSClément Léger 	if (clk->parent == parent)
2672305544bSClément Léger 		return TEE_SUCCESS;
2682305544bSClément Léger 
2692305544bSClément Léger 	was_enabled = clk_is_enabled_no_lock(clk);
2702305544bSClément Léger 	/* Call is needed to decrement refcount on current parent tree */
2712305544bSClément Léger 	if (was_enabled)
2722305544bSClément Léger 		clk_disable_no_lock(clk);
2732305544bSClément Léger 
2742305544bSClément Léger 	res = clk->ops->set_parent(clk, pidx);
2752305544bSClément Léger 	if (res)
2762305544bSClément Léger 		goto out;
2772305544bSClément Léger 
2782305544bSClément Léger 	clk->parent = parent;
2792305544bSClément Léger 
2802305544bSClément Léger 	/* The parent changed and the rate might also have changed */
2812305544bSClément Léger 	clk_compute_rate_no_lock(clk);
2822305544bSClément Léger 
2832305544bSClément Léger out:
2842305544bSClément Léger 	/* Call is needed to increment refcount on the new parent tree */
2852305544bSClément Léger 	if (was_enabled) {
2862305544bSClément Léger 		res = clk_enable_no_lock(clk);
2872305544bSClément Léger 		if (res)
2882305544bSClément Léger 			panic("Failed to re-enable clock after setting parent");
2892305544bSClément Léger 	}
2902305544bSClément Léger 
2912305544bSClément Léger 	return res;
2922305544bSClément Léger }
2932305544bSClément Léger 
2942305544bSClément Léger TEE_Result clk_set_parent(struct clk *clk, struct clk *parent)
2952305544bSClément Léger {
2962305544bSClément Léger 	size_t pidx = 0;
2972305544bSClément Léger 	uint32_t exceptions = 0;
2982305544bSClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
2992305544bSClément Léger 
3002305544bSClément Léger 	if (clk_get_parent_idx(clk, parent, &pidx) || !clk->ops->set_parent)
3012305544bSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
3022305544bSClément Léger 
3032305544bSClément Léger 	exceptions = cpu_spin_lock_xsave(&clk_lock);
3042305544bSClément Léger 	if (clk->flags & CLK_SET_PARENT_GATE && clk_is_enabled_no_lock(clk)) {
3052305544bSClément Léger 		res = TEE_ERROR_BAD_STATE;
3062305544bSClément Léger 		goto out;
3072305544bSClément Léger 	}
3082305544bSClément Léger 
3092305544bSClément Léger 	res = clk_set_parent_no_lock(clk, parent, pidx);
3102305544bSClément Léger out:
3112305544bSClément Léger 	cpu_spin_unlock_xrestore(&clk_lock, exceptions);
3122305544bSClément Léger 
3132305544bSClément Léger 	return res;
3142305544bSClément Léger }
3155df61a5dSClément Léger 
3165df61a5dSClément Léger TEE_Result clk_get_rates_array(struct clk *clk, size_t start_index,
3175df61a5dSClément Léger 			       unsigned long *rates, size_t *nb_elts)
3185df61a5dSClément Léger {
3195df61a5dSClément Léger 	if (!clk->ops->get_rates_array)
3205df61a5dSClément Léger 		return TEE_ERROR_NOT_SUPPORTED;
3215df61a5dSClément Léger 
3225df61a5dSClément Léger 	return clk->ops->get_rates_array(clk, start_index, rates, nb_elts);
3235df61a5dSClément Léger }
324*cd04d138SEtienne Carriere 
325*cd04d138SEtienne Carriere /* Return updated message buffer position of NULL on failure */
326*cd04d138SEtienne Carriere static __printf(3, 4) char *add_msg(char *cur, char *end, const char *fmt, ...)
327*cd04d138SEtienne Carriere {
328*cd04d138SEtienne Carriere 	va_list ap = { };
329*cd04d138SEtienne Carriere 	int max_len = end - cur;
330*cd04d138SEtienne Carriere 	int ret = 0;
331*cd04d138SEtienne Carriere 
332*cd04d138SEtienne Carriere 	va_start(ap, fmt);
333*cd04d138SEtienne Carriere 	ret = vsnprintf(cur, max_len, fmt, ap);
334*cd04d138SEtienne Carriere 	va_end(ap);
335*cd04d138SEtienne Carriere 
336*cd04d138SEtienne Carriere 	if (ret < 0 || ret >= max_len)
337*cd04d138SEtienne Carriere 		return NULL;
338*cd04d138SEtienne Carriere 
339*cd04d138SEtienne Carriere 	return cur + ret;
340*cd04d138SEtienne Carriere }
341*cd04d138SEtienne Carriere 
342*cd04d138SEtienne Carriere static void __maybe_unused print_clock(struct clk *clk, int indent)
343*cd04d138SEtienne Carriere {
344*cd04d138SEtienne Carriere 	static const char * const rate_unit[] = { "Hz", "kHz", "MHz", "GHz" };
345*cd04d138SEtienne Carriere 	int max_unit = ARRAY_SIZE(rate_unit);
346*cd04d138SEtienne Carriere 	unsigned long rate = 0;
347*cd04d138SEtienne Carriere 	char msg_buf[128] = { };
348*cd04d138SEtienne Carriere 	char *msg_end = msg_buf + sizeof(msg_buf);
349*cd04d138SEtienne Carriere 	char *msg = msg_buf;
350*cd04d138SEtienne Carriere 	int n = 0;
351*cd04d138SEtienne Carriere 
352*cd04d138SEtienne Carriere 	/*
353*cd04d138SEtienne Carriere 	 * Currently prints the clock state based on the clock refcount.
354*cd04d138SEtienne Carriere 	 * A future change could print the hardware clock state when
355*cd04d138SEtienne Carriere 	 * related clock driver provides a struct clk_ops::is_enabled handler
356*cd04d138SEtienne Carriere 	 */
357*cd04d138SEtienne Carriere 
358*cd04d138SEtienne Carriere 	if (indent) {
359*cd04d138SEtienne Carriere 		for (n = 0; n < indent - 1; n++) {
360*cd04d138SEtienne Carriere 			msg = add_msg(msg, msg_end, "|   ");
361*cd04d138SEtienne Carriere 			if (!msg)
362*cd04d138SEtienne Carriere 				goto out;
363*cd04d138SEtienne Carriere 		}
364*cd04d138SEtienne Carriere 
365*cd04d138SEtienne Carriere 		msg = add_msg(msg, msg_end, "+-- ");
366*cd04d138SEtienne Carriere 		if (!msg)
367*cd04d138SEtienne Carriere 			goto out;
368*cd04d138SEtienne Carriere 	}
369*cd04d138SEtienne Carriere 
370*cd04d138SEtienne Carriere 	rate = clk_get_rate(clk);
371*cd04d138SEtienne Carriere 	for (n = 1; rate && !(rate % 1000) && n < max_unit; n++)
372*cd04d138SEtienne Carriere 		rate /= 1000;
373*cd04d138SEtienne Carriere 
374*cd04d138SEtienne Carriere 	msg = add_msg(msg, msg_end, "%s \t(%3s / refcnt %u / %ld %s)",
375*cd04d138SEtienne Carriere 		      clk_get_name(clk),
376*cd04d138SEtienne Carriere 		      refcount_val(&clk->enabled_count) ? "on " : "off",
377*cd04d138SEtienne Carriere 		      refcount_val(&clk->enabled_count),
378*cd04d138SEtienne Carriere 		      rate, rate_unit[n - 1]);
379*cd04d138SEtienne Carriere 	if (!msg)
380*cd04d138SEtienne Carriere 		goto out;
381*cd04d138SEtienne Carriere 
382*cd04d138SEtienne Carriere out:
383*cd04d138SEtienne Carriere 	if (!msg)
384*cd04d138SEtienne Carriere 		snprintf(msg_end - 4, 4, "...");
385*cd04d138SEtienne Carriere 
386*cd04d138SEtienne Carriere 	IMSG("%s", msg_buf);
387*cd04d138SEtienne Carriere }
388*cd04d138SEtienne Carriere 
389*cd04d138SEtienne Carriere static void print_clock_subtree(struct clk *clk_root __maybe_unused,
390*cd04d138SEtienne Carriere 				int indent __maybe_unused)
391*cd04d138SEtienne Carriere {
392*cd04d138SEtienne Carriere #ifdef CFG_DRIVERS_CLK_PRINT_TREE
393*cd04d138SEtienne Carriere 	struct clk *clk = NULL;
394*cd04d138SEtienne Carriere 
395*cd04d138SEtienne Carriere 	STAILQ_FOREACH(clk, &clock_list, link) {
396*cd04d138SEtienne Carriere 		if (clk_get_parent(clk) == clk_root) {
397*cd04d138SEtienne Carriere 			print_clock(clk, indent + 1);
398*cd04d138SEtienne Carriere 			print_clock_subtree(clk, indent + 1);
399*cd04d138SEtienne Carriere 			if (indent == -1)
400*cd04d138SEtienne Carriere 				IMSG("%s", "");
401*cd04d138SEtienne Carriere 		}
402*cd04d138SEtienne Carriere 	}
403*cd04d138SEtienne Carriere #endif
404*cd04d138SEtienne Carriere }
405*cd04d138SEtienne Carriere 
406*cd04d138SEtienne Carriere void clk_print_tree(void)
407*cd04d138SEtienne Carriere {
408*cd04d138SEtienne Carriere 	if (IS_ENABLED(CFG_DRIVERS_CLK_PRINT_TREE)) {
409*cd04d138SEtienne Carriere 		uint32_t exceptions = 0;
410*cd04d138SEtienne Carriere 
411*cd04d138SEtienne Carriere 		exceptions = cpu_spin_lock_xsave(&clk_lock);
412*cd04d138SEtienne Carriere 		IMSG("Clock tree summary");
413*cd04d138SEtienne Carriere 		IMSG("%s", "");
414*cd04d138SEtienne Carriere 		print_clock_subtree(NULL, -1);
415*cd04d138SEtienne Carriere 		cpu_spin_unlock_xrestore(&clk_lock, exceptions);
416*cd04d138SEtienne Carriere 	}
417*cd04d138SEtienne Carriere }
418