xref: /rk3399_ARM-atf/plat/arm/board/fvp/fconf/fconf_hw_config_getter.c (revision 79e7aae82dd173d1ccc63e5d553222f1d58f12f5)
1 /*
2  * Copyright (c) 2020-2025, Arm Limited. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <inttypes.h>
9 #include <stdint.h>
10 
11 #include <common/debug.h>
12 #include <common/fdt_wrappers.h>
13 #include <fconf_hw_config_getter.h>
14 #include <libfdt.h>
15 #include <plat/common/platform.h>
16 
17 struct gicv3_config_t gicv3_config;
18 struct hw_topology_t soc_topology;
19 struct uart_serial_config_t uart_serial_config;
20 struct cpu_timer_t cpu_timer;
21 struct dram_layout_t dram_layout;
22 struct pci_props_t pci_props;
23 
24 /*
25  * Each NS DRAM bank entry is 'reg' node property which is
26  * a sequence of (address, length) pairs of 32-bit values.
27  */
28 #define DRAM_ENTRY_SIZE		(4UL * sizeof(uint32_t))
29 
30 CASSERT(ARM_DRAM_NUM_BANKS == 2UL, ARM_DRAM_NUM_BANKS_mismatch);
31 CASSERT(ARM_PCI_NUM_REGIONS == 2UL, ARM_PCI_NUM_REGIONS_mismatch);
32 
33 #define ILLEGAL_ADDR	ULL(~0)
34 
35 int fconf_populate_gicv3_config(uintptr_t config)
36 {
37 	int err;
38 	int node;
39 	uintptr_t addr;
40 
41 	/* Necessary to work with libfdt APIs */
42 	const void *hw_config_dtb = (const void *)config;
43 
44 	/*
45 	 * Find the offset of the node containing "arm,gic-v3" compatible property.
46 	 * Populating fconf strucutures dynamically is not supported for legacy
47 	 * systems which use GICv2 IP. Simply skip extracting GIC properties.
48 	 */
49 	node = fdt_node_offset_by_compatible(hw_config_dtb, -1, "arm,gic-v3");
50 	if (node < 0) {
51 		WARN("FCONF: Unable to locate node with arm,gic-v3 compatible property\n");
52 		return 0;
53 	}
54 	/* The GICv3 DT binding holds at least two address/size pairs,
55 	 * the first describing the distributor, the second the redistributors.
56 	 * See: bindings/interrupt-controller/arm,gic-v3.yaml
57 	 */
58 	err = fdt_get_reg_props_by_index(hw_config_dtb, node, 0, &addr, NULL);
59 	if (err < 0) {
60 		ERROR("FCONF: Failed to read GICD reg property of GIC node\n");
61 		return err;
62 	}
63 	gicv3_config.gicd_base = addr;
64 
65 	err = fdt_get_reg_props_by_index(hw_config_dtb, node, 1, &addr, NULL);
66 	if (err < 0) {
67 		ERROR("FCONF: Failed to read GICR reg property of GIC node\n");
68 	} else {
69 		gicv3_config.gicr_base = addr;
70 	}
71 
72 	return err;
73 }
74 
75 int fconf_populate_topology(uintptr_t config)
76 {
77 	int err, node, cluster_node, core_node, thread_node;
78 	uint32_t cluster_count = 0, max_cpu_per_cluster = 0, total_cpu_count = 0;
79 	uint32_t max_pwr_lvl = 0;
80 
81 	/* Necessary to work with libfdt APIs */
82 	const void *hw_config_dtb = (const void *)config;
83 
84 	/* Find the offset of the node containing "arm,psci-1.0" compatible property */
85 	node = fdt_node_offset_by_compatible(hw_config_dtb, -1, "arm,psci-1.0");
86 	if (node < 0) {
87 		ERROR("FCONF: Unable to locate node with arm,psci-1.0 compatible property\n");
88 		return node;
89 	}
90 
91 	err = fdt_read_uint32(hw_config_dtb, node, "max-pwr-lvl", &max_pwr_lvl);
92 	if (err < 0) {
93 		/*
94 		 * Some legacy FVP dts may not have this property. Assign the default
95 		 * value.
96 		 */
97 		WARN("FCONF: Could not locate max-pwr-lvl property\n");
98 		max_pwr_lvl = 2;
99 	}
100 
101 	assert(max_pwr_lvl <= MPIDR_AFFLVL2);
102 
103 	/* Find the offset of the "cpus" node */
104 	node = fdt_path_offset(hw_config_dtb, "/cpus");
105 	if (node < 0) {
106 		ERROR("FCONF: Node '%s' not found in hardware configuration dtb\n", "cpus");
107 		return node;
108 	}
109 
110 	/* A typical cpu-map node in a device tree is shown here for reference
111 	cpu-map {
112 		cluster0 {
113 			core0 {
114 				cpu = <&CPU0>;
115 			};
116 			core1 {
117 				cpu = <&CPU1>;
118 			};
119 		};
120 
121 		cluster1 {
122 			core0 {
123 				cpu = <&CPU2>;
124 			};
125 			core1 {
126 				cpu = <&CPU3>;
127 			};
128 		};
129 	};
130 	*/
131 
132 	/* Locate the cpu-map child node */
133 	node = fdt_subnode_offset(hw_config_dtb, node, "cpu-map");
134 	if (node < 0) {
135 		ERROR("FCONF: Node '%s' not found in hardware configuration dtb\n", "cpu-map");
136 		return node;
137 	}
138 
139 	uint32_t cpus_per_cluster[PLAT_ARM_CLUSTER_COUNT] = {0};
140 
141 	/* Iterate through cluster nodes */
142 	fdt_for_each_subnode(cluster_node, hw_config_dtb, node) {
143 		assert(cluster_count < PLAT_ARM_CLUSTER_COUNT);
144 
145 		/* Iterate through core nodes */
146 		fdt_for_each_subnode(core_node, hw_config_dtb, cluster_node) {
147 			/* core nodes may have child nodes i.e., "thread" nodes */
148 			if (fdt_first_subnode(hw_config_dtb, core_node) < 0) {
149 				cpus_per_cluster[cluster_count]++;
150 			} else {
151 				/* Multi-threaded CPU description is found in dtb */
152 				fdt_for_each_subnode(thread_node, hw_config_dtb, core_node) {
153 					cpus_per_cluster[cluster_count]++;
154 				}
155 
156 				/* Since in some dtbs, core nodes may not have thread node,
157 				 * no need to error if even one child node is not found.
158 				 */
159 			}
160 		}
161 
162 		/* Ensure every cluster node has at least 1 child node */
163 		if (cpus_per_cluster[cluster_count] < 1U) {
164 			ERROR("FCONF: Unable to locate the core node in cluster %d\n", cluster_count);
165 			return -1;
166 		}
167 
168 		VERBOSE("CLUSTER ID: %d cpu-count: %d\n", cluster_count,
169 					cpus_per_cluster[cluster_count]);
170 
171 		/* Find the maximum number of cpus in any cluster */
172 		max_cpu_per_cluster = MAX(max_cpu_per_cluster, cpus_per_cluster[cluster_count]);
173 		total_cpu_count += cpus_per_cluster[cluster_count];
174 		cluster_count++;
175 	}
176 
177 
178 	/* At least one cluster node is expected in hardware configuration dtb */
179 	if (cluster_count < 1U) {
180 		ERROR("FCONF: Unable to locate the cluster node in cpu-map node\n");
181 		return -1;
182 	}
183 
184 	soc_topology.plat_max_pwr_level = max_pwr_lvl;
185 	soc_topology.plat_cluster_count = cluster_count;
186 	soc_topology.cluster_cpu_count = max_cpu_per_cluster;
187 	soc_topology.plat_cpu_count = total_cpu_count;
188 
189 	return 0;
190 }
191 
192 int fconf_populate_uart_config(uintptr_t config)
193 {
194 	int uart_node, node, err;
195 	uintptr_t addr;
196 	const char *path;
197 	uint32_t phandle;
198 	uint64_t translated_addr;
199 
200 	/* Necessary to work with libfdt APIs */
201 	const void *hw_config_dtb = (const void *)config;
202 
203 	/*
204 	 * uart child node is indirectly referenced through its path which is
205 	 * specified in the `serial1` property of the "aliases" node.
206 	 * Note that TF-A boot console is mapped to serial0 while runtime
207 	 * console is mapped to serial1.
208 	 */
209 
210 	path = fdt_get_alias(hw_config_dtb, "serial1");
211 	if (path == NULL) {
212 		ERROR("FCONF: Could not read serial1 property in aliases node\n");
213 		return -1;
214 	}
215 
216 	/* Find the offset of the uart serial node */
217 	uart_node = fdt_path_offset(hw_config_dtb, path);
218 	if (uart_node < 0) {
219 		ERROR("FCONF: Failed to locate uart serial node using its path\n");
220 		return -1;
221 	}
222 
223 	/* uart serial node has its offset and size of address in reg property */
224 	err = fdt_get_reg_props_by_index(hw_config_dtb, uart_node, 0, &addr,
225 						NULL);
226 	if (err < 0) {
227 		ERROR("FCONF: Failed to read reg property of '%s' node\n",
228 			"uart serial");
229 		return err;
230 	}
231 	VERBOSE("FCONF: UART node address: %lx\n", addr);
232 
233 	/*
234 	 * Perform address translation of local device address to CPU address
235 	 * domain.
236 	 */
237 	translated_addr = fdtw_translate_address(hw_config_dtb,
238 						 uart_node, (uint64_t)addr);
239 	if (translated_addr == ILLEGAL_ADDR) {
240 		ERROR("FCONF: failed to translate UART node base address");
241 		return -1;
242 	}
243 
244 	uart_serial_config.uart_base = translated_addr;
245 
246 	VERBOSE("FCONF: UART serial device base address: %" PRIx64 "\n",
247 		uart_serial_config.uart_base);
248 
249 	/*
250 	 * The phandle of the DT node which captures the clock info of uart
251 	 * serial node is specified in the "clocks" property.
252 	 */
253 	err = fdt_read_uint32(hw_config_dtb, uart_node, "clocks", &phandle);
254 	if (err < 0) {
255 		ERROR("FCONF: Could not read clocks property in uart serial node\n");
256 		return err;
257 	}
258 
259 	node = fdt_node_offset_by_phandle(hw_config_dtb, phandle);
260 	if (node < 0) {
261 		ERROR("FCONF: Failed to locate clk node using its path\n");
262 		return node;
263 	}
264 
265 	/*
266 	 * Retrieve clock frequency. We assume clock provider generates a fixed
267 	 * clock.
268 	 */
269 	err = fdt_read_uint32(hw_config_dtb, node, "clock-frequency",
270 				&uart_serial_config.uart_clk);
271 	if (err < 0) {
272 		ERROR("FCONF: Could not read clock-frequency property in clk node\n");
273 		return err;
274 	}
275 
276 	VERBOSE("FCONF: UART serial device clk frequency: %x\n",
277 		uart_serial_config.uart_clk);
278 
279 	return 0;
280 }
281 
282 int fconf_populate_cpu_timer(uintptr_t config)
283 {
284 	int err, node;
285 
286 	/* Necessary to work with libfdt APIs */
287 	const void *hw_config_dtb = (const void *)config;
288 
289 	/* Find the node offset point to "arm,armv8-timer" compatible property,
290 	 * a per-core architected timer attached to a GIC to deliver its per-processor
291 	 * interrupts via PPIs */
292 	node = fdt_node_offset_by_compatible(hw_config_dtb, -1, "arm,armv8-timer");
293 	if (node < 0) {
294 		ERROR("FCONF: Unrecognized hardware configuration dtb (%d)\n", node);
295 		return node;
296 	}
297 
298 	/* Locate the cell holding the clock-frequency, an optional field */
299 	err = fdt_read_uint32(hw_config_dtb, node, "clock-frequency", &cpu_timer.clock_freq);
300 	if (err < 0) {
301 		WARN("FCONF failed to read clock-frequency property\n");
302 	}
303 
304 	return 0;
305 }
306 
307 int fconf_populate_dram_layout(uintptr_t config)
308 {
309 	int node, len;
310 	const uint32_t *reg;
311 
312 	/* Necessary to work with libfdt APIs */
313 	const void *hw_config_dtb = (const void *)config;
314 
315 	/* Find 'memory' node */
316 	node = fdt_node_offset_by_prop_value(hw_config_dtb, -1, "device_type",
317 					     "memory", sizeof("memory"));
318 	if (node < 0) {
319 		WARN("FCONF: Unable to locate 'memory' node\n");
320 		return node;
321 	}
322 
323 	reg = fdt_getprop(hw_config_dtb, node, "reg", &len);
324 	if (reg == NULL) {
325 		ERROR("FCONF failed to read 'reg' property\n");
326 		return len;
327 	}
328 
329 	switch (len) {
330 	case DRAM_ENTRY_SIZE:
331 		/* 1 DRAM bank */
332 		dram_layout.num_banks = 1UL;
333 		break;
334 	case 2UL * DRAM_ENTRY_SIZE:
335 		/* 2 DRAM banks */
336 		dram_layout.num_banks = 2UL;
337 		break;
338 	default:
339 		ERROR("FCONF: Invalid 'memory' node\n");
340 		return -FDT_ERR_BADLAYOUT;
341 	}
342 
343 	for (unsigned long i = 0UL; i < dram_layout.num_banks; i++) {
344 		int err = fdt_get_reg_props_by_index(
345 				hw_config_dtb, node, (int)i,
346 				&dram_layout.dram_bank[i].base,
347 				(size_t *)&dram_layout.dram_bank[i].size);
348 		if (err < 0) {
349 			ERROR("FCONF: Failed to read 'reg' property #%lu of 'memory' node\n", i);
350 			return err;
351 		}
352 	}
353 
354 	return 0;
355 }
356 
357 /*
358  * Each PCIe memory region entry is 'ranges' node property which is
359  * an arbitrary number of (child-bus-address, parent-bus-address, length)
360  * triplets. E.g. with
361  * #address-cells = <3>
362  * #size-cells = <2>
363  * parent's #address-cells = <2>
364  * each entry occupies 7 32-bit words.
365  */
366 int fconf_populate_pci_props(uintptr_t config)
367 {
368 	int node, parent, len, err;
369 	int parent_ac, ac, sc, entry_len;
370 	const uint32_t *reg, *ranges;
371 
372 	/* Necessary to work with libfdt APIs */
373 	const void *hw_config_dtb = (const void *)config;
374 
375 	/* Find 'pci' node */
376 	node = fdt_node_offset_by_prop_value(hw_config_dtb, -1, "device_type",
377 					     "pci", sizeof("pci"));
378 	if (node < 0) {
379 		WARN("FCONF: Unable to locate 'pci' node\n");
380 		pci_props.ecam_base = 0UL;
381 		pci_props.size = 0UL;
382 		pci_props.num_ncoh_regions = 0UL;
383 		/* Don't return error code if 'pci' node not found */
384 		return 0;
385 	}
386 
387 	reg = fdt_getprop(hw_config_dtb, node, "reg", &len);
388 	if (reg == NULL) {
389 		ERROR("FCONF failed to read 'reg' property\n");
390 		return len;
391 	}
392 
393 	err = fdt_get_reg_props_by_index(hw_config_dtb, node, 0,
394 					 (uintptr_t *)&pci_props.ecam_base,
395 					 (size_t *)&pci_props.size);
396 	if (err < 0) {
397 		ERROR("FCONF: Failed to read 'reg' property of 'pci' node\n");
398 		return err;
399 	}
400 
401 	parent = fdt_parent_offset(hw_config_dtb, node);
402 	if (parent < 0) {
403 		return -FDT_ERR_BADOFFSET;
404 	}
405 
406 	parent_ac = fdt_address_cells(hw_config_dtb, parent);
407 	ac = fdt_address_cells(hw_config_dtb, node);
408 	sc = fdt_size_cells(hw_config_dtb, node);
409 
410 	entry_len = parent_ac + ac + sc;
411 
412 	ranges = fdt_getprop(hw_config_dtb, node, "ranges", &len);
413 	if (ranges == NULL) {
414 		ERROR("FCONF failed to read 'ranges' property\n");
415 		return len;
416 	}
417 
418 	/* 'ranges' length in 32-bit words */
419 	len /= sizeof(uint32_t);
420 	if ((len % entry_len) != 0) {
421 		return -FDT_ERR_BADVALUE;
422 	}
423 
424 	pci_props.num_ncoh_regions = (uint64_t)(len / entry_len);
425 
426 	if (pci_props.num_ncoh_regions > ARM_PCI_NUM_REGIONS) {
427 		WARN("FCONF: 'ranges' reports more memory regions than supported\n");
428 		pci_props.num_ncoh_regions = ARM_PCI_NUM_REGIONS;
429 	}
430 
431 	for (unsigned int i = 0U; i < (unsigned int)pci_props.num_ncoh_regions; i++) {
432 		unsigned int cell = i * entry_len + ac;
433 
434 		/* Read CPU address (parent-bus-address) space */
435 		pci_props.ncoh_regions[i].base =
436 			fdt_read_prop_cells(&ranges[cell], ac);
437 
438 		/* Read CPU address size */
439 		pci_props.ncoh_regions[i].size =
440 			fdt_read_prop_cells(&ranges[cell + parent_ac], sc);
441 	}
442 
443 	return 0;
444 }
445 
446 FCONF_REGISTER_POPULATOR(HW_CONFIG, gicv3_config, fconf_populate_gicv3_config);
447 FCONF_REGISTER_POPULATOR(HW_CONFIG, topology, fconf_populate_topology);
448 FCONF_REGISTER_POPULATOR(HW_CONFIG, uart_config, fconf_populate_uart_config);
449 FCONF_REGISTER_POPULATOR(HW_CONFIG, cpu_timer, fconf_populate_cpu_timer);
450 FCONF_REGISTER_POPULATOR(HW_CONFIG, dram_layout, fconf_populate_dram_layout);
451 FCONF_REGISTER_POPULATOR(HW_CONFIG, pci_props, fconf_populate_pci_props);
452