1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include "qemu/osdep.h"
17#include "cpu.h"
18#include "exec/cpu-all.h"
19#include "qemu/config-file.h"
20#include "exec/memory.h"
21#include "exec/address-spaces.h"
22#include "sysemu/sysemu.h"
23#include "sysemu/blockdev.h"
24#include "hw/sysbus.h"
25#include "hw/boards.h"
26#include "hw/loader.h"
27#include "qapi/error.h"
28#include "hw/block/flash.h"
29#include "qemu/error-report.h"
30
31#include <libfdt.h>
32#include "hw/fdt_generic_util.h"
33#include "hw/fdt_generic_devices.h"
34
35#include "hw/arm/arm.h"
36
37#define MACHINE_NAME "arm-generic-fdt"
38
39#define MAX_CPUS 4
40
41#define ZYNQ7000_MPCORE_PERIPHBASE 0xF8F00000
42
43#define SMP_BOOT_ADDR 0xfffffff0
44
45#define SMP_BOOTREG_ADDR 0xfffffffc
46
47
48static uint32_t zynq_smpboot[] = {
49 0xe320f003,
50 0xeafffffd,
51};
52
53static void zynq_write_secondary_boot(ARMCPU *cpu,
54 const struct arm_boot_info *info)
55{
56 int n;
57
58 for (n = 0; n < ARRAY_SIZE(zynq_smpboot); n++) {
59 zynq_smpboot[n] = tswap32(zynq_smpboot[n]);
60 }
61 rom_add_blob_fixed("smpboot", zynq_smpboot, sizeof(zynq_smpboot),
62 SMP_BOOT_ADDR);
63}
64
65static void zynq_ps7_usb_nuke_phy(void *fdt)
66{
67 char usb_node_path[DT_PATH_LENGTH];
68
69 int ret = qemu_devtree_node_by_compatible(fdt, usb_node_path,
70 "xlnx,ps7-usb-1.00.a");
71 if (!ret) {
72 qemu_fdt_setprop_string(fdt, usb_node_path, "dr_mode", "host");
73 }
74}
75
76static int zynq_ps7_mdio_phy_connect(char *node_path, FDTMachineInfo *fdti,
77 void *Opaque)
78{
79 Object *parent;
80 char parent_node_path[DT_PATH_LENGTH];
81
82
83 fdt_init_set_opaque(fdti, node_path, Opaque);
84 if (qemu_devtree_getparent(fdti->fdt, parent_node_path, node_path)) {
85 abort();
86 }
87
88
89 while (!fdt_init_has_opaque(fdti, parent_node_path)) {
90 fdt_init_yield(fdti);
91 }
92
93
94 parent = fdt_init_get_opaque(fdti, parent_node_path);
95
96
97 object_property_add_child(OBJECT(parent), "mdio_child", OBJECT(Opaque),
98 NULL);
99
100
101 object_property_set_link(OBJECT(parent), OBJECT(Opaque), "mdio", NULL);
102 return 0;
103}
104
105static char *zynq_ps7_qspi_flash_node_clone(void *fdt)
106{
107 char qspi_node_path[DT_PATH_LENGTH];
108 char qspi_new_node_path[DT_PATH_LENGTH];
109 char *qspi_clone_name = NULL;
110 uint32_t val[2];
111
112
113 memset(qspi_node_path, 0, sizeof(qspi_node_path));
114 memset(qspi_new_node_path, 0, sizeof(qspi_new_node_path));
115
116
117 int ret = qemu_devtree_node_by_compatible(fdt, qspi_node_path,
118 "xlnx,zynq-qspi-1.0");
119 if (ret == 0) {
120 int qspi_is_dual = qemu_fdt_getprop_cell(fdt, qspi_node_path,
121 "is-dual", 0, false, NULL);
122
123 val[0] = cpu_to_be32(1);
124 val[1] = 0;
125 fdt_setprop(fdt, fdt_path_offset(fdt, qspi_node_path),
126 "#bus-cells", val, 4);
127
128
129 snprintf(qspi_new_node_path, DT_PATH_LENGTH, "%s/ps7-qspi-dummy@0",
130 qspi_node_path);
131
132
133 int child_num = qemu_devtree_get_num_children(fdt, qspi_node_path, 1);
134 char **child_flash = qemu_devtree_get_children(fdt, qspi_node_path, 1);
135 if (child_num > 0) {
136 char *compat_str = NULL;
137 compat_str = qemu_fdt_getprop(fdt, child_flash[0],
138 "compatible", NULL, false, NULL);
139
140 val[0] = 0;
141 val[1] = 0;
142 fdt_setprop(fdt, fdt_path_offset(fdt, child_flash[0]), "reg", val, 8);
143
144
145
146 if (compat_str != NULL) {
147 if (qspi_is_dual == 1) {
148
149 qemu_fdt_add_subnode(fdt, qspi_new_node_path);
150 qemu_fdt_setprop_string(fdt, qspi_new_node_path,
151 "compatible", compat_str);
152 qspi_clone_name = g_strdup(qspi_new_node_path);
153
154
155 val[0] = 0;
156 val[1] = cpu_to_be32(1);
157 fdt_setprop(fdt, fdt_path_offset(fdt, qspi_new_node_path),
158 "reg", val, 8);
159 }
160 g_free(compat_str);
161 }
162 }
163 g_free(child_flash);
164 }
165
166 return qspi_clone_name;
167}
168
169static struct arm_boot_info arm_generic_fdt_binfo = {};
170
171static void arm_generic_fdt_init(MachineState *machine)
172{
173 ram_addr_t ram_kernel_base = 0, ram_kernel_size = 0, start_addr;
174
175 void *fdt = NULL;
176 void *sw_fdt = NULL;
177 int fdt_size, sw_fdt_size, mem_offset = 0;
178 const char *dtb_arg, *hw_dtb_arg;
179 char node_path[DT_PATH_LENGTH];
180 FDTMachineInfo *fdti;
181 MemoryRegion *mem_area;
182 char *qspi_clone_spi_flash_node_name = NULL;
183
184 dtb_arg = qemu_opt_get(qemu_get_machine_opts(), "dtb");
185 hw_dtb_arg = qemu_opt_get(qemu_get_machine_opts(), "hw-dtb");
186 if (!dtb_arg && !hw_dtb_arg) {
187 fprintf(stderr, "hw_dtb_arg: %s\n", hw_dtb_arg);
188 goto no_dtb_arg;
189 }
190
191
192 if (dtb_arg) {
193 sw_fdt = load_device_tree(dtb_arg, &sw_fdt_size);
194 if (!sw_fdt) {
195 error_report("Error: Unable to load Device Tree %s\n", dtb_arg);
196 exit(1);
197 }
198 }
199
200
201 if (hw_dtb_arg) {
202 fdt = load_device_tree(hw_dtb_arg, &fdt_size);
203 if (!fdt) {
204 hw_error("Error: Unable to load Device Tree %s\n", hw_dtb_arg);
205 return;
206 }
207 } else if (sw_fdt) {
208 fdt = sw_fdt;
209 fdt_size = sw_fdt_size;
210 }
211
212
213
214 if (!strcmp(MACHINE_GET_CLASS(machine)->name, "arm-generic-fdt-plnx")) {
215 int node_offset = 0;
216
217
218 qspi_clone_spi_flash_node_name = zynq_ps7_qspi_flash_node_clone(fdt);
219
220
221 if (!qemu_devtree_get_node_by_name(fdt, node_path,
222 "interrupt-controller")) {
223 qemu_fdt_setprop_cells(fdt, node_path,
224 "disable-linux-gic-init", true);
225 }
226
227
228
229
230
231
232 do {
233 node_offset = fdt_node_offset_by_compatible(fdt, node_offset,
234 "arm,cortex-a9");
235 if (node_offset > 0) {
236 fdt_get_path(fdt, node_offset, node_path, DT_PATH_LENGTH);
237 qemu_fdt_setprop_cells(fdt, node_path, "reset-cbar",
238 ZYNQ7000_MPCORE_PERIPHBASE);
239 }
240 } while (node_offset > 0);
241 }
242
243
244 while (qemu_devtree_get_node_by_name(fdt, node_path, "memory")) {
245 qemu_fdt_add_subnode(fdt, "/memory@0");
246 qemu_fdt_setprop_cells(fdt, "/memory@0", "reg", 0, machine->ram_size);
247 }
248
249 if (!qemu_fdt_getprop(fdt, "/memory", "compatible", NULL, 0, NULL)) {
250 qemu_fdt_setprop_string(fdt, "/memory", "compatible",
251 "qemu:memory-region");
252 qemu_fdt_setprop_cells(fdt, "/memory", "qemu,ram", 1);
253 }
254
255
256 fdti = fdt_generic_create_machine(fdt, NULL);
257
258 mem_area = MEMORY_REGION(object_resolve_path(node_path, NULL));
259 ram_kernel_base = object_property_get_int(OBJECT(mem_area), "addr", NULL);
260 ram_kernel_size = object_property_get_int(OBJECT(mem_area), "size", NULL);
261
262 if (!strcmp(MACHINE_GET_CLASS(machine)->name, "arm-generic-fdt-plnx")) {
263 do {
264 mem_offset = fdt_node_offset_by_compatible(fdt, mem_offset,
265 "qemu:memory-region");
266 if (mem_offset > 0) {
267 fdt_get_path(fdt, mem_offset, node_path, DT_PATH_LENGTH);
268 mem_area = MEMORY_REGION(object_resolve_path(node_path, NULL));
269
270 if (!memory_region_is_mapped(mem_area)) {
271 start_addr = object_property_get_int(OBJECT(mem_area),
272 "addr", NULL);
273 memory_region_add_subregion(get_system_memory(), start_addr,
274 mem_area);
275 }
276 }
277 } while (mem_offset > 0);
278 }
279
280 fdt_init_destroy_fdti(fdti);
281
282 arm_generic_fdt_binfo.fdt = sw_fdt;
283 arm_generic_fdt_binfo.fdt_size = sw_fdt_size;
284 arm_generic_fdt_binfo.ram_size = ram_kernel_size;
285 arm_generic_fdt_binfo.kernel_filename = machine->kernel_filename;
286 arm_generic_fdt_binfo.kernel_cmdline = machine->kernel_cmdline;
287 arm_generic_fdt_binfo.initrd_filename = machine->initrd_filename;
288 arm_generic_fdt_binfo.nb_cpus = fdt_generic_num_cpus;
289 arm_generic_fdt_binfo.write_secondary_boot = zynq_write_secondary_boot;
290 arm_generic_fdt_binfo.smp_loader_start = SMP_BOOT_ADDR;
291 arm_generic_fdt_binfo.smp_bootreg_addr = SMP_BOOTREG_ADDR;
292 arm_generic_fdt_binfo.board_id = 0xd32;
293 arm_generic_fdt_binfo.loader_start = ram_kernel_base;
294 arm_generic_fdt_binfo.secure_boot = true;
295
296 if (qspi_clone_spi_flash_node_name != NULL) {
297
298 int offset = fdt_path_offset(fdt, qspi_clone_spi_flash_node_name);
299 fdt_del_node(fdt, offset);
300 g_free(qspi_clone_spi_flash_node_name);
301 }
302
303
304
305
306
307 zynq_ps7_usb_nuke_phy(fdt);
308
309 if (machine->kernel_filename) {
310 arm_load_kernel(ARM_CPU(first_cpu), &arm_generic_fdt_binfo);
311 }
312 return;
313
314no_dtb_arg:
315 hw_error("DTB must be specified for %s machine model\n", MACHINE_NAME);
316 return;
317}
318
319static void arm_generic_fdt_init_plnx(MachineState *machine)
320{
321 MemoryRegion *address_space_mem = get_system_memory();
322 DeviceState *dev;
323 SysBusDevice *busdev;
324
325
326
327 {
328 MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
329 memory_region_init_ram(ocm_ram, NULL, "zynq.ocm_ram", 256 << 10,
330 &error_abort);
331 vmstate_register_ram_global(ocm_ram);
332 memory_region_add_subregion(address_space_mem, 0xFFFC0000, ocm_ram);
333 }
334
335
336
337 dev = qdev_create(NULL, "arm.pl35x");
338
339 object_property_add_child(container_get(qdev_get_machine(), "/unattached"),
340 "pl353", OBJECT(dev), NULL);
341 qdev_prop_set_uint8(dev, "x", 3);
342 {
343 DriveInfo *dinfo = drive_get_next(IF_PFLASH);
344 DeviceState *att_dev = nand_init(dinfo ? blk_by_legacy_dinfo(dinfo)
345 : NULL,
346 NAND_MFR_STMICRO, 0xaa);
347
348 object_property_set_link(OBJECT(dev), OBJECT(att_dev), "dev1",
349 &error_abort);
350 }
351 qdev_init_nofail(dev);
352 busdev = SYS_BUS_DEVICE(dev);
353 sysbus_mmio_map(busdev, 0, 0xe000e000);
354 sysbus_mmio_map(busdev, 2, 0xe1000000);
355
356
357 add_to_compat_table(NULL, "compatible:simple-bus", NULL);
358
359 {
360 DeviceState *dev = qdev_create(NULL, "mdio");
361 qdev_init_nofail(dev);
362
363 add_to_inst_bind_table(zynq_ps7_mdio_phy_connect, "mdio", dev);
364 }
365
366 arm_generic_fdt_init(machine);
367
368
369
370 {
371 DeviceState *dev = qdev_create(NULL, "a9-scu");
372 SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
373
374 qdev_prop_set_uint32(dev, "num-cpu", fdt_generic_num_cpus);
375 qdev_init_nofail(dev);
376 sysbus_mmio_map(busdev, 0, ZYNQ7000_MPCORE_PERIPHBASE);
377 }
378
379}
380
381int endian = 0;
382
383static void arm_generic_fdt_machine_init(MachineClass *mc)
384{
385 mc->desc = "ARM device tree driven machine model";
386 mc->init = arm_generic_fdt_init;
387 mc->max_cpus = MAX_CPUS;
388}
389
390static void arm_generic_fdt_plnx_machine_init(MachineClass *mc)
391{
392 mc->desc = "ARM device tree driven machine model for PetaLinux Zynq";
393 mc->init = arm_generic_fdt_init_plnx;
394 mc->max_cpus = MAX_CPUS;
395}
396
397fdt_register_compatibility_opaque(pflash_cfi01_fdt_init,
398 "compatibile:cfi-flash", 0, &endian);
399
400DEFINE_MACHINE(MACHINE_NAME, arm_generic_fdt_machine_init)
401DEFINE_MACHINE(MACHINE_NAME "-plnx", arm_generic_fdt_plnx_machine_init)
402