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 static const uint8_t plat_power_domain_tree_desc[] = {
27 /* Number of root nodes */
28 1,
29 /* Number of clusters */
30 PLATFORM_CLUSTER_COUNT,
31 /* Number of children for the first cluster node */
32 PLATFORM_CORE_COUNT_PER_CLUSTER,
33 /* Number of children for the second cluster node */
34 PLATFORM_CORE_COUNT_PER_CLUSTER,
35 /* Number of children for the third cluster node */
36 PLATFORM_CORE_COUNT_PER_CLUSTER,
37 /* Number of children for the fourth cluster node */
38 PLATFORM_CORE_COUNT_PER_CLUSTER,
39 };
40
41 /* cluster and core updated at runtime */
42 static uint8_t plat_power_domain_tree_dyn[PLATFORM_CLUSTER_COUNT + PLAT_PWR_DOMAIN_PREFIX_SIZE] = {0};
43
44 /*
45 * Parse DT /cpus and cpu-map to determine cluster/core topology.
46 * Returns 0 on success, <0 on error.
47 */
parse_dt_topology(uint8_t * out_clusters,uint8_t * out_cores_per_cluster,uint8_t * out_children_per_cluster,uint32_t max_clusters)48 static int32_t parse_dt_topology(uint8_t *out_clusters, uint8_t *out_cores_per_cluster,
49 uint8_t *out_children_per_cluster, uint32_t max_clusters)
50 {
51 void *dtb = (void *)plat_retrieve_dt_addr();
52 int32_t cpus_off, cmap_off, cluster_off;
53 int32_t ret = 0;
54 int32_t core_off;
55 uint8_t clusters = 0, cores = 0;
56 const char *node_name;
57 uint8_t i;
58
59 if (dtb == NULL) {
60 ret = -FDT_ERR_NOTFOUND;
61 goto exit_on_failure;
62 }
63
64 ret = add_mmap_dynamic_region((unsigned long long)dtb,
65 (uintptr_t)dtb,
66 XILINX_OF_BOARD_DTB_MAX_SIZE,
67 MT_MEMORY | MT_RO | MT_NS);
68 if (ret != 0) {
69 goto exit_on_failure;
70 }
71
72 ret = fdt_check_header(dtb);
73 if (ret < 0) {
74 goto unmap_dtb;
75 }
76
77 /* cpus node in DT */
78 cpus_off = fdt_path_offset(dtb, "/cpus");
79 if (cpus_off < 0) {
80 ret = cpus_off;
81 goto unmap_dtb;
82 }
83
84 /* cpu-map dt path /cpus/cpu-map */
85 cmap_off = fdt_subnode_offset(dtb, cpus_off, "cpu-map");
86 if (cmap_off < 0) {
87 ret = cmap_off;
88 goto unmap_dtb;
89 }
90
91 /* Count clusters under cpu-map */
92 fdt_for_each_subnode(cluster_off, dtb, cmap_off) {
93 /* Validate cluster node name */
94 node_name = fdt_get_name(dtb, cluster_off, NULL);
95 if ((node_name == NULL) || (strncmp(node_name, "cluster", 7) != 0)) {
96 continue;
97 }
98
99 if (clusters >= max_clusters) {
100 WARN("cluster count exceeds max clusters: %u\n", max_clusters);
101 break;
102 }
103
104 /* Count cores in this cluster */
105 fdt_for_each_subnode(core_off, dtb, cluster_off) {
106 /* Validate core node name */
107 node_name = fdt_get_name(dtb, core_off, NULL);
108 if ((node_name == NULL) || (strncmp(node_name, "core", 4) != 0)) {
109 continue;
110 }
111 cores++;
112 }
113
114 /* If this cluster has no cores return error */
115 if (cores == 0) {
116 ret = E_INVALID_CORE_COUNT;
117 goto unmap_dtb;
118 }
119
120 out_children_per_cluster[clusters] = cores;
121 clusters++;
122 /* reset the core count for next cluster */
123 cores = 0;
124 }
125
126 if (clusters == 0) {
127 ret = -FDT_ERR_NOTFOUND;
128 goto unmap_dtb;
129 }
130
131 /* Check if all clusters have the same number of cores */
132 for (i = 1; i < clusters; i++) {
133 if (out_children_per_cluster[i] != out_children_per_cluster[0]) {
134 ret = E_INVALID_CORE_COUNT;
135 WARN("Asymmetric cluster: cluster %u has %u cores, cluster0 has %u cores\n",
136 i, out_children_per_cluster[i],
137 out_children_per_cluster[0]);
138 goto unmap_dtb;
139 }
140 }
141
142 *out_clusters = clusters;
143 *out_cores_per_cluster = out_children_per_cluster[0];
144
145 unmap_dtb:
146 (void)remove_mmap_dynamic_region((uintptr_t)dtb,
147 XILINX_OF_BOARD_DTB_MAX_SIZE);
148
149 exit_on_failure:
150 return ret;
151 }
152
init_topology_from_dt(void)153 static int32_t init_topology_from_dt(void)
154 {
155 static bool topology_initialized;
156 static int32_t cached_result = E_INVALID_CORE_COUNT;
157 uint8_t clusters = PLATFORM_CLUSTER_COUNT;
158 uint8_t cores_per_cluster = PLATFORM_CORE_COUNT_PER_CLUSTER;
159 uint8_t children[PLATFORM_CLUSTER_COUNT] = {0};
160 int32_t ret = 0;
161 uint8_t i;
162
163 if (topology_initialized) {
164 ret = cached_result;
165 goto done;
166 }
167
168 ret = parse_dt_topology(&clusters, &cores_per_cluster, children, PLATFORM_CLUSTER_COUNT);
169 if (ret != 0) {
170 goto exit_on_failure;
171 }
172
173 if ((clusters * cores_per_cluster) > PLATFORM_CORE_COUNT) {
174 ERROR("cluster * core count exceeds max cores %u\n", PLATFORM_CORE_COUNT);
175 ret = E_INVALID_CORE_COUNT;
176 goto exit_on_failure;
177 }
178
179 plat_cluster_count = clusters;
180 plat_cores_per_cluster = cores_per_cluster;
181
182 plat_power_domain_tree_dyn[0] = 1;
183 plat_power_domain_tree_dyn[1] = clusters;
184
185 for (i = 0; i < clusters; i++) {
186 plat_power_domain_tree_dyn[PLAT_PWR_DOMAIN_PREFIX_SIZE + i] = children[i];
187 }
188
189 exit_on_failure:
190 topology_initialized = true;
191 cached_result = ret;
192 done:
193 return ret;
194 }
195
plat_get_power_domain_tree_desc(void)196 const uint8_t *plat_get_power_domain_tree_desc(void)
197 {
198 const uint8_t *ret = NULL;
199
200 if (init_topology_from_dt() == 0) {
201 ret = plat_power_domain_tree_dyn;
202 } else {
203 ret = plat_power_domain_tree_desc;
204 }
205
206 return ret;
207 }
208