xref: /rk3399_ARM-atf/drivers/st/clk/stm32mp_clkfunc.c (revision 812daf916c9c977a4f6d7d745d22b90c8492fc71)
1447b2b13SYann Gautier /*
2bcccdaccSPatrick Delaunay  * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved
3447b2b13SYann Gautier  *
4447b2b13SYann Gautier  * SPDX-License-Identifier: BSD-3-Clause
5447b2b13SYann Gautier  */
6447b2b13SYann Gautier 
7447b2b13SYann Gautier #include <errno.h>
8447b2b13SYann Gautier 
9591d80c8SLionel Debieve #include <arch_helpers.h>
1052a616b4SAndre Przywara #include <common/fdt_wrappers.h>
1133667d29SYann Gautier #include <drivers/clk.h>
12591d80c8SLionel Debieve #include <drivers/generic_delay_timer.h>
13447b2b13SYann Gautier #include <drivers/st/stm32_gpio.h>
14447b2b13SYann Gautier #include <drivers/st/stm32mp_clkfunc.h>
15591d80c8SLionel Debieve #include <lib/mmio.h>
16bcccdaccSPatrick Delaunay #include <libfdt.h>
17bcccdaccSPatrick Delaunay 
18bcccdaccSPatrick Delaunay #include <platform_def.h>
19447b2b13SYann Gautier 
20165ad556SNicolas Le Bayon #define DT_UART_COMPAT		"st,stm32h7-uart"
21447b2b13SYann Gautier /*
22f66358afSYann Gautier  * Get the frequency of an oscillator from its name in device tree.
23f66358afSYann Gautier  * @param name: oscillator name
24f66358afSYann Gautier  * @param freq: stores the frequency of the oscillator
25f66358afSYann Gautier  * @return: 0 on success, and a negative FDT/ERRNO error code on failure.
26f66358afSYann Gautier  */
27f66358afSYann Gautier int fdt_osc_read_freq(const char *name, uint32_t *freq)
28f66358afSYann Gautier {
29f66358afSYann Gautier 	int node, subnode;
30f66358afSYann Gautier 	void *fdt;
31f66358afSYann Gautier 
32f66358afSYann Gautier 	if (fdt_get_address(&fdt) == 0) {
33f66358afSYann Gautier 		return -ENOENT;
34f66358afSYann Gautier 	}
35f66358afSYann Gautier 
36f66358afSYann Gautier 	node = fdt_path_offset(fdt, "/clocks");
37f66358afSYann Gautier 	if (node < 0) {
38f66358afSYann Gautier 		return -FDT_ERR_NOTFOUND;
39f66358afSYann Gautier 	}
40f66358afSYann Gautier 
41f66358afSYann Gautier 	fdt_for_each_subnode(subnode, fdt, node) {
42f66358afSYann Gautier 		const char *cchar;
43f66358afSYann Gautier 		int ret;
44f66358afSYann Gautier 
45f66358afSYann Gautier 		cchar = fdt_get_name(fdt, subnode, &ret);
46f66358afSYann Gautier 		if (cchar == NULL) {
47f66358afSYann Gautier 			return ret;
48f66358afSYann Gautier 		}
49f66358afSYann Gautier 
50bcccdaccSPatrick Delaunay 		if ((strncmp(cchar, name, (size_t)ret) == 0) &&
51bcccdaccSPatrick Delaunay 		    (fdt_get_status(subnode) != DT_DISABLED)) {
52f66358afSYann Gautier 			const fdt32_t *cuint;
53f66358afSYann Gautier 
54f66358afSYann Gautier 			cuint = fdt_getprop(fdt, subnode, "clock-frequency",
55f66358afSYann Gautier 					    &ret);
56f66358afSYann Gautier 			if (cuint == NULL) {
57f66358afSYann Gautier 				return ret;
58f66358afSYann Gautier 			}
59f66358afSYann Gautier 
60f66358afSYann Gautier 			*freq = fdt32_to_cpu(*cuint);
61f66358afSYann Gautier 
62f66358afSYann Gautier 			return 0;
63f66358afSYann Gautier 		}
64f66358afSYann Gautier 	}
65f66358afSYann Gautier 
66f66358afSYann Gautier 	/* Oscillator not found, freq=0 */
67f66358afSYann Gautier 	*freq = 0;
68f66358afSYann Gautier 	return 0;
69f66358afSYann Gautier }
70f66358afSYann Gautier 
71f66358afSYann Gautier /*
72f66358afSYann Gautier  * Check the presence of an oscillator property from its id.
73b208e3daSGabriel Fernandez  * @param node_label: clock node name
74f66358afSYann Gautier  * @param prop_name: property name
75f66358afSYann Gautier  * @return: true/false regarding search result.
76f66358afSYann Gautier  */
77b208e3daSGabriel Fernandez bool fdt_clk_read_bool(const char *node_label, const char *prop_name)
78f66358afSYann Gautier {
79f66358afSYann Gautier 	int node, subnode;
80f66358afSYann Gautier 	void *fdt;
81f66358afSYann Gautier 
82f66358afSYann Gautier 	if (fdt_get_address(&fdt) == 0) {
83f66358afSYann Gautier 		return false;
84f66358afSYann Gautier 	}
85f66358afSYann Gautier 
86f66358afSYann Gautier 	node = fdt_path_offset(fdt, "/clocks");
87f66358afSYann Gautier 	if (node < 0) {
88f66358afSYann Gautier 		return false;
89f66358afSYann Gautier 	}
90f66358afSYann Gautier 
91f66358afSYann Gautier 	fdt_for_each_subnode(subnode, fdt, node) {
92f66358afSYann Gautier 		const char *cchar;
93f66358afSYann Gautier 		int ret;
94f66358afSYann Gautier 
95f66358afSYann Gautier 		cchar = fdt_get_name(fdt, subnode, &ret);
96f66358afSYann Gautier 		if (cchar == NULL) {
97f66358afSYann Gautier 			return false;
98f66358afSYann Gautier 		}
99f66358afSYann Gautier 
100b208e3daSGabriel Fernandez 		if (strncmp(cchar, node_label, (size_t)ret) != 0) {
101f66358afSYann Gautier 			continue;
102f66358afSYann Gautier 		}
103f66358afSYann Gautier 
104f66358afSYann Gautier 		if (fdt_getprop(fdt, subnode, prop_name, NULL) != NULL) {
105f66358afSYann Gautier 			return true;
106f66358afSYann Gautier 		}
107f66358afSYann Gautier 	}
108f66358afSYann Gautier 
109f66358afSYann Gautier 	return false;
110f66358afSYann Gautier }
111f66358afSYann Gautier 
112f66358afSYann Gautier /*
113b208e3daSGabriel Fernandez  * Get the value of a oscillator property from its name.
114b208e3daSGabriel Fernandez  * @param node_label: oscillator name
115f66358afSYann Gautier  * @param prop_name: property name
116f66358afSYann Gautier  * @param dflt_value: default value
117f66358afSYann Gautier  * @return oscillator value on success, default value if property not found.
118f66358afSYann Gautier  */
119b208e3daSGabriel Fernandez uint32_t fdt_clk_read_uint32_default(const char *node_label,
120f66358afSYann Gautier 				     const char *prop_name, uint32_t dflt_value)
121f66358afSYann Gautier {
122f66358afSYann Gautier 	int node, subnode;
123f66358afSYann Gautier 	void *fdt;
124f66358afSYann Gautier 
125f66358afSYann Gautier 	if (fdt_get_address(&fdt) == 0) {
126f66358afSYann Gautier 		return dflt_value;
127f66358afSYann Gautier 	}
128f66358afSYann Gautier 
129f66358afSYann Gautier 	node = fdt_path_offset(fdt, "/clocks");
130f66358afSYann Gautier 	if (node < 0) {
131f66358afSYann Gautier 		return dflt_value;
132f66358afSYann Gautier 	}
133f66358afSYann Gautier 
134f66358afSYann Gautier 	fdt_for_each_subnode(subnode, fdt, node) {
135f66358afSYann Gautier 		const char *cchar;
136f66358afSYann Gautier 		int ret;
137f66358afSYann Gautier 
138f66358afSYann Gautier 		cchar = fdt_get_name(fdt, subnode, &ret);
139f66358afSYann Gautier 		if (cchar == NULL) {
140f66358afSYann Gautier 			return dflt_value;
141f66358afSYann Gautier 		}
142f66358afSYann Gautier 
143b208e3daSGabriel Fernandez 		if (strncmp(cchar, node_label, (size_t)ret) != 0) {
144f66358afSYann Gautier 			continue;
145f66358afSYann Gautier 		}
146f66358afSYann Gautier 
147be858cffSAndre Przywara 		return fdt_read_uint32_default(fdt, subnode, prop_name,
148be858cffSAndre Przywara 					       dflt_value);
149f66358afSYann Gautier 	}
150f66358afSYann Gautier 
151f66358afSYann Gautier 	return dflt_value;
152f66358afSYann Gautier }
153f66358afSYann Gautier 
154f66358afSYann Gautier /*
155447b2b13SYann Gautier  * Get the RCC node offset from the device tree
156447b2b13SYann Gautier  * @param fdt: Device tree reference
157447b2b13SYann Gautier  * @return: Node offset or a negative value on error
158447b2b13SYann Gautier  */
159ff18c4cdSPatrick Delaunay static int fdt_get_rcc_node(void *fdt)
160447b2b13SYann Gautier {
161ba57711cSYann Gautier 	static int node;
162ba57711cSYann Gautier 
163ba57711cSYann Gautier 	if (node <= 0) {
164ba57711cSYann Gautier 		node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT);
165ba57711cSYann Gautier 	}
166ba57711cSYann Gautier 
167ba57711cSYann Gautier 	return node;
168447b2b13SYann Gautier }
169447b2b13SYann Gautier 
170447b2b13SYann Gautier /*
171447b2b13SYann Gautier  * Read a series of parameters in rcc-clk section in device tree
172447b2b13SYann Gautier  * @param prop_name: Name of the RCC property to be read
173447b2b13SYann Gautier  * @param array: the array to store the property parameters
174447b2b13SYann Gautier  * @param count: number of parameters to be read
175447b2b13SYann Gautier  * @return: 0 on succes or a negative value on error
176447b2b13SYann Gautier  */
17752a616b4SAndre Przywara int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t count,
17852a616b4SAndre Przywara 			      uint32_t *array)
179447b2b13SYann Gautier {
180447b2b13SYann Gautier 	int node;
181447b2b13SYann Gautier 	void *fdt;
182447b2b13SYann Gautier 
183447b2b13SYann Gautier 	if (fdt_get_address(&fdt) == 0) {
184447b2b13SYann Gautier 		return -ENOENT;
185447b2b13SYann Gautier 	}
186447b2b13SYann Gautier 
187447b2b13SYann Gautier 	node = fdt_get_rcc_node(fdt);
188447b2b13SYann Gautier 	if (node < 0) {
189447b2b13SYann Gautier 		return -FDT_ERR_NOTFOUND;
190447b2b13SYann Gautier 	}
191447b2b13SYann Gautier 
19252a616b4SAndre Przywara 	return fdt_read_uint32_array(fdt, node, prop_name, count, array);
193447b2b13SYann Gautier }
194447b2b13SYann Gautier 
195447b2b13SYann Gautier /*
196447b2b13SYann Gautier  * Get the subnode offset in rcc-clk section from its name in device tree
197447b2b13SYann Gautier  * @param name: name of the RCC property
198447b2b13SYann Gautier  * @return: offset on success, and a negative FDT/ERRNO error code on failure.
199447b2b13SYann Gautier  */
200447b2b13SYann Gautier int fdt_rcc_subnode_offset(const char *name)
201447b2b13SYann Gautier {
202447b2b13SYann Gautier 	int node, subnode;
203447b2b13SYann Gautier 	void *fdt;
204447b2b13SYann Gautier 
205447b2b13SYann Gautier 	if (fdt_get_address(&fdt) == 0) {
206447b2b13SYann Gautier 		return -ENOENT;
207447b2b13SYann Gautier 	}
208447b2b13SYann Gautier 
209447b2b13SYann Gautier 	node = fdt_get_rcc_node(fdt);
210447b2b13SYann Gautier 	if (node < 0) {
211447b2b13SYann Gautier 		return -FDT_ERR_NOTFOUND;
212447b2b13SYann Gautier 	}
213447b2b13SYann Gautier 
214447b2b13SYann Gautier 	subnode = fdt_subnode_offset(fdt, node, name);
215447b2b13SYann Gautier 	if (subnode <= 0) {
216447b2b13SYann Gautier 		return -FDT_ERR_NOTFOUND;
217447b2b13SYann Gautier 	}
218447b2b13SYann Gautier 
219447b2b13SYann Gautier 	return subnode;
220447b2b13SYann Gautier }
221447b2b13SYann Gautier 
222447b2b13SYann Gautier /*
223447b2b13SYann Gautier  * Get the pointer to a rcc-clk property from its name.
224447b2b13SYann Gautier  * @param name: name of the RCC property
225447b2b13SYann Gautier  * @param lenp: stores the length of the property.
226447b2b13SYann Gautier  * @return: pointer to the property on success, and NULL value on failure.
227447b2b13SYann Gautier  */
228447b2b13SYann Gautier const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp)
229447b2b13SYann Gautier {
230447b2b13SYann Gautier 	const fdt32_t *cuint;
231447b2b13SYann Gautier 	int node, len;
232447b2b13SYann Gautier 	void *fdt;
233447b2b13SYann Gautier 
234447b2b13SYann Gautier 	if (fdt_get_address(&fdt) == 0) {
235447b2b13SYann Gautier 		return NULL;
236447b2b13SYann Gautier 	}
237447b2b13SYann Gautier 
238447b2b13SYann Gautier 	node = fdt_get_rcc_node(fdt);
239447b2b13SYann Gautier 	if (node < 0) {
240447b2b13SYann Gautier 		return NULL;
241447b2b13SYann Gautier 	}
242447b2b13SYann Gautier 
243447b2b13SYann Gautier 	cuint = fdt_getprop(fdt, node, prop_name, &len);
244447b2b13SYann Gautier 	if (cuint == NULL) {
245447b2b13SYann Gautier 		return NULL;
246447b2b13SYann Gautier 	}
247447b2b13SYann Gautier 
248447b2b13SYann Gautier 	*lenp = len;
249447b2b13SYann Gautier 	return cuint;
250447b2b13SYann Gautier }
251447b2b13SYann Gautier 
252447b2b13SYann Gautier /*
253*812daf91SLionel Debieve  * Get the secure state for rcc node in device tree.
254*812daf91SLionel Debieve  * @return: true if rcc is configured for secure world access, false if not.
255447b2b13SYann Gautier  */
256*812daf91SLionel Debieve bool fdt_get_rcc_secure_state(void)
257447b2b13SYann Gautier {
258447b2b13SYann Gautier 	void *fdt;
259447b2b13SYann Gautier 
260447b2b13SYann Gautier 	if (fdt_get_address(&fdt) == 0) {
261447b2b13SYann Gautier 		return false;
262447b2b13SYann Gautier 	}
263447b2b13SYann Gautier 
264*812daf91SLionel Debieve 	if (fdt_node_offset_by_compatible(fdt, -1, DT_RCC_SEC_CLK_COMPAT) < 0) {
265447b2b13SYann Gautier 		return false;
266447b2b13SYann Gautier 	}
267447b2b13SYann Gautier 
268*812daf91SLionel Debieve 	return true;
269447b2b13SYann Gautier }
270447b2b13SYann Gautier 
271447b2b13SYann Gautier /*
272447b2b13SYann Gautier  * Get the clock ID of the given node in device tree.
273447b2b13SYann Gautier  * @param node: node offset
274447b2b13SYann Gautier  * @return: Clock ID on success, and a negative FDT/ERRNO error code on failure.
275447b2b13SYann Gautier  */
276447b2b13SYann Gautier int fdt_get_clock_id(int node)
277447b2b13SYann Gautier {
278447b2b13SYann Gautier 	const fdt32_t *cuint;
279447b2b13SYann Gautier 	void *fdt;
280447b2b13SYann Gautier 
281447b2b13SYann Gautier 	if (fdt_get_address(&fdt) == 0) {
282447b2b13SYann Gautier 		return -ENOENT;
283447b2b13SYann Gautier 	}
284447b2b13SYann Gautier 
285447b2b13SYann Gautier 	cuint = fdt_getprop(fdt, node, "clocks", NULL);
286447b2b13SYann Gautier 	if (cuint == NULL) {
287447b2b13SYann Gautier 		return -FDT_ERR_NOTFOUND;
288447b2b13SYann Gautier 	}
289447b2b13SYann Gautier 
290447b2b13SYann Gautier 	cuint++;
291447b2b13SYann Gautier 	return (int)fdt32_to_cpu(*cuint);
292447b2b13SYann Gautier }
293165ad556SNicolas Le Bayon 
294165ad556SNicolas Le Bayon /*
295165ad556SNicolas Le Bayon  * Get the frequency of the specified UART instance.
296165ad556SNicolas Le Bayon  * @param instance: UART interface registers base address.
297165ad556SNicolas Le Bayon  * @return: clock frequency on success, 0 value on failure.
298165ad556SNicolas Le Bayon  */
299165ad556SNicolas Le Bayon unsigned long fdt_get_uart_clock_freq(uintptr_t instance)
300165ad556SNicolas Le Bayon {
301165ad556SNicolas Le Bayon 	void *fdt;
302165ad556SNicolas Le Bayon 	int node;
303165ad556SNicolas Le Bayon 	int clk_id;
304165ad556SNicolas Le Bayon 
305165ad556SNicolas Le Bayon 	if (fdt_get_address(&fdt) == 0) {
306165ad556SNicolas Le Bayon 		return 0UL;
307165ad556SNicolas Le Bayon 	}
308165ad556SNicolas Le Bayon 
309165ad556SNicolas Le Bayon 	/* Check for UART nodes */
310165ad556SNicolas Le Bayon 	node = dt_match_instance_by_compatible(DT_UART_COMPAT, instance);
311165ad556SNicolas Le Bayon 	if (node < 0) {
312165ad556SNicolas Le Bayon 		return 0UL;
313165ad556SNicolas Le Bayon 	}
314165ad556SNicolas Le Bayon 
315165ad556SNicolas Le Bayon 	clk_id = fdt_get_clock_id(node);
316165ad556SNicolas Le Bayon 	if (clk_id < 0) {
317165ad556SNicolas Le Bayon 		return 0UL;
318165ad556SNicolas Le Bayon 	}
319165ad556SNicolas Le Bayon 
32033667d29SYann Gautier 	return clk_get_rate((unsigned long)clk_id);
321165ad556SNicolas Le Bayon }
322591d80c8SLionel Debieve 
323591d80c8SLionel Debieve /*******************************************************************************
324591d80c8SLionel Debieve  * This function configures and restores the STGEN counter depending on the
325591d80c8SLionel Debieve  * connected clock.
326591d80c8SLionel Debieve  ******************************************************************************/
327591d80c8SLionel Debieve void stm32mp_stgen_config(unsigned long rate)
328591d80c8SLionel Debieve {
329591d80c8SLionel Debieve 	uint32_t cntfid0;
330591d80c8SLionel Debieve 	unsigned long long counter;
331591d80c8SLionel Debieve 
332591d80c8SLionel Debieve 	cntfid0 = mmio_read_32(STGEN_BASE + CNTFID_OFF);
333591d80c8SLionel Debieve 
334591d80c8SLionel Debieve 	if (cntfid0 == rate) {
335591d80c8SLionel Debieve 		return;
336591d80c8SLionel Debieve 	}
337591d80c8SLionel Debieve 
338591d80c8SLionel Debieve 	mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
339591d80c8SLionel Debieve 	counter = stm32mp_stgen_get_counter() * rate / cntfid0;
340591d80c8SLionel Debieve 
341591d80c8SLionel Debieve 	mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)counter);
342591d80c8SLionel Debieve 	mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(counter >> 32));
343591d80c8SLionel Debieve 	mmio_write_32(STGEN_BASE + CNTFID_OFF, rate);
344591d80c8SLionel Debieve 	mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
345591d80c8SLionel Debieve 
346591d80c8SLionel Debieve 	write_cntfrq_el0(rate);
347591d80c8SLionel Debieve 
348591d80c8SLionel Debieve 	/* Need to update timer with new frequency */
349591d80c8SLionel Debieve 	generic_delay_timer_init();
350591d80c8SLionel Debieve }
351591d80c8SLionel Debieve 
352591d80c8SLionel Debieve /*******************************************************************************
353591d80c8SLionel Debieve  * This function returns the STGEN counter value.
354591d80c8SLionel Debieve  ******************************************************************************/
355591d80c8SLionel Debieve unsigned long long stm32mp_stgen_get_counter(void)
356591d80c8SLionel Debieve {
357591d80c8SLionel Debieve 	return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) |
358591d80c8SLionel Debieve 		mmio_read_32(STGEN_BASE + CNTCVL_OFF));
359591d80c8SLionel Debieve }
360591d80c8SLionel Debieve 
361591d80c8SLionel Debieve /*******************************************************************************
362591d80c8SLionel Debieve  * This function restores the STGEN counter value.
363591d80c8SLionel Debieve  * It takes a first input value as a counter backup value to be restored and a
364591d80c8SLionel Debieve  * offset in ms to be added.
365591d80c8SLionel Debieve  ******************************************************************************/
366591d80c8SLionel Debieve void stm32mp_stgen_restore_counter(unsigned long long value,
367591d80c8SLionel Debieve 				   unsigned long long offset_in_ms)
368591d80c8SLionel Debieve {
369591d80c8SLionel Debieve 	unsigned long long cnt;
370591d80c8SLionel Debieve 
371591d80c8SLionel Debieve 	cnt = value + ((offset_in_ms *
372591d80c8SLionel Debieve 			mmio_read_32(STGEN_BASE + CNTFID_OFF)) / 1000U);
373591d80c8SLionel Debieve 
374591d80c8SLionel Debieve 	mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
375591d80c8SLionel Debieve 	mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)cnt);
376591d80c8SLionel Debieve 	mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(cnt >> 32));
377591d80c8SLionel Debieve 	mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
378591d80c8SLionel Debieve }
379