blob: 26d36478db26b7a247b0118f1cf30b285e0e4e57 [file] [log] [blame]
Mathieu Poirierecadac72024-10-17 16:39:51 -06001/*
2 * Copyright (c) 2024-2025, Linaro Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8
9#include <common/fdt_wrappers.h>
10#include <libfdt.h>
11
12#include <sbsa_platform.h>
13
14/* default platform version is 0.0 */
15static int platform_version_major;
16static int platform_version_minor;
17
18static uint64_t gic_its_addr;
19static struct qemu_platform_info dynamic_platform_info;
20
21void sbsa_set_gic_bases(const uintptr_t gicd_base, const uintptr_t gicr_base);
22
23/*
24 * QEMU provides us with minimal information about hardware platform using
25 * minimalistic DeviceTree. This is not a Linux DeviceTree. It is not even
26 * a firmware DeviceTree.
27 *
28 * It is information passed from QEMU to describe the information a hardware
29 * platform would have other mechanisms to discover at runtime, that are
30 * affected by the QEMU command line.
31 *
32 * Ultimately this device tree will be replaced by IPC calls to an emulated SCP.
33 * And when we do that, we won't then have to rewrite Normal world firmware to
34 * cope.
35 */
36
37static void read_cpu_topology_from_dt(void *dtb)
38{
39 int node;
40
41 /*
42 * QEMU gives us this DeviceTree node when we config:
43 * -smp 16,sockets=2,clusters=2,cores=2,threads=2
44 *
45 * topology {
46 * threads = <0x02>;
47 * cores = <0x02>;
48 * clusters = <0x02>;
49 * sockets = <0x02>;
50 * };
51 */
52
53 node = fdt_path_offset(dtb, "/cpus/topology");
54 if (node > 0) {
55 dynamic_platform_info.cpu_topo.sockets =
56 fdt_read_uint32_default(dtb, node, "sockets", 0);
57 dynamic_platform_info.cpu_topo.clusters =
58 fdt_read_uint32_default(dtb, node, "clusters", 0);
59 dynamic_platform_info.cpu_topo.cores =
60 fdt_read_uint32_default(dtb, node, "cores", 0);
61 dynamic_platform_info.cpu_topo.threads =
62 fdt_read_uint32_default(dtb, node, "threads", 0);
63 }
64
65 INFO("Cpu topology: sockets: %d, clusters: %d, cores: %d, threads: %d\n",
66 dynamic_platform_info.cpu_topo.sockets,
67 dynamic_platform_info.cpu_topo.clusters,
68 dynamic_platform_info.cpu_topo.cores,
69 dynamic_platform_info.cpu_topo.threads);
70}
71
72static void read_cpuinfo_from_dt(void *dtb)
73{
74 int node;
75 int prev;
76 int cpu = 0;
77 uintptr_t mpidr;
78
79 /*
80 * QEMU gives us this DeviceTree node:
81 * numa-node-id entries are only when NUMA config is used
82 *
83 * cpus {
84 * #size-cells = <0x00>;
85 * #address-cells = <0x02>;
86 *
87 * cpu@0 {
88 * numa-node-id = <0x00>;
89 * reg = <0x00 0x00>;
90 * };
91 *
92 * cpu@1 {
93 * numa-node-id = <0x03>;
94 * reg = <0x00 0x01>;
95 * };
96 * };
97 */
98 node = fdt_path_offset(dtb, "/cpus");
99 if (node < 0) {
100 ERROR("No information about cpus in DeviceTree.\n");
101 panic();
102 }
103
104 /*
105 * QEMU numbers cpus from 0 and there can be /cpus/cpu-map present so we
106 * cannot use fdt_first_subnode() here
107 */
108 node = fdt_path_offset(dtb, "/cpus/cpu@0");
109
110 while (node > 0) {
111 if (fdt_getprop(dtb, node, "reg", NULL)) {
112 fdt_get_reg_props_by_index(dtb, node, 0, &mpidr, NULL);
113 } else {
114 ERROR("Incomplete information for cpu %d in DeviceTree.\n", cpu);
115 panic();
116 }
117
118 dynamic_platform_info.cpu[cpu].mpidr = mpidr;
119 dynamic_platform_info.cpu[cpu].nodeid =
120 fdt_read_uint32_default(dtb, node, "numa-node-id", 0);
121
122 INFO("CPU %d: node-id: %d, mpidr: %ld\n", cpu,
123 dynamic_platform_info.cpu[cpu].nodeid, mpidr);
124
125 cpu++;
126
127 prev = node;
128 node = fdt_next_subnode(dtb, prev);
129 }
130
131 dynamic_platform_info.num_cpus = cpu;
132 INFO("Found %d cpus\n", dynamic_platform_info.num_cpus);
133
134 read_cpu_topology_from_dt(dtb);
135}
136
137static void read_meminfo_from_dt(void *dtb)
138{
139 const fdt32_t *prop;
140 const char *type;
141 int prev, node;
142 int len;
143 uint32_t memnode = 0;
144 uint32_t higher_value, lower_value;
145 uint64_t cur_base, cur_size;
146
147 /*
148 * QEMU gives us this DeviceTree node:
149 *
150 * memory@100c0000000 {
151 * numa-node-id = <0x01>;
152 * reg = <0x100 0xc0000000 0x00 0x40000000>;
153 * device_type = "memory";
154 * };
155 *
156 * memory@10000000000 {
157 * numa-node-id = <0x00>;
158 * reg = <0x100 0x00 0x00 0xc0000000>;
159 * device_type = "memory";
160 * }
161 */
162
163 for (prev = 0;; prev = node) {
164 node = fdt_next_node(dtb, prev, NULL);
165 if (node < 0) {
166 break;
167 }
168
169 type = fdt_getprop(dtb, node, "device_type", &len);
170 if (type && strncmp(type, "memory", len) == 0) {
171 dynamic_platform_info.memory[memnode].nodeid =
172 fdt_read_uint32_default(dtb, node, "numa-node-id", 0);
173
174 /*
175 * Get the 'reg' property of this node and
176 * assume two 8 bytes for base and size.
177 */
178 prop = fdt_getprop(dtb, node, "reg", &len);
179 if (prop != 0 && len == (2 * sizeof(int64_t))) {
180 higher_value = fdt32_to_cpu(*prop);
181 lower_value = fdt32_to_cpu(*(prop + 1));
182 cur_base = (uint64_t)(lower_value | ((uint64_t)higher_value) << 32);
183
184 higher_value = fdt32_to_cpu(*(prop + 2));
185 lower_value = fdt32_to_cpu(*(prop + 3));
186 cur_size = (uint64_t)(lower_value | ((uint64_t)higher_value) << 32);
187
188 dynamic_platform_info.memory[memnode].addr_base = cur_base;
189 dynamic_platform_info.memory[memnode].addr_size = cur_size;
190
191 INFO("RAM %d: node-id: %d, address: 0x%lx - 0x%lx\n",
192 memnode,
193 dynamic_platform_info.memory[memnode].nodeid,
194 dynamic_platform_info.memory[memnode].addr_base,
195 dynamic_platform_info.memory[memnode].addr_base +
196 dynamic_platform_info.memory[memnode].addr_size - 1);
197 }
198
199 memnode++;
200 }
201 }
202
203 dynamic_platform_info.num_memnodes = memnode;
204}
205
206static void read_platform_config_from_dt(void *dtb)
207{
208 int node;
209 const fdt64_t *data;
210 int err;
211 uintptr_t gicd_base;
212 uintptr_t gicr_base;
213
214 /*
215 * QEMU gives us this DeviceTree node:
216 *
217 * intc {
218 * reg = < 0x00 0x40060000 0x00 0x10000
219 * 0x00 0x40080000 0x00 0x4000000>;
220 * its {
221 * reg = <0x00 0x44081000 0x00 0x20000>;
222 * };
223 * };
224 */
225 node = fdt_path_offset(dtb, "/intc");
226 if (node < 0) {
227 return;
228 }
229
230 data = fdt_getprop(dtb, node, "reg", NULL);
231 if (data == NULL) {
232 return;
233 }
234
235 err = fdt_get_reg_props_by_index(dtb, node, 0, &gicd_base, NULL);
236 if (err < 0) {
237 ERROR("Failed to read GICD reg property of GIC node\n");
238 return;
239 }
240 INFO("GICD base = 0x%lx\n", gicd_base);
241
242 err = fdt_get_reg_props_by_index(dtb, node, 1, &gicr_base, NULL);
243 if (err < 0) {
244 ERROR("Failed to read GICR reg property of GIC node\n");
245 return;
246 }
247 INFO("GICR base = 0x%lx\n", gicr_base);
248
249 sbsa_set_gic_bases(gicd_base, gicr_base);
250
251 node = fdt_path_offset(dtb, "/intc/its");
252 if (node < 0) {
253 return;
254 }
255
256 err = fdt_get_reg_props_by_index(dtb, node, 0, &gic_its_addr, NULL);
257 if (err < 0) {
258 ERROR("Failed to read GICI reg property of GIC node\n");
259 return;
260 }
261 INFO("GICI base = 0x%lx\n", gic_its_addr);
262}
263
264static void read_platform_version(void *dtb)
265{
266 int node;
267
268 node = fdt_path_offset(dtb, "/");
269 if (node >= 0) {
270 platform_version_major =
271 fdt_read_uint32_default(dtb, node, "machine-version-major", 0);
272 platform_version_minor =
273 fdt_read_uint32_default(dtb, node, "machine-version-minor", 0);
274 }
275}
276
277void sbsa_platform_init(void)
278{
279 /* Read DeviceTree data before MMU is enabled */
280
281 void *dtb = (void *)(uintptr_t)ARM_PRELOADED_DTB_BASE;
282 int err;
283
284 err = fdt_open_into(dtb, dtb, PLAT_QEMU_DT_MAX_SIZE);
285 if (err < 0) {
286 ERROR("Invalid Device Tree at %p: error %d\n", dtb, err);
287 return;
288 }
289
290 err = fdt_check_header(dtb);
291 if (err < 0) {
292 ERROR("Invalid DTB file passed\n");
293 return;
294 }
295
296 read_platform_version(dtb);
297 INFO("Platform version: %d.%d\n", platform_version_major, platform_version_minor);
298
299 read_platform_config_from_dt(dtb);
300 read_cpuinfo_from_dt(dtb);
301 read_meminfo_from_dt(dtb);
302}
303
304int sbsa_platform_version_major(void)
305{
306 return platform_version_major;
307}
308
309int sbsa_platform_version_minor(void)
310{
311 return platform_version_minor;
312}
313
314uint32_t sbsa_platform_num_cpus(void)
315{
316 return dynamic_platform_info.num_cpus;
317}
318
319uint32_t sbsa_platform_num_memnodes(void)
320{
321 return dynamic_platform_info.num_memnodes;
322}
323
324uint64_t sbsa_platform_gic_its_addr(void)
325{
326 return gic_its_addr;
327}
328
329struct platform_cpu_data sbsa_platform_cpu_node(uint64_t index)
330{
331 return dynamic_platform_info.cpu[index];
332}
333
334struct platform_memory_data sbsa_platform_memory_node(uint64_t index)
335{
336 return dynamic_platform_info.memory[index];
337}
338
339struct platform_cpu_topology sbsa_platform_cpu_topology(void)
340{
341 return dynamic_platform_info.cpu_topo;
342}