1 /*
2  * Copyright (c) 2017-2021, STMicroelectronics - All Rights Reserved
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <errno.h>
8 
9 #include <libfdt.h>
10 
11 #include <platform_def.h>
12 
13 #include <common/fdt_wrappers.h>
14 #include <drivers/st/stm32_gpio.h>
15 #include <drivers/st/stm32mp_clkfunc.h>
16 
17 /*
18  * Get the frequency of an oscillator from its name in device tree.
19  * @param name: oscillator name
20  * @param freq: stores the frequency of the oscillator
21  * @return: 0 on success, and a negative FDT/ERRNO error code on failure.
22  */
fdt_osc_read_freq(const char * name,uint32_t * freq)23 int fdt_osc_read_freq(const char *name, uint32_t *freq)
24 {
25 	int node, subnode;
26 	void *fdt;
27 
28 	if (fdt_get_address(&fdt) == 0) {
29 		return -ENOENT;
30 	}
31 
32 	node = fdt_path_offset(fdt, "/clocks");
33 	if (node < 0) {
34 		return -FDT_ERR_NOTFOUND;
35 	}
36 
37 	fdt_for_each_subnode(subnode, fdt, node) {
38 		const char *cchar;
39 		int ret;
40 
41 		cchar = fdt_get_name(fdt, subnode, &ret);
42 		if (cchar == NULL) {
43 			return ret;
44 		}
45 
46 		if (strncmp(cchar, name, (size_t)ret) == 0) {
47 			const fdt32_t *cuint;
48 
49 			cuint = fdt_getprop(fdt, subnode, "clock-frequency",
50 					    &ret);
51 			if (cuint == NULL) {
52 				return ret;
53 			}
54 
55 			*freq = fdt32_to_cpu(*cuint);
56 
57 			return 0;
58 		}
59 	}
60 
61 	/* Oscillator not found, freq=0 */
62 	*freq = 0;
63 	return 0;
64 }
65 
66 /*
67  * Check the presence of an oscillator property from its id.
68  * @param osc_id: oscillator ID
69  * @param prop_name: property name
70  * @return: true/false regarding search result.
71  */
fdt_osc_read_bool(enum stm32mp_osc_id osc_id,const char * prop_name)72 bool fdt_osc_read_bool(enum stm32mp_osc_id osc_id, const char *prop_name)
73 {
74 	int node, subnode;
75 	void *fdt;
76 
77 	if (fdt_get_address(&fdt) == 0) {
78 		return false;
79 	}
80 
81 	if (osc_id >= NB_OSC) {
82 		return false;
83 	}
84 
85 	node = fdt_path_offset(fdt, "/clocks");
86 	if (node < 0) {
87 		return false;
88 	}
89 
90 	fdt_for_each_subnode(subnode, fdt, node) {
91 		const char *cchar;
92 		int ret;
93 
94 		cchar = fdt_get_name(fdt, subnode, &ret);
95 		if (cchar == NULL) {
96 			return false;
97 		}
98 
99 		if (strncmp(cchar, stm32mp_osc_node_label[osc_id],
100 			    (size_t)ret) != 0) {
101 			continue;
102 		}
103 
104 		if (fdt_getprop(fdt, subnode, prop_name, NULL) != NULL) {
105 			return true;
106 		}
107 	}
108 
109 	return false;
110 }
111 
112 /*
113  * Get the value of a oscillator property from its ID.
114  * @param osc_id: oscillator ID
115  * @param prop_name: property name
116  * @param dflt_value: default value
117  * @return oscillator value on success, default value if property not found.
118  */
fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id,const char * prop_name,uint32_t dflt_value)119 uint32_t fdt_osc_read_uint32_default(enum stm32mp_osc_id osc_id,
120 				     const char *prop_name, uint32_t dflt_value)
121 {
122 	int node, subnode;
123 	void *fdt;
124 
125 	if (fdt_get_address(&fdt) == 0) {
126 		return dflt_value;
127 	}
128 
129 	if (osc_id >= NB_OSC) {
130 		return dflt_value;
131 	}
132 
133 	node = fdt_path_offset(fdt, "/clocks");
134 	if (node < 0) {
135 		return dflt_value;
136 	}
137 
138 	fdt_for_each_subnode(subnode, fdt, node) {
139 		const char *cchar;
140 		int ret;
141 
142 		cchar = fdt_get_name(fdt, subnode, &ret);
143 		if (cchar == NULL) {
144 			return dflt_value;
145 		}
146 
147 		if (strncmp(cchar, stm32mp_osc_node_label[osc_id],
148 			    (size_t)ret) != 0) {
149 			continue;
150 		}
151 
152 		return fdt_read_uint32_default(fdt, subnode, prop_name,
153 					       dflt_value);
154 	}
155 
156 	return dflt_value;
157 }
158 
159 /*
160  * Get the RCC node offset from the device tree
161  * @param fdt: Device tree reference
162  * @return: Node offset or a negative value on error
163  */
fdt_get_rcc_node(void * fdt)164 static int fdt_get_rcc_node(void *fdt)
165 {
166 	static int node;
167 
168 	if (node <= 0) {
169 		node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT);
170 	}
171 
172 	return node;
173 }
174 
175 /*
176  * Read a series of parameters in rcc-clk section in device tree
177  * @param prop_name: Name of the RCC property to be read
178  * @param array: the array to store the property parameters
179  * @param count: number of parameters to be read
180  * @return: 0 on succes or a negative value on error
181  */
fdt_rcc_read_uint32_array(const char * prop_name,uint32_t count,uint32_t * array)182 int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t count,
183 			      uint32_t *array)
184 {
185 	int node;
186 	void *fdt;
187 
188 	if (fdt_get_address(&fdt) == 0) {
189 		return -ENOENT;
190 	}
191 
192 	node = fdt_get_rcc_node(fdt);
193 	if (node < 0) {
194 		return -FDT_ERR_NOTFOUND;
195 	}
196 
197 	return fdt_read_uint32_array(fdt, node, prop_name, count, array);
198 }
199 
200 /*
201  * Get the subnode offset in rcc-clk section from its name in device tree
202  * @param name: name of the RCC property
203  * @return: offset on success, and a negative FDT/ERRNO error code on failure.
204  */
fdt_rcc_subnode_offset(const char * name)205 int fdt_rcc_subnode_offset(const char *name)
206 {
207 	int node, subnode;
208 	void *fdt;
209 
210 	if (fdt_get_address(&fdt) == 0) {
211 		return -ENOENT;
212 	}
213 
214 	node = fdt_get_rcc_node(fdt);
215 	if (node < 0) {
216 		return -FDT_ERR_NOTFOUND;
217 	}
218 
219 	subnode = fdt_subnode_offset(fdt, node, name);
220 	if (subnode <= 0) {
221 		return -FDT_ERR_NOTFOUND;
222 	}
223 
224 	return subnode;
225 }
226 
227 /*
228  * Get the pointer to a rcc-clk property from its name.
229  * @param name: name of the RCC property
230  * @param lenp: stores the length of the property.
231  * @return: pointer to the property on success, and NULL value on failure.
232  */
fdt_rcc_read_prop(const char * prop_name,int * lenp)233 const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp)
234 {
235 	const fdt32_t *cuint;
236 	int node, len;
237 	void *fdt;
238 
239 	if (fdt_get_address(&fdt) == 0) {
240 		return NULL;
241 	}
242 
243 	node = fdt_get_rcc_node(fdt);
244 	if (node < 0) {
245 		return NULL;
246 	}
247 
248 	cuint = fdt_getprop(fdt, node, prop_name, &len);
249 	if (cuint == NULL) {
250 		return NULL;
251 	}
252 
253 	*lenp = len;
254 	return cuint;
255 }
256 
257 /*
258  * Get the secure status for rcc node in device tree.
259  * @return: true if rcc is available from secure world, false if not.
260  */
fdt_get_rcc_secure_status(void)261 bool fdt_get_rcc_secure_status(void)
262 {
263 	int node;
264 	void *fdt;
265 
266 	if (fdt_get_address(&fdt) == 0) {
267 		return false;
268 	}
269 
270 	node = fdt_get_rcc_node(fdt);
271 	if (node < 0) {
272 		return false;
273 	}
274 
275 	return !!(fdt_get_status(node) & DT_SECURE);
276 }
277 
278 /*
279  * Get the clock ID of the given node in device tree.
280  * @param node: node offset
281  * @return: Clock ID on success, and a negative FDT/ERRNO error code on failure.
282  */
fdt_get_clock_id(int node)283 int fdt_get_clock_id(int node)
284 {
285 	const fdt32_t *cuint;
286 	void *fdt;
287 
288 	if (fdt_get_address(&fdt) == 0) {
289 		return -ENOENT;
290 	}
291 
292 	cuint = fdt_getprop(fdt, node, "clocks", NULL);
293 	if (cuint == NULL) {
294 		return -FDT_ERR_NOTFOUND;
295 	}
296 
297 	cuint++;
298 	return (int)fdt32_to_cpu(*cuint);
299 }
300