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