1 | /* |
2 | * Xilinx Versal SoC model. |
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 "qapi/error.h" |
14 | #include "qemu/log.h" |
15 | #include "qemu/module.h" |
16 | #include "hw/sysbus.h" |
17 | #include "net/net.h" |
18 | #include "sysemu/sysemu.h" |
19 | #include "sysemu/kvm.h" |
20 | #include "hw/arm/boot.h" |
21 | #include "kvm_arm.h" |
22 | #include "hw/misc/unimp.h" |
23 | #include "hw/intc/arm_gicv3_common.h" |
24 | #include "hw/arm/xlnx-versal.h" |
25 | |
26 | #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") |
27 | #define GEM_REVISION 0x40070106 |
28 | |
29 | static void versal_create_apu_cpus(Versal *s) |
30 | { |
31 | int i; |
32 | |
33 | for (i = 0; i < ARRAY_SIZE(s->fpd.apu.cpu); i++) { |
34 | Object *obj; |
35 | char *name; |
36 | |
37 | obj = object_new(XLNX_VERSAL_ACPU_TYPE); |
38 | if (!obj) { |
39 | /* Secondary CPUs start in PSCI powered-down state */ |
40 | error_report("Unable to create apu.cpu[%d] of type %s" , |
41 | i, XLNX_VERSAL_ACPU_TYPE); |
42 | exit(EXIT_FAILURE); |
43 | } |
44 | |
45 | name = g_strdup_printf("apu-cpu[%d]" , i); |
46 | object_property_add_child(OBJECT(s), name, obj, &error_fatal); |
47 | g_free(name); |
48 | |
49 | object_property_set_int(obj, s->cfg.psci_conduit, |
50 | "psci-conduit" , &error_abort); |
51 | if (i) { |
52 | object_property_set_bool(obj, true, |
53 | "start-powered-off" , &error_abort); |
54 | } |
55 | |
56 | object_property_set_int(obj, ARRAY_SIZE(s->fpd.apu.cpu), |
57 | "core-count" , &error_abort); |
58 | object_property_set_link(obj, OBJECT(&s->fpd.apu.mr), "memory" , |
59 | &error_abort); |
60 | object_property_set_bool(obj, true, "realized" , &error_fatal); |
61 | s->fpd.apu.cpu[i] = ARM_CPU(obj); |
62 | } |
63 | } |
64 | |
65 | static void versal_create_apu_gic(Versal *s, qemu_irq *pic) |
66 | { |
67 | static const uint64_t addrs[] = { |
68 | MM_GIC_APU_DIST_MAIN, |
69 | MM_GIC_APU_REDIST_0 |
70 | }; |
71 | SysBusDevice *gicbusdev; |
72 | DeviceState *gicdev; |
73 | int nr_apu_cpus = ARRAY_SIZE(s->fpd.apu.cpu); |
74 | int i; |
75 | |
76 | sysbus_init_child_obj(OBJECT(s), "apu-gic" , |
77 | &s->fpd.apu.gic, sizeof(s->fpd.apu.gic), |
78 | gicv3_class_name()); |
79 | gicbusdev = SYS_BUS_DEVICE(&s->fpd.apu.gic); |
80 | gicdev = DEVICE(&s->fpd.apu.gic); |
81 | qdev_prop_set_uint32(gicdev, "revision" , 3); |
82 | qdev_prop_set_uint32(gicdev, "num-cpu" , 2); |
83 | qdev_prop_set_uint32(gicdev, "num-irq" , XLNX_VERSAL_NR_IRQS + 32); |
84 | qdev_prop_set_uint32(gicdev, "len-redist-region-count" , 1); |
85 | qdev_prop_set_uint32(gicdev, "redist-region-count[0]" , 2); |
86 | qdev_prop_set_bit(gicdev, "has-security-extensions" , true); |
87 | |
88 | object_property_set_bool(OBJECT(&s->fpd.apu.gic), true, "realized" , |
89 | &error_fatal); |
90 | |
91 | for (i = 0; i < ARRAY_SIZE(addrs); i++) { |
92 | MemoryRegion *mr; |
93 | |
94 | mr = sysbus_mmio_get_region(gicbusdev, i); |
95 | memory_region_add_subregion(&s->fpd.apu.mr, addrs[i], mr); |
96 | } |
97 | |
98 | for (i = 0; i < nr_apu_cpus; i++) { |
99 | DeviceState *cpudev = DEVICE(s->fpd.apu.cpu[i]); |
100 | int ppibase = XLNX_VERSAL_NR_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; |
101 | qemu_irq maint_irq; |
102 | int ti; |
103 | /* Mapping from the output timer irq lines from the CPU to the |
104 | * GIC PPI inputs. |
105 | */ |
106 | const int timer_irq[] = { |
107 | [GTIMER_PHYS] = VERSAL_TIMER_NS_EL1_IRQ, |
108 | [GTIMER_VIRT] = VERSAL_TIMER_VIRT_IRQ, |
109 | [GTIMER_HYP] = VERSAL_TIMER_NS_EL2_IRQ, |
110 | [GTIMER_SEC] = VERSAL_TIMER_S_EL1_IRQ, |
111 | }; |
112 | |
113 | for (ti = 0; ti < ARRAY_SIZE(timer_irq); ti++) { |
114 | qdev_connect_gpio_out(cpudev, ti, |
115 | qdev_get_gpio_in(gicdev, |
116 | ppibase + timer_irq[ti])); |
117 | } |
118 | maint_irq = qdev_get_gpio_in(gicdev, |
119 | ppibase + VERSAL_GIC_MAINT_IRQ); |
120 | qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt" , |
121 | 0, maint_irq); |
122 | sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); |
123 | sysbus_connect_irq(gicbusdev, i + nr_apu_cpus, |
124 | qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); |
125 | sysbus_connect_irq(gicbusdev, i + 2 * nr_apu_cpus, |
126 | qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); |
127 | sysbus_connect_irq(gicbusdev, i + 3 * nr_apu_cpus, |
128 | qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); |
129 | } |
130 | |
131 | for (i = 0; i < XLNX_VERSAL_NR_IRQS; i++) { |
132 | pic[i] = qdev_get_gpio_in(gicdev, i); |
133 | } |
134 | } |
135 | |
136 | static void versal_create_uarts(Versal *s, qemu_irq *pic) |
137 | { |
138 | int i; |
139 | |
140 | for (i = 0; i < ARRAY_SIZE(s->lpd.iou.uart); i++) { |
141 | static const int irqs[] = { VERSAL_UART0_IRQ_0, VERSAL_UART1_IRQ_0}; |
142 | static const uint64_t addrs[] = { MM_UART0, MM_UART1 }; |
143 | char *name = g_strdup_printf("uart%d" , i); |
144 | DeviceState *dev; |
145 | MemoryRegion *mr; |
146 | |
147 | dev = qdev_create(NULL, "pl011" ); |
148 | s->lpd.iou.uart[i] = SYS_BUS_DEVICE(dev); |
149 | qdev_prop_set_chr(dev, "chardev" , serial_hd(i)); |
150 | object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal); |
151 | qdev_init_nofail(dev); |
152 | |
153 | mr = sysbus_mmio_get_region(s->lpd.iou.uart[i], 0); |
154 | memory_region_add_subregion(&s->mr_ps, addrs[i], mr); |
155 | |
156 | sysbus_connect_irq(s->lpd.iou.uart[i], 0, pic[irqs[i]]); |
157 | g_free(name); |
158 | } |
159 | } |
160 | |
161 | static void versal_create_gems(Versal *s, qemu_irq *pic) |
162 | { |
163 | int i; |
164 | |
165 | for (i = 0; i < ARRAY_SIZE(s->lpd.iou.gem); i++) { |
166 | static const int irqs[] = { VERSAL_GEM0_IRQ_0, VERSAL_GEM1_IRQ_0}; |
167 | static const uint64_t addrs[] = { MM_GEM0, MM_GEM1 }; |
168 | char *name = g_strdup_printf("gem%d" , i); |
169 | NICInfo *nd = &nd_table[i]; |
170 | DeviceState *dev; |
171 | MemoryRegion *mr; |
172 | |
173 | dev = qdev_create(NULL, "cadence_gem" ); |
174 | s->lpd.iou.gem[i] = SYS_BUS_DEVICE(dev); |
175 | object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal); |
176 | if (nd->used) { |
177 | qemu_check_nic_model(nd, "cadence_gem" ); |
178 | qdev_set_nic_properties(dev, nd); |
179 | } |
180 | object_property_set_int(OBJECT(s->lpd.iou.gem[i]), |
181 | 2, "num-priority-queues" , |
182 | &error_abort); |
183 | object_property_set_link(OBJECT(s->lpd.iou.gem[i]), |
184 | OBJECT(&s->mr_ps), "dma" , |
185 | &error_abort); |
186 | qdev_init_nofail(dev); |
187 | |
188 | mr = sysbus_mmio_get_region(s->lpd.iou.gem[i], 0); |
189 | memory_region_add_subregion(&s->mr_ps, addrs[i], mr); |
190 | |
191 | sysbus_connect_irq(s->lpd.iou.gem[i], 0, pic[irqs[i]]); |
192 | g_free(name); |
193 | } |
194 | } |
195 | |
196 | /* This takes the board allocated linear DDR memory and creates aliases |
197 | * for each split DDR range/aperture on the Versal address map. |
198 | */ |
199 | static void versal_map_ddr(Versal *s) |
200 | { |
201 | uint64_t size = memory_region_size(s->cfg.mr_ddr); |
202 | /* Describes the various split DDR access regions. */ |
203 | static const struct { |
204 | uint64_t base; |
205 | uint64_t size; |
206 | } addr_ranges[] = { |
207 | { MM_TOP_DDR, MM_TOP_DDR_SIZE }, |
208 | { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE }, |
209 | { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE }, |
210 | { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE } |
211 | }; |
212 | uint64_t offset = 0; |
213 | int i; |
214 | |
215 | assert(ARRAY_SIZE(addr_ranges) == ARRAY_SIZE(s->noc.mr_ddr_ranges)); |
216 | for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) { |
217 | char *name; |
218 | uint64_t mapsize; |
219 | |
220 | mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size; |
221 | name = g_strdup_printf("noc-ddr-range%d" , i); |
222 | /* Create the MR alias. */ |
223 | memory_region_init_alias(&s->noc.mr_ddr_ranges[i], OBJECT(s), |
224 | name, s->cfg.mr_ddr, |
225 | offset, mapsize); |
226 | |
227 | /* Map it onto the NoC MR. */ |
228 | memory_region_add_subregion(&s->mr_ps, addr_ranges[i].base, |
229 | &s->noc.mr_ddr_ranges[i]); |
230 | offset += mapsize; |
231 | size -= mapsize; |
232 | g_free(name); |
233 | } |
234 | } |
235 | |
236 | static void versal_unimp_area(Versal *s, const char *name, |
237 | MemoryRegion *mr, |
238 | hwaddr base, hwaddr size) |
239 | { |
240 | DeviceState *dev = qdev_create(NULL, TYPE_UNIMPLEMENTED_DEVICE); |
241 | MemoryRegion *mr_dev; |
242 | |
243 | qdev_prop_set_string(dev, "name" , name); |
244 | qdev_prop_set_uint64(dev, "size" , size); |
245 | object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal); |
246 | qdev_init_nofail(dev); |
247 | |
248 | mr_dev = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); |
249 | memory_region_add_subregion(mr, base, mr_dev); |
250 | } |
251 | |
252 | static void versal_unimp(Versal *s) |
253 | { |
254 | versal_unimp_area(s, "psm" , &s->mr_ps, |
255 | MM_PSM_START, MM_PSM_END - MM_PSM_START); |
256 | versal_unimp_area(s, "crl" , &s->mr_ps, |
257 | MM_CRL, MM_CRL_SIZE); |
258 | versal_unimp_area(s, "crf" , &s->mr_ps, |
259 | MM_FPD_CRF, MM_FPD_CRF_SIZE); |
260 | versal_unimp_area(s, "iou-scntr" , &s->mr_ps, |
261 | MM_IOU_SCNTR, MM_IOU_SCNTR_SIZE); |
262 | versal_unimp_area(s, "iou-scntr-seucre" , &s->mr_ps, |
263 | MM_IOU_SCNTRS, MM_IOU_SCNTRS_SIZE); |
264 | } |
265 | |
266 | static void versal_realize(DeviceState *dev, Error **errp) |
267 | { |
268 | Versal *s = XLNX_VERSAL(dev); |
269 | qemu_irq pic[XLNX_VERSAL_NR_IRQS]; |
270 | |
271 | versal_create_apu_cpus(s); |
272 | versal_create_apu_gic(s, pic); |
273 | versal_create_uarts(s, pic); |
274 | versal_create_gems(s, pic); |
275 | versal_map_ddr(s); |
276 | versal_unimp(s); |
277 | |
278 | /* Create the On Chip Memory (OCM). */ |
279 | memory_region_init_ram(&s->lpd.mr_ocm, OBJECT(s), "ocm" , |
280 | MM_OCM_SIZE, &error_fatal); |
281 | |
282 | memory_region_add_subregion_overlap(&s->mr_ps, MM_OCM, &s->lpd.mr_ocm, 0); |
283 | memory_region_add_subregion_overlap(&s->fpd.apu.mr, 0, &s->mr_ps, 0); |
284 | } |
285 | |
286 | static void versal_init(Object *obj) |
287 | { |
288 | Versal *s = XLNX_VERSAL(obj); |
289 | |
290 | memory_region_init(&s->fpd.apu.mr, obj, "mr-apu" , UINT64_MAX); |
291 | memory_region_init(&s->mr_ps, obj, "mr-ps-switch" , UINT64_MAX); |
292 | } |
293 | |
294 | static Property versal_properties[] = { |
295 | DEFINE_PROP_LINK("ddr" , Versal, cfg.mr_ddr, TYPE_MEMORY_REGION, |
296 | MemoryRegion *), |
297 | DEFINE_PROP_UINT32("psci-conduit" , Versal, cfg.psci_conduit, 0), |
298 | DEFINE_PROP_END_OF_LIST() |
299 | }; |
300 | |
301 | static void versal_class_init(ObjectClass *klass, void *data) |
302 | { |
303 | DeviceClass *dc = DEVICE_CLASS(klass); |
304 | |
305 | dc->realize = versal_realize; |
306 | dc->props = versal_properties; |
307 | /* No VMSD since we haven't got any top-level SoC state to save. */ |
308 | } |
309 | |
310 | static const TypeInfo versal_info = { |
311 | .name = TYPE_XLNX_VERSAL, |
312 | .parent = TYPE_SYS_BUS_DEVICE, |
313 | .instance_size = sizeof(Versal), |
314 | .instance_init = versal_init, |
315 | .class_init = versal_class_init, |
316 | }; |
317 | |
318 | static void versal_register_types(void) |
319 | { |
320 | type_register_static(&versal_info); |
321 | } |
322 | |
323 | type_init(versal_register_types); |
324 | |