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