master
c 284 lines 11 KB
Raw
1 /*
2 * RISC-V board helpers for FDT generation.
3 *
4 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "qemu/osdep.h"
10
11 #include "qemu/error-report.h"
12 #include "system/device_tree.h"
13 #include "hw/core/boards.h"
14 #include "hw/riscv/fdt-common.h"
15 #include "target/riscv/cpu_bits.h"
16
17 void *create_board_device_tree(const char *model, const char *compatible,
18 int *fdt_size)
19 {
20 void *fdt = create_device_tree(fdt_size);
21
22 if (!fdt) {
23 error_report("create_device_tree() failed");
24 exit(1);
25 }
26
27 qemu_fdt_setprop_string(fdt, "/", "model", model);
28 qemu_fdt_setprop_string(fdt, "/", "compatible", compatible);
29 qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
30 qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
31
32 qemu_fdt_add_subnode(fdt, "/soc");
33 qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
34 qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
35 qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
36 qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
37
38 return fdt;
39 }
40
41 void create_fdt_socket_memory(void *fdt, hwaddr addr, uint64_t size,
42 int socket_id, bool numa_enabled)
43 {
44 g_autofree char *mem_name = g_strdup_printf("/memory@%"HWADDR_PRIx, addr);
45
46 qemu_fdt_add_subnode(fdt, mem_name);
47 qemu_fdt_setprop_sized_cells(fdt, mem_name, "reg", 2, addr, 2, size);
48 qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory");
49
50 if (numa_enabled) {
51 qemu_fdt_setprop_cell(fdt, mem_name, "numa-node-id", socket_id);
52 }
53 }
54
55 void create_fdt_socket_clint(void *fdt, hwaddr addr, uint64_t size,
56 int socket_id, uint32_t *intc_phandles,
57 int num_harts, bool numa_enabled)
58 {
59 g_autofree uint32_t *clint_cells = g_new0(uint32_t, num_harts * 4);
60 g_autofree char *clint_name = NULL;
61 static const char * const clint_compat[2] = {
62 "sifive,clint0", "riscv,clint0"
63 };
64
65 for (int cpu = 0; cpu < num_harts; cpu++) {
66 clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
67 clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
68 clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
69 clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
70 }
71
72 clint_name = g_strdup_printf("/soc/clint@%"HWADDR_PRIx, addr);
73 qemu_fdt_add_subnode(fdt, clint_name);
74 qemu_fdt_setprop_string_array(fdt, clint_name, "compatible",
75 (char **)&clint_compat,
76 ARRAY_SIZE(clint_compat));
77 qemu_fdt_setprop_sized_cells(fdt, clint_name, "reg",
78 2, addr, 2, size);
79 qemu_fdt_setprop(fdt, clint_name, "interrupts-extended",
80 clint_cells, num_harts * sizeof(uint32_t) * 4);
81
82 if (numa_enabled) {
83 qemu_fdt_setprop_cell(fdt, clint_name, "numa-node-id", socket_id);
84 }
85 }
86
87 void fdt_create_cpu_socket_subnode(void *fdt, uint64_t timebase_frequency)
88 {
89 qemu_fdt_add_subnode(fdt, "/cpus");
90 qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
91 timebase_frequency);
92 qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
93 qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
94 qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
95 }
96
97 static void
98 create_fdt_socket_cpu_internal(void *fdt, char *clust_name, RISCVCPU *cpu_ptr,
99 int cpu, int socket_id, int socket_hartid_base,
100 uint32_t *phandle, uint32_t *intc_phandles,
101 bool numa_enabled, bool is_32_bit)
102 {
103 g_autofree char *cpu_name = NULL;
104 g_autofree char *core_name = NULL;
105 g_autofree char *intc_name = NULL;
106 uint32_t cpu_phandle = (*phandle)++;
107 bool is_sifive_u = cpu_ptr == NULL;
108
109 cpu_name = g_strdup_printf("/cpus/cpu@%d", socket_hartid_base + cpu);
110
111 /*
112 * The sifive_u board has an exclusive satp and riscv,isa
113 * schema that can't be shared with other boards, so part
114 * of the CPU FDT creation (i.e. the /cpus/cpu@N subnode)
115 * is still being done by the board.
116 */
117 if (!is_sifive_u) {
118 int8_t satp_mode_max = cpu_ptr->cfg.max_satp_mode;
119
120 qemu_fdt_add_subnode(fdt, cpu_name);
121
122 if (satp_mode_max != -1) {
123 g_autofree char *sv_name = NULL;
124 sv_name = g_strdup_printf("riscv,%s",
125 satp_mode_str(satp_mode_max, is_32_bit));
126 qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", sv_name);
127 }
128 riscv_isa_write_fdt(cpu_ptr, fdt, cpu_name);
129
130 if (cpu_ptr->cfg.ext_zicbom) {
131 qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbom-block-size",
132 cpu_ptr->cfg.cbom_blocksize);
133 }
134
135 if (cpu_ptr->cfg.ext_zicboz) {
136 qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cboz-block-size",
137 cpu_ptr->cfg.cboz_blocksize);
138 }
139
140 if (cpu_ptr->cfg.ext_zicbop) {
141 qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbop-block-size",
142 cpu_ptr->cfg.cbop_blocksize);
143 }
144 }
145
146 qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
147 qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
148 qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
149 socket_hartid_base + cpu);
150 qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
151 if (numa_enabled) {
152 qemu_fdt_setprop_cell(fdt, cpu_name, "numa-node-id", socket_id);
153 }
154 qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle);
155
156 intc_phandles[cpu] = (*phandle)++;
157
158 intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
159 qemu_fdt_add_subnode(fdt, intc_name);
160 qemu_fdt_setprop_cell(fdt, intc_name, "phandle",
161 intc_phandles[cpu]);
162 qemu_fdt_setprop_string(fdt, intc_name, "compatible",
163 "riscv,cpu-intc");
164 qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
165 qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
166
167 core_name = g_strdup_printf("%s/core%d", clust_name, cpu);
168 qemu_fdt_add_subnode(fdt, core_name);
169 qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle);
170 }
171
172 void create_fdt_socket_cpus(void *fdt, RISCVCPU *socket_harts,
173 int socket_id, int num_harts_socket,
174 int socket_hartid_base, uint32_t *phandle,
175 uint32_t *intc_phandles, bool numa_enabled,
176 bool is_32_bit)
177 {
178 g_autofree char *clust_name = NULL;
179
180 clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket_id);
181 qemu_fdt_add_subnode(fdt, clust_name);
182
183 for (int cpu = num_harts_socket - 1; cpu >= 0; cpu--) {
184 RISCVCPU *cpu_ptr = &socket_harts[cpu];
185
186 create_fdt_socket_cpu_internal(fdt, clust_name, cpu_ptr, cpu,
187 socket_id, socket_hartid_base,
188 phandle, intc_phandles, numa_enabled,
189 is_32_bit);
190 }
191 }
192
193 void
194 create_fdt_socket_cpu_sifive(void *fdt, char *clust_name,
195 int cpu_id, int socket_id,
196 int socket_hartid_base, uint32_t *phandle,
197 uint32_t *intc_phandles)
198 {
199 create_fdt_socket_cpu_internal(fdt, clust_name, NULL, cpu_id,
200 socket_id, socket_hartid_base,
201 phandle, intc_phandles, false, false);
202 }
203
204 void create_fdt_plic(void *fdt, hwaddr addr, uint64_t size,
205 uint32_t plic_phandle, uint32_t int_cells,
206 uint32_t addr_cells, uint32_t *plic_cells,
207 uint32_t cells_size, uint32_t ndev_sources,
208 bool numa_enabled, int socket_id)
209 {
210 g_autofree char *nodename = NULL;
211 static const char * const plic_compat[2] = {
212 "sifive,plic-1.0.0", "riscv,plic0"
213 };
214
215 nodename = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIx, addr);
216
217 qemu_fdt_add_subnode(fdt, nodename);
218 qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", int_cells);
219 qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", addr_cells);
220 qemu_fdt_setprop_string_array(fdt, nodename, "compatible",
221 (char **)&plic_compat, ARRAY_SIZE(plic_compat));
222 qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
223 qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
224 plic_cells, cells_size);
225 qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
226 2, addr, 2, size);
227 qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", ndev_sources);
228 if (numa_enabled) {
229 qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", socket_id);
230 }
231 qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle);
232 }
233
234 /*
235 * To keep it simple, any event can be mapped to any programmable counters in
236 * QEMU. The generic cycle & instruction count events can also be monitored
237 * using programmable counters. In that case, mcycle & minstret must continue
238 * to provide the correct value as well. Heterogeneous PMU per hart is not
239 * supported yet. Thus, number of counters are same across all harts.
240 */
241 void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name)
242 {
243 uint32_t fdt_event_ctr_map[15] = {};
244
245 /*
246 * The event encoding is specified in the SBI specification
247 * Event idx is a 20bits wide number encoded as follows:
248 * event_idx[19:16] = type
249 * event_idx[15:0] = code
250 * The code field in cache events are encoded as follows:
251 * event_idx.code[15:3] = cache_id
252 * event_idx.code[2:1] = op_id
253 * event_idx.code[0:0] = result_id
254 */
255
256 /* SBI_PMU_HW_CPU_CYCLES: 0x01 : type(0x00) */
257 fdt_event_ctr_map[0] = cpu_to_be32(0x00000001);
258 fdt_event_ctr_map[1] = cpu_to_be32(0x00000001);
259 fdt_event_ctr_map[2] = cpu_to_be32(cmask | 1 << 0);
260
261 /* SBI_PMU_HW_INSTRUCTIONS: 0x02 : type(0x00) */
262 fdt_event_ctr_map[3] = cpu_to_be32(0x00000002);
263 fdt_event_ctr_map[4] = cpu_to_be32(0x00000002);
264 fdt_event_ctr_map[5] = cpu_to_be32(cmask | 1 << 2);
265
266 /* SBI_PMU_HW_CACHE_DTLB : 0x03 READ : 0x00 MISS : 0x00 type(0x01) */
267 fdt_event_ctr_map[6] = cpu_to_be32(0x00010019);
268 fdt_event_ctr_map[7] = cpu_to_be32(0x00010019);
269 fdt_event_ctr_map[8] = cpu_to_be32(cmask);
270
271 /* SBI_PMU_HW_CACHE_DTLB : 0x03 WRITE : 0x01 MISS : 0x00 type(0x01) */
272 fdt_event_ctr_map[9] = cpu_to_be32(0x0001001B);
273 fdt_event_ctr_map[10] = cpu_to_be32(0x0001001B);
274 fdt_event_ctr_map[11] = cpu_to_be32(cmask);
275
276 /* SBI_PMU_HW_CACHE_ITLB : 0x04 READ : 0x00 MISS : 0x00 type(0x01) */
277 fdt_event_ctr_map[12] = cpu_to_be32(0x00010021);
278 fdt_event_ctr_map[13] = cpu_to_be32(0x00010021);
279 fdt_event_ctr_map[14] = cpu_to_be32(cmask);
280
281 /* This a OpenSBI specific DT property documented in OpenSBI docs */
282 qemu_fdt_setprop(fdt, pmu_name, "riscv,event-to-mhpmcounters",
283 fdt_event_ctr_map, sizeof(fdt_event_ctr_map));
284 }