1 /*
2  * Copyright (c) 2017-2023, STMicroelectronics - All Rights Reserved
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <errno.h>
8 
9 #include <arch_helpers.h>
10 #include <common/fdt_wrappers.h>
11 #include <drivers/clk.h>
12 #include <drivers/generic_delay_timer.h>
13 #include <drivers/st/stm32_gpio.h>
14 #include <drivers/st/stm32mp_clkfunc.h>
15 #include <lib/mmio.h>
16 #include <libfdt.h>
17 
18 #include <platform_def.h>
19 
20 /*
21  * Get the frequency of an oscillator from its name in device tree.
22  * @param name: oscillator name
23  * @param freq: stores the frequency of the oscillator
24  * @return: 0 on success, and a negative FDT/ERRNO error code on failure.
25  */
fdt_osc_read_freq(const char * name,uint32_t * freq)26 int fdt_osc_read_freq(const char *name, uint32_t *freq)
27 {
28 	int node, subnode;
29 	void *fdt;
30 
31 	if (fdt_get_address(&fdt) == 0) {
32 		return -ENOENT;
33 	}
34 
35 	node = fdt_path_offset(fdt, "/clocks");
36 	if (node < 0) {
37 		return -FDT_ERR_NOTFOUND;
38 	}
39 
40 	fdt_for_each_subnode(subnode, fdt, node) {
41 		const char *cchar;
42 		int ret;
43 
44 		cchar = fdt_get_name(fdt, subnode, &ret);
45 		if (cchar == NULL) {
46 			return ret;
47 		}
48 
49 		if ((strncmp(cchar, name, (size_t)ret) == 0) &&
50 		    (fdt_get_status(subnode) != DT_DISABLED)) {
51 			const fdt32_t *cuint;
52 
53 			cuint = fdt_getprop(fdt, subnode, "clock-frequency",
54 					    &ret);
55 			if (cuint == NULL) {
56 				return ret;
57 			}
58 
59 			*freq = fdt32_to_cpu(*cuint);
60 
61 			return 0;
62 		}
63 	}
64 
65 	/* Oscillator not found, freq=0 */
66 	*freq = 0;
67 	return 0;
68 }
69 
70 /*
71  * Check the presence of an oscillator property from its id.
72  * @param node_label: clock node name
73  * @param prop_name: property name
74  * @return: true/false regarding search result.
75  */
fdt_clk_read_bool(const char * node_label,const char * prop_name)76 bool fdt_clk_read_bool(const char *node_label, const char *prop_name)
77 {
78 	int node, subnode;
79 	void *fdt;
80 
81 	if (fdt_get_address(&fdt) == 0) {
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, node_label, (size_t)ret) != 0) {
100 			continue;
101 		}
102 
103 		if (fdt_getprop(fdt, subnode, prop_name, NULL) != NULL) {
104 			return true;
105 		}
106 	}
107 
108 	return false;
109 }
110 
111 /*
112  * Get the value of a oscillator property from its name.
113  * @param node_label: oscillator name
114  * @param prop_name: property name
115  * @param dflt_value: default value
116  * @return oscillator value on success, default value if property not found.
117  */
fdt_clk_read_uint32_default(const char * node_label,const char * prop_name,uint32_t dflt_value)118 uint32_t fdt_clk_read_uint32_default(const char *node_label,
119 				     const char *prop_name, uint32_t dflt_value)
120 {
121 	int node, subnode;
122 	void *fdt;
123 
124 	if (fdt_get_address(&fdt) == 0) {
125 		return dflt_value;
126 	}
127 
128 	node = fdt_path_offset(fdt, "/clocks");
129 	if (node < 0) {
130 		return dflt_value;
131 	}
132 
133 	fdt_for_each_subnode(subnode, fdt, node) {
134 		const char *cchar;
135 		int ret;
136 
137 		cchar = fdt_get_name(fdt, subnode, &ret);
138 		if (cchar == NULL) {
139 			return dflt_value;
140 		}
141 
142 		if (strncmp(cchar, node_label, (size_t)ret) != 0) {
143 			continue;
144 		}
145 
146 		return fdt_read_uint32_default(fdt, subnode, prop_name,
147 					       dflt_value);
148 	}
149 
150 	return dflt_value;
151 }
152 
153 /*
154  * Get the RCC node offset from the device tree
155  * @param fdt: Device tree reference
156  * @return: Node offset or a negative value on error
157  */
fdt_get_rcc_node(void * fdt)158 static int fdt_get_rcc_node(void *fdt)
159 {
160 	static int node;
161 
162 	if (node <= 0) {
163 		node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT);
164 	}
165 
166 	return node;
167 }
168 
169 /*
170  * Read a series of parameters in rcc-clk section in device tree
171  * @param prop_name: Name of the RCC property to be read
172  * @param array: the array to store the property parameters
173  * @param count: number of parameters to be read
174  * @return: 0 on succes or a negative value on error
175  */
fdt_rcc_read_uint32_array(const char * prop_name,uint32_t count,uint32_t * array)176 int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t count,
177 			      uint32_t *array)
178 {
179 	int node;
180 	void *fdt;
181 
182 	if (fdt_get_address(&fdt) == 0) {
183 		return -ENOENT;
184 	}
185 
186 	node = fdt_get_rcc_node(fdt);
187 	if (node < 0) {
188 		return -FDT_ERR_NOTFOUND;
189 	}
190 
191 	return fdt_read_uint32_array(fdt, node, prop_name, count, array);
192 }
193 
194 /*
195  * Get the subnode offset in rcc-clk section from its name in device tree
196  * @param name: name of the RCC property
197  * @return: offset on success, and a negative FDT/ERRNO error code on failure.
198  */
fdt_rcc_subnode_offset(const char * name)199 int fdt_rcc_subnode_offset(const char *name)
200 {
201 	int node, subnode;
202 	void *fdt;
203 
204 	if (fdt_get_address(&fdt) == 0) {
205 		return -ENOENT;
206 	}
207 
208 	node = fdt_get_rcc_node(fdt);
209 	if (node < 0) {
210 		return -FDT_ERR_NOTFOUND;
211 	}
212 
213 	subnode = fdt_subnode_offset(fdt, node, name);
214 	if (subnode <= 0) {
215 		return -FDT_ERR_NOTFOUND;
216 	}
217 
218 	return subnode;
219 }
220 
221 /*
222  * Get the pointer to a rcc-clk property from its name.
223  * @param name: name of the RCC property
224  * @param lenp: stores the length of the property.
225  * @return: pointer to the property on success, and NULL value on failure.
226  */
fdt_rcc_read_prop(const char * prop_name,int * lenp)227 const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp)
228 {
229 	const fdt32_t *cuint;
230 	int node, len;
231 	void *fdt;
232 
233 	if (fdt_get_address(&fdt) == 0) {
234 		return NULL;
235 	}
236 
237 	node = fdt_get_rcc_node(fdt);
238 	if (node < 0) {
239 		return NULL;
240 	}
241 
242 	cuint = fdt_getprop(fdt, node, prop_name, &len);
243 	if (cuint == NULL) {
244 		return NULL;
245 	}
246 
247 	*lenp = len;
248 	return cuint;
249 }
250 
251 #if defined(IMAGE_BL32)
252 /*
253  * Get the secure state for rcc node in device tree.
254  * @return: true if rcc is configured for secure world access, false if not.
255  */
fdt_get_rcc_secure_state(void)256 bool fdt_get_rcc_secure_state(void)
257 {
258 	void *fdt;
259 
260 	if (fdt_get_address(&fdt) == 0) {
261 		return false;
262 	}
263 
264 	if (fdt_node_offset_by_compatible(fdt, -1, DT_RCC_SEC_CLK_COMPAT) < 0) {
265 		return false;
266 	}
267 
268 	return true;
269 }
270 #endif
271 
272 /*
273  * Get the clock ID of the given node in device tree.
274  * @param node: node offset
275  * @return: Clock ID on success, and a negative FDT/ERRNO error code on failure.
276  */
fdt_get_clock_id(int node)277 int fdt_get_clock_id(int node)
278 {
279 	const fdt32_t *cuint;
280 	void *fdt;
281 
282 	if (fdt_get_address(&fdt) == 0) {
283 		return -ENOENT;
284 	}
285 
286 	cuint = fdt_getprop(fdt, node, "clocks", NULL);
287 	if (cuint == NULL) {
288 		return -FDT_ERR_NOTFOUND;
289 	}
290 
291 	cuint++;
292 	return (int)fdt32_to_cpu(*cuint);
293 }
294 
295 /*
296  * Get the frequency of the specified UART instance.
297  * @param instance: UART interface registers base address.
298  * @return: clock frequency on success, 0 value on failure.
299  */
fdt_get_uart_clock_freq(uintptr_t instance)300 unsigned long fdt_get_uart_clock_freq(uintptr_t instance)
301 {
302 	void *fdt;
303 	int node;
304 	int clk_id;
305 
306 	if (fdt_get_address(&fdt) == 0) {
307 		return 0UL;
308 	}
309 
310 	/* Check for UART nodes */
311 	node = dt_match_instance_by_compatible(DT_UART_COMPAT, instance);
312 	if (node < 0) {
313 		return 0UL;
314 	}
315 
316 	clk_id = fdt_get_clock_id(node);
317 	if (clk_id < 0) {
318 		return 0UL;
319 	}
320 
321 	return clk_get_rate((unsigned long)clk_id);
322 }
323 
324 /*******************************************************************************
325  * This function sets the STGEN counter value.
326  ******************************************************************************/
stgen_set_counter(unsigned long long counter)327 static void stgen_set_counter(unsigned long long counter)
328 {
329 #ifdef __aarch64__
330 	mmio_write_64(STGEN_BASE + CNTCV_OFF, counter);
331 #else
332 	mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)counter);
333 	mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(counter >> 32));
334 #endif
335 }
336 
337 /*******************************************************************************
338  * This function configures and restores the STGEN counter depending on the
339  * connected clock.
340  ******************************************************************************/
stm32mp_stgen_config(unsigned long rate)341 void stm32mp_stgen_config(unsigned long rate)
342 {
343 	uint32_t cntfid0;
344 	unsigned long long counter;
345 
346 	cntfid0 = mmio_read_32(STGEN_BASE + CNTFID_OFF);
347 
348 	if (cntfid0 == rate) {
349 		return;
350 	}
351 
352 	mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
353 	counter = stm32mp_stgen_get_counter() * rate / cntfid0;
354 
355 	stgen_set_counter(counter);
356 	mmio_write_32(STGEN_BASE + CNTFID_OFF, rate);
357 	mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
358 
359 	write_cntfrq_el0(rate);
360 
361 	/* Need to update timer with new frequency */
362 	generic_delay_timer_init();
363 }
364 
365 /*******************************************************************************
366  * This function returns the STGEN counter value.
367  ******************************************************************************/
stm32mp_stgen_get_counter(void)368 unsigned long long stm32mp_stgen_get_counter(void)
369 {
370 #ifdef __aarch64__
371 	return mmio_read_64(STGEN_BASE + CNTCV_OFF);
372 #else
373 	return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) |
374 		mmio_read_32(STGEN_BASE + CNTCVL_OFF));
375 #endif
376 }
377 
378 /*******************************************************************************
379  * This function restores the STGEN counter value.
380  * It takes a first input value as a counter backup value to be restored and a
381  * offset in ms to be added.
382  ******************************************************************************/
stm32mp_stgen_restore_counter(unsigned long long value,unsigned long long offset_in_ms)383 void stm32mp_stgen_restore_counter(unsigned long long value,
384 				   unsigned long long offset_in_ms)
385 {
386 	unsigned long long cnt;
387 
388 	cnt = value + ((offset_in_ms *
389 			mmio_read_32(STGEN_BASE + CNTFID_OFF)) / 1000U);
390 
391 	mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
392 	stgen_set_counter(cnt);
393 	mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
394 }
395