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