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