]>
Commit | Line | Data |
---|---|---|
9e5e54d1 PM |
1 | /* |
2 | * Arm IoT Kit | |
3 | * | |
4 | * Copyright (c) 2018 Linaro Limited | |
5 | * Written by Peter Maydell | |
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 "qapi/error.h" | |
15 | #include "trace.h" | |
16 | #include "hw/sysbus.h" | |
17 | #include "hw/registerfields.h" | |
18 | #include "hw/arm/iotkit.h" | |
19 | #include "hw/misc/unimp.h" | |
20 | #include "hw/arm/arm.h" | |
21 | ||
22 | /* Create an alias region of @size bytes starting at @base | |
23 | * which mirrors the memory starting at @orig. | |
24 | */ | |
25 | static void make_alias(IoTKit *s, MemoryRegion *mr, const char *name, | |
26 | hwaddr base, hwaddr size, hwaddr orig) | |
27 | { | |
28 | memory_region_init_alias(mr, NULL, name, &s->container, orig, size); | |
29 | /* The alias is even lower priority than unimplemented_device regions */ | |
30 | memory_region_add_subregion_overlap(&s->container, base, mr, -1500); | |
31 | } | |
32 | ||
33 | static void init_sysbus_child(Object *parent, const char *childname, | |
34 | void *child, size_t childsize, | |
35 | const char *childtype) | |
36 | { | |
37 | object_initialize(child, childsize, childtype); | |
38 | object_property_add_child(parent, childname, OBJECT(child), &error_abort); | |
39 | qdev_set_parent_bus(DEVICE(child), sysbus_get_default()); | |
40 | } | |
41 | ||
42 | static void irq_status_forwarder(void *opaque, int n, int level) | |
43 | { | |
44 | qemu_irq destirq = opaque; | |
45 | ||
46 | qemu_set_irq(destirq, level); | |
47 | } | |
48 | ||
49 | static void nsccfg_handler(void *opaque, int n, int level) | |
50 | { | |
51 | IoTKit *s = IOTKIT(opaque); | |
52 | ||
53 | s->nsccfg = level; | |
54 | } | |
55 | ||
56 | static void iotkit_forward_ppc(IoTKit *s, const char *ppcname, int ppcnum) | |
57 | { | |
58 | /* Each of the 4 AHB and 4 APB PPCs that might be present in a | |
59 | * system using the IoTKit has a collection of control lines which | |
60 | * are provided by the security controller and which we want to | |
61 | * expose as control lines on the IoTKit device itself, so the | |
62 | * code using the IoTKit can wire them up to the PPCs. | |
63 | */ | |
64 | SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum]; | |
65 | DeviceState *iotkitdev = DEVICE(s); | |
66 | DeviceState *dev_secctl = DEVICE(&s->secctl); | |
67 | DeviceState *dev_splitter = DEVICE(splitter); | |
68 | char *name; | |
69 | ||
70 | name = g_strdup_printf("%s_nonsec", ppcname); | |
71 | qdev_pass_gpios(dev_secctl, iotkitdev, name); | |
72 | g_free(name); | |
73 | name = g_strdup_printf("%s_ap", ppcname); | |
74 | qdev_pass_gpios(dev_secctl, iotkitdev, name); | |
75 | g_free(name); | |
76 | name = g_strdup_printf("%s_irq_enable", ppcname); | |
77 | qdev_pass_gpios(dev_secctl, iotkitdev, name); | |
78 | g_free(name); | |
79 | name = g_strdup_printf("%s_irq_clear", ppcname); | |
80 | qdev_pass_gpios(dev_secctl, iotkitdev, name); | |
81 | g_free(name); | |
82 | ||
83 | /* irq_status is a little more tricky, because we need to | |
84 | * split it so we can send it both to the security controller | |
85 | * and to our OR gate for the NVIC interrupt line. | |
86 | * Connect up the splitter's outputs, and create a GPIO input | |
87 | * which will pass the line state to the input splitter. | |
88 | */ | |
89 | name = g_strdup_printf("%s_irq_status", ppcname); | |
90 | qdev_connect_gpio_out(dev_splitter, 0, | |
91 | qdev_get_gpio_in_named(dev_secctl, | |
92 | name, 0)); | |
93 | qdev_connect_gpio_out(dev_splitter, 1, | |
94 | qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum)); | |
95 | s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0); | |
96 | qdev_init_gpio_in_named_with_opaque(iotkitdev, irq_status_forwarder, | |
97 | s->irq_status_in[ppcnum], name, 1); | |
98 | g_free(name); | |
99 | } | |
100 | ||
101 | static void iotkit_forward_sec_resp_cfg(IoTKit *s) | |
102 | { | |
103 | /* Forward the 3rd output from the splitter device as a | |
104 | * named GPIO output of the iotkit object. | |
105 | */ | |
106 | DeviceState *dev = DEVICE(s); | |
107 | DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter); | |
108 | ||
109 | qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); | |
110 | s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder, | |
111 | s->sec_resp_cfg, 1); | |
112 | qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); | |
113 | } | |
114 | ||
115 | static void iotkit_init(Object *obj) | |
116 | { | |
117 | IoTKit *s = IOTKIT(obj); | |
118 | int i; | |
119 | ||
120 | memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX); | |
121 | ||
122 | init_sysbus_child(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), | |
123 | TYPE_ARMV7M); | |
124 | qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", | |
125 | ARM_CPU_TYPE_NAME("cortex-m33")); | |
126 | ||
127 | init_sysbus_child(obj, "secctl", &s->secctl, sizeof(s->secctl), | |
128 | TYPE_IOTKIT_SECCTL); | |
129 | init_sysbus_child(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), | |
130 | TYPE_TZ_PPC); | |
131 | init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), | |
132 | TYPE_TZ_PPC); | |
133 | init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0), | |
134 | TYPE_CMSDK_APB_TIMER); | |
135 | init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1), | |
136 | TYPE_CMSDK_APB_TIMER); | |
137 | init_sysbus_child(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), | |
138 | TYPE_UNIMPLEMENTED_DEVICE); | |
139 | object_initialize(&s->ppc_irq_orgate, sizeof(s->ppc_irq_orgate), | |
140 | TYPE_OR_IRQ); | |
141 | object_property_add_child(obj, "ppc-irq-orgate", | |
142 | OBJECT(&s->ppc_irq_orgate), &error_abort); | |
143 | object_initialize(&s->sec_resp_splitter, sizeof(s->sec_resp_splitter), | |
144 | TYPE_SPLIT_IRQ); | |
145 | object_property_add_child(obj, "sec-resp-splitter", | |
146 | OBJECT(&s->sec_resp_splitter), &error_abort); | |
147 | for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { | |
148 | char *name = g_strdup_printf("ppc-irq-splitter-%d", i); | |
149 | SplitIRQ *splitter = &s->ppc_irq_splitter[i]; | |
150 | ||
151 | object_initialize(splitter, sizeof(*splitter), TYPE_SPLIT_IRQ); | |
152 | object_property_add_child(obj, name, OBJECT(splitter), &error_abort); | |
153 | } | |
154 | init_sysbus_child(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), | |
155 | TYPE_UNIMPLEMENTED_DEVICE); | |
156 | } | |
157 | ||
158 | static void iotkit_exp_irq(void *opaque, int n, int level) | |
159 | { | |
160 | IoTKit *s = IOTKIT(opaque); | |
161 | ||
162 | qemu_set_irq(s->exp_irqs[n], level); | |
163 | } | |
164 | ||
165 | static void iotkit_realize(DeviceState *dev, Error **errp) | |
166 | { | |
167 | IoTKit *s = IOTKIT(dev); | |
168 | int i; | |
169 | MemoryRegion *mr; | |
170 | Error *err = NULL; | |
171 | SysBusDevice *sbd_apb_ppc0; | |
172 | SysBusDevice *sbd_secctl; | |
173 | DeviceState *dev_apb_ppc0; | |
174 | DeviceState *dev_apb_ppc1; | |
175 | DeviceState *dev_secctl; | |
176 | DeviceState *dev_splitter; | |
177 | ||
178 | if (!s->board_memory) { | |
179 | error_setg(errp, "memory property was not set"); | |
180 | return; | |
181 | } | |
182 | ||
183 | if (!s->mainclk_frq) { | |
184 | error_setg(errp, "MAINCLK property was not set"); | |
185 | return; | |
186 | } | |
187 | ||
188 | /* Handling of which devices should be available only to secure | |
189 | * code is usually done differently for M profile than for A profile. | |
190 | * Instead of putting some devices only into the secure address space, | |
191 | * devices exist in both address spaces but with hard-wired security | |
192 | * permissions that will cause the CPU to fault for non-secure accesses. | |
193 | * | |
194 | * The IoTKit has an IDAU (Implementation Defined Access Unit), | |
195 | * which specifies hard-wired security permissions for different | |
196 | * areas of the physical address space. For the IoTKit IDAU, the | |
197 | * top 4 bits of the physical address are the IDAU region ID, and | |
198 | * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS | |
199 | * region, otherwise it is an S region. | |
200 | * | |
201 | * The various devices and RAMs are generally all mapped twice, | |
202 | * once into a region that the IDAU defines as secure and once | |
203 | * into a non-secure region. They sit behind either a Memory | |
204 | * Protection Controller (for RAM) or a Peripheral Protection | |
205 | * Controller (for devices), which allow a more fine grained | |
206 | * configuration of whether non-secure accesses are permitted. | |
207 | * | |
208 | * (The other place that guest software can configure security | |
209 | * permissions is in the architected SAU (Security Attribution | |
210 | * Unit), which is entirely inside the CPU. The IDAU can upgrade | |
211 | * the security attributes for a region to more restrictive than | |
212 | * the SAU specifies, but cannot downgrade them.) | |
213 | * | |
214 | * 0x10000000..0x1fffffff alias of 0x00000000..0x0fffffff | |
215 | * 0x20000000..0x2007ffff 32KB FPGA block RAM | |
216 | * 0x30000000..0x3fffffff alias of 0x20000000..0x2fffffff | |
217 | * 0x40000000..0x4000ffff base peripheral region 1 | |
218 | * 0x40010000..0x4001ffff CPU peripherals (none for IoTKit) | |
219 | * 0x40020000..0x4002ffff system control element peripherals | |
220 | * 0x40080000..0x400fffff base peripheral region 2 | |
221 | * 0x50000000..0x5fffffff alias of 0x40000000..0x4fffffff | |
222 | */ | |
223 | ||
224 | memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); | |
225 | ||
226 | qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", s->exp_numirq + 32); | |
227 | /* In real hardware the initial Secure VTOR is set from the INITSVTOR0 | |
228 | * register in the IoT Kit System Control Register block, and the | |
229 | * initial value of that is in turn specifiable by the FPGA that | |
230 | * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, | |
231 | * and simply set the CPU's init-svtor to the IoT Kit default value. | |
232 | */ | |
233 | qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor", 0x10000000); | |
234 | object_property_set_link(OBJECT(&s->armv7m), OBJECT(&s->container), | |
235 | "memory", &err); | |
236 | if (err) { | |
237 | error_propagate(errp, err); | |
238 | return; | |
239 | } | |
240 | object_property_set_link(OBJECT(&s->armv7m), OBJECT(s), "idau", &err); | |
241 | if (err) { | |
242 | error_propagate(errp, err); | |
243 | return; | |
244 | } | |
245 | object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); | |
246 | if (err) { | |
247 | error_propagate(errp, err); | |
248 | return; | |
249 | } | |
250 | ||
251 | /* Connect our EXP_IRQ GPIOs to the NVIC's lines 32 and up. */ | |
252 | s->exp_irqs = g_new(qemu_irq, s->exp_numirq); | |
253 | for (i = 0; i < s->exp_numirq; i++) { | |
254 | s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32); | |
255 | } | |
256 | qdev_init_gpio_in_named(dev, iotkit_exp_irq, "EXP_IRQ", s->exp_numirq); | |
257 | ||
258 | /* Set up the big aliases first */ | |
259 | make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); | |
260 | make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000); | |
261 | /* The 0x50000000..0x5fffffff region is not a pure alias: it has | |
262 | * a few extra devices that only appear there (generally the | |
263 | * control interfaces for the protection controllers). | |
264 | * We implement this by mapping those devices over the top of this | |
265 | * alias MR at a higher priority. | |
266 | */ | |
267 | make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); | |
268 | ||
269 | /* This RAM should be behind a Memory Protection Controller, but we | |
270 | * don't implement that yet. | |
271 | */ | |
272 | memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); | |
273 | if (err) { | |
274 | error_propagate(errp, err); | |
275 | return; | |
276 | } | |
277 | memory_region_add_subregion(&s->container, 0x20000000, &s->sram0); | |
278 | ||
279 | /* Security controller */ | |
280 | object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); | |
281 | if (err) { | |
282 | error_propagate(errp, err); | |
283 | return; | |
284 | } | |
285 | sbd_secctl = SYS_BUS_DEVICE(&s->secctl); | |
286 | dev_secctl = DEVICE(&s->secctl); | |
287 | sysbus_mmio_map(sbd_secctl, 0, 0x50080000); | |
288 | sysbus_mmio_map(sbd_secctl, 1, 0x40080000); | |
289 | ||
290 | s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1); | |
291 | qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in); | |
292 | ||
293 | /* The sec_resp_cfg output from the security controller must be split into | |
294 | * multiple lines, one for each of the PPCs within the IoTKit and one | |
295 | * that will be an output from the IoTKit to the system. | |
296 | */ | |
297 | object_property_set_int(OBJECT(&s->sec_resp_splitter), 3, | |
298 | "num-lines", &err); | |
299 | if (err) { | |
300 | error_propagate(errp, err); | |
301 | return; | |
302 | } | |
303 | object_property_set_bool(OBJECT(&s->sec_resp_splitter), true, | |
304 | "realized", &err); | |
305 | if (err) { | |
306 | error_propagate(errp, err); | |
307 | return; | |
308 | } | |
309 | dev_splitter = DEVICE(&s->sec_resp_splitter); | |
310 | qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, | |
311 | qdev_get_gpio_in(dev_splitter, 0)); | |
312 | ||
313 | /* Devices behind APB PPC0: | |
314 | * 0x40000000: timer0 | |
315 | * 0x40001000: timer1 | |
316 | * 0x40002000: dual timer | |
317 | * We must configure and realize each downstream device and connect | |
318 | * it to the appropriate PPC port; then we can realize the PPC and | |
319 | * map its upstream ends to the right place in the container. | |
320 | */ | |
321 | qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq); | |
322 | object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err); | |
323 | if (err) { | |
324 | error_propagate(errp, err); | |
325 | return; | |
326 | } | |
327 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, | |
328 | qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); | |
329 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0); | |
330 | object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err); | |
331 | if (err) { | |
332 | error_propagate(errp, err); | |
333 | return; | |
334 | } | |
335 | ||
336 | qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq); | |
337 | object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err); | |
338 | if (err) { | |
339 | error_propagate(errp, err); | |
340 | return; | |
341 | } | |
342 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, | |
343 | qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); | |
344 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); | |
345 | object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); | |
346 | if (err) { | |
347 | error_propagate(errp, err); | |
348 | return; | |
349 | } | |
350 | ||
351 | qdev_prop_set_string(DEVICE(&s->dualtimer), "name", "Dual timer"); | |
352 | qdev_prop_set_uint64(DEVICE(&s->dualtimer), "size", 0x1000); | |
353 | object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); | |
354 | if (err) { | |
355 | error_propagate(errp, err); | |
356 | return; | |
357 | } | |
358 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); | |
359 | object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); | |
360 | if (err) { | |
361 | error_propagate(errp, err); | |
362 | return; | |
363 | } | |
364 | ||
365 | object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err); | |
366 | if (err) { | |
367 | error_propagate(errp, err); | |
368 | return; | |
369 | } | |
370 | ||
371 | sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0); | |
372 | dev_apb_ppc0 = DEVICE(&s->apb_ppc0); | |
373 | ||
374 | mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0); | |
375 | memory_region_add_subregion(&s->container, 0x40000000, mr); | |
376 | mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1); | |
377 | memory_region_add_subregion(&s->container, 0x40001000, mr); | |
378 | mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2); | |
379 | memory_region_add_subregion(&s->container, 0x40002000, mr); | |
380 | for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) { | |
381 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i, | |
382 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
383 | "cfg_nonsec", i)); | |
384 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i, | |
385 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
386 | "cfg_ap", i)); | |
387 | } | |
388 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0, | |
389 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
390 | "irq_enable", 0)); | |
391 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0, | |
392 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
393 | "irq_clear", 0)); | |
394 | qdev_connect_gpio_out(dev_splitter, 0, | |
395 | qdev_get_gpio_in_named(dev_apb_ppc0, | |
396 | "cfg_sec_resp", 0)); | |
397 | ||
398 | /* All the PPC irq lines (from the 2 internal PPCs and the 8 external | |
399 | * ones) are sent individually to the security controller, and also | |
400 | * ORed together to give a single combined PPC interrupt to the NVIC. | |
401 | */ | |
402 | object_property_set_int(OBJECT(&s->ppc_irq_orgate), | |
403 | NUM_PPCS, "num-lines", &err); | |
404 | if (err) { | |
405 | error_propagate(errp, err); | |
406 | return; | |
407 | } | |
408 | object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true, | |
409 | "realized", &err); | |
410 | if (err) { | |
411 | error_propagate(errp, err); | |
412 | return; | |
413 | } | |
414 | qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, | |
415 | qdev_get_gpio_in(DEVICE(&s->armv7m), 10)); | |
416 | ||
417 | /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ | |
418 | ||
419 | /* 0x40020000 .. 0x4002ffff : IoTKit system control peripheral region */ | |
420 | /* Devices behind APB PPC1: | |
421 | * 0x4002f000: S32K timer | |
422 | */ | |
423 | qdev_prop_set_string(DEVICE(&s->s32ktimer), "name", "S32KTIMER"); | |
424 | qdev_prop_set_uint64(DEVICE(&s->s32ktimer), "size", 0x1000); | |
425 | object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); | |
426 | if (err) { | |
427 | error_propagate(errp, err); | |
428 | return; | |
429 | } | |
430 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); | |
431 | object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); | |
432 | if (err) { | |
433 | error_propagate(errp, err); | |
434 | return; | |
435 | } | |
436 | ||
437 | object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err); | |
438 | if (err) { | |
439 | error_propagate(errp, err); | |
440 | return; | |
441 | } | |
442 | mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0); | |
443 | memory_region_add_subregion(&s->container, 0x4002f000, mr); | |
444 | ||
445 | dev_apb_ppc1 = DEVICE(&s->apb_ppc1); | |
446 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0, | |
447 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
448 | "cfg_nonsec", 0)); | |
449 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0, | |
450 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
451 | "cfg_ap", 0)); | |
452 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0, | |
453 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
454 | "irq_enable", 0)); | |
455 | qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0, | |
456 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
457 | "irq_clear", 0)); | |
458 | qdev_connect_gpio_out(dev_splitter, 1, | |
459 | qdev_get_gpio_in_named(dev_apb_ppc1, | |
460 | "cfg_sec_resp", 0)); | |
461 | ||
462 | /* Using create_unimplemented_device() maps the stub into the | |
463 | * system address space rather than into our container, but the | |
464 | * overall effect to the guest is the same. | |
465 | */ | |
466 | create_unimplemented_device("SYSINFO", 0x40020000, 0x1000); | |
467 | ||
468 | create_unimplemented_device("SYSCONTROL", 0x50021000, 0x1000); | |
469 | create_unimplemented_device("S32KWATCHDOG", 0x5002e000, 0x1000); | |
470 | ||
471 | /* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */ | |
472 | ||
473 | create_unimplemented_device("NS watchdog", 0x40081000, 0x1000); | |
474 | create_unimplemented_device("S watchdog", 0x50081000, 0x1000); | |
475 | ||
476 | create_unimplemented_device("SRAM0 MPC", 0x50083000, 0x1000); | |
477 | ||
478 | for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { | |
479 | Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); | |
480 | ||
481 | object_property_set_int(splitter, 2, "num-lines", &err); | |
482 | if (err) { | |
483 | error_propagate(errp, err); | |
484 | return; | |
485 | } | |
486 | object_property_set_bool(splitter, true, "realized", &err); | |
487 | if (err) { | |
488 | error_propagate(errp, err); | |
489 | return; | |
490 | } | |
491 | } | |
492 | ||
493 | for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { | |
494 | char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); | |
495 | ||
496 | iotkit_forward_ppc(s, ppcname, i); | |
497 | g_free(ppcname); | |
498 | } | |
499 | ||
500 | for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { | |
501 | char *ppcname = g_strdup_printf("apb_ppcexp%d", i); | |
502 | ||
503 | iotkit_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); | |
504 | g_free(ppcname); | |
505 | } | |
506 | ||
507 | for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) { | |
508 | /* Wire up IRQ splitter for internal PPCs */ | |
509 | DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]); | |
510 | char *gpioname = g_strdup_printf("apb_ppc%d_irq_status", | |
511 | i - NUM_EXTERNAL_PPCS); | |
512 | TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1; | |
513 | ||
514 | qdev_connect_gpio_out(devs, 0, | |
515 | qdev_get_gpio_in_named(dev_secctl, gpioname, 0)); | |
516 | qdev_connect_gpio_out(devs, 1, | |
517 | qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); | |
518 | qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, | |
519 | qdev_get_gpio_in(devs, 0)); | |
520 | } | |
521 | ||
522 | iotkit_forward_sec_resp_cfg(s); | |
523 | ||
524 | system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; | |
525 | } | |
526 | ||
527 | static void iotkit_idau_check(IDAUInterface *ii, uint32_t address, | |
528 | int *iregion, bool *exempt, bool *ns, bool *nsc) | |
529 | { | |
530 | /* For IoTKit systems the IDAU responses are simple logical functions | |
531 | * of the address bits. The NSC attribute is guest-adjustable via the | |
532 | * NSCCFG register in the security controller. | |
533 | */ | |
534 | IoTKit *s = IOTKIT(ii); | |
535 | int region = extract32(address, 28, 4); | |
536 | ||
537 | *ns = !(region & 1); | |
538 | *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2)); | |
539 | /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ | |
540 | *exempt = (address & 0xeff00000) == 0xe0000000; | |
541 | *iregion = region; | |
542 | } | |
543 | ||
544 | static const VMStateDescription iotkit_vmstate = { | |
545 | .name = "iotkit", | |
546 | .version_id = 1, | |
547 | .minimum_version_id = 1, | |
548 | .fields = (VMStateField[]) { | |
549 | VMSTATE_UINT32(nsccfg, IoTKit), | |
550 | VMSTATE_END_OF_LIST() | |
551 | } | |
552 | }; | |
553 | ||
554 | static Property iotkit_properties[] = { | |
555 | DEFINE_PROP_LINK("memory", IoTKit, board_memory, TYPE_MEMORY_REGION, | |
556 | MemoryRegion *), | |
557 | DEFINE_PROP_UINT32("EXP_NUMIRQ", IoTKit, exp_numirq, 64), | |
558 | DEFINE_PROP_UINT32("MAINCLK", IoTKit, mainclk_frq, 0), | |
559 | DEFINE_PROP_END_OF_LIST() | |
560 | }; | |
561 | ||
562 | static void iotkit_reset(DeviceState *dev) | |
563 | { | |
564 | IoTKit *s = IOTKIT(dev); | |
565 | ||
566 | s->nsccfg = 0; | |
567 | } | |
568 | ||
569 | static void iotkit_class_init(ObjectClass *klass, void *data) | |
570 | { | |
571 | DeviceClass *dc = DEVICE_CLASS(klass); | |
572 | IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); | |
573 | ||
574 | dc->realize = iotkit_realize; | |
575 | dc->vmsd = &iotkit_vmstate; | |
576 | dc->props = iotkit_properties; | |
577 | dc->reset = iotkit_reset; | |
578 | iic->check = iotkit_idau_check; | |
579 | } | |
580 | ||
581 | static const TypeInfo iotkit_info = { | |
582 | .name = TYPE_IOTKIT, | |
583 | .parent = TYPE_SYS_BUS_DEVICE, | |
584 | .instance_size = sizeof(IoTKit), | |
585 | .instance_init = iotkit_init, | |
586 | .class_init = iotkit_class_init, | |
587 | .interfaces = (InterfaceInfo[]) { | |
588 | { TYPE_IDAU_INTERFACE }, | |
589 | { } | |
590 | } | |
591 | }; | |
592 | ||
593 | static void iotkit_register_types(void) | |
594 | { | |
595 | type_register_static(&iotkit_info); | |
596 | } | |
597 | ||
598 | type_init(iotkit_register_types); |