xref: /optee_os/core/drivers/clk/clk_dt.c (revision 5ca2c36555d169a2be60964e2cbe39340c5245a4)
1dbe94a85SClément Léger // SPDX-License-Identifier: BSD-2-Clause
2dbe94a85SClément Léger /*
3dbe94a85SClément Léger  * Copyright (c) 2021, Bootlin
4dbe94a85SClément Léger  */
5dbe94a85SClément Léger 
6dbe94a85SClément Léger #include <assert.h>
7dbe94a85SClément Léger #include <drivers/clk.h>
8dbe94a85SClément Léger #include <drivers/clk_dt.h>
9dbe94a85SClément Léger #include <initcall.h>
10e6027f48SAlvin Chang #include <kernel/dt.h>
118c0c44c9SEtienne Carriere #include <kernel/dt_driver.h>
12dbe94a85SClément Léger #include <kernel/panic.h>
13dbe94a85SClément Léger #include <libfdt.h>
14dbe94a85SClément Léger #include <stddef.h>
15dbe94a85SClément Léger 
clk_dt_get_by_name(const void * fdt,int nodeoffset,const char * name,struct clk ** clk)16056e7438SEtienne Carriere TEE_Result clk_dt_get_by_name(const void *fdt, int nodeoffset,
17056e7438SEtienne Carriere 			      const char *name, struct clk **clk)
18dbe94a85SClément Léger {
19dbe94a85SClément Léger 	int clk_id = 0;
20dbe94a85SClément Léger 
21dbe94a85SClément Léger 	clk_id = fdt_stringlist_search(fdt, nodeoffset, "clock-names", name);
22d8b14b46SEtienne Carriere 	if (clk_id < 0) {
23056e7438SEtienne Carriere 		*clk = NULL;
24056e7438SEtienne Carriere 		return TEE_ERROR_GENERIC;
25d8b14b46SEtienne Carriere 	}
26dbe94a85SClément Léger 
27056e7438SEtienne Carriere 	return clk_dt_get_by_index(fdt, nodeoffset, clk_id, clk);
28dbe94a85SClément Léger }
29dbe94a85SClément Léger 
clk_dt_get_by_idx_prop(const char * prop_name,const void * fdt,int nodeoffset,unsigned int clk_idx,struct clk ** clk)30056e7438SEtienne Carriere static TEE_Result clk_dt_get_by_idx_prop(const char *prop_name, const void *fdt,
31056e7438SEtienne Carriere 					 int nodeoffset, unsigned int clk_idx,
32056e7438SEtienne Carriere 					 struct clk **clk)
33dbe94a85SClément Léger {
34056e7438SEtienne Carriere 	TEE_Result res = TEE_ERROR_GENERIC;
35b357d34fSEtienne Carriere 	void *out_clk = NULL;
36dbe94a85SClément Léger 
37b357d34fSEtienne Carriere 	res = dt_driver_device_from_node_idx_prop(prop_name, fdt, nodeoffset,
38056e7438SEtienne Carriere 						  clk_idx, DT_DRIVER_CLK,
39b357d34fSEtienne Carriere 						  &out_clk);
40b357d34fSEtienne Carriere 	if (!res)
41b357d34fSEtienne Carriere 		*clk = out_clk;
42b357d34fSEtienne Carriere 
43056e7438SEtienne Carriere 	return res;
44dbe94a85SClément Léger }
45dbe94a85SClément Léger 
clk_dt_get_by_index(const void * fdt,int nodeoffset,unsigned int clk_idx,struct clk ** clk)46056e7438SEtienne Carriere TEE_Result clk_dt_get_by_index(const void *fdt, int nodeoffset,
47056e7438SEtienne Carriere 			       unsigned int clk_idx, struct clk **clk)
48dbe94a85SClément Léger {
49056e7438SEtienne Carriere 	return clk_dt_get_by_idx_prop("clocks", fdt, nodeoffset, clk_idx, clk);
50dbe94a85SClément Léger }
51dbe94a85SClément Léger 
5217fd9102SEtienne Carriere #ifdef CFG_DRIVERS_CLK_EARLY_PROBE
53dbe94a85SClément Léger /* Recursively called from parse_clock_property() */
54dbe94a85SClément Léger static TEE_Result clk_probe_clock_provider_node(const void *fdt, int node);
55dbe94a85SClément Léger 
parse_clock_property(const void * fdt,int node)56dbe94a85SClément Léger static TEE_Result parse_clock_property(const void *fdt, int node)
57dbe94a85SClément Léger {
58dbe94a85SClément Léger 	int len = 0;
59dbe94a85SClément Léger 	int idx = 0;
60dbe94a85SClément Léger 	int parent_node = 0;
61dbe94a85SClément Léger 	int clock_cells = 0;
62dbe94a85SClément Léger 	uint32_t phandle = 0;
63dbe94a85SClément Léger 	const uint32_t *prop = NULL;
64dbe94a85SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
65dbe94a85SClément Léger 
66dbe94a85SClément Léger 	prop = fdt_getprop(fdt, node, "clocks", &len);
67dbe94a85SClément Léger 	if (!prop)
68dbe94a85SClément Léger 		return TEE_SUCCESS;
69dbe94a85SClément Léger 
70dbe94a85SClément Léger 	len /= sizeof(uint32_t);
71dbe94a85SClément Léger 	while (idx < len) {
72dbe94a85SClément Léger 		phandle = fdt32_to_cpu(prop[idx]);
73dbe94a85SClément Léger 
74dbe94a85SClément Léger 		parent_node = fdt_node_offset_by_phandle(fdt, phandle);
75dbe94a85SClément Léger 		if (parent_node < 0)
76dbe94a85SClément Léger 			return TEE_ERROR_GENERIC;
77dbe94a85SClément Léger 
78ced0ec63SEtienne Carriere 		/* Parent probe should not fail or clock won't be available */
79dbe94a85SClément Léger 		res = clk_probe_clock_provider_node(fdt, parent_node);
807ca695bfSEtienne Carriere 		if (res) {
817ca695bfSEtienne Carriere 			EMSG("Probe parent clock node %s on node %s: %#"PRIx32,
827ca695bfSEtienne Carriere 			     fdt_get_name(fdt, parent_node, NULL),
837ca695bfSEtienne Carriere 			     fdt_get_name(fdt, node, NULL), res);
847ca695bfSEtienne Carriere 			panic();
857ca695bfSEtienne Carriere 		}
86dbe94a85SClément Léger 
873fd340e5SEtienne Carriere 		clock_cells = fdt_get_dt_driver_cells(fdt, parent_node,
883fd340e5SEtienne Carriere 						      DT_DRIVER_CLK);
89dbe94a85SClément Léger 		if (clock_cells < 0)
90dbe94a85SClément Léger 			return TEE_ERROR_GENERIC;
91dbe94a85SClément Léger 
92dbe94a85SClément Léger 		idx += 1 + clock_cells;
93dbe94a85SClément Léger 	}
94dbe94a85SClément Léger 
95dbe94a85SClément Léger 	return TEE_SUCCESS;
96dbe94a85SClément Léger }
97dbe94a85SClément Léger 
clk_probe_clock_provider_node(const void * fdt,int node)98dbe94a85SClément Léger static TEE_Result clk_probe_clock_provider_node(const void *fdt, int node)
99dbe94a85SClément Léger {
100dbe94a85SClément Léger 	int len = 0;
101dbe94a85SClément Léger 	int status = 0;
102dbe94a85SClément Léger 	TEE_Result res = TEE_ERROR_GENERIC;
103dbe94a85SClément Léger 
104f354a5d8SGatien Chevallier 	status = fdt_get_status(fdt, node);
105dbe94a85SClément Léger 	if (!(status & DT_STATUS_OK_SEC))
106dbe94a85SClément Léger 		return TEE_ERROR_ITEM_NOT_FOUND;
107dbe94a85SClément Léger 
108dbe94a85SClément Léger 	/* Check if the node is a clock provider */
109dbe94a85SClément Léger 	if (!fdt_getprop(fdt, node, "#clock-cells", &len))
110dbe94a85SClément Léger 		return TEE_ERROR_ITEM_NOT_FOUND;
111dbe94a85SClément Léger 
112dbe94a85SClément Léger 	/* Check if node has already been probed */
1138dca59b4SEtienne Carriere 	if (dt_driver_get_provider_by_node(node, DT_DRIVER_CLK))
114dbe94a85SClément Léger 		return TEE_SUCCESS;
115dbe94a85SClément Léger 
116ced0ec63SEtienne Carriere 	/* Check if the node has a clock property first to probe parent */
117dbe94a85SClément Léger 	res = parse_clock_property(fdt, node);
118dbe94a85SClément Léger 	if (res)
119dbe94a85SClément Léger 		return res;
120dbe94a85SClément Léger 
121ef20efc4SEtienne Carriere 	return dt_driver_probe_device_by_node(fdt, node, DT_DRIVER_CLK);
122dbe94a85SClément Léger }
123dbe94a85SClément Léger 
clk_probe_node(const void * fdt,int parent_node)124dbe94a85SClément Léger static void clk_probe_node(const void *fdt, int parent_node)
125dbe94a85SClément Léger {
126dbe94a85SClément Léger 	int child = 0;
127dbe94a85SClément Léger 	int status = 0;
128dbe94a85SClément Léger 	__maybe_unused TEE_Result res = TEE_ERROR_GENERIC;
129dbe94a85SClément Léger 
130dbe94a85SClément Léger 	fdt_for_each_subnode(child, fdt, parent_node) {
131f354a5d8SGatien Chevallier 		status = fdt_get_status(fdt, child);
132dbe94a85SClément Léger 		if (status == DT_STATUS_DISABLED)
133dbe94a85SClément Léger 			continue;
134dbe94a85SClément Léger 
135dbe94a85SClément Léger 		res = clk_probe_clock_provider_node(fdt, child);
136dbe94a85SClément Léger 		assert(res == TEE_SUCCESS || res == TEE_ERROR_ITEM_NOT_FOUND);
137dbe94a85SClément Léger 
138dbe94a85SClément Léger 		clk_probe_node(fdt, child);
139dbe94a85SClément Léger 	}
140dbe94a85SClément Léger }
141dbe94a85SClément Léger 
parse_assigned_clock(const void * fdt,int nodeoffset)142dbe94a85SClément Léger static void parse_assigned_clock(const void *fdt, int nodeoffset)
143dbe94a85SClément Léger {
144dbe94a85SClément Léger 	int rate_len = 0;
145dbe94a85SClément Léger 	int clock_idx = 0;
146dbe94a85SClément Léger 	struct clk *clk = NULL;
147dbe94a85SClément Léger 	unsigned long rate = 0;
148dbe94a85SClément Léger 	struct clk *parent = NULL;
149dbe94a85SClément Léger 	const uint32_t *rate_prop = NULL;
150*6c4cb223STony Han 	TEE_Result res = TEE_ERROR_GENERIC;
151dbe94a85SClément Léger 
152dbe94a85SClément Léger 	rate_prop = fdt_getprop(fdt, nodeoffset, "assigned-clock-rates",
153dbe94a85SClément Léger 				&rate_len);
154dbe94a85SClément Léger 	rate_len /= sizeof(uint32_t);
155dbe94a85SClément Léger 
156aaf9cefeSEtienne Carriere 	while (true) {
157056e7438SEtienne Carriere 		res = clk_dt_get_by_idx_prop("assigned-clocks", fdt, nodeoffset,
158056e7438SEtienne Carriere 					     clock_idx, &clk);
159*6c4cb223STony Han 		if (res)
160dbe94a85SClément Léger 			return;
161*6c4cb223STony Han 		assert(clk);
162dbe94a85SClément Léger 
163056e7438SEtienne Carriere 		res = clk_dt_get_by_idx_prop("assigned-clock-parents", fdt,
164056e7438SEtienne Carriere 					     nodeoffset, clock_idx, &parent);
165dbe94a85SClément Léger 		if (parent) {
166056e7438SEtienne Carriere 			assert(!res);
167dbe94a85SClément Léger 			if (clk_set_parent(clk, parent)) {
168dbe94a85SClément Léger 				EMSG("Could not set clk %s parent to clock %s",
169dbe94a85SClément Léger 				     clk->name, parent->name);
170dbe94a85SClément Léger 				panic();
171dbe94a85SClément Léger 			}
172dbe94a85SClément Léger 		}
173dbe94a85SClément Léger 
174627f246dSClément Léger 		if (rate_prop && clock_idx < rate_len) {
175dbe94a85SClément Léger 			rate = fdt32_to_cpu(rate_prop[clock_idx]);
176dbe94a85SClément Léger 			if (rate && clk_set_rate(clk, rate) != TEE_SUCCESS)
177dbe94a85SClément Léger 				panic();
178dbe94a85SClément Léger 		}
179dbe94a85SClément Léger 
180dbe94a85SClément Léger 		clock_idx++;
181dbe94a85SClément Léger 	}
182dbe94a85SClément Léger }
183dbe94a85SClément Léger 
clk_probe_assigned(const void * fdt,int parent_node)184dbe94a85SClément Léger static void clk_probe_assigned(const void *fdt, int parent_node)
185dbe94a85SClément Léger {
186dbe94a85SClément Léger 	int len = 0;
187dbe94a85SClément Léger 	int child = 0;
188dbe94a85SClément Léger 	int status = 0;
189dbe94a85SClément Léger 
190dbe94a85SClément Léger 	fdt_for_each_subnode(child, fdt, parent_node) {
191dbe94a85SClément Léger 		clk_probe_assigned(fdt, child);
192dbe94a85SClément Léger 
193f354a5d8SGatien Chevallier 		status = fdt_get_status(fdt, child);
194dbe94a85SClément Léger 		if (status == DT_STATUS_DISABLED)
195dbe94a85SClément Léger 			continue;
196dbe94a85SClément Léger 
197dbe94a85SClément Léger 		if (fdt_getprop(fdt, child, "assigned-clocks", &len))
198dbe94a85SClément Léger 			parse_assigned_clock(fdt, child);
199dbe94a85SClément Léger 	}
200dbe94a85SClément Léger }
201dbe94a85SClément Léger 
clk_dt_probe(void)202dbe94a85SClément Léger static TEE_Result clk_dt_probe(void)
203dbe94a85SClément Léger {
20425a36f4cSVesa Jääskeläinen 	const void *fdt = get_secure_dt();
205dbe94a85SClément Léger 
206dbe94a85SClément Léger 	DMSG("Probing clocks from devicetree");
207dbe94a85SClément Léger 	if (!fdt)
208dbe94a85SClément Léger 		panic();
209dbe94a85SClément Léger 
210dbe94a85SClément Léger 	clk_probe_node(fdt, -1);
211dbe94a85SClément Léger 
212dbe94a85SClément Léger 	clk_probe_assigned(fdt, -1);
213dbe94a85SClément Léger 
214dbe94a85SClément Léger 	return TEE_SUCCESS;
215dbe94a85SClément Léger }
216dbe94a85SClément Léger early_init(clk_dt_probe);
21717fd9102SEtienne Carriere #endif
218