blob: 80c2f4102ec0fb5565dee6b3fcfcd01f476d4a6f [file] [log] [blame]
Yann Gautier4d429472019-02-14 11:15:20 +01001/*
Patrick Delaunay48519b32019-07-01 08:59:24 +02002 * Copyright (c) 2017-2022, STMicroelectronics - All Rights Reserved
Yann Gautier4d429472019-02-14 11:15:20 +01003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <errno.h>
8
Lionel Debievedeef9692019-12-04 21:50:19 +01009#include <arch_helpers.h>
Andre Przywaracc99f3f2020-03-26 12:51:21 +000010#include <common/fdt_wrappers.h>
Yann Gautiera205a5c2021-08-30 15:06:54 +020011#include <drivers/clk.h>
Lionel Debievedeef9692019-12-04 21:50:19 +010012#include <drivers/generic_delay_timer.h>
Yann Gautier4d429472019-02-14 11:15:20 +010013#include <drivers/st/stm32_gpio.h>
14#include <drivers/st/stm32mp_clkfunc.h>
Lionel Debievedeef9692019-12-04 21:50:19 +010015#include <lib/mmio.h>
Patrick Delaunay48519b32019-07-01 08:59:24 +020016#include <libfdt.h>
17
18#include <platform_def.h>
Yann Gautier4d429472019-02-14 11:15:20 +010019
Nicolas Le Bayondc08ebe2019-09-11 11:46:40 +020020#define DT_UART_COMPAT "st,stm32h7-uart"
Yann Gautier4d429472019-02-14 11:15:20 +010021/*
Yann Gautier5f2e8742019-05-17 15:57:56 +020022 * Get the frequency of an oscillator from its name in device tree.
23 * @param name: oscillator name
24 * @param freq: stores the frequency of the oscillator
25 * @return: 0 on success, and a negative FDT/ERRNO error code on failure.
26 */
27int fdt_osc_read_freq(const char *name, uint32_t *freq)
28{
29 int node, subnode;
30 void *fdt;
31
32 if (fdt_get_address(&fdt) == 0) {
33 return -ENOENT;
34 }
35
36 node = fdt_path_offset(fdt, "/clocks");
37 if (node < 0) {
38 return -FDT_ERR_NOTFOUND;
39 }
40
41 fdt_for_each_subnode(subnode, fdt, node) {
42 const char *cchar;
43 int ret;
44
45 cchar = fdt_get_name(fdt, subnode, &ret);
46 if (cchar == NULL) {
47 return ret;
48 }
49
Patrick Delaunay48519b32019-07-01 08:59:24 +020050 if ((strncmp(cchar, name, (size_t)ret) == 0) &&
51 (fdt_get_status(subnode) != DT_DISABLED)) {
Yann Gautier5f2e8742019-05-17 15:57:56 +020052 const fdt32_t *cuint;
53
54 cuint = fdt_getprop(fdt, subnode, "clock-frequency",
55 &ret);
56 if (cuint == NULL) {
57 return ret;
58 }
59
60 *freq = fdt32_to_cpu(*cuint);
61
62 return 0;
63 }
64 }
65
66 /* Oscillator not found, freq=0 */
67 *freq = 0;
68 return 0;
69}
70
71/*
72 * Check the presence of an oscillator property from its id.
Gabriel Fernandez5177ea22020-05-15 08:00:03 +020073 * @param node_label: clock node name
Yann Gautier5f2e8742019-05-17 15:57:56 +020074 * @param prop_name: property name
75 * @return: true/false regarding search result.
76 */
Gabriel Fernandez5177ea22020-05-15 08:00:03 +020077bool fdt_clk_read_bool(const char *node_label, const char *prop_name)
Yann Gautier5f2e8742019-05-17 15:57:56 +020078{
79 int node, subnode;
80 void *fdt;
81
82 if (fdt_get_address(&fdt) == 0) {
83 return false;
84 }
85
Yann Gautier5f2e8742019-05-17 15:57:56 +020086 node = fdt_path_offset(fdt, "/clocks");
87 if (node < 0) {
88 return false;
89 }
90
91 fdt_for_each_subnode(subnode, fdt, node) {
92 const char *cchar;
93 int ret;
94
95 cchar = fdt_get_name(fdt, subnode, &ret);
96 if (cchar == NULL) {
97 return false;
98 }
99
Gabriel Fernandez5177ea22020-05-15 08:00:03 +0200100 if (strncmp(cchar, node_label, (size_t)ret) != 0) {
Yann Gautier5f2e8742019-05-17 15:57:56 +0200101 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/*
Gabriel Fernandez5177ea22020-05-15 08:00:03 +0200113 * Get the value of a oscillator property from its name.
114 * @param node_label: oscillator name
Yann Gautier5f2e8742019-05-17 15:57:56 +0200115 * @param prop_name: property name
116 * @param dflt_value: default value
117 * @return oscillator value on success, default value if property not found.
118 */
Gabriel Fernandez5177ea22020-05-15 08:00:03 +0200119uint32_t fdt_clk_read_uint32_default(const char *node_label,
Yann Gautier5f2e8742019-05-17 15:57:56 +0200120 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
Yann Gautier5f2e8742019-05-17 15:57:56 +0200129 node = fdt_path_offset(fdt, "/clocks");
130 if (node < 0) {
131 return dflt_value;
132 }
133
134 fdt_for_each_subnode(subnode, fdt, node) {
135 const char *cchar;
136 int ret;
137
138 cchar = fdt_get_name(fdt, subnode, &ret);
139 if (cchar == NULL) {
140 return dflt_value;
141 }
142
Gabriel Fernandez5177ea22020-05-15 08:00:03 +0200143 if (strncmp(cchar, node_label, (size_t)ret) != 0) {
Yann Gautier5f2e8742019-05-17 15:57:56 +0200144 continue;
145 }
146
Andre Przywara2d5690c2020-03-26 11:50:33 +0000147 return fdt_read_uint32_default(fdt, subnode, prop_name,
148 dflt_value);
Yann Gautier5f2e8742019-05-17 15:57:56 +0200149 }
150
151 return dflt_value;
152}
153
154/*
Yann Gautier4d429472019-02-14 11:15:20 +0100155 * Get the RCC node offset from the device tree
156 * @param fdt: Device tree reference
157 * @return: Node offset or a negative value on error
158 */
Patrick Delaunay286d81e2020-10-06 14:32:26 +0200159static int fdt_get_rcc_node(void *fdt)
Yann Gautier4d429472019-02-14 11:15:20 +0100160{
Yann Gautiereaab37d2020-10-22 15:37:22 +0200161 static int node;
162
163 if (node <= 0) {
164 node = fdt_node_offset_by_compatible(fdt, -1, DT_RCC_CLK_COMPAT);
165 }
166
167 return node;
Yann Gautier4d429472019-02-14 11:15:20 +0100168}
169
170/*
Yann Gautier4d429472019-02-14 11:15:20 +0100171 * Read a series of parameters in rcc-clk section in device tree
172 * @param prop_name: Name of the RCC property to be read
173 * @param array: the array to store the property parameters
174 * @param count: number of parameters to be read
175 * @return: 0 on succes or a negative value on error
176 */
Andre Przywaracc99f3f2020-03-26 12:51:21 +0000177int fdt_rcc_read_uint32_array(const char *prop_name, uint32_t count,
178 uint32_t *array)
Yann Gautier4d429472019-02-14 11:15:20 +0100179{
180 int node;
181 void *fdt;
182
183 if (fdt_get_address(&fdt) == 0) {
184 return -ENOENT;
185 }
186
187 node = fdt_get_rcc_node(fdt);
188 if (node < 0) {
189 return -FDT_ERR_NOTFOUND;
190 }
191
Andre Przywaracc99f3f2020-03-26 12:51:21 +0000192 return fdt_read_uint32_array(fdt, node, prop_name, count, array);
Yann Gautier4d429472019-02-14 11:15:20 +0100193}
194
195/*
196 * Get the subnode offset in rcc-clk section from its name in device tree
197 * @param name: name of the RCC property
198 * @return: offset on success, and a negative FDT/ERRNO error code on failure.
199 */
200int fdt_rcc_subnode_offset(const char *name)
201{
202 int node, subnode;
203 void *fdt;
204
205 if (fdt_get_address(&fdt) == 0) {
206 return -ENOENT;
207 }
208
209 node = fdt_get_rcc_node(fdt);
210 if (node < 0) {
211 return -FDT_ERR_NOTFOUND;
212 }
213
214 subnode = fdt_subnode_offset(fdt, node, name);
215 if (subnode <= 0) {
216 return -FDT_ERR_NOTFOUND;
217 }
218
219 return subnode;
220}
221
222/*
223 * Get the pointer to a rcc-clk property from its name.
224 * @param name: name of the RCC property
225 * @param lenp: stores the length of the property.
226 * @return: pointer to the property on success, and NULL value on failure.
227 */
228const fdt32_t *fdt_rcc_read_prop(const char *prop_name, int *lenp)
229{
230 const fdt32_t *cuint;
231 int node, len;
232 void *fdt;
233
234 if (fdt_get_address(&fdt) == 0) {
235 return NULL;
236 }
237
238 node = fdt_get_rcc_node(fdt);
239 if (node < 0) {
240 return NULL;
241 }
242
243 cuint = fdt_getprop(fdt, node, prop_name, &len);
244 if (cuint == NULL) {
245 return NULL;
246 }
247
248 *lenp = len;
249 return cuint;
250}
251
252/*
Lionel Debieve3c0fbfe2020-12-15 10:35:59 +0100253 * Get the secure state for rcc node in device tree.
254 * @return: true if rcc is configured for secure world access, false if not.
Yann Gautier4d429472019-02-14 11:15:20 +0100255 */
Lionel Debieve3c0fbfe2020-12-15 10:35:59 +0100256bool fdt_get_rcc_secure_state(void)
Yann Gautier4d429472019-02-14 11:15:20 +0100257{
Yann Gautier4d429472019-02-14 11:15:20 +0100258 void *fdt;
259
260 if (fdt_get_address(&fdt) == 0) {
261 return false;
262 }
263
Lionel Debieve3c0fbfe2020-12-15 10:35:59 +0100264 if (fdt_node_offset_by_compatible(fdt, -1, DT_RCC_SEC_CLK_COMPAT) < 0) {
Yann Gautier4d429472019-02-14 11:15:20 +0100265 return false;
266 }
267
Lionel Debieve3c0fbfe2020-12-15 10:35:59 +0100268 return true;
Yann Gautier4d429472019-02-14 11:15:20 +0100269}
270
271/*
Yann Gautier4d429472019-02-14 11:15:20 +0100272 * Get the clock ID of the given node in device tree.
273 * @param node: node offset
274 * @return: Clock ID on success, and a negative FDT/ERRNO error code on failure.
275 */
276int fdt_get_clock_id(int node)
277{
278 const fdt32_t *cuint;
279 void *fdt;
280
281 if (fdt_get_address(&fdt) == 0) {
282 return -ENOENT;
283 }
284
285 cuint = fdt_getprop(fdt, node, "clocks", NULL);
286 if (cuint == NULL) {
287 return -FDT_ERR_NOTFOUND;
288 }
289
290 cuint++;
291 return (int)fdt32_to_cpu(*cuint);
292}
Nicolas Le Bayondc08ebe2019-09-11 11:46:40 +0200293
294/*
295 * Get the frequency of the specified UART instance.
296 * @param instance: UART interface registers base address.
297 * @return: clock frequency on success, 0 value on failure.
298 */
299unsigned long fdt_get_uart_clock_freq(uintptr_t instance)
300{
301 void *fdt;
302 int node;
303 int clk_id;
304
305 if (fdt_get_address(&fdt) == 0) {
306 return 0UL;
307 }
308
309 /* Check for UART nodes */
310 node = dt_match_instance_by_compatible(DT_UART_COMPAT, instance);
311 if (node < 0) {
312 return 0UL;
313 }
314
315 clk_id = fdt_get_clock_id(node);
316 if (clk_id < 0) {
317 return 0UL;
318 }
319
Yann Gautiera205a5c2021-08-30 15:06:54 +0200320 return clk_get_rate((unsigned long)clk_id);
Nicolas Le Bayondc08ebe2019-09-11 11:46:40 +0200321}
Lionel Debievedeef9692019-12-04 21:50:19 +0100322
323/*******************************************************************************
324 * This function configures and restores the STGEN counter depending on the
325 * connected clock.
326 ******************************************************************************/
327void stm32mp_stgen_config(unsigned long rate)
328{
329 uint32_t cntfid0;
330 unsigned long long counter;
331
332 cntfid0 = mmio_read_32(STGEN_BASE + CNTFID_OFF);
333
334 if (cntfid0 == rate) {
335 return;
336 }
337
338 mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
339 counter = stm32mp_stgen_get_counter() * rate / cntfid0;
340
341 mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)counter);
342 mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(counter >> 32));
343 mmio_write_32(STGEN_BASE + CNTFID_OFF, rate);
344 mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
345
346 write_cntfrq_el0(rate);
347
348 /* Need to update timer with new frequency */
349 generic_delay_timer_init();
350}
351
352/*******************************************************************************
353 * This function returns the STGEN counter value.
354 ******************************************************************************/
355unsigned long long stm32mp_stgen_get_counter(void)
356{
357 return (((unsigned long long)mmio_read_32(STGEN_BASE + CNTCVU_OFF) << 32) |
358 mmio_read_32(STGEN_BASE + CNTCVL_OFF));
359}
360
361/*******************************************************************************
362 * This function restores the STGEN counter value.
363 * It takes a first input value as a counter backup value to be restored and a
364 * offset in ms to be added.
365 ******************************************************************************/
366void stm32mp_stgen_restore_counter(unsigned long long value,
367 unsigned long long offset_in_ms)
368{
369 unsigned long long cnt;
370
371 cnt = value + ((offset_in_ms *
372 mmio_read_32(STGEN_BASE + CNTFID_OFF)) / 1000U);
373
374 mmio_clrbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
375 mmio_write_32(STGEN_BASE + CNTCVL_OFF, (uint32_t)cnt);
376 mmio_write_32(STGEN_BASE + CNTCVU_OFF, (uint32_t)(cnt >> 32));
377 mmio_setbits_32(STGEN_BASE + CNTCR_OFF, CNTCR_EN);
378}