1 /*
2 * Copyright (c) 2018, Arm Limited and Contributors. All rights reserved.
3 * Copyright (c) 2018-2022, Xilinx, Inc. All rights reserved.
4 * Copyright (c) 2022-2026, Advanced Micro Devices, Inc. All rights reserved.
5 *
6 * SPDX-License-Identifier: BSD-3-Clause
7 */
8
9 #include <libfdt.h>
10
11 #include <common/debug.h>
12 #include <plat/common/platform.h>
13
14 #include <plat_fdt.h>
15 #include <platform_def.h>
16
17 #include <plat_private.h>
18
19 /* Global definition of primary core variable */
20 uint32_t plat_primary_cpu_core = PLAT_INVALID_CPU_CORE;
21
22 /* Global definition of core and cluster variables defaulting to fixed configuration */
23 uint32_t plat_cluster_count = PLATFORM_CLUSTER_COUNT;
24 uint32_t plat_cores_per_cluster = PLATFORM_CORE_COUNT_PER_CLUSTER;
25
26 #if VERSAL2_VARIANT == 14
27 static const uint8_t plat_power_domain_tree_desc[] = {
28 /* Number of root nodes */
29 1,
30 /* Number of clusters */
31 PLATFORM_CLUSTER_COUNT,
32 /* Number of children for the only cluster node */
33 PLATFORM_CORE_COUNT_PER_CLUSTER,
34 };
35 #elif VERSAL2_VARIANT == 42
36 static const uint8_t plat_power_domain_tree_desc[] = {
37 /* Number of root nodes */
38 1,
39 /* Number of clusters */
40 PLATFORM_CLUSTER_COUNT,
41 /* Number of children for the first cluster node */
42 PLATFORM_CORE_COUNT_PER_CLUSTER,
43 /* Number of children for the second cluster node */
44 PLATFORM_CORE_COUNT_PER_CLUSTER,
45 /* Number of children for the third cluster node */
46 PLATFORM_CORE_COUNT_PER_CLUSTER,
47 /* Number of children for the fourth cluster node */
48 PLATFORM_CORE_COUNT_PER_CLUSTER,
49 };
50 #endif
51
52 #if VERSAL2_VARIANT == 42
53 /* cluster and core updated at runtime */
54 static uint8_t plat_power_domain_tree_dyn[PLATFORM_CLUSTER_COUNT + PLAT_PWR_DOMAIN_PREFIX_SIZE] = {0};
55
56 /*
57 * Parse DT /cpus and cpu-map to determine cluster/core topology.
58 * Returns 0 on success, <0 on error.
59 */
parse_dt_topology(uint8_t * out_clusters,uint8_t * out_cores_per_cluster,uint8_t * out_children_per_cluster,uint32_t max_clusters)60 static int32_t parse_dt_topology(uint8_t *out_clusters, uint8_t *out_cores_per_cluster,
61 uint8_t *out_children_per_cluster, uint32_t max_clusters)
62 {
63 void *dtb = (void *)plat_retrieve_dt_addr();
64 int32_t cpus_off, cmap_off, cluster_off;
65 int32_t ret = 0;
66 int32_t core_off;
67 uint8_t clusters = 0, cores = 0;
68 const char *node_name;
69 uint8_t i;
70
71 if (dtb == NULL) {
72 ret = -FDT_ERR_NOTFOUND;
73 goto exit_on_failure;
74 }
75
76 ret = add_mmap_dynamic_region((unsigned long long)dtb,
77 (uintptr_t)dtb,
78 XILINX_OF_BOARD_DTB_MAX_SIZE,
79 MT_MEMORY | MT_RO | MT_NS);
80 if (ret != 0) {
81 goto exit_on_failure;
82 }
83
84 ret = fdt_check_header(dtb);
85 if (ret < 0) {
86 goto unmap_dtb;
87 }
88
89 /* cpus node in DT */
90 cpus_off = fdt_path_offset(dtb, "/cpus");
91 if (cpus_off < 0) {
92 ret = cpus_off;
93 goto unmap_dtb;
94 }
95
96 /* cpu-map dt path /cpus/cpu-map */
97 cmap_off = fdt_subnode_offset(dtb, cpus_off, "cpu-map");
98 if (cmap_off < 0) {
99 ret = cmap_off;
100 goto unmap_dtb;
101 }
102
103 /* Count clusters under cpu-map */
104 fdt_for_each_subnode(cluster_off, dtb, cmap_off) {
105 /* Validate cluster node name */
106 node_name = fdt_get_name(dtb, cluster_off, NULL);
107 if ((node_name == NULL) || (strncmp(node_name, "cluster", 7) != 0)) {
108 continue;
109 }
110
111 if (clusters >= max_clusters) {
112 WARN("cluster count exceeds max clusters: %u\n", max_clusters);
113 break;
114 }
115
116 /* Count cores in this cluster */
117 fdt_for_each_subnode(core_off, dtb, cluster_off) {
118 /* Validate core node name */
119 node_name = fdt_get_name(dtb, core_off, NULL);
120 if ((node_name == NULL) || (strncmp(node_name, "core", 4) != 0)) {
121 continue;
122 }
123 cores++;
124 }
125
126 /* If this cluster has no cores return error */
127 if (cores == 0) {
128 ret = E_INVALID_CORE_COUNT;
129 goto unmap_dtb;
130 }
131
132 out_children_per_cluster[clusters] = cores;
133 clusters++;
134 /* reset the core count for next cluster */
135 cores = 0;
136 }
137
138 if (clusters == 0) {
139 ret = -FDT_ERR_NOTFOUND;
140 goto unmap_dtb;
141 }
142
143 /* Check if all clusters have the same number of cores */
144 for (i = 1; i < clusters; i++) {
145 if (out_children_per_cluster[i] != out_children_per_cluster[0]) {
146 ret = E_INVALID_CORE_COUNT;
147 WARN("Asymmetric cluster: cluster %u has %u cores, cluster0 has %u cores\n",
148 i, out_children_per_cluster[i],
149 out_children_per_cluster[0]);
150 goto unmap_dtb;
151 }
152 }
153
154 *out_clusters = clusters;
155 *out_cores_per_cluster = out_children_per_cluster[0];
156
157 unmap_dtb:
158 (void)remove_mmap_dynamic_region((uintptr_t)dtb,
159 XILINX_OF_BOARD_DTB_MAX_SIZE);
160
161 exit_on_failure:
162 return ret;
163 }
164
init_topology_from_dt(void)165 static int32_t init_topology_from_dt(void)
166 {
167 static bool topology_initialized;
168 static int32_t cached_result = E_INVALID_CORE_COUNT;
169 uint8_t clusters = PLATFORM_CLUSTER_COUNT;
170 uint8_t cores_per_cluster = PLATFORM_CORE_COUNT_PER_CLUSTER;
171 uint8_t children[PLATFORM_CLUSTER_COUNT] = {0};
172 int32_t ret = 0;
173 uint8_t i;
174
175 if (topology_initialized) {
176 ret = cached_result;
177 goto done;
178 }
179
180 ret = parse_dt_topology(&clusters, &cores_per_cluster, children, PLATFORM_CLUSTER_COUNT);
181 if (ret != 0) {
182 goto exit_on_failure;
183 }
184
185 if ((clusters * cores_per_cluster) > PLATFORM_CORE_COUNT) {
186 ERROR("cluster * core count exceeds max cores %u\n", PLATFORM_CORE_COUNT);
187 ret = E_INVALID_CORE_COUNT;
188 goto exit_on_failure;
189 }
190
191 plat_cluster_count = clusters;
192 plat_cores_per_cluster = cores_per_cluster;
193
194 plat_power_domain_tree_dyn[0] = 1;
195 plat_power_domain_tree_dyn[1] = clusters;
196
197 for (i = 0; i < clusters; i++) {
198 plat_power_domain_tree_dyn[PLAT_PWR_DOMAIN_PREFIX_SIZE + i] = children[i];
199 }
200
201 exit_on_failure:
202 topology_initialized = true;
203 cached_result = ret;
204 done:
205 return ret;
206 }
207 #endif
208
plat_get_power_domain_tree_desc(void)209 const uint8_t *plat_get_power_domain_tree_desc(void)
210 {
211 const uint8_t *ret = NULL;
212
213 #if VERSAL2_VARIANT == 14
214 ret = plat_power_domain_tree_desc;
215 #elif VERSAL2_VARIANT == 42
216 if (init_topology_from_dt() == 0) {
217 ret = plat_power_domain_tree_dyn;
218 } else {
219 ret = plat_power_domain_tree_desc;
220 }
221 #endif
222
223 return ret;
224 }
225