fconf_hw_config_getter.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. * Copyright (c) 2020-2023, Arm Limited. All rights reserved.
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. #include <assert.h>
  7. #include <inttypes.h>
  8. #include <stdint.h>
  9. #include <common/debug.h>
  10. #include <common/fdt_wrappers.h>
  11. #include <fconf_hw_config_getter.h>
  12. #include <libfdt.h>
  13. #include <plat/common/platform.h>
  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. struct ns_dram_layout dram_layout;
  19. /*
  20. * Each NS DRAM bank entry is 'reg' node property which is
  21. * a sequence of (address, length) pairs of 32-bit values.
  22. */
  23. #define DRAM_ENTRY_SIZE (4UL * sizeof(uint32_t))
  24. CASSERT(ARM_DRAM_NUM_BANKS == 2UL, ARM_DRAM_NUM_BANKS_mismatch);
  25. #define ILLEGAL_ADDR ULL(~0)
  26. int fconf_populate_gicv3_config(uintptr_t config)
  27. {
  28. int err;
  29. int node;
  30. uintptr_t addr;
  31. /* Necessary to work with libfdt APIs */
  32. const void *hw_config_dtb = (const void *)config;
  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. err = fdt_get_reg_props_by_index(hw_config_dtb, node, 1, &addr, NULL);
  54. if (err < 0) {
  55. ERROR("FCONF: Failed to read GICR reg property of GIC node\n");
  56. } else {
  57. gicv3_config.gicr_base = addr;
  58. }
  59. return err;
  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. /* Necessary to work with libfdt APIs */
  67. const void *hw_config_dtb = (const void *)config;
  68. /* Find the offset of the node containing "arm,psci-1.0" compatible property */
  69. node = fdt_node_offset_by_compatible(hw_config_dtb, -1, "arm,psci-1.0");
  70. if (node < 0) {
  71. ERROR("FCONF: Unable to locate node with arm,psci-1.0 compatible property\n");
  72. return node;
  73. }
  74. err = fdt_read_uint32(hw_config_dtb, node, "max-pwr-lvl", &max_pwr_lvl);
  75. if (err < 0) {
  76. /*
  77. * Some legacy FVP dts may not have this property. Assign the default
  78. * value.
  79. */
  80. WARN("FCONF: Could not locate max-pwr-lvl property\n");
  81. max_pwr_lvl = 2;
  82. }
  83. assert(max_pwr_lvl <= MPIDR_AFFLVL2);
  84. /* Find the offset of the "cpus" node */
  85. node = fdt_path_offset(hw_config_dtb, "/cpus");
  86. if (node < 0) {
  87. ERROR("FCONF: Node '%s' not found in hardware configuration dtb\n", "cpus");
  88. return node;
  89. }
  90. /* A typical cpu-map node in a device tree is shown here for reference
  91. cpu-map {
  92. cluster0 {
  93. core0 {
  94. cpu = <&CPU0>;
  95. };
  96. core1 {
  97. cpu = <&CPU1>;
  98. };
  99. };
  100. cluster1 {
  101. core0 {
  102. cpu = <&CPU2>;
  103. };
  104. core1 {
  105. cpu = <&CPU3>;
  106. };
  107. };
  108. };
  109. */
  110. /* Locate the cpu-map child node */
  111. node = fdt_subnode_offset(hw_config_dtb, node, "cpu-map");
  112. if (node < 0) {
  113. ERROR("FCONF: Node '%s' not found in hardware configuration dtb\n", "cpu-map");
  114. return node;
  115. }
  116. uint32_t cpus_per_cluster[PLAT_ARM_CLUSTER_COUNT] = {0};
  117. /* Iterate through cluster nodes */
  118. fdt_for_each_subnode(cluster_node, hw_config_dtb, node) {
  119. assert(cluster_count < PLAT_ARM_CLUSTER_COUNT);
  120. /* Iterate through core nodes */
  121. fdt_for_each_subnode(core_node, hw_config_dtb, cluster_node) {
  122. /* core nodes may have child nodes i.e., "thread" nodes */
  123. if (fdt_first_subnode(hw_config_dtb, core_node) < 0) {
  124. cpus_per_cluster[cluster_count]++;
  125. } else {
  126. /* Multi-threaded CPU description is found in dtb */
  127. fdt_for_each_subnode(thread_node, hw_config_dtb, core_node) {
  128. cpus_per_cluster[cluster_count]++;
  129. }
  130. /* Since in some dtbs, core nodes may not have thread node,
  131. * no need to error if even one child node is not found.
  132. */
  133. }
  134. }
  135. /* Ensure every cluster node has at least 1 child node */
  136. if (cpus_per_cluster[cluster_count] < 1U) {
  137. ERROR("FCONF: Unable to locate the core node in cluster %d\n", cluster_count);
  138. return -1;
  139. }
  140. VERBOSE("CLUSTER ID: %d cpu-count: %d\n", cluster_count,
  141. cpus_per_cluster[cluster_count]);
  142. /* Find the maximum number of cpus in any cluster */
  143. max_cpu_per_cluster = MAX(max_cpu_per_cluster, cpus_per_cluster[cluster_count]);
  144. total_cpu_count += cpus_per_cluster[cluster_count];
  145. cluster_count++;
  146. }
  147. /* At least one cluster node is expected in hardware configuration dtb */
  148. if (cluster_count < 1U) {
  149. ERROR("FCONF: Unable to locate the cluster node in cpu-map node\n");
  150. return -1;
  151. }
  152. soc_topology.plat_max_pwr_level = max_pwr_lvl;
  153. soc_topology.plat_cluster_count = cluster_count;
  154. soc_topology.cluster_cpu_count = max_cpu_per_cluster;
  155. soc_topology.plat_cpu_count = total_cpu_count;
  156. return 0;
  157. }
  158. int fconf_populate_uart_config(uintptr_t config)
  159. {
  160. int uart_node, node, err;
  161. uintptr_t addr;
  162. const char *path;
  163. uint32_t phandle;
  164. uint64_t translated_addr;
  165. /* Necessary to work with libfdt APIs */
  166. const void *hw_config_dtb = (const void *)config;
  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. path = fdt_get_alias(hw_config_dtb, "serial1");
  174. if (path == NULL) {
  175. ERROR("FCONF: Could not read serial1 property in aliases node\n");
  176. return -1;
  177. }
  178. /* Find the offset of the uart serial node */
  179. uart_node = fdt_path_offset(hw_config_dtb, path);
  180. if (uart_node < 0) {
  181. ERROR("FCONF: Failed to locate uart serial node using its path\n");
  182. return -1;
  183. }
  184. /* uart serial node has its offset and size of address in reg property */
  185. err = fdt_get_reg_props_by_index(hw_config_dtb, uart_node, 0, &addr,
  186. NULL);
  187. if (err < 0) {
  188. ERROR("FCONF: Failed to read reg property of '%s' node\n",
  189. "uart serial");
  190. return err;
  191. }
  192. VERBOSE("FCONF: UART node address: %lx\n", addr);
  193. /*
  194. * Perform address translation of local device address to CPU address
  195. * domain.
  196. */
  197. translated_addr = fdtw_translate_address(hw_config_dtb,
  198. uart_node, (uint64_t)addr);
  199. if (translated_addr == ILLEGAL_ADDR) {
  200. ERROR("FCONF: failed to translate UART node base address");
  201. return -1;
  202. }
  203. uart_serial_config.uart_base = translated_addr;
  204. VERBOSE("FCONF: UART serial device base address: %" PRIx64 "\n",
  205. uart_serial_config.uart_base);
  206. /*
  207. * The phandle of the DT node which captures the clock info of uart
  208. * serial node is specified in the "clocks" property.
  209. */
  210. err = fdt_read_uint32(hw_config_dtb, uart_node, "clocks", &phandle);
  211. if (err < 0) {
  212. ERROR("FCONF: Could not read clocks property in uart serial node\n");
  213. return err;
  214. }
  215. node = fdt_node_offset_by_phandle(hw_config_dtb, phandle);
  216. if (node < 0) {
  217. ERROR("FCONF: Failed to locate clk node using its path\n");
  218. return node;
  219. }
  220. /*
  221. * Retrieve clock frequency. We assume clock provider generates a fixed
  222. * clock.
  223. */
  224. err = fdt_read_uint32(hw_config_dtb, node, "clock-frequency",
  225. &uart_serial_config.uart_clk);
  226. if (err < 0) {
  227. ERROR("FCONF: Could not read clock-frequency property in clk node\n");
  228. return err;
  229. }
  230. VERBOSE("FCONF: UART serial device clk frequency: %x\n",
  231. uart_serial_config.uart_clk);
  232. return 0;
  233. }
  234. int fconf_populate_cpu_timer(uintptr_t config)
  235. {
  236. int err, node;
  237. /* Necessary to work with libfdt APIs */
  238. const void *hw_config_dtb = (const void *)config;
  239. /* Find the node offset point to "arm,armv8-timer" compatible property,
  240. * a per-core architected timer attached to a GIC to deliver its per-processor
  241. * interrupts via PPIs */
  242. node = fdt_node_offset_by_compatible(hw_config_dtb, -1, "arm,armv8-timer");
  243. if (node < 0) {
  244. ERROR("FCONF: Unrecognized hardware configuration dtb (%d)\n", node);
  245. return node;
  246. }
  247. /* Locate the cell holding the clock-frequency, an optional field */
  248. err = fdt_read_uint32(hw_config_dtb, node, "clock-frequency", &cpu_timer.clock_freq);
  249. if (err < 0) {
  250. WARN("FCONF failed to read clock-frequency property\n");
  251. }
  252. return 0;
  253. }
  254. int fconf_populate_dram_layout(uintptr_t config)
  255. {
  256. int node, len;
  257. const uint32_t *reg;
  258. /* Necessary to work with libfdt APIs */
  259. const void *hw_config_dtb = (const void *)config;
  260. /* Find 'memory' node */
  261. node = fdt_node_offset_by_prop_value(hw_config_dtb, -1, "device_type",
  262. "memory", sizeof("memory"));
  263. if (node < 0) {
  264. WARN("FCONF: Unable to locate 'memory' node\n");
  265. return node;
  266. }
  267. reg = fdt_getprop(hw_config_dtb, node, "reg", &len);
  268. if (reg == NULL) {
  269. ERROR("FCONF failed to read 'reg' property\n");
  270. return len;
  271. }
  272. switch (len) {
  273. case DRAM_ENTRY_SIZE:
  274. /* 1 DRAM bank */
  275. dram_layout.num_banks = 1UL;
  276. break;
  277. case 2UL * DRAM_ENTRY_SIZE:
  278. /* 2 DRAM banks */
  279. dram_layout.num_banks = 2UL;
  280. break;
  281. default:
  282. ERROR("FCONF: Invalid 'memory' node\n");
  283. return -FDT_ERR_BADLAYOUT;
  284. }
  285. for (unsigned long i = 0UL; i < dram_layout.num_banks; i++) {
  286. int err = fdt_get_reg_props_by_index(
  287. hw_config_dtb, node, (int)i,
  288. &dram_layout.dram_bank[i].base,
  289. (size_t *)&dram_layout.dram_bank[i].size);
  290. if (err < 0) {
  291. ERROR("FCONF: Failed to read 'reg' property #%lu of 'memory' node\n", i);
  292. return err;
  293. }
  294. }
  295. return 0;
  296. }
  297. FCONF_REGISTER_POPULATOR(HW_CONFIG, gicv3_config, fconf_populate_gicv3_config);
  298. FCONF_REGISTER_POPULATOR(HW_CONFIG, topology, fconf_populate_topology);
  299. FCONF_REGISTER_POPULATOR(HW_CONFIG, uart_config, fconf_populate_uart_config);
  300. FCONF_REGISTER_POPULATOR(HW_CONFIG, cpu_timer, fconf_populate_cpu_timer);
  301. FCONF_REGISTER_POPULATOR(HW_CONFIG, dram_layout, fconf_populate_dram_layout);