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