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