1447b2b13SYann Gautier /*
2bfe8a12eSPascal Paillet * Copyright (c) 2017-2024, 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
20447b2b13SYann Gautier /*
21f66358afSYann Gautier * Get the frequency of an oscillator from its name in device tree.
22f66358afSYann Gautier * @param name: oscillator name
23f66358afSYann Gautier * @param freq: stores the frequency of the oscillator
24f66358afSYann Gautier * @return: 0 on success, and a negative FDT/ERRNO error code on failure.
25f66358afSYann Gautier */
fdt_osc_read_freq(const char * name,uint32_t * freq)26f66358afSYann Gautier int fdt_osc_read_freq(const char *name, uint32_t *freq)
27f66358afSYann Gautier {
28f66358afSYann Gautier int node, subnode;
29f66358afSYann Gautier void *fdt;
30f66358afSYann Gautier
31f66358afSYann Gautier if (fdt_get_address(&fdt) == 0) {
32f66358afSYann Gautier return -ENOENT;
33f66358afSYann Gautier }
34f66358afSYann Gautier
35f66358afSYann Gautier node = fdt_path_offset(fdt, "/clocks");
36f66358afSYann Gautier if (node < 0) {
37f66358afSYann Gautier return -FDT_ERR_NOTFOUND;
38f66358afSYann Gautier }
39f66358afSYann Gautier
40f66358afSYann Gautier fdt_for_each_subnode(subnode, fdt, node) {
41f66358afSYann Gautier const char *cchar;
42f66358afSYann Gautier int ret;
43f66358afSYann Gautier
44f66358afSYann Gautier cchar = fdt_get_name(fdt, subnode, &ret);
45f66358afSYann Gautier if (cchar == NULL) {
46f66358afSYann Gautier return ret;
47f66358afSYann Gautier }
48f66358afSYann Gautier
49bcccdaccSPatrick Delaunay if ((strncmp(cchar, name, (size_t)ret) == 0) &&
50bcccdaccSPatrick Delaunay (fdt_get_status(subnode) != DT_DISABLED)) {
51f66358afSYann Gautier const fdt32_t *cuint;
52f66358afSYann Gautier
53f66358afSYann Gautier cuint = fdt_getprop(fdt, subnode, "clock-frequency",
54f66358afSYann Gautier &ret);
55f66358afSYann Gautier if (cuint == NULL) {
56f66358afSYann Gautier return ret;
57f66358afSYann Gautier }
58f66358afSYann Gautier
59f66358afSYann Gautier *freq = fdt32_to_cpu(*cuint);
60f66358afSYann Gautier
61f66358afSYann Gautier return 0;
62f66358afSYann Gautier }
63f66358afSYann Gautier }
64f66358afSYann Gautier
65f66358afSYann Gautier /* Oscillator not found, freq=0 */
66f66358afSYann Gautier *freq = 0;
67f66358afSYann Gautier return 0;
68f66358afSYann Gautier }
69f66358afSYann Gautier
70f66358afSYann Gautier /*
71f66358afSYann Gautier * Check the presence of an oscillator property from its id.
72b208e3daSGabriel Fernandez * @param node_label: clock node name
73f66358afSYann Gautier * @param prop_name: property name
74f66358afSYann Gautier * @return: true/false regarding search result.
75f66358afSYann Gautier */
fdt_clk_read_bool(const char * node_label,const char * prop_name)76b208e3daSGabriel Fernandez bool fdt_clk_read_bool(const char *node_label, const char *prop_name)
77f66358afSYann Gautier {
78f66358afSYann Gautier int node, subnode;
79f66358afSYann Gautier void *fdt;
80f66358afSYann Gautier
81f66358afSYann Gautier if (fdt_get_address(&fdt) == 0) {
82f66358afSYann Gautier return false;
83f66358afSYann Gautier }
84f66358afSYann Gautier
85f66358afSYann Gautier node = fdt_path_offset(fdt, "/clocks");
86f66358afSYann Gautier if (node < 0) {
87f66358afSYann Gautier return false;
88f66358afSYann Gautier }
89f66358afSYann Gautier
90f66358afSYann Gautier fdt_for_each_subnode(subnode, fdt, node) {
91f66358afSYann Gautier const char *cchar;
92f66358afSYann Gautier int ret;
93f66358afSYann Gautier
94f66358afSYann Gautier cchar = fdt_get_name(fdt, subnode, &ret);
95f66358afSYann Gautier if (cchar == NULL) {
96f66358afSYann Gautier return false;
97f66358afSYann Gautier }
98f66358afSYann Gautier
99b208e3daSGabriel Fernandez if (strncmp(cchar, node_label, (size_t)ret) != 0) {
100f66358afSYann Gautier continue;
101f66358afSYann Gautier }
102f66358afSYann Gautier
103f66358afSYann Gautier if (fdt_getprop(fdt, subnode, prop_name, NULL) != NULL) {
104f66358afSYann Gautier return true;
105f66358afSYann Gautier }
106f66358afSYann Gautier }
107f66358afSYann Gautier
108f66358afSYann Gautier return false;
109f66358afSYann Gautier }
110f66358afSYann Gautier
111f66358afSYann Gautier /*
112b208e3daSGabriel Fernandez * Get the value of a oscillator property from its name.
113b208e3daSGabriel Fernandez * @param node_label: oscillator name
114f66358afSYann Gautier * @param prop_name: property name
115f66358afSYann Gautier * @param dflt_value: default value
116f66358afSYann Gautier * @return oscillator value on success, default value if property not found.
117f66358afSYann Gautier */
fdt_clk_read_uint32_default(const char * node_label,const char * prop_name,uint32_t dflt_value)118b208e3daSGabriel Fernandez uint32_t fdt_clk_read_uint32_default(const char *node_label,
119f66358afSYann Gautier const char *prop_name, uint32_t dflt_value)
120f66358afSYann Gautier {
121f66358afSYann Gautier int node, subnode;
122f66358afSYann Gautier void *fdt;
123f66358afSYann Gautier
124f66358afSYann Gautier if (fdt_get_address(&fdt) == 0) {
125f66358afSYann Gautier return dflt_value;
126f66358afSYann Gautier }
127f66358afSYann Gautier
128f66358afSYann Gautier node = fdt_path_offset(fdt, "/clocks");
129f66358afSYann Gautier if (node < 0) {
130f66358afSYann Gautier return dflt_value;
131f66358afSYann Gautier }
132f66358afSYann Gautier
133f66358afSYann Gautier fdt_for_each_subnode(subnode, fdt, node) {
134f66358afSYann Gautier const char *cchar;
135f66358afSYann Gautier int ret;
136f66358afSYann Gautier
137f66358afSYann Gautier cchar = fdt_get_name(fdt, subnode, &ret);
138f66358afSYann Gautier if (cchar == NULL) {
139f66358afSYann Gautier return dflt_value;
140f66358afSYann Gautier }
141f66358afSYann Gautier
142b208e3daSGabriel Fernandez if (strncmp(cchar, node_label, (size_t)ret) != 0) {
143f66358afSYann Gautier continue;
144f66358afSYann Gautier }
145f66358afSYann Gautier
146be858cffSAndre Przywara return fdt_read_uint32_default(fdt, subnode, prop_name,
147be858cffSAndre Przywara dflt_value);
148f66358afSYann Gautier }
149f66358afSYann Gautier
150f66358afSYann Gautier return dflt_value;
151f66358afSYann Gautier }
152f66358afSYann Gautier
153f66358afSYann Gautier /*
154447b2b13SYann Gautier * Get the RCC node offset from the device tree
155447b2b13SYann Gautier * @param fdt: Device tree reference
156447b2b13SYann Gautier * @return: Node offset or a negative value on error
157447b2b13SYann Gautier */
fdt_get_rcc_node(void * fdt)158ff18c4cdSPatrick Delaunay static int fdt_get_rcc_node(void *fdt)
159447b2b13SYann Gautier {
160ba57711cSYann Gautier static int node;
161ba57711cSYann Gautier
162ba57711cSYann Gautier if (node <= 0) {
163ba57711cSYann Gautier node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT);
164ba57711cSYann Gautier }
165ba57711cSYann Gautier
166ba57711cSYann Gautier return node;
167447b2b13SYann Gautier }
168447b2b13SYann Gautier
169447b2b13SYann Gautier /*
170447b2b13SYann Gautier * Read a series of parameters in rcc-clk section in device tree
171447b2b13SYann Gautier * @param prop_name: Name of the RCC property to be read
172447b2b13SYann Gautier * @param array: the array to store the property parameters
173447b2b13SYann Gautier * @param count: number of parameters to be read
174447b2b13SYann Gautier * @return: 0 on succes or a negative value on error
175447b2b13SYann Gautier */
fdt_rcc_read_uint32_array(const char * prop_name,uint32_t count,uint32_t * array)17652a616b4SAndre Przywara int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t count,
17752a616b4SAndre Przywara uint32_t *array)
178447b2b13SYann Gautier {
179447b2b13SYann Gautier int node;
180447b2b13SYann Gautier void *fdt;
181447b2b13SYann Gautier
182447b2b13SYann Gautier if (fdt_get_address(&fdt) == 0) {
183447b2b13SYann Gautier return -ENOENT;
184447b2b13SYann Gautier }
185447b2b13SYann Gautier
186447b2b13SYann Gautier node = fdt_get_rcc_node(fdt);
187447b2b13SYann Gautier if (node < 0) {
188447b2b13SYann Gautier return -FDT_ERR_NOTFOUND;
189447b2b13SYann Gautier }
190447b2b13SYann Gautier
19152a616b4SAndre Przywara return fdt_read_uint32_array(fdt, node, prop_name, count, array);
192447b2b13SYann Gautier }
193447b2b13SYann Gautier
194447b2b13SYann Gautier /*
195447b2b13SYann Gautier * Get the subnode offset in rcc-clk section from its name in device tree
196447b2b13SYann Gautier * @param name: name of the RCC property
197447b2b13SYann Gautier * @return: offset on success, and a negative FDT/ERRNO error code on failure.
198447b2b13SYann Gautier */
fdt_rcc_subnode_offset(const char * name)199447b2b13SYann Gautier int fdt_rcc_subnode_offset(const char *name)
200447b2b13SYann Gautier {
201447b2b13SYann Gautier int node, subnode;
202447b2b13SYann Gautier void *fdt;
203447b2b13SYann Gautier
204447b2b13SYann Gautier if (fdt_get_address(&fdt) == 0) {
205447b2b13SYann Gautier return -ENOENT;
206447b2b13SYann Gautier }
207447b2b13SYann Gautier
208447b2b13SYann Gautier node = fdt_get_rcc_node(fdt);
209447b2b13SYann Gautier if (node < 0) {
210447b2b13SYann Gautier return -FDT_ERR_NOTFOUND;
211447b2b13SYann Gautier }
212447b2b13SYann Gautier
213447b2b13SYann Gautier subnode = fdt_subnode_offset(fdt, node, name);
214447b2b13SYann Gautier if (subnode <= 0) {
215447b2b13SYann Gautier return -FDT_ERR_NOTFOUND;
216447b2b13SYann Gautier }
217447b2b13SYann Gautier
218447b2b13SYann Gautier return subnode;
219447b2b13SYann Gautier }
220447b2b13SYann Gautier
221447b2b13SYann Gautier /*
222447b2b13SYann Gautier * Get the pointer to a rcc-clk property from its name.
223447b2b13SYann Gautier * @param name: name of the RCC property
224447b2b13SYann Gautier * @param lenp: stores the length of the property.
225447b2b13SYann Gautier * @return: pointer to the property on success, and NULL value on failure.
226447b2b13SYann Gautier */
fdt_rcc_read_prop(const char * prop_name,int * lenp)227447b2b13SYann Gautier const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp)
228447b2b13SYann Gautier {
229447b2b13SYann Gautier const fdt32_t *cuint;
230447b2b13SYann Gautier int node, len;
231447b2b13SYann Gautier void *fdt;
232447b2b13SYann Gautier
233447b2b13SYann Gautier if (fdt_get_address(&fdt) == 0) {
234447b2b13SYann Gautier return NULL;
235447b2b13SYann Gautier }
236447b2b13SYann Gautier
237447b2b13SYann Gautier node = fdt_get_rcc_node(fdt);
238447b2b13SYann Gautier if (node < 0) {
239447b2b13SYann Gautier return NULL;
240447b2b13SYann Gautier }
241447b2b13SYann Gautier
242447b2b13SYann Gautier cuint = fdt_getprop(fdt, node, prop_name, &len);
243447b2b13SYann Gautier if (cuint == NULL) {
244447b2b13SYann Gautier return NULL;
245447b2b13SYann Gautier }
246447b2b13SYann Gautier
247447b2b13SYann Gautier *lenp = len;
248447b2b13SYann Gautier return cuint;
249447b2b13SYann Gautier }
250447b2b13SYann Gautier
25119c38081SYann Gautier #if defined(IMAGE_BL32)
252447b2b13SYann Gautier /*
253812daf91SLionel Debieve * Get the secure state for rcc node in device tree.
254812daf91SLionel Debieve * @return: true if rcc is configured for secure world access, false if not.
255447b2b13SYann Gautier */
fdt_get_rcc_secure_state(void)256812daf91SLionel 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
264812daf91SLionel Debieve if (fdt_node_offset_by_compatible(fdt, -1, DT_RCC_SEC_CLK_COMPAT) < 0) {
265447b2b13SYann Gautier return false;
266447b2b13SYann Gautier }
267447b2b13SYann Gautier
268812daf91SLionel Debieve return true;
269447b2b13SYann Gautier }
27019c38081SYann Gautier #endif
271447b2b13SYann Gautier
272447b2b13SYann Gautier /*
273447b2b13SYann Gautier * Get the clock ID of the given node in device tree.
274447b2b13SYann Gautier * @param node: node offset
275447b2b13SYann Gautier * @return: Clock ID on success, and a negative FDT/ERRNO error code on failure.
276447b2b13SYann Gautier */
fdt_get_clock_id(int node)277447b2b13SYann Gautier int fdt_get_clock_id(int node)
278447b2b13SYann Gautier {
279447b2b13SYann Gautier const fdt32_t *cuint;
280447b2b13SYann Gautier void *fdt;
281447b2b13SYann Gautier
282447b2b13SYann Gautier if (fdt_get_address(&fdt) == 0) {
283447b2b13SYann Gautier return -ENOENT;
284447b2b13SYann Gautier }
285447b2b13SYann Gautier
286447b2b13SYann Gautier cuint = fdt_getprop(fdt, node, "clocks", NULL);
287447b2b13SYann Gautier if (cuint == NULL) {
288447b2b13SYann Gautier return -FDT_ERR_NOTFOUND;
289447b2b13SYann Gautier }
290447b2b13SYann Gautier
291447b2b13SYann Gautier cuint++;
292447b2b13SYann Gautier return (int)fdt32_to_cpu(*cuint);
293447b2b13SYann Gautier }
294165ad556SNicolas Le Bayon
295165ad556SNicolas Le Bayon /*
296165ad556SNicolas Le Bayon * Get the frequency of the specified UART instance.
297165ad556SNicolas Le Bayon * @param instance: UART interface registers base address.
298165ad556SNicolas Le Bayon * @return: clock frequency on success, 0 value on failure.
299165ad556SNicolas Le Bayon */
fdt_get_uart_clock_freq(uintptr_t instance)300165ad556SNicolas Le Bayon unsigned long fdt_get_uart_clock_freq(uintptr_t instance)
301165ad556SNicolas Le Bayon {
302165ad556SNicolas Le Bayon void *fdt;
303165ad556SNicolas Le Bayon int node;
304165ad556SNicolas Le Bayon int clk_id;
305165ad556SNicolas Le Bayon
306165ad556SNicolas Le Bayon if (fdt_get_address(&fdt) == 0) {
307165ad556SNicolas Le Bayon return 0UL;
308165ad556SNicolas Le Bayon }
309165ad556SNicolas Le Bayon
310165ad556SNicolas Le Bayon /* Check for UART nodes */
311165ad556SNicolas Le Bayon node = dt_match_instance_by_compatible(DT_UART_COMPAT, instance);
312165ad556SNicolas Le Bayon if (node < 0) {
313165ad556SNicolas Le Bayon return 0UL;
314165ad556SNicolas Le Bayon }
315165ad556SNicolas Le Bayon
316165ad556SNicolas Le Bayon clk_id = fdt_get_clock_id(node);
317165ad556SNicolas Le Bayon if (clk_id < 0) {
318165ad556SNicolas Le Bayon return 0UL;
319165ad556SNicolas Le Bayon }
320165ad556SNicolas Le Bayon
32133667d29SYann Gautier return clk_get_rate((unsigned long)clk_id);
322165ad556SNicolas Le Bayon }
323591d80c8SLionel Debieve
324591d80c8SLionel Debieve /*******************************************************************************
325b1718c63SYann Gautier * This function sets the STGEN counter value.
326b1718c63SYann Gautier ******************************************************************************/
stgen_set_counter(unsigned long long counter)327b1718c63SYann Gautier static void stgen_set_counter(unsigned long long counter)
328b1718c63SYann Gautier {
329b1718c63SYann Gautier #ifdef __aarch64__
330b1718c63SYann Gautier mmio_write_64(STGEN_BASE + CNTCV_OFF, counter);
331b1718c63SYann Gautier #else
332b1718c63SYann Gautier mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)counter);
333b1718c63SYann Gautier mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(counter >> 32));
334b1718c63SYann Gautier #endif
335b1718c63SYann Gautier }
336b1718c63SYann Gautier
337b1718c63SYann Gautier /*******************************************************************************
338*caa12957SPatrick Delaunay * This function returns the STGEN counter value.
339*caa12957SPatrick Delaunay ******************************************************************************/
stm32mp_stgen_get_counter(void)340*caa12957SPatrick Delaunay static unsigned long long stm32mp_stgen_get_counter(void)
341*caa12957SPatrick Delaunay {
342*caa12957SPatrick Delaunay #ifdef __aarch64__
343*caa12957SPatrick Delaunay return mmio_read_64(STGEN_BASE + CNTCV_OFF);
344*caa12957SPatrick Delaunay #else
345*caa12957SPatrick Delaunay return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) |
346*caa12957SPatrick Delaunay mmio_read_32(STGEN_BASE + CNTCVL_OFF));
347*caa12957SPatrick Delaunay #endif
348*caa12957SPatrick Delaunay }
349*caa12957SPatrick Delaunay
350*caa12957SPatrick Delaunay /*******************************************************************************
351591d80c8SLionel Debieve * This function configures and restores the STGEN counter depending on the
352591d80c8SLionel Debieve * connected clock.
353591d80c8SLionel Debieve ******************************************************************************/
stm32mp_stgen_config(unsigned long rate)354591d80c8SLionel Debieve void stm32mp_stgen_config(unsigned long rate)
355591d80c8SLionel Debieve {
356591d80c8SLionel Debieve uint32_t cntfid0;
357591d80c8SLionel Debieve unsigned long long counter;
358591d80c8SLionel Debieve
359591d80c8SLionel Debieve cntfid0 = mmio_read_32(STGEN_BASE + CNTFID_OFF);
360591d80c8SLionel Debieve
361591d80c8SLionel Debieve if (cntfid0 == rate) {
362591d80c8SLionel Debieve return;
363591d80c8SLionel Debieve }
364591d80c8SLionel Debieve
365591d80c8SLionel Debieve mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
366591d80c8SLionel Debieve
3673b3a9afdSPatrick Delaunay if (cntfid0 != 0U) {
3683b3a9afdSPatrick Delaunay counter = stm32mp_stgen_get_counter() * rate / cntfid0;
369b1718c63SYann Gautier stgen_set_counter(counter);
3703b3a9afdSPatrick Delaunay }
371591d80c8SLionel Debieve mmio_write_32(STGEN_BASE + CNTFID_OFF, rate);
372591d80c8SLionel Debieve mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
373591d80c8SLionel Debieve
374591d80c8SLionel Debieve write_cntfrq_el0(rate);
375591d80c8SLionel Debieve
376591d80c8SLionel Debieve /* Need to update timer with new frequency */
377591d80c8SLionel Debieve generic_delay_timer_init();
378591d80c8SLionel Debieve }
379591d80c8SLionel Debieve
380591d80c8SLionel Debieve /*******************************************************************************
381bfe8a12eSPascal Paillet * This function restores CPU generic timer rate from the STGEN clock rate.
382bfe8a12eSPascal Paillet ******************************************************************************/
stm32mp_stgen_restore_rate(void)383bfe8a12eSPascal Paillet void stm32mp_stgen_restore_rate(void)
384bfe8a12eSPascal Paillet {
385bfe8a12eSPascal Paillet unsigned long rate;
386bfe8a12eSPascal Paillet
387bfe8a12eSPascal Paillet rate = mmio_read_32(STGEN_BASE + CNTFID_OFF);
388bfe8a12eSPascal Paillet
389bfe8a12eSPascal Paillet write_cntfrq_el0((u_register_t)rate);
390bfe8a12eSPascal Paillet }
391