1 | /* |
2 | * Xilinx Versal Virtual board. |
3 | * |
4 | * Copyright (c) 2018 Xilinx Inc. |
5 | * Written by Edgar E. Iglesias |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 or |
9 | * (at your option) any later version. |
10 | */ |
11 | |
12 | #include "qemu/osdep.h" |
13 | #include "qemu/log.h" |
14 | #include "qemu/error-report.h" |
15 | #include "qapi/error.h" |
16 | #include "sysemu/device_tree.h" |
17 | #include "exec/address-spaces.h" |
18 | #include "hw/boards.h" |
19 | #include "hw/sysbus.h" |
20 | #include "hw/arm/sysbus-fdt.h" |
21 | #include "hw/arm/fdt.h" |
22 | #include "cpu.h" |
23 | #include "hw/arm/xlnx-versal.h" |
24 | |
25 | #define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("xlnx-versal-virt") |
26 | #define XLNX_VERSAL_VIRT_MACHINE(obj) \ |
27 | OBJECT_CHECK(VersalVirt, (obj), TYPE_XLNX_VERSAL_VIRT_MACHINE) |
28 | |
29 | typedef struct VersalVirt { |
30 | MachineState parent_obj; |
31 | |
32 | Versal soc; |
33 | MemoryRegion mr_ddr; |
34 | |
35 | void *fdt; |
36 | int fdt_size; |
37 | struct { |
38 | uint32_t gic; |
39 | uint32_t ethernet_phy[2]; |
40 | uint32_t clk_125Mhz; |
41 | uint32_t clk_25Mhz; |
42 | } phandle; |
43 | struct arm_boot_info binfo; |
44 | |
45 | struct { |
46 | bool secure; |
47 | } cfg; |
48 | } VersalVirt; |
49 | |
50 | static void fdt_create(VersalVirt *s) |
51 | { |
52 | MachineClass *mc = MACHINE_GET_CLASS(s); |
53 | int i; |
54 | |
55 | s->fdt = create_device_tree(&s->fdt_size); |
56 | if (!s->fdt) { |
57 | error_report("create_device_tree() failed" ); |
58 | exit(1); |
59 | } |
60 | |
61 | /* Allocate all phandles. */ |
62 | s->phandle.gic = qemu_fdt_alloc_phandle(s->fdt); |
63 | for (i = 0; i < ARRAY_SIZE(s->phandle.ethernet_phy); i++) { |
64 | s->phandle.ethernet_phy[i] = qemu_fdt_alloc_phandle(s->fdt); |
65 | } |
66 | s->phandle.clk_25Mhz = qemu_fdt_alloc_phandle(s->fdt); |
67 | s->phandle.clk_125Mhz = qemu_fdt_alloc_phandle(s->fdt); |
68 | |
69 | /* Create /chosen node for load_dtb. */ |
70 | qemu_fdt_add_subnode(s->fdt, "/chosen" ); |
71 | |
72 | /* Header */ |
73 | qemu_fdt_setprop_cell(s->fdt, "/" , "interrupt-parent" , s->phandle.gic); |
74 | qemu_fdt_setprop_cell(s->fdt, "/" , "#size-cells" , 0x2); |
75 | qemu_fdt_setprop_cell(s->fdt, "/" , "#address-cells" , 0x2); |
76 | qemu_fdt_setprop_string(s->fdt, "/" , "model" , mc->desc); |
77 | qemu_fdt_setprop_string(s->fdt, "/" , "compatible" , "xlnx-versal-virt" ); |
78 | } |
79 | |
80 | static void fdt_add_clk_node(VersalVirt *s, const char *name, |
81 | unsigned int freq_hz, uint32_t phandle) |
82 | { |
83 | qemu_fdt_add_subnode(s->fdt, name); |
84 | qemu_fdt_setprop_cell(s->fdt, name, "phandle" , phandle); |
85 | qemu_fdt_setprop_cell(s->fdt, name, "clock-frequency" , freq_hz); |
86 | qemu_fdt_setprop_cell(s->fdt, name, "#clock-cells" , 0x0); |
87 | qemu_fdt_setprop_string(s->fdt, name, "compatible" , "fixed-clock" ); |
88 | qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc" , NULL, 0); |
89 | } |
90 | |
91 | static void fdt_add_cpu_nodes(VersalVirt *s, uint32_t psci_conduit) |
92 | { |
93 | int i; |
94 | |
95 | qemu_fdt_add_subnode(s->fdt, "/cpus" ); |
96 | qemu_fdt_setprop_cell(s->fdt, "/cpus" , "#size-cells" , 0x0); |
97 | qemu_fdt_setprop_cell(s->fdt, "/cpus" , "#address-cells" , 1); |
98 | |
99 | for (i = XLNX_VERSAL_NR_ACPUS - 1; i >= 0; i--) { |
100 | char *name = g_strdup_printf("/cpus/cpu@%d" , i); |
101 | ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); |
102 | |
103 | qemu_fdt_add_subnode(s->fdt, name); |
104 | qemu_fdt_setprop_cell(s->fdt, name, "reg" , armcpu->mp_affinity); |
105 | if (psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { |
106 | qemu_fdt_setprop_string(s->fdt, name, "enable-method" , "psci" ); |
107 | } |
108 | qemu_fdt_setprop_string(s->fdt, name, "device_type" , "cpu" ); |
109 | qemu_fdt_setprop_string(s->fdt, name, "compatible" , |
110 | armcpu->dtb_compatible); |
111 | g_free(name); |
112 | } |
113 | } |
114 | |
115 | static void fdt_add_gic_nodes(VersalVirt *s) |
116 | { |
117 | char *nodename; |
118 | |
119 | nodename = g_strdup_printf("/gic@%x" , MM_GIC_APU_DIST_MAIN); |
120 | qemu_fdt_add_subnode(s->fdt, nodename); |
121 | qemu_fdt_setprop_cell(s->fdt, nodename, "phandle" , s->phandle.gic); |
122 | qemu_fdt_setprop_cells(s->fdt, nodename, "interrupts" , |
123 | GIC_FDT_IRQ_TYPE_PPI, VERSAL_GIC_MAINT_IRQ, |
124 | GIC_FDT_IRQ_FLAGS_LEVEL_HI); |
125 | qemu_fdt_setprop(s->fdt, nodename, "interrupt-controller" , NULL, 0); |
126 | qemu_fdt_setprop_sized_cells(s->fdt, nodename, "reg" , |
127 | 2, MM_GIC_APU_DIST_MAIN, |
128 | 2, MM_GIC_APU_DIST_MAIN_SIZE, |
129 | 2, MM_GIC_APU_REDIST_0, |
130 | 2, MM_GIC_APU_REDIST_0_SIZE); |
131 | qemu_fdt_setprop_cell(s->fdt, nodename, "#interrupt-cells" , 3); |
132 | qemu_fdt_setprop_string(s->fdt, nodename, "compatible" , "arm,gic-v3" ); |
133 | g_free(nodename); |
134 | } |
135 | |
136 | static void fdt_add_timer_nodes(VersalVirt *s) |
137 | { |
138 | const char compat[] = "arm,armv8-timer" ; |
139 | uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI; |
140 | |
141 | qemu_fdt_add_subnode(s->fdt, "/timer" ); |
142 | qemu_fdt_setprop_cells(s->fdt, "/timer" , "interrupts" , |
143 | GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_S_EL1_IRQ, irqflags, |
144 | GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL1_IRQ, irqflags, |
145 | GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_VIRT_IRQ, irqflags, |
146 | GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL2_IRQ, irqflags); |
147 | qemu_fdt_setprop(s->fdt, "/timer" , "compatible" , |
148 | compat, sizeof(compat)); |
149 | } |
150 | |
151 | static void fdt_add_uart_nodes(VersalVirt *s) |
152 | { |
153 | uint64_t addrs[] = { MM_UART1, MM_UART0 }; |
154 | unsigned int irqs[] = { VERSAL_UART1_IRQ_0, VERSAL_UART0_IRQ_0 }; |
155 | const char compat[] = "arm,pl011\0arm,sbsa-uart" ; |
156 | const char clocknames[] = "uartclk\0apb_pclk" ; |
157 | int i; |
158 | |
159 | for (i = 0; i < ARRAY_SIZE(addrs); i++) { |
160 | char *name = g_strdup_printf("/uart@%" PRIx64, addrs[i]); |
161 | qemu_fdt_add_subnode(s->fdt, name); |
162 | qemu_fdt_setprop_cell(s->fdt, name, "current-speed" , 115200); |
163 | qemu_fdt_setprop_cells(s->fdt, name, "clocks" , |
164 | s->phandle.clk_125Mhz, s->phandle.clk_125Mhz); |
165 | qemu_fdt_setprop(s->fdt, name, "clock-names" , |
166 | clocknames, sizeof(clocknames)); |
167 | |
168 | qemu_fdt_setprop_cells(s->fdt, name, "interrupts" , |
169 | GIC_FDT_IRQ_TYPE_SPI, irqs[i], |
170 | GIC_FDT_IRQ_FLAGS_LEVEL_HI); |
171 | qemu_fdt_setprop_sized_cells(s->fdt, name, "reg" , |
172 | 2, addrs[i], 2, 0x1000); |
173 | qemu_fdt_setprop(s->fdt, name, "compatible" , |
174 | compat, sizeof(compat)); |
175 | qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc" , NULL, 0); |
176 | |
177 | if (addrs[i] == MM_UART0) { |
178 | /* Select UART0. */ |
179 | qemu_fdt_setprop_string(s->fdt, "/chosen" , "stdout-path" , name); |
180 | } |
181 | g_free(name); |
182 | } |
183 | } |
184 | |
185 | static void fdt_add_fixed_link_nodes(VersalVirt *s, char *gemname, |
186 | uint32_t phandle) |
187 | { |
188 | char *name = g_strdup_printf("%s/fixed-link" , gemname); |
189 | |
190 | qemu_fdt_add_subnode(s->fdt, name); |
191 | qemu_fdt_setprop_cell(s->fdt, name, "phandle" , phandle); |
192 | qemu_fdt_setprop(s->fdt, name, "full-duplex" , NULL, 0); |
193 | qemu_fdt_setprop_cell(s->fdt, name, "speed" , 1000); |
194 | g_free(name); |
195 | } |
196 | |
197 | static void fdt_add_gem_nodes(VersalVirt *s) |
198 | { |
199 | uint64_t addrs[] = { MM_GEM1, MM_GEM0 }; |
200 | unsigned int irqs[] = { VERSAL_GEM1_IRQ_0, VERSAL_GEM0_IRQ_0 }; |
201 | const char clocknames[] = "pclk\0hclk\0tx_clk\0rx_clk" ; |
202 | const char compat_gem[] = "cdns,zynqmp-gem\0cdns,gem" ; |
203 | int i; |
204 | |
205 | for (i = 0; i < ARRAY_SIZE(addrs); i++) { |
206 | char *name = g_strdup_printf("/ethernet@%" PRIx64, addrs[i]); |
207 | qemu_fdt_add_subnode(s->fdt, name); |
208 | |
209 | fdt_add_fixed_link_nodes(s, name, s->phandle.ethernet_phy[i]); |
210 | qemu_fdt_setprop_string(s->fdt, name, "phy-mode" , "rgmii-id" ); |
211 | qemu_fdt_setprop_cell(s->fdt, name, "phy-handle" , |
212 | s->phandle.ethernet_phy[i]); |
213 | qemu_fdt_setprop_cells(s->fdt, name, "clocks" , |
214 | s->phandle.clk_25Mhz, s->phandle.clk_25Mhz, |
215 | s->phandle.clk_25Mhz, s->phandle.clk_25Mhz); |
216 | qemu_fdt_setprop(s->fdt, name, "clock-names" , |
217 | clocknames, sizeof(clocknames)); |
218 | qemu_fdt_setprop_cells(s->fdt, name, "interrupts" , |
219 | GIC_FDT_IRQ_TYPE_SPI, irqs[i], |
220 | GIC_FDT_IRQ_FLAGS_LEVEL_HI, |
221 | GIC_FDT_IRQ_TYPE_SPI, irqs[i], |
222 | GIC_FDT_IRQ_FLAGS_LEVEL_HI); |
223 | qemu_fdt_setprop_sized_cells(s->fdt, name, "reg" , |
224 | 2, addrs[i], 2, 0x1000); |
225 | qemu_fdt_setprop(s->fdt, name, "compatible" , |
226 | compat_gem, sizeof(compat_gem)); |
227 | qemu_fdt_setprop_cell(s->fdt, name, "#address-cells" , 1); |
228 | qemu_fdt_setprop_cell(s->fdt, name, "#size-cells" , 0); |
229 | g_free(name); |
230 | } |
231 | } |
232 | |
233 | static void fdt_nop_memory_nodes(void *fdt, Error **errp) |
234 | { |
235 | Error *err = NULL; |
236 | char **node_path; |
237 | int n = 0; |
238 | |
239 | node_path = qemu_fdt_node_unit_path(fdt, "memory" , &err); |
240 | if (err) { |
241 | error_propagate(errp, err); |
242 | return; |
243 | } |
244 | while (node_path[n]) { |
245 | if (g_str_has_prefix(node_path[n], "/memory" )) { |
246 | qemu_fdt_nop_node(fdt, node_path[n]); |
247 | } |
248 | n++; |
249 | } |
250 | g_strfreev(node_path); |
251 | } |
252 | |
253 | static void fdt_add_memory_nodes(VersalVirt *s, void *fdt, uint64_t ram_size) |
254 | { |
255 | /* Describes the various split DDR access regions. */ |
256 | static const struct { |
257 | uint64_t base; |
258 | uint64_t size; |
259 | } addr_ranges[] = { |
260 | { MM_TOP_DDR, MM_TOP_DDR_SIZE }, |
261 | { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE }, |
262 | { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE }, |
263 | { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE } |
264 | }; |
265 | uint64_t mem_reg_prop[8] = {0}; |
266 | uint64_t size = ram_size; |
267 | Error *err = NULL; |
268 | char *name; |
269 | int i; |
270 | |
271 | fdt_nop_memory_nodes(fdt, &err); |
272 | if (err) { |
273 | error_report_err(err); |
274 | return; |
275 | } |
276 | |
277 | name = g_strdup_printf("/memory@%x" , MM_TOP_DDR); |
278 | for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) { |
279 | uint64_t mapsize; |
280 | |
281 | mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size; |
282 | |
283 | mem_reg_prop[i * 2] = addr_ranges[i].base; |
284 | mem_reg_prop[i * 2 + 1] = mapsize; |
285 | size -= mapsize; |
286 | } |
287 | qemu_fdt_add_subnode(fdt, name); |
288 | qemu_fdt_setprop_string(fdt, name, "device_type" , "memory" ); |
289 | |
290 | switch (i) { |
291 | case 1: |
292 | qemu_fdt_setprop_sized_cells(fdt, name, "reg" , |
293 | 2, mem_reg_prop[0], |
294 | 2, mem_reg_prop[1]); |
295 | break; |
296 | case 2: |
297 | qemu_fdt_setprop_sized_cells(fdt, name, "reg" , |
298 | 2, mem_reg_prop[0], |
299 | 2, mem_reg_prop[1], |
300 | 2, mem_reg_prop[2], |
301 | 2, mem_reg_prop[3]); |
302 | break; |
303 | case 3: |
304 | qemu_fdt_setprop_sized_cells(fdt, name, "reg" , |
305 | 2, mem_reg_prop[0], |
306 | 2, mem_reg_prop[1], |
307 | 2, mem_reg_prop[2], |
308 | 2, mem_reg_prop[3], |
309 | 2, mem_reg_prop[4], |
310 | 2, mem_reg_prop[5]); |
311 | break; |
312 | case 4: |
313 | qemu_fdt_setprop_sized_cells(fdt, name, "reg" , |
314 | 2, mem_reg_prop[0], |
315 | 2, mem_reg_prop[1], |
316 | 2, mem_reg_prop[2], |
317 | 2, mem_reg_prop[3], |
318 | 2, mem_reg_prop[4], |
319 | 2, mem_reg_prop[5], |
320 | 2, mem_reg_prop[6], |
321 | 2, mem_reg_prop[7]); |
322 | break; |
323 | default: |
324 | g_assert_not_reached(); |
325 | } |
326 | g_free(name); |
327 | } |
328 | |
329 | static void versal_virt_modify_dtb(const struct arm_boot_info *binfo, |
330 | void *fdt) |
331 | { |
332 | VersalVirt *s = container_of(binfo, VersalVirt, binfo); |
333 | |
334 | fdt_add_memory_nodes(s, fdt, binfo->ram_size); |
335 | } |
336 | |
337 | static void *versal_virt_get_dtb(const struct arm_boot_info *binfo, |
338 | int *fdt_size) |
339 | { |
340 | const VersalVirt *board = container_of(binfo, VersalVirt, binfo); |
341 | |
342 | *fdt_size = board->fdt_size; |
343 | return board->fdt; |
344 | } |
345 | |
346 | #define NUM_VIRTIO_TRANSPORT 8 |
347 | static void create_virtio_regions(VersalVirt *s) |
348 | { |
349 | int virtio_mmio_size = 0x200; |
350 | int i; |
351 | |
352 | for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) { |
353 | char *name = g_strdup_printf("virtio%d" , i);; |
354 | hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size; |
355 | int irq = VERSAL_RSVD_IRQ_FIRST + i; |
356 | MemoryRegion *mr; |
357 | DeviceState *dev; |
358 | qemu_irq pic_irq; |
359 | |
360 | pic_irq = qdev_get_gpio_in(DEVICE(&s->soc.fpd.apu.gic), irq); |
361 | dev = qdev_create(NULL, "virtio-mmio" ); |
362 | object_property_add_child(OBJECT(&s->soc), name, OBJECT(dev), |
363 | &error_fatal); |
364 | qdev_init_nofail(dev); |
365 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic_irq); |
366 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); |
367 | memory_region_add_subregion(&s->soc.mr_ps, base, mr); |
368 | g_free(name); |
369 | } |
370 | |
371 | for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) { |
372 | hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size; |
373 | int irq = VERSAL_RSVD_IRQ_FIRST + i; |
374 | char *name = g_strdup_printf("/virtio_mmio@%" PRIx64, base); |
375 | |
376 | qemu_fdt_add_subnode(s->fdt, name); |
377 | qemu_fdt_setprop(s->fdt, name, "dma-coherent" , NULL, 0); |
378 | qemu_fdt_setprop_cells(s->fdt, name, "interrupts" , |
379 | GIC_FDT_IRQ_TYPE_SPI, irq, |
380 | GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); |
381 | qemu_fdt_setprop_sized_cells(s->fdt, name, "reg" , |
382 | 2, base, 2, virtio_mmio_size); |
383 | qemu_fdt_setprop_string(s->fdt, name, "compatible" , "virtio,mmio" ); |
384 | g_free(name); |
385 | } |
386 | } |
387 | |
388 | static void versal_virt_init(MachineState *machine) |
389 | { |
390 | VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(machine); |
391 | int psci_conduit = QEMU_PSCI_CONDUIT_DISABLED; |
392 | |
393 | /* |
394 | * If the user provides an Operating System to be loaded, we expect them |
395 | * to use the -kernel command line option. |
396 | * |
397 | * Users can load firmware or boot-loaders with the -device loader options. |
398 | * |
399 | * When loading an OS, we generate a dtb and let arm_load_kernel() select |
400 | * where it gets loaded. This dtb will be passed to the kernel in x0. |
401 | * |
402 | * If there's no -kernel option, we generate a DTB and place it at 0x1000 |
403 | * for the bootloaders or firmware to pick up. |
404 | * |
405 | * If users want to provide their own DTB, they can use the -dtb option. |
406 | * These dtb's will have their memory nodes modified to match QEMU's |
407 | * selected ram_size option before they get passed to the kernel or fw. |
408 | * |
409 | * When loading an OS, we turn on QEMU's PSCI implementation with SMC |
410 | * as the PSCI conduit. When there's no -kernel, we assume the user |
411 | * provides EL3 firmware to handle PSCI. |
412 | */ |
413 | if (machine->kernel_filename) { |
414 | psci_conduit = QEMU_PSCI_CONDUIT_SMC; |
415 | } |
416 | |
417 | memory_region_allocate_system_memory(&s->mr_ddr, NULL, "ddr" , |
418 | machine->ram_size); |
419 | |
420 | sysbus_init_child_obj(OBJECT(machine), "xlnx-ve" , &s->soc, |
421 | sizeof(s->soc), TYPE_XLNX_VERSAL); |
422 | object_property_set_link(OBJECT(&s->soc), OBJECT(&s->mr_ddr), |
423 | "ddr" , &error_abort); |
424 | object_property_set_int(OBJECT(&s->soc), psci_conduit, |
425 | "psci-conduit" , &error_abort); |
426 | object_property_set_bool(OBJECT(&s->soc), true, "realized" , &error_fatal); |
427 | |
428 | fdt_create(s); |
429 | create_virtio_regions(s); |
430 | fdt_add_gem_nodes(s); |
431 | fdt_add_uart_nodes(s); |
432 | fdt_add_gic_nodes(s); |
433 | fdt_add_timer_nodes(s); |
434 | fdt_add_cpu_nodes(s, psci_conduit); |
435 | fdt_add_clk_node(s, "/clk125" , 125000000, s->phandle.clk_125Mhz); |
436 | fdt_add_clk_node(s, "/clk25" , 25000000, s->phandle.clk_25Mhz); |
437 | |
438 | /* Make the APU cpu address space visible to virtio and other |
439 | * modules unaware of muliple address-spaces. */ |
440 | memory_region_add_subregion_overlap(get_system_memory(), |
441 | 0, &s->soc.fpd.apu.mr, 0); |
442 | |
443 | s->binfo.ram_size = machine->ram_size; |
444 | s->binfo.loader_start = 0x0; |
445 | s->binfo.get_dtb = versal_virt_get_dtb; |
446 | s->binfo.modify_dtb = versal_virt_modify_dtb; |
447 | if (machine->kernel_filename) { |
448 | arm_load_kernel(s->soc.fpd.apu.cpu[0], machine, &s->binfo); |
449 | } else { |
450 | AddressSpace *as = arm_boot_address_space(s->soc.fpd.apu.cpu[0], |
451 | &s->binfo); |
452 | /* Some boot-loaders (e.g u-boot) don't like blobs at address 0 (NULL). |
453 | * Offset things by 4K. */ |
454 | s->binfo.loader_start = 0x1000; |
455 | s->binfo.dtb_limit = 0x1000000; |
456 | if (arm_load_dtb(s->binfo.loader_start, |
457 | &s->binfo, s->binfo.dtb_limit, as, machine) < 0) { |
458 | exit(EXIT_FAILURE); |
459 | } |
460 | } |
461 | } |
462 | |
463 | static void versal_virt_machine_instance_init(Object *obj) |
464 | { |
465 | } |
466 | |
467 | static void versal_virt_machine_class_init(ObjectClass *oc, void *data) |
468 | { |
469 | MachineClass *mc = MACHINE_CLASS(oc); |
470 | |
471 | mc->desc = "Xilinx Versal Virtual development board" ; |
472 | mc->init = versal_virt_init; |
473 | mc->max_cpus = XLNX_VERSAL_NR_ACPUS; |
474 | mc->default_cpus = XLNX_VERSAL_NR_ACPUS; |
475 | mc->no_cdrom = true; |
476 | } |
477 | |
478 | static const TypeInfo versal_virt_machine_init_typeinfo = { |
479 | .name = TYPE_XLNX_VERSAL_VIRT_MACHINE, |
480 | .parent = TYPE_MACHINE, |
481 | .class_init = versal_virt_machine_class_init, |
482 | .instance_init = versal_virt_machine_instance_init, |
483 | .instance_size = sizeof(VersalVirt), |
484 | }; |
485 | |
486 | static void versal_virt_machine_init_register_types(void) |
487 | { |
488 | type_register_static(&versal_virt_machine_init_typeinfo); |
489 | } |
490 | |
491 | type_init(versal_virt_machine_init_register_types) |
492 | |
493 | |