1 /* 2 * Copyright (c) 2020-2023, 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 ns_dram_layout dram_layout; 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 31 #define ILLEGAL_ADDR ULL(~0) 32 33 int fconf_populate_gicv3_config(uintptr_t config) 34 { 35 int err; 36 int node; 37 uintptr_t addr; 38 39 /* Necessary to work with libfdt APIs */ 40 const void *hw_config_dtb = (const void *)config; 41 42 /* 43 * Find the offset of the node containing "arm,gic-v3" compatible property. 44 * Populating fconf strucutures dynamically is not supported for legacy 45 * systems which use GICv2 IP. Simply skip extracting GIC properties. 46 */ 47 node = fdt_node_offset_by_compatible(hw_config_dtb, -1, "arm,gic-v3"); 48 if (node < 0) { 49 WARN("FCONF: Unable to locate node with arm,gic-v3 compatible property\n"); 50 return 0; 51 } 52 /* The GICv3 DT binding holds at least two address/size pairs, 53 * the first describing the distributor, the second the redistributors. 54 * See: bindings/interrupt-controller/arm,gic-v3.yaml 55 */ 56 err = fdt_get_reg_props_by_index(hw_config_dtb, node, 0, &addr, NULL); 57 if (err < 0) { 58 ERROR("FCONF: Failed to read GICD reg property of GIC node\n"); 59 return err; 60 } 61 gicv3_config.gicd_base = addr; 62 63 err = fdt_get_reg_props_by_index(hw_config_dtb, node, 1, &addr, NULL); 64 if (err < 0) { 65 ERROR("FCONF: Failed to read GICR reg property of GIC node\n"); 66 } else { 67 gicv3_config.gicr_base = addr; 68 } 69 70 return err; 71 } 72 73 int fconf_populate_topology(uintptr_t config) 74 { 75 int err, node, cluster_node, core_node, thread_node; 76 uint32_t cluster_count = 0, max_cpu_per_cluster = 0, total_cpu_count = 0; 77 uint32_t max_pwr_lvl = 0; 78 79 /* Necessary to work with libfdt APIs */ 80 const void *hw_config_dtb = (const void *)config; 81 82 /* Find the offset of the node containing "arm,psci-1.0" compatible property */ 83 node = fdt_node_offset_by_compatible(hw_config_dtb, -1, "arm,psci-1.0"); 84 if (node < 0) { 85 ERROR("FCONF: Unable to locate node with arm,psci-1.0 compatible property\n"); 86 return node; 87 } 88 89 err = fdt_read_uint32(hw_config_dtb, node, "max-pwr-lvl", &max_pwr_lvl); 90 if (err < 0) { 91 /* 92 * Some legacy FVP dts may not have this property. Assign the default 93 * value. 94 */ 95 WARN("FCONF: Could not locate max-pwr-lvl property\n"); 96 max_pwr_lvl = 2; 97 } 98 99 assert(max_pwr_lvl <= MPIDR_AFFLVL2); 100 101 /* Find the offset of the "cpus" node */ 102 node = fdt_path_offset(hw_config_dtb, "/cpus"); 103 if (node < 0) { 104 ERROR("FCONF: Node '%s' not found in hardware configuration dtb\n", "cpus"); 105 return node; 106 } 107 108 /* A typical cpu-map node in a device tree is shown here for reference 109 cpu-map { 110 cluster0 { 111 core0 { 112 cpu = <&CPU0>; 113 }; 114 core1 { 115 cpu = <&CPU1>; 116 }; 117 }; 118 119 cluster1 { 120 core0 { 121 cpu = <&CPU2>; 122 }; 123 core1 { 124 cpu = <&CPU3>; 125 }; 126 }; 127 }; 128 */ 129 130 /* Locate the cpu-map child node */ 131 node = fdt_subnode_offset(hw_config_dtb, node, "cpu-map"); 132 if (node < 0) { 133 ERROR("FCONF: Node '%s' not found in hardware configuration dtb\n", "cpu-map"); 134 return node; 135 } 136 137 uint32_t cpus_per_cluster[PLAT_ARM_CLUSTER_COUNT] = {0}; 138 139 /* Iterate through cluster nodes */ 140 fdt_for_each_subnode(cluster_node, hw_config_dtb, node) { 141 assert(cluster_count < PLAT_ARM_CLUSTER_COUNT); 142 143 /* Iterate through core nodes */ 144 fdt_for_each_subnode(core_node, hw_config_dtb, cluster_node) { 145 /* core nodes may have child nodes i.e., "thread" nodes */ 146 if (fdt_first_subnode(hw_config_dtb, core_node) < 0) { 147 cpus_per_cluster[cluster_count]++; 148 } else { 149 /* Multi-threaded CPU description is found in dtb */ 150 fdt_for_each_subnode(thread_node, hw_config_dtb, core_node) { 151 cpus_per_cluster[cluster_count]++; 152 } 153 154 /* Since in some dtbs, core nodes may not have thread node, 155 * no need to error if even one child node is not found. 156 */ 157 } 158 } 159 160 /* Ensure every cluster node has at least 1 child node */ 161 if (cpus_per_cluster[cluster_count] < 1U) { 162 ERROR("FCONF: Unable to locate the core node in cluster %d\n", cluster_count); 163 return -1; 164 } 165 166 VERBOSE("CLUSTER ID: %d cpu-count: %d\n", cluster_count, 167 cpus_per_cluster[cluster_count]); 168 169 /* Find the maximum number of cpus in any cluster */ 170 max_cpu_per_cluster = MAX(max_cpu_per_cluster, cpus_per_cluster[cluster_count]); 171 total_cpu_count += cpus_per_cluster[cluster_count]; 172 cluster_count++; 173 } 174 175 176 /* At least one cluster node is expected in hardware configuration dtb */ 177 if (cluster_count < 1U) { 178 ERROR("FCONF: Unable to locate the cluster node in cpu-map node\n"); 179 return -1; 180 } 181 182 soc_topology.plat_max_pwr_level = max_pwr_lvl; 183 soc_topology.plat_cluster_count = cluster_count; 184 soc_topology.cluster_cpu_count = max_cpu_per_cluster; 185 soc_topology.plat_cpu_count = total_cpu_count; 186 187 return 0; 188 } 189 190 int fconf_populate_uart_config(uintptr_t config) 191 { 192 int uart_node, node, err; 193 uintptr_t addr; 194 const char *path; 195 uint32_t phandle; 196 uint64_t translated_addr; 197 198 /* Necessary to work with libfdt APIs */ 199 const void *hw_config_dtb = (const void *)config; 200 201 /* 202 * uart child node is indirectly referenced through its path which is 203 * specified in the `serial1` property of the "aliases" node. 204 * Note that TF-A boot console is mapped to serial0 while runtime 205 * console is mapped to serial1. 206 */ 207 208 path = fdt_get_alias(hw_config_dtb, "serial1"); 209 if (path == NULL) { 210 ERROR("FCONF: Could not read serial1 property in aliases node\n"); 211 return -1; 212 } 213 214 /* Find the offset of the uart serial node */ 215 uart_node = fdt_path_offset(hw_config_dtb, path); 216 if (uart_node < 0) { 217 ERROR("FCONF: Failed to locate uart serial node using its path\n"); 218 return -1; 219 } 220 221 /* uart serial node has its offset and size of address in reg property */ 222 err = fdt_get_reg_props_by_index(hw_config_dtb, uart_node, 0, &addr, 223 NULL); 224 if (err < 0) { 225 ERROR("FCONF: Failed to read reg property of '%s' node\n", 226 "uart serial"); 227 return err; 228 } 229 VERBOSE("FCONF: UART node address: %lx\n", addr); 230 231 /* 232 * Perform address translation of local device address to CPU address 233 * domain. 234 */ 235 translated_addr = fdtw_translate_address(hw_config_dtb, 236 uart_node, (uint64_t)addr); 237 if (translated_addr == ILLEGAL_ADDR) { 238 ERROR("FCONF: failed to translate UART node base address"); 239 return -1; 240 } 241 242 uart_serial_config.uart_base = translated_addr; 243 244 VERBOSE("FCONF: UART serial device base address: %" PRIx64 "\n", 245 uart_serial_config.uart_base); 246 247 /* 248 * The phandle of the DT node which captures the clock info of uart 249 * serial node is specified in the "clocks" property. 250 */ 251 err = fdt_read_uint32(hw_config_dtb, uart_node, "clocks", &phandle); 252 if (err < 0) { 253 ERROR("FCONF: Could not read clocks property in uart serial node\n"); 254 return err; 255 } 256 257 node = fdt_node_offset_by_phandle(hw_config_dtb, phandle); 258 if (node < 0) { 259 ERROR("FCONF: Failed to locate clk node using its path\n"); 260 return node; 261 } 262 263 /* 264 * Retrieve clock frequency. We assume clock provider generates a fixed 265 * clock. 266 */ 267 err = fdt_read_uint32(hw_config_dtb, node, "clock-frequency", 268 &uart_serial_config.uart_clk); 269 if (err < 0) { 270 ERROR("FCONF: Could not read clock-frequency property in clk node\n"); 271 return err; 272 } 273 274 VERBOSE("FCONF: UART serial device clk frequency: %x\n", 275 uart_serial_config.uart_clk); 276 277 return 0; 278 } 279 280 int fconf_populate_cpu_timer(uintptr_t config) 281 { 282 int err, node; 283 284 /* Necessary to work with libfdt APIs */ 285 const void *hw_config_dtb = (const void *)config; 286 287 /* Find the node offset point to "arm,armv8-timer" compatible property, 288 * a per-core architected timer attached to a GIC to deliver its per-processor 289 * interrupts via PPIs */ 290 node = fdt_node_offset_by_compatible(hw_config_dtb, -1, "arm,armv8-timer"); 291 if (node < 0) { 292 ERROR("FCONF: Unrecognized hardware configuration dtb (%d)\n", node); 293 return node; 294 } 295 296 /* Locate the cell holding the clock-frequency, an optional field */ 297 err = fdt_read_uint32(hw_config_dtb, node, "clock-frequency", &cpu_timer.clock_freq); 298 if (err < 0) { 299 WARN("FCONF failed to read clock-frequency property\n"); 300 } 301 302 return 0; 303 } 304 305 int fconf_populate_dram_layout(uintptr_t config) 306 { 307 int node, len; 308 const uint32_t *reg; 309 310 /* Necessary to work with libfdt APIs */ 311 const void *hw_config_dtb = (const void *)config; 312 313 /* Find 'memory' node */ 314 node = fdt_node_offset_by_prop_value(hw_config_dtb, -1, "device_type", 315 "memory", sizeof("memory")); 316 if (node < 0) { 317 WARN("FCONF: Unable to locate 'memory' node\n"); 318 return node; 319 } 320 321 reg = fdt_getprop(hw_config_dtb, node, "reg", &len); 322 if (reg == NULL) { 323 ERROR("FCONF failed to read 'reg' property\n"); 324 return len; 325 } 326 327 switch (len) { 328 case DRAM_ENTRY_SIZE: 329 /* 1 DRAM bank */ 330 dram_layout.num_banks = 1UL; 331 break; 332 case 2UL * DRAM_ENTRY_SIZE: 333 /* 2 DRAM banks */ 334 dram_layout.num_banks = 2UL; 335 break; 336 default: 337 ERROR("FCONF: Invalid 'memory' node\n"); 338 return -FDT_ERR_BADLAYOUT; 339 } 340 341 for (unsigned long i = 0UL; i < dram_layout.num_banks; i++) { 342 int err = fdt_get_reg_props_by_index( 343 hw_config_dtb, node, (int)i, 344 &dram_layout.dram_bank[i].base, 345 (size_t *)&dram_layout.dram_bank[i].size); 346 if (err < 0) { 347 ERROR("FCONF: Failed to read 'reg' property #%lu of 'memory' node\n", i); 348 return err; 349 } 350 } 351 352 return 0; 353 } 354 355 FCONF_REGISTER_POPULATOR(HW_CONFIG, gicv3_config, fconf_populate_gicv3_config); 356 FCONF_REGISTER_POPULATOR(HW_CONFIG, topology, fconf_populate_topology); 357 FCONF_REGISTER_POPULATOR(HW_CONFIG, uart_config, fconf_populate_uart_config); 358 FCONF_REGISTER_POPULATOR(HW_CONFIG, cpu_timer, fconf_populate_cpu_timer); 359 FCONF_REGISTER_POPULATOR(HW_CONFIG, dram_layout, fconf_populate_dram_layout); 360