xref: /optee_os/core/drivers/regulator/regulator.c (revision 4e51bea94caee316c36c2c52a31177d0e385c6cb)
11a3d3273SEtienne Carriere // SPDX-License-Identifier: BSD-2-Clause
21a3d3273SEtienne Carriere /*
31a3d3273SEtienne Carriere  * Copyright (c) 2023, STMicroelectronics
41a3d3273SEtienne Carriere  */
51a3d3273SEtienne Carriere 
61a3d3273SEtienne Carriere #include <assert.h>
71a3d3273SEtienne Carriere #include <compiler.h>
81a3d3273SEtienne Carriere #include <config.h>
91a3d3273SEtienne Carriere #include <drivers/regulator.h>
101a3d3273SEtienne Carriere #include <initcall.h>
111a3d3273SEtienne Carriere #include <keep.h>
121a3d3273SEtienne Carriere #include <kernel/boot.h>
131a3d3273SEtienne Carriere #include <kernel/delay.h>
14c80790feSEtienne Carriere #include <kernel/mutex_pm_aware.h>
151a3d3273SEtienne Carriere #include <kernel/panic.h>
161a3d3273SEtienne Carriere #include <kernel/pm.h>
171a3d3273SEtienne Carriere #include <kernel/tee_time.h>
181a3d3273SEtienne Carriere #include <kernel/thread.h>
191a3d3273SEtienne Carriere #include <libfdt.h>
201a3d3273SEtienne Carriere #include <limits.h>
211a3d3273SEtienne Carriere #include <stdint.h>
221a3d3273SEtienne Carriere #include <stdio.h>
231a3d3273SEtienne Carriere #include <string.h>
241a3d3273SEtienne Carriere #include <util.h>
251a3d3273SEtienne Carriere 
261a3d3273SEtienne Carriere static SLIST_HEAD(, regulator) regulator_device_list =
271a3d3273SEtienne Carriere 	SLIST_HEAD_INITIALIZER(regulator);
281a3d3273SEtienne Carriere 
29c80790feSEtienne Carriere /* Access protection mutex complying the power state transitions context */
lock_regulator(struct regulator * regulator)301a3d3273SEtienne Carriere static void lock_regulator(struct regulator *regulator)
311a3d3273SEtienne Carriere {
32c80790feSEtienne Carriere 	mutex_pm_aware_lock(&regulator->mutex);
331a3d3273SEtienne Carriere }
341a3d3273SEtienne Carriere 
unlock_regulator(struct regulator * regulator)351a3d3273SEtienne Carriere static void unlock_regulator(struct regulator *regulator)
361a3d3273SEtienne Carriere {
37c80790feSEtienne Carriere 	mutex_pm_aware_unlock(&regulator->mutex);
381a3d3273SEtienne Carriere }
391a3d3273SEtienne Carriere 
set_state(struct regulator * regulator,bool on_not_off)401a3d3273SEtienne Carriere static TEE_Result set_state(struct regulator *regulator, bool on_not_off)
411a3d3273SEtienne Carriere {
421a3d3273SEtienne Carriere 	if (!regulator->ops->set_state)
431a3d3273SEtienne Carriere 		return TEE_SUCCESS;
441a3d3273SEtienne Carriere 
451a3d3273SEtienne Carriere 	return regulator->ops->set_state(regulator, on_not_off);
461a3d3273SEtienne Carriere }
471a3d3273SEtienne Carriere 
regulator_refcnt_enable(struct regulator * regulator)481a3d3273SEtienne Carriere static TEE_Result regulator_refcnt_enable(struct regulator *regulator)
491a3d3273SEtienne Carriere {
501a3d3273SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
511a3d3273SEtienne Carriere 
521a3d3273SEtienne Carriere 	FMSG("%s", regulator_name(regulator));
531a3d3273SEtienne Carriere 
541a3d3273SEtienne Carriere 	if (regulator->supply) {
551a3d3273SEtienne Carriere 		res = regulator_enable(regulator->supply);
561a3d3273SEtienne Carriere 		if (res)
571a3d3273SEtienne Carriere 			return res;
581a3d3273SEtienne Carriere 	}
591a3d3273SEtienne Carriere 
601a3d3273SEtienne Carriere 	lock_regulator(regulator);
611a3d3273SEtienne Carriere 
621a3d3273SEtienne Carriere 	if (!regulator->refcount) {
631a3d3273SEtienne Carriere 		res = set_state(regulator, true);
641a3d3273SEtienne Carriere 		if (res) {
651a3d3273SEtienne Carriere 			EMSG("regul %s set state failed with %#"PRIx32,
661a3d3273SEtienne Carriere 			     regulator_name(regulator), res);
671a3d3273SEtienne Carriere 
681a3d3273SEtienne Carriere 			unlock_regulator(regulator);
691a3d3273SEtienne Carriere 
701a3d3273SEtienne Carriere 			if (regulator->supply &&
711a3d3273SEtienne Carriere 			    regulator_disable(regulator->supply))
721a3d3273SEtienne Carriere 				panic();
731a3d3273SEtienne Carriere 
741a3d3273SEtienne Carriere 			return res;
751a3d3273SEtienne Carriere 		}
76*4e51bea9SEtienne Carriere 
77*4e51bea9SEtienne Carriere 		udelay(regulator->enable_ramp_delay_us);
781a3d3273SEtienne Carriere 	}
791a3d3273SEtienne Carriere 
801a3d3273SEtienne Carriere 	regulator->refcount++;
811a3d3273SEtienne Carriere 	if (!regulator->refcount)
821a3d3273SEtienne Carriere 		panic();
831a3d3273SEtienne Carriere 
841a3d3273SEtienne Carriere 	FMSG("%s refcount: %u", regulator_name(regulator), regulator->refcount);
851a3d3273SEtienne Carriere 
861a3d3273SEtienne Carriere 	unlock_regulator(regulator);
871a3d3273SEtienne Carriere 
881a3d3273SEtienne Carriere 	return TEE_SUCCESS;
891a3d3273SEtienne Carriere }
901a3d3273SEtienne Carriere 
regulator_enable(struct regulator * regulator)911a3d3273SEtienne Carriere TEE_Result regulator_enable(struct regulator *regulator)
921a3d3273SEtienne Carriere {
931a3d3273SEtienne Carriere 	assert(regulator);
941a3d3273SEtienne Carriere 	FMSG("%s", regulator_name(regulator));
951a3d3273SEtienne Carriere 
96e3830fc7SEtienne Carriere 	if (regulator_is_always_on(regulator))
97e3830fc7SEtienne Carriere 		return TEE_SUCCESS;
98e3830fc7SEtienne Carriere 
991a3d3273SEtienne Carriere 	return regulator_refcnt_enable(regulator);
1001a3d3273SEtienne Carriere }
1011a3d3273SEtienne Carriere 
regulator_refcnt_disable(struct regulator * regulator)1021a3d3273SEtienne Carriere static TEE_Result regulator_refcnt_disable(struct regulator *regulator)
1031a3d3273SEtienne Carriere {
1041a3d3273SEtienne Carriere 	FMSG("%s", regulator_name(regulator));
1051a3d3273SEtienne Carriere 
1061a3d3273SEtienne Carriere 	lock_regulator(regulator);
1071a3d3273SEtienne Carriere 
1081a3d3273SEtienne Carriere 	if (regulator->refcount == 1) {
1091a3d3273SEtienne Carriere 		TEE_Result res = set_state(regulator, false);
1101a3d3273SEtienne Carriere 
1111a3d3273SEtienne Carriere 		if (res) {
1121a3d3273SEtienne Carriere 			EMSG("regul %s set state failed with %#"PRIx32,
1131a3d3273SEtienne Carriere 			     regulator_name(regulator), res);
1141a3d3273SEtienne Carriere 			unlock_regulator(regulator);
1151a3d3273SEtienne Carriere 			return res;
1161a3d3273SEtienne Carriere 		}
1171a3d3273SEtienne Carriere 	}
1181a3d3273SEtienne Carriere 
1191a3d3273SEtienne Carriere 	if (!regulator->refcount) {
1201a3d3273SEtienne Carriere 		EMSG("Unbalanced %s", regulator_name(regulator));
1211a3d3273SEtienne Carriere 		panic();
1221a3d3273SEtienne Carriere 	}
1231a3d3273SEtienne Carriere 
1241a3d3273SEtienne Carriere 	regulator->refcount--;
1251a3d3273SEtienne Carriere 
1261a3d3273SEtienne Carriere 	FMSG("%s refcount: %u", regulator_name(regulator), regulator->refcount);
1271a3d3273SEtienne Carriere 
1281a3d3273SEtienne Carriere 	unlock_regulator(regulator);
1291a3d3273SEtienne Carriere 
1301a3d3273SEtienne Carriere 	if (regulator->supply && regulator_disable(regulator->supply)) {
1311a3d3273SEtienne Carriere 		/* We can't leave this unbalanced */
1321a3d3273SEtienne Carriere 		EMSG("Can't disable %s", regulator_name(regulator->supply));
1331a3d3273SEtienne Carriere 		panic();
1341a3d3273SEtienne Carriere 	}
1351a3d3273SEtienne Carriere 
1361a3d3273SEtienne Carriere 	return TEE_SUCCESS;
1371a3d3273SEtienne Carriere }
1381a3d3273SEtienne Carriere 
regulator_disable(struct regulator * regulator)1391a3d3273SEtienne Carriere TEE_Result regulator_disable(struct regulator *regulator)
1401a3d3273SEtienne Carriere {
1411a3d3273SEtienne Carriere 	assert(regulator);
1421a3d3273SEtienne Carriere 	FMSG("%s", regulator_name(regulator));
1431a3d3273SEtienne Carriere 
144e3830fc7SEtienne Carriere 	if (regulator_is_always_on(regulator))
145e3830fc7SEtienne Carriere 		return TEE_SUCCESS;
146e3830fc7SEtienne Carriere 
1471a3d3273SEtienne Carriere 	return regulator_refcnt_disable(regulator);
1481a3d3273SEtienne Carriere }
1491a3d3273SEtienne Carriere 
regulator_is_enabled(struct regulator * regulator)1501a3d3273SEtienne Carriere bool regulator_is_enabled(struct regulator *regulator)
1511a3d3273SEtienne Carriere {
1521a3d3273SEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
1531a3d3273SEtienne Carriere 	bool enabled = false;
1541a3d3273SEtienne Carriere 
1551a3d3273SEtienne Carriere 	if (!regulator->ops->get_state)
1561a3d3273SEtienne Carriere 		return true;
1571a3d3273SEtienne Carriere 
1581a3d3273SEtienne Carriere 	lock_regulator(regulator);
1591a3d3273SEtienne Carriere 	res = regulator->ops->get_state(regulator, &enabled);
1601a3d3273SEtienne Carriere 	unlock_regulator(regulator);
1611a3d3273SEtienne Carriere 
1621a3d3273SEtienne Carriere 	if (res)
1631a3d3273SEtienne Carriere 		EMSG("regul %s get state failed with %#"PRIx32,
1641a3d3273SEtienne Carriere 		     regulator_name(regulator), res);
1651a3d3273SEtienne Carriere 
1661a3d3273SEtienne Carriere 	return !res && enabled;
1671a3d3273SEtienne Carriere }
1681a3d3273SEtienne Carriere 
regulator_get_voltage(struct regulator * regulator)169b4d1c08aSPatrick Delaunay int regulator_get_voltage(struct regulator *regulator)
170b4d1c08aSPatrick Delaunay {
171b4d1c08aSPatrick Delaunay 	TEE_Result res = TEE_SUCCESS;
172b4d1c08aSPatrick Delaunay 	int level_uv = regulator->min_uv;
173b4d1c08aSPatrick Delaunay 
174b4d1c08aSPatrick Delaunay 	if (regulator->ops->get_voltage) {
175b4d1c08aSPatrick Delaunay 		res = regulator->ops->get_voltage(regulator, &level_uv);
176b4d1c08aSPatrick Delaunay 		if (res) {
177b4d1c08aSPatrick Delaunay 			EMSG("%s get_voltage failed with %#"PRIx32,
178b4d1c08aSPatrick Delaunay 			     regulator_name(regulator), res);
179b4d1c08aSPatrick Delaunay 			level_uv = 0;
180b4d1c08aSPatrick Delaunay 		}
181b4d1c08aSPatrick Delaunay 	}
182b4d1c08aSPatrick Delaunay 
183b4d1c08aSPatrick Delaunay 	return level_uv;
184b4d1c08aSPatrick Delaunay }
185b4d1c08aSPatrick Delaunay 
regulator_set_voltage(struct regulator * regulator,int level_uv)1861a3d3273SEtienne Carriere TEE_Result regulator_set_voltage(struct regulator *regulator, int level_uv)
1871a3d3273SEtienne Carriere {
1881a3d3273SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
189b4d1c08aSPatrick Delaunay 	int cur_uv = 0;
1901a3d3273SEtienne Carriere 
1911a3d3273SEtienne Carriere 	assert(regulator);
1921a3d3273SEtienne Carriere 	FMSG("%s %duV", regulator_name(regulator), level_uv);
1931a3d3273SEtienne Carriere 
1941a3d3273SEtienne Carriere 	if (level_uv < regulator->min_uv || level_uv > regulator->max_uv)
1951a3d3273SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
1961a3d3273SEtienne Carriere 
197b4d1c08aSPatrick Delaunay 	cur_uv = regulator_get_voltage(regulator);
198b4d1c08aSPatrick Delaunay 	if (level_uv == cur_uv)
1991a3d3273SEtienne Carriere 		return TEE_SUCCESS;
2001a3d3273SEtienne Carriere 
2011a3d3273SEtienne Carriere 	if (!regulator->ops->set_voltage)
2021a3d3273SEtienne Carriere 		return TEE_ERROR_NOT_SUPPORTED;
2031a3d3273SEtienne Carriere 
2041a3d3273SEtienne Carriere 	lock_regulator(regulator);
2051a3d3273SEtienne Carriere 	res = regulator->ops->set_voltage(regulator, level_uv);
2061a3d3273SEtienne Carriere 	unlock_regulator(regulator);
2071a3d3273SEtienne Carriere 
2081a3d3273SEtienne Carriere 	if (res) {
2091a3d3273SEtienne Carriere 		EMSG("regul %s set volt failed with %#"PRIx32,
2101a3d3273SEtienne Carriere 		     regulator_name(regulator), res);
2111a3d3273SEtienne Carriere 		return res;
2121a3d3273SEtienne Carriere 	}
2131a3d3273SEtienne Carriere 
2148c48c11bSEtienne Carriere 	if (regulator->ramp_delay_uv_per_us) {
2158c48c11bSEtienne Carriere 		unsigned int d = 0;
2168c48c11bSEtienne Carriere 
2178c48c11bSEtienne Carriere 		if (cur_uv > level_uv)
2188c48c11bSEtienne Carriere 			d = cur_uv - level_uv;
2198c48c11bSEtienne Carriere 		else
2208c48c11bSEtienne Carriere 			d = level_uv - cur_uv;
2218c48c11bSEtienne Carriere 
2228c48c11bSEtienne Carriere 		d /= regulator->ramp_delay_uv_per_us;
2238c48c11bSEtienne Carriere 
2248c48c11bSEtienne Carriere 		FMSG("%s %"PRIu32"uS", regulator_name(regulator), d);
2258c48c11bSEtienne Carriere 		udelay(d);
2268c48c11bSEtienne Carriere 	}
2278c48c11bSEtienne Carriere 
2281a3d3273SEtienne Carriere 	return TEE_SUCCESS;
2291a3d3273SEtienne Carriere }
2301a3d3273SEtienne Carriere 
regulator_supported_voltages(struct regulator * regulator,struct regulator_voltages_desc ** desc,const int ** levels)23143c155baSEtienne Carriere TEE_Result regulator_supported_voltages(struct regulator *regulator,
232ace929f0SEtienne Carriere 					struct regulator_voltages_desc **desc,
233ace929f0SEtienne Carriere 					const int **levels)
23443c155baSEtienne Carriere {
235dd019e44SEtienne Carriere 	TEE_Result res = TEE_ERROR_NOT_SUPPORTED;
236dd019e44SEtienne Carriere 
237ace929f0SEtienne Carriere 	assert(regulator && desc && levels);
23843c155baSEtienne Carriere 
239dd019e44SEtienne Carriere 	if (regulator->ops->supported_voltages)
240ace929f0SEtienne Carriere 		res = regulator->ops->supported_voltages(regulator, desc,
241ace929f0SEtienne Carriere 							 levels);
242dd019e44SEtienne Carriere 	if (res == TEE_ERROR_NOT_SUPPORTED) {
243ace929f0SEtienne Carriere 		*desc = &regulator->voltages_fallback.desc;
244ace929f0SEtienne Carriere 		*levels = regulator->voltages_fallback.levels;
245dd019e44SEtienne Carriere 	} else if (res) {
246dd019e44SEtienne Carriere 		return res;
247af5b9881SEtienne Carriere 	}
248af5b9881SEtienne Carriere 
249ba2dff77SEtienne Carriere 	if ((*desc)->type == VOLTAGE_TYPE_FULL_LIST) {
250ba2dff77SEtienne Carriere 		assert((*desc)->num_levels);
251ba2dff77SEtienne Carriere 		assert((*levels)[0] >= regulator->min_uv);
252ba2dff77SEtienne Carriere 		assert((*levels)[(*desc)->num_levels - 1] <= regulator->max_uv);
253ba2dff77SEtienne Carriere 	} else if ((*desc)->type == VOLTAGE_TYPE_INCREMENT) {
254ba2dff77SEtienne Carriere 		assert((*levels)[0] >= regulator->min_uv);
255ba2dff77SEtienne Carriere 		assert((*levels)[1] <= regulator->max_uv);
256ba2dff77SEtienne Carriere 	} else {
257ba2dff77SEtienne Carriere 		assert(0);
258ba2dff77SEtienne Carriere 	}
25943c155baSEtienne Carriere 
26043c155baSEtienne Carriere 	return TEE_SUCCESS;
26143c155baSEtienne Carriere }
26243c155baSEtienne Carriere 
regulator_register(struct regulator * regulator)2631a3d3273SEtienne Carriere TEE_Result regulator_register(struct regulator *regulator)
2641a3d3273SEtienne Carriere {
2651a3d3273SEtienne Carriere 	TEE_Result res = TEE_SUCCESS;
2661a3d3273SEtienne Carriere 	int min_uv = 0;
2671a3d3273SEtienne Carriere 	int max_uv = 0;
2681a3d3273SEtienne Carriere 	int uv = 0;
2691a3d3273SEtienne Carriere 
270e3830fc7SEtienne Carriere 	if (!regulator || !regulator->ops ||
271e3830fc7SEtienne Carriere 	    regulator->flags & ~REGULATOR_FLAGS_MASK)
2721a3d3273SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
2731a3d3273SEtienne Carriere 
274c80790feSEtienne Carriere 	mutex_pm_aware_init(&regulator->mutex);
275c80790feSEtienne Carriere 
2761a3d3273SEtienne Carriere 	regulator_get_range(regulator, &min_uv, &max_uv);
2771a3d3273SEtienne Carriere 	if (min_uv > max_uv)
2781a3d3273SEtienne Carriere 		return TEE_ERROR_BAD_PARAMETERS;
2791a3d3273SEtienne Carriere 
2801a3d3273SEtienne Carriere 	/* Sanitize regulator effective level */
281b4d1c08aSPatrick Delaunay 	uv = regulator_get_voltage(regulator);
2821a3d3273SEtienne Carriere 
2831a3d3273SEtienne Carriere 	if (uv < min_uv || uv > max_uv) {
2841a3d3273SEtienne Carriere 		res = regulator_set_voltage(regulator, min_uv);
2851a3d3273SEtienne Carriere 		if (res)
2861a3d3273SEtienne Carriere 			return res;
2871a3d3273SEtienne Carriere 	}
2881a3d3273SEtienne Carriere 
289e3830fc7SEtienne Carriere 	/* Unbalanced enable refcount to keep always-on regulators enabled */
290e3830fc7SEtienne Carriere 	if (regulator_is_always_on(regulator)) {
291e3830fc7SEtienne Carriere 		res = regulator_refcnt_enable(regulator);
292e3830fc7SEtienne Carriere 		if (res)
293e3830fc7SEtienne Carriere 			return res;
294e3830fc7SEtienne Carriere 	}
295e3830fc7SEtienne Carriere 
29643c155baSEtienne Carriere 	/* Preset voltage list in case ops::supported_voltages is NULL */
29743c155baSEtienne Carriere 	if (regulator->min_uv == regulator->max_uv) {
29843c155baSEtienne Carriere 		regulator->voltages_fallback.desc.type = VOLTAGE_TYPE_FULL_LIST;
29943c155baSEtienne Carriere 		regulator->voltages_fallback.desc.num_levels = 1;
30043c155baSEtienne Carriere 		regulator->voltages_fallback.levels[0] = regulator->min_uv;
30143c155baSEtienne Carriere 	} else {
30243c155baSEtienne Carriere 		regulator->voltages_fallback.desc.type = VOLTAGE_TYPE_INCREMENT;
30343c155baSEtienne Carriere 		regulator->voltages_fallback.levels[0] = regulator->min_uv;
30443c155baSEtienne Carriere 		regulator->voltages_fallback.levels[1] = regulator->max_uv;
30543c155baSEtienne Carriere 		regulator->voltages_fallback.levels[2] = 1;
30643c155baSEtienne Carriere 	}
30743c155baSEtienne Carriere 
3081a3d3273SEtienne Carriere 	SLIST_INSERT_HEAD(&regulator_device_list, regulator, link);
3091a3d3273SEtienne Carriere 
3101a3d3273SEtienne Carriere 	return TEE_SUCCESS;
3111a3d3273SEtienne Carriere }
3121a3d3273SEtienne Carriere 
3131a3d3273SEtienne Carriere /*
3141a3d3273SEtienne Carriere  * Clean-up regulators that are not used.
3151a3d3273SEtienne Carriere  */
regulator_core_cleanup(void)3161a3d3273SEtienne Carriere static TEE_Result regulator_core_cleanup(void)
3171a3d3273SEtienne Carriere {
3181a3d3273SEtienne Carriere 	struct regulator *regulator = NULL;
3191a3d3273SEtienne Carriere 
3201a3d3273SEtienne Carriere 	SLIST_FOREACH(regulator, &regulator_device_list, link) {
3211a3d3273SEtienne Carriere 		if (!regulator->refcount) {
3221a3d3273SEtienne Carriere 			DMSG("disable %s", regulator_name(regulator));
3231a3d3273SEtienne Carriere 			lock_regulator(regulator);
3241a3d3273SEtienne Carriere 			set_state(regulator, false /* disable */);
3251a3d3273SEtienne Carriere 			unlock_regulator(regulator);
3261a3d3273SEtienne Carriere 		}
3271a3d3273SEtienne Carriere 	}
3281a3d3273SEtienne Carriere 
3293c1b8123SPascal Paillet 	if (TRACE_LEVEL >= TRACE_DEBUG)
330c4292779SEtienne Carriere 		regulator_print_tree();
3311a3d3273SEtienne Carriere 
3321a3d3273SEtienne Carriere 	return TEE_SUCCESS;
3331a3d3273SEtienne Carriere }
3341a3d3273SEtienne Carriere 
3351a3d3273SEtienne Carriere release_init_resource(regulator_core_cleanup);
336c4292779SEtienne Carriere 
337c4292779SEtienne Carriere /* Return updated message buffer position of NULL on failure */
add_msg(char * cur,char * end,const char * fmt,...)338c4292779SEtienne Carriere static __printf(3, 4) char *add_msg(char *cur, char *end, const char *fmt, ...)
339c4292779SEtienne Carriere {
340c4292779SEtienne Carriere 	va_list ap = { };
341c4292779SEtienne Carriere 	int max_len = end - cur;
342c4292779SEtienne Carriere 	int ret = 0;
343c4292779SEtienne Carriere 
344c4292779SEtienne Carriere 	va_start(ap, fmt);
345c4292779SEtienne Carriere 	ret = vsnprintf(cur, max_len, fmt, ap);
346c4292779SEtienne Carriere 	va_end(ap);
347c4292779SEtienne Carriere 
348c4292779SEtienne Carriere 	if (ret < 0 || ret >= max_len)
349c4292779SEtienne Carriere 		return NULL;
350c4292779SEtienne Carriere 
351c4292779SEtienne Carriere 	return cur + ret;
352c4292779SEtienne Carriere }
353c4292779SEtienne Carriere 
find_next_regulator(struct regulator * parent,struct regulator * sibling)354c4292779SEtienne Carriere static struct regulator *find_next_regulator(struct regulator *parent,
355c4292779SEtienne Carriere 					     struct regulator *sibling)
356c4292779SEtienne Carriere {
357c4292779SEtienne Carriere 	struct regulator *regulator = NULL;
358c4292779SEtienne Carriere 
359c4292779SEtienne Carriere 	if (sibling)
360c4292779SEtienne Carriere 		regulator = SLIST_NEXT(sibling, link);
361c4292779SEtienne Carriere 	else
362c4292779SEtienne Carriere 		regulator = SLIST_FIRST(&regulator_device_list);
363c4292779SEtienne Carriere 
364c4292779SEtienne Carriere 	while (regulator && regulator->supply != parent)
365c4292779SEtienne Carriere 		regulator = SLIST_NEXT(regulator, link);
366c4292779SEtienne Carriere 
367c4292779SEtienne Carriere 	return regulator;
368c4292779SEtienne Carriere }
369c4292779SEtienne Carriere 
370c4292779SEtienne Carriere /* Regulator is the last supplied one by its supply in the registered list */
regulator_is_supply_last_supplied(struct regulator * regulator)371c4292779SEtienne Carriere static bool regulator_is_supply_last_supplied(struct regulator *regulator)
372c4292779SEtienne Carriere {
373c4292779SEtienne Carriere 	return !find_next_regulator(regulator->supply, regulator);
374c4292779SEtienne Carriere }
375c4292779SEtienne Carriere 
376c4292779SEtienne Carriere /* Supply last node may already be printed for indentation level @cur_indent */
indent_with_empty_string(struct regulator * node_regulator,int node_indent,int cur_indent)377c4292779SEtienne Carriere static bool indent_with_empty_string(struct regulator *node_regulator,
378c4292779SEtienne Carriere 				     int node_indent, int cur_indent)
379c4292779SEtienne Carriere {
380c4292779SEtienne Carriere 	struct regulator *r = node_regulator;
381c4292779SEtienne Carriere 	int n = 0;
382c4292779SEtienne Carriere 
383c4292779SEtienne Carriere 	/* Find supply at indentation level @node_indent - @cur_indent - 1 */
384c4292779SEtienne Carriere 	for (n = 0; n < node_indent - cur_indent - 1; n++)
385c4292779SEtienne Carriere 		r = r->supply;
386c4292779SEtienne Carriere 
387c4292779SEtienne Carriere 	return regulator_is_supply_last_supplied(r);
388c4292779SEtienne Carriere }
389c4292779SEtienne Carriere 
print_regulator(struct regulator * regulator,int indent)390c4292779SEtienne Carriere static void __maybe_unused print_regulator(struct regulator *regulator,
391c4292779SEtienne Carriere 					   int indent)
392c4292779SEtienne Carriere {
393c4292779SEtienne Carriere 	static const char * const level_unit[] = { "uV", "mV", "V" };
394c4292779SEtienne Carriere 	int max_unit = ARRAY_SIZE(level_unit);
395c4292779SEtienne Carriere 	int level_max = 0;
396c4292779SEtienne Carriere 	int level_min = 0;
397c4292779SEtienne Carriere 	int level_cur = 0;
398c4292779SEtienne Carriere 	char msg_buf[128] = { };
399c4292779SEtienne Carriere 	char *msg_end = msg_buf + sizeof(msg_buf);
400c4292779SEtienne Carriere 	char *msg = msg_buf;
401c4292779SEtienne Carriere 	int n_max = 0;
402c4292779SEtienne Carriere 	int n_min = 0;
403c4292779SEtienne Carriere 	int n_cur = 0;
404c4292779SEtienne Carriere 	int n = 0;
405c4292779SEtienne Carriere 
406c4292779SEtienne Carriere 	if (indent) {
407c4292779SEtienne Carriere 		/* Indent for root clock level */
408c4292779SEtienne Carriere 		msg = add_msg(msg, msg_end, "   ");
409c4292779SEtienne Carriere 		if (!msg)
410c4292779SEtienne Carriere 			goto out;
411c4292779SEtienne Carriere 
412c4292779SEtienne Carriere 		/* Indent for root supply to regulator supply levels */
413c4292779SEtienne Carriere 		for (n = 0; n < indent - 1; n++) {
414c4292779SEtienne Carriere 			if (indent_with_empty_string(regulator, indent, n))
415c4292779SEtienne Carriere 				msg = add_msg(msg, msg_end, "    ");
416c4292779SEtienne Carriere 			else
417c4292779SEtienne Carriere 				msg = add_msg(msg, msg_end, "|   ");
418c4292779SEtienne Carriere 			if (!msg)
419c4292779SEtienne Carriere 				goto out;
420c4292779SEtienne Carriere 		}
421c4292779SEtienne Carriere 
422c4292779SEtienne Carriere 		/* Regulator indentation */
423c4292779SEtienne Carriere 		if (regulator_is_supply_last_supplied(regulator))
424c4292779SEtienne Carriere 			msg = add_msg(msg, msg_end, "`-- ");
425c4292779SEtienne Carriere 		else
426c4292779SEtienne Carriere 			msg = add_msg(msg, msg_end, "|-- ");
427c4292779SEtienne Carriere 
428c4292779SEtienne Carriere 		if (!msg)
429c4292779SEtienne Carriere 			goto out;
430c4292779SEtienne Carriere 	} else {
431c4292779SEtienne Carriere 		/* Root supply indentation */
432c4292779SEtienne Carriere 		msg = add_msg(msg, msg_end, "o- ");
433c4292779SEtienne Carriere 	}
434c4292779SEtienne Carriere 
435c4292779SEtienne Carriere 	regulator_get_range(regulator, &level_min, &level_max);
436c4292779SEtienne Carriere 	level_cur = regulator_get_voltage(regulator);
437c4292779SEtienne Carriere 
438c4292779SEtienne Carriere 	for (n_cur = 1; !(level_cur % 1000) && n_cur < max_unit; n_cur++)
439c4292779SEtienne Carriere 		level_cur /= 1000;
440c4292779SEtienne Carriere 	for (n_max = 1; !(level_max % 1000) && n_max < max_unit; n_max++)
441c4292779SEtienne Carriere 		level_max /= 1000;
442c4292779SEtienne Carriere 	for (n_min = 1; !(level_min % 1000) && n_min < max_unit; n_min++)
443c4292779SEtienne Carriere 		level_min /= 1000;
444c4292779SEtienne Carriere 
445c4292779SEtienne Carriere 	msg = add_msg(msg, msg_end, "%s \t(%3s / refcnt %u / flags %#"PRIx32
446c4292779SEtienne Carriere 		      " / %d %s ", regulator_name(regulator),
447c4292779SEtienne Carriere 		      regulator_is_enabled(regulator) ? "on " : "off",
448c4292779SEtienne Carriere 		      regulator->refcount, regulator->flags,
449c4292779SEtienne Carriere 		      level_cur, level_unit[n_cur - 1]);
450c4292779SEtienne Carriere 	if (!msg)
451c4292779SEtienne Carriere 		goto out;
452c4292779SEtienne Carriere 
453c4292779SEtienne Carriere 	if (level_min == level_max)
454c4292779SEtienne Carriere 		msg = add_msg(msg, msg_end, "fixed)");
455c4292779SEtienne Carriere 	else if (level_max == INT_MAX)
456c4292779SEtienne Carriere 		msg = add_msg(msg, msg_end, "[%d %s .. MAX])",
457c4292779SEtienne Carriere 			      level_min, level_unit[n_min - 1]);
458c4292779SEtienne Carriere 	else
459c4292779SEtienne Carriere 		msg = add_msg(msg, msg_end, "[%d %s .. %d %s])",
460c4292779SEtienne Carriere 			      level_min, level_unit[n_min - 1],
461c4292779SEtienne Carriere 			      level_max, level_unit[n_max - 1]);
462c4292779SEtienne Carriere 
463c4292779SEtienne Carriere out:
464c4292779SEtienne Carriere 	if (!msg)
465c4292779SEtienne Carriere 		snprintf(msg_end - 4, 4, "...");
466c4292779SEtienne Carriere 
4673c1b8123SPascal Paillet 	IMSG("%s", msg_buf);
468c4292779SEtienne Carriere }
469c4292779SEtienne Carriere 
print_tree(void)470c4292779SEtienne Carriere static void print_tree(void)
471c4292779SEtienne Carriere {
472c4292779SEtienne Carriere 	struct regulator *regulator = NULL;
473c4292779SEtienne Carriere 	struct regulator *parent = NULL;
474c4292779SEtienne Carriere 	struct regulator *next = NULL;
475c4292779SEtienne Carriere 	int indent = -1;
476c4292779SEtienne Carriere 
477c4292779SEtienne Carriere 	while (true) {
478c4292779SEtienne Carriere 		next = find_next_regulator(parent, regulator);
479c4292779SEtienne Carriere 		if (next) {
480c4292779SEtienne Carriere 			print_regulator(next, indent + 1);
481c4292779SEtienne Carriere 			/* Enter the subtree of the next regulator */
482c4292779SEtienne Carriere 			parent = next;
483c4292779SEtienne Carriere 			indent++;
484c4292779SEtienne Carriere 			regulator = NULL;
485c4292779SEtienne Carriere 		} else {
486c4292779SEtienne Carriere 			/*
487c4292779SEtienne Carriere 			 * We've processed all children at this level.
488c4292779SEtienne Carriere 			 * If parent is NULL we're at the top and are done.
489c4292779SEtienne Carriere 			 */
490c4292779SEtienne Carriere 			if (!parent)
491c4292779SEtienne Carriere 				break;
492c4292779SEtienne Carriere 			/*
493c4292779SEtienne Carriere 			 * Move up one level to resume with the next
494c4292779SEtienne Carriere 			 * regulator of the parent.
495c4292779SEtienne Carriere 			 */
496c4292779SEtienne Carriere 			regulator = parent;
497c4292779SEtienne Carriere 			parent = regulator->supply;
498c4292779SEtienne Carriere 			indent--;
499c4292779SEtienne Carriere 		}
500c4292779SEtienne Carriere 	}
501c4292779SEtienne Carriere }
502c4292779SEtienne Carriere 
regulator_print_tree(void)503c4292779SEtienne Carriere void regulator_print_tree(void)
504c4292779SEtienne Carriere {
505c4292779SEtienne Carriere 	if (IS_ENABLED(CFG_DRIVERS_REGULATOR_PRINT_TREE) &&
5063c1b8123SPascal Paillet 	    TRACE_LEVEL >= TRACE_INFO) {
5073c1b8123SPascal Paillet 		IMSG("Regulator tree summary");
508c4292779SEtienne Carriere 		if (SLIST_EMPTY(&regulator_device_list))
5093c1b8123SPascal Paillet 			IMSG("-- No registered regulator");
510c4292779SEtienne Carriere 		else
511c4292779SEtienne Carriere 			print_tree();
512c4292779SEtienne Carriere 	}
513c4292779SEtienne Carriere }
514