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