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