| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Arm IoT Kit | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2018 Linaro Limited | 
					
						
							|  |  |  |  * Written by Peter Maydell | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License version 2 or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "qemu/osdep.h"
 | 
					
						
							|  |  |  | #include "qemu/log.h"
 | 
					
						
							|  |  |  | #include "qapi/error.h"
 | 
					
						
							|  |  |  | #include "trace.h"
 | 
					
						
							|  |  |  | #include "hw/sysbus.h"
 | 
					
						
							|  |  |  | #include "hw/registerfields.h"
 | 
					
						
							|  |  |  | #include "hw/arm/iotkit.h"
 | 
					
						
							|  |  |  | #include "hw/arm/arm.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:42 +01:00
										 |  |  | /* Clock frequency in HZ of the 32KHz "slow clock" */ | 
					
						
							|  |  |  | #define S32KCLK (32 * 1000)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  | /* Create an alias region of @size bytes starting at @base
 | 
					
						
							|  |  |  |  * which mirrors the memory starting at @orig. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void make_alias(IoTKit *s, MemoryRegion *mr, const char *name, | 
					
						
							|  |  |  |                        hwaddr base, hwaddr size, hwaddr orig) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     memory_region_init_alias(mr, NULL, name, &s->container, orig, size); | 
					
						
							|  |  |  |     /* The alias is even lower priority than unimplemented_device regions */ | 
					
						
							|  |  |  |     memory_region_add_subregion_overlap(&s->container, base, mr, -1500); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void irq_status_forwarder(void *opaque, int n, int level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qemu_irq destirq = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_set_irq(destirq, level); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void nsccfg_handler(void *opaque, int n, int level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IoTKit *s = IOTKIT(opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->nsccfg = level; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void iotkit_forward_ppc(IoTKit *s, const char *ppcname, int ppcnum) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* Each of the 4 AHB and 4 APB PPCs that might be present in a
 | 
					
						
							|  |  |  |      * system using the IoTKit has a collection of control lines which | 
					
						
							|  |  |  |      * are provided by the security controller and which we want to | 
					
						
							|  |  |  |      * expose as control lines on the IoTKit device itself, so the | 
					
						
							|  |  |  |      * code using the IoTKit can wire them up to the PPCs. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum]; | 
					
						
							|  |  |  |     DeviceState *iotkitdev = DEVICE(s); | 
					
						
							|  |  |  |     DeviceState *dev_secctl = DEVICE(&s->secctl); | 
					
						
							|  |  |  |     DeviceState *dev_splitter = DEVICE(splitter); | 
					
						
							|  |  |  |     char *name; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     name = g_strdup_printf("%s_nonsec", ppcname); | 
					
						
							|  |  |  |     qdev_pass_gpios(dev_secctl, iotkitdev, name); | 
					
						
							|  |  |  |     g_free(name); | 
					
						
							|  |  |  |     name = g_strdup_printf("%s_ap", ppcname); | 
					
						
							|  |  |  |     qdev_pass_gpios(dev_secctl, iotkitdev, name); | 
					
						
							|  |  |  |     g_free(name); | 
					
						
							|  |  |  |     name = g_strdup_printf("%s_irq_enable", ppcname); | 
					
						
							|  |  |  |     qdev_pass_gpios(dev_secctl, iotkitdev, name); | 
					
						
							|  |  |  |     g_free(name); | 
					
						
							|  |  |  |     name = g_strdup_printf("%s_irq_clear", ppcname); | 
					
						
							|  |  |  |     qdev_pass_gpios(dev_secctl, iotkitdev, name); | 
					
						
							|  |  |  |     g_free(name); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* irq_status is a little more tricky, because we need to
 | 
					
						
							|  |  |  |      * split it so we can send it both to the security controller | 
					
						
							|  |  |  |      * and to our OR gate for the NVIC interrupt line. | 
					
						
							|  |  |  |      * Connect up the splitter's outputs, and create a GPIO input | 
					
						
							|  |  |  |      * which will pass the line state to the input splitter. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     name = g_strdup_printf("%s_irq_status", ppcname); | 
					
						
							|  |  |  |     qdev_connect_gpio_out(dev_splitter, 0, | 
					
						
							|  |  |  |                           qdev_get_gpio_in_named(dev_secctl, | 
					
						
							|  |  |  |                                                  name, 0)); | 
					
						
							|  |  |  |     qdev_connect_gpio_out(dev_splitter, 1, | 
					
						
							|  |  |  |                           qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum)); | 
					
						
							|  |  |  |     s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0); | 
					
						
							|  |  |  |     qdev_init_gpio_in_named_with_opaque(iotkitdev, irq_status_forwarder, | 
					
						
							|  |  |  |                                         s->irq_status_in[ppcnum], name, 1); | 
					
						
							|  |  |  |     g_free(name); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void iotkit_forward_sec_resp_cfg(IoTKit *s) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* Forward the 3rd output from the splitter device as a
 | 
					
						
							|  |  |  |      * named GPIO output of the iotkit object. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     DeviceState *dev = DEVICE(s); | 
					
						
							|  |  |  |     DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); | 
					
						
							|  |  |  |     s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder, | 
					
						
							|  |  |  |                                            s->sec_resp_cfg, 1); | 
					
						
							|  |  |  |     qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void iotkit_init(Object *obj) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IoTKit *s = IOTKIT(obj); | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-16 14:59:21 +02:00
										 |  |  |     sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), | 
					
						
							|  |  |  |                           TYPE_ARMV7M); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", | 
					
						
							|  |  |  |                          ARM_CPU_TYPE_NAME("cortex-m33")); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-16 14:59:21 +02:00
										 |  |  |     sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), | 
					
						
							|  |  |  |                           TYPE_IOTKIT_SECCTL); | 
					
						
							|  |  |  |     sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), | 
					
						
							|  |  |  |                           TYPE_TZ_PPC); | 
					
						
							|  |  |  |     sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), | 
					
						
							|  |  |  |                           TYPE_TZ_PPC); | 
					
						
							|  |  |  |     sysbus_init_child_obj(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); | 
					
						
							|  |  |  |     object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate, | 
					
						
							|  |  |  |                             sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ, | 
					
						
							|  |  |  |                             &error_abort, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-22 13:28:40 +01:00
										 |  |  |     for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) { | 
					
						
							|  |  |  |         char *name = g_strdup_printf("mpc-irq-splitter-%d", i); | 
					
						
							|  |  |  |         SplitIRQ *splitter = &s->mpc_irq_splitter[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-16 14:59:21 +02:00
										 |  |  |         object_initialize_child(obj, name, splitter, sizeof(*splitter), | 
					
						
							|  |  |  |                                 TYPE_SPLIT_IRQ, &error_abort, NULL); | 
					
						
							| 
									
										
										
										
											2018-06-22 13:28:40 +01:00
										 |  |  |         g_free(name); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-16 14:59:21 +02:00
										 |  |  |     sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0), | 
					
						
							|  |  |  |                           TYPE_CMSDK_APB_TIMER); | 
					
						
							|  |  |  |     sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), | 
					
						
							|  |  |  |                           TYPE_CMSDK_APB_TIMER); | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:42 +01:00
										 |  |  |     sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), | 
					
						
							|  |  |  |                           TYPE_CMSDK_APB_TIMER); | 
					
						
							| 
									
										
										
										
											2018-07-16 14:59:21 +02:00
										 |  |  |     sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:41 +01:00
										 |  |  |                           TYPE_CMSDK_APB_DUALTIMER); | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:42 +01:00
										 |  |  |     sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog, | 
					
						
							|  |  |  |                           sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG); | 
					
						
							|  |  |  |     sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog, | 
					
						
							|  |  |  |                           sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG); | 
					
						
							|  |  |  |     sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog, | 
					
						
							|  |  |  |                           sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG); | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:43 +01:00
										 |  |  |     sysbus_init_child_obj(obj, "iotkit-sysctl", &s->sysctl, | 
					
						
							|  |  |  |                           sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL); | 
					
						
							|  |  |  |     sysbus_init_child_obj(obj, "iotkit-sysinfo", &s->sysinfo, | 
					
						
							|  |  |  |                           sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO); | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:42 +01:00
										 |  |  |     object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, | 
					
						
							|  |  |  |                             sizeof(s->nmi_orgate), TYPE_OR_IRQ, | 
					
						
							|  |  |  |                             &error_abort, NULL); | 
					
						
							| 
									
										
										
										
											2018-07-16 14:59:21 +02:00
										 |  |  |     object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, | 
					
						
							|  |  |  |                             sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, | 
					
						
							|  |  |  |                             &error_abort, NULL); | 
					
						
							|  |  |  |     object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter, | 
					
						
							|  |  |  |                             sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ, | 
					
						
							|  |  |  |                             &error_abort, NULL); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { | 
					
						
							|  |  |  |         char *name = g_strdup_printf("ppc-irq-splitter-%d", i); | 
					
						
							|  |  |  |         SplitIRQ *splitter = &s->ppc_irq_splitter[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-16 14:59:21 +02:00
										 |  |  |         object_initialize_child(obj, name, splitter, sizeof(*splitter), | 
					
						
							|  |  |  |                                 TYPE_SPLIT_IRQ, &error_abort, NULL); | 
					
						
							|  |  |  |         g_free(name); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void iotkit_exp_irq(void *opaque, int n, int level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IoTKit *s = IOTKIT(opaque); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qemu_set_irq(s->exp_irqs[n], level); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-22 13:28:40 +01:00
										 |  |  | static void iotkit_mpcexp_status(void *opaque, int n, int level) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IoTKit *s = IOTKIT(opaque); | 
					
						
							|  |  |  |     qemu_set_irq(s->mpcexp_status_in[n], level); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  | static void iotkit_realize(DeviceState *dev, Error **errp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IoTKit *s = IOTKIT(dev); | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  |     MemoryRegion *mr; | 
					
						
							|  |  |  |     Error *err = NULL; | 
					
						
							|  |  |  |     SysBusDevice *sbd_apb_ppc0; | 
					
						
							|  |  |  |     SysBusDevice *sbd_secctl; | 
					
						
							|  |  |  |     DeviceState *dev_apb_ppc0; | 
					
						
							|  |  |  |     DeviceState *dev_apb_ppc1; | 
					
						
							|  |  |  |     DeviceState *dev_secctl; | 
					
						
							|  |  |  |     DeviceState *dev_splitter; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!s->board_memory) { | 
					
						
							|  |  |  |         error_setg(errp, "memory property was not set"); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!s->mainclk_frq) { | 
					
						
							|  |  |  |         error_setg(errp, "MAINCLK property was not set"); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Handling of which devices should be available only to secure
 | 
					
						
							|  |  |  |      * code is usually done differently for M profile than for A profile. | 
					
						
							|  |  |  |      * Instead of putting some devices only into the secure address space, | 
					
						
							|  |  |  |      * devices exist in both address spaces but with hard-wired security | 
					
						
							|  |  |  |      * permissions that will cause the CPU to fault for non-secure accesses. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * The IoTKit has an IDAU (Implementation Defined Access Unit), | 
					
						
							|  |  |  |      * which specifies hard-wired security permissions for different | 
					
						
							|  |  |  |      * areas of the physical address space. For the IoTKit IDAU, the | 
					
						
							|  |  |  |      * top 4 bits of the physical address are the IDAU region ID, and | 
					
						
							|  |  |  |      * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS | 
					
						
							|  |  |  |      * region, otherwise it is an S region. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * The various devices and RAMs are generally all mapped twice, | 
					
						
							|  |  |  |      * once into a region that the IDAU defines as secure and once | 
					
						
							|  |  |  |      * into a non-secure region. They sit behind either a Memory | 
					
						
							|  |  |  |      * Protection Controller (for RAM) or a Peripheral Protection | 
					
						
							|  |  |  |      * Controller (for devices), which allow a more fine grained | 
					
						
							|  |  |  |      * configuration of whether non-secure accesses are permitted. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * (The other place that guest software can configure security | 
					
						
							|  |  |  |      * permissions is in the architected SAU (Security Attribution | 
					
						
							|  |  |  |      * Unit), which is entirely inside the CPU. The IDAU can upgrade | 
					
						
							|  |  |  |      * the security attributes for a region to more restrictive than | 
					
						
							|  |  |  |      * the SAU specifies, but cannot downgrade them.) | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * 0x10000000..0x1fffffff  alias of 0x00000000..0x0fffffff | 
					
						
							|  |  |  |      * 0x20000000..0x2007ffff  32KB FPGA block RAM | 
					
						
							|  |  |  |      * 0x30000000..0x3fffffff  alias of 0x20000000..0x2fffffff | 
					
						
							|  |  |  |      * 0x40000000..0x4000ffff  base peripheral region 1 | 
					
						
							|  |  |  |      * 0x40010000..0x4001ffff  CPU peripherals (none for IoTKit) | 
					
						
							|  |  |  |      * 0x40020000..0x4002ffff  system control element peripherals | 
					
						
							|  |  |  |      * 0x40080000..0x400fffff  base peripheral region 2 | 
					
						
							|  |  |  |      * 0x50000000..0x5fffffff  alias of 0x40000000..0x4fffffff | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", s->exp_numirq + 32); | 
					
						
							|  |  |  |     /* In real hardware the initial Secure VTOR is set from the INITSVTOR0
 | 
					
						
							|  |  |  |      * register in the IoT Kit System Control Register block, and the | 
					
						
							|  |  |  |      * initial value of that is in turn specifiable by the FPGA that | 
					
						
							|  |  |  |      * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, | 
					
						
							|  |  |  |      * and simply set the CPU's init-svtor to the IoT Kit default value. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor", 0x10000000); | 
					
						
							|  |  |  |     object_property_set_link(OBJECT(&s->armv7m), OBJECT(&s->container), | 
					
						
							|  |  |  |                              "memory", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     object_property_set_link(OBJECT(&s->armv7m), OBJECT(s), "idau", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Connect our EXP_IRQ GPIOs to the NVIC's lines 32 and up. */ | 
					
						
							|  |  |  |     s->exp_irqs = g_new(qemu_irq, s->exp_numirq); | 
					
						
							|  |  |  |     for (i = 0; i < s->exp_numirq; i++) { | 
					
						
							|  |  |  |         s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     qdev_init_gpio_in_named(dev, iotkit_exp_irq, "EXP_IRQ", s->exp_numirq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Set up the big aliases first */ | 
					
						
							|  |  |  |     make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); | 
					
						
							|  |  |  |     make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000); | 
					
						
							|  |  |  |     /* The 0x50000000..0x5fffffff region is not a pure alias: it has
 | 
					
						
							|  |  |  |      * a few extra devices that only appear there (generally the | 
					
						
							|  |  |  |      * control interfaces for the protection controllers). | 
					
						
							|  |  |  |      * We implement this by mapping those devices over the top of this | 
					
						
							|  |  |  |      * alias MR at a higher priority. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Security controller */ | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     sbd_secctl = SYS_BUS_DEVICE(&s->secctl); | 
					
						
							|  |  |  |     dev_secctl = DEVICE(&s->secctl); | 
					
						
							|  |  |  |     sysbus_mmio_map(sbd_secctl, 0, 0x50080000); | 
					
						
							|  |  |  |     sysbus_mmio_map(sbd_secctl, 1, 0x40080000); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1); | 
					
						
							|  |  |  |     qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* The sec_resp_cfg output from the security controller must be split into
 | 
					
						
							|  |  |  |      * multiple lines, one for each of the PPCs within the IoTKit and one | 
					
						
							|  |  |  |      * that will be an output from the IoTKit to the system. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     object_property_set_int(OBJECT(&s->sec_resp_splitter), 3, | 
					
						
							|  |  |  |                             "num-lines", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->sec_resp_splitter), true, | 
					
						
							|  |  |  |                              "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     dev_splitter = DEVICE(&s->sec_resp_splitter); | 
					
						
							|  |  |  |     qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, | 
					
						
							|  |  |  |                                 qdev_get_gpio_in(dev_splitter, 0)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-22 13:28:40 +01:00
										 |  |  |     /* This RAM lives behind the Memory Protection Controller */ | 
					
						
							|  |  |  |     memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     object_property_set_link(OBJECT(&s->mpc), OBJECT(&s->sram0), | 
					
						
							|  |  |  |                              "downstream", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->mpc), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* Map the upstream end of the MPC into the right place... */ | 
					
						
							|  |  |  |     memory_region_add_subregion(&s->container, 0x20000000, | 
					
						
							|  |  |  |                                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), | 
					
						
							|  |  |  |                                                        1)); | 
					
						
							|  |  |  |     /* ...and its register interface */ | 
					
						
							|  |  |  |     memory_region_add_subregion(&s->container, 0x50083000, | 
					
						
							|  |  |  |                                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), | 
					
						
							|  |  |  |                                                        0)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-22 13:28:40 +01:00
										 |  |  |     /* We must OR together lines from the MPC splitters to go to the NVIC */ | 
					
						
							|  |  |  |     object_property_set_int(OBJECT(&s->mpc_irq_orgate), | 
					
						
							|  |  |  |                             IOTS_NUM_EXP_MPC + IOTS_NUM_MPC, "num-lines", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true, | 
					
						
							|  |  |  |                              "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, | 
					
						
							|  |  |  |                           qdev_get_gpio_in(DEVICE(&s->armv7m), 9)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     /* Devices behind APB PPC0:
 | 
					
						
							|  |  |  |      *   0x40000000: timer0 | 
					
						
							|  |  |  |      *   0x40001000: timer1 | 
					
						
							|  |  |  |      *   0x40002000: dual timer | 
					
						
							|  |  |  |      * We must configure and realize each downstream device and connect | 
					
						
							|  |  |  |      * it to the appropriate PPC port; then we can realize the PPC and | 
					
						
							|  |  |  |      * map its upstream ends to the right place in the container. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq); | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, | 
					
						
							|  |  |  |                        qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); | 
					
						
							|  |  |  |     mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0); | 
					
						
							|  |  |  |     object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq); | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, | 
					
						
							| 
									
										
										
										
											2018-07-27 12:38:54 +01:00
										 |  |  |                        qdev_get_gpio_in(DEVICE(&s->armv7m), 4)); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); | 
					
						
							|  |  |  |     object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:41 +01:00
										 |  |  |     sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0, | 
					
						
							|  |  |  |                        qdev_get_gpio_in(DEVICE(&s->armv7m), 5)); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); | 
					
						
							|  |  |  |     object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0); | 
					
						
							|  |  |  |     dev_apb_ppc0 = DEVICE(&s->apb_ppc0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0); | 
					
						
							|  |  |  |     memory_region_add_subregion(&s->container, 0x40000000, mr); | 
					
						
							|  |  |  |     mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1); | 
					
						
							|  |  |  |     memory_region_add_subregion(&s->container, 0x40001000, mr); | 
					
						
							|  |  |  |     mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2); | 
					
						
							|  |  |  |     memory_region_add_subregion(&s->container, 0x40002000, mr); | 
					
						
							|  |  |  |     for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) { | 
					
						
							|  |  |  |         qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i, | 
					
						
							|  |  |  |                                     qdev_get_gpio_in_named(dev_apb_ppc0, | 
					
						
							|  |  |  |                                                            "cfg_nonsec", i)); | 
					
						
							|  |  |  |         qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i, | 
					
						
							|  |  |  |                                     qdev_get_gpio_in_named(dev_apb_ppc0, | 
					
						
							|  |  |  |                                                            "cfg_ap", i)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0, | 
					
						
							|  |  |  |                                 qdev_get_gpio_in_named(dev_apb_ppc0, | 
					
						
							|  |  |  |                                                        "irq_enable", 0)); | 
					
						
							|  |  |  |     qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0, | 
					
						
							|  |  |  |                                 qdev_get_gpio_in_named(dev_apb_ppc0, | 
					
						
							|  |  |  |                                                        "irq_clear", 0)); | 
					
						
							|  |  |  |     qdev_connect_gpio_out(dev_splitter, 0, | 
					
						
							|  |  |  |                           qdev_get_gpio_in_named(dev_apb_ppc0, | 
					
						
							|  |  |  |                                                  "cfg_sec_resp", 0)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* All the PPC irq lines (from the 2 internal PPCs and the 8 external
 | 
					
						
							|  |  |  |      * ones) are sent individually to the security controller, and also | 
					
						
							|  |  |  |      * ORed together to give a single combined PPC interrupt to the NVIC. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     object_property_set_int(OBJECT(&s->ppc_irq_orgate), | 
					
						
							|  |  |  |                             NUM_PPCS, "num-lines", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true, | 
					
						
							|  |  |  |                              "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, | 
					
						
							|  |  |  |                           qdev_get_gpio_in(DEVICE(&s->armv7m), 10)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 0x40020000 .. 0x4002ffff : IoTKit system control peripheral region */ | 
					
						
							|  |  |  |     /* Devices behind APB PPC1:
 | 
					
						
							|  |  |  |      *   0x4002f000: S32K timer | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:42 +01:00
										 |  |  |     qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:42 +01:00
										 |  |  |     sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0, | 
					
						
							|  |  |  |                        qdev_get_gpio_in(DEVICE(&s->armv7m), 2)); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); | 
					
						
							|  |  |  |     object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0); | 
					
						
							|  |  |  |     memory_region_add_subregion(&s->container, 0x4002f000, mr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dev_apb_ppc1 = DEVICE(&s->apb_ppc1); | 
					
						
							|  |  |  |     qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0, | 
					
						
							|  |  |  |                                 qdev_get_gpio_in_named(dev_apb_ppc1, | 
					
						
							|  |  |  |                                                        "cfg_nonsec", 0)); | 
					
						
							|  |  |  |     qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0, | 
					
						
							|  |  |  |                                 qdev_get_gpio_in_named(dev_apb_ppc1, | 
					
						
							|  |  |  |                                                        "cfg_ap", 0)); | 
					
						
							|  |  |  |     qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0, | 
					
						
							|  |  |  |                                 qdev_get_gpio_in_named(dev_apb_ppc1, | 
					
						
							|  |  |  |                                                        "irq_enable", 0)); | 
					
						
							|  |  |  |     qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0, | 
					
						
							|  |  |  |                                 qdev_get_gpio_in_named(dev_apb_ppc1, | 
					
						
							|  |  |  |                                                        "irq_clear", 0)); | 
					
						
							|  |  |  |     qdev_connect_gpio_out(dev_splitter, 1, | 
					
						
							|  |  |  |                           qdev_get_gpio_in_named(dev_apb_ppc1, | 
					
						
							|  |  |  |                                                  "cfg_sec_resp", 0)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:43 +01:00
										 |  |  |     object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* System information registers */ | 
					
						
							|  |  |  |     sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000); | 
					
						
							|  |  |  |     /* System control registers */ | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000); | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:42 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* This OR gate wires together outputs from the secure watchdogs to NMI */ | 
					
						
							|  |  |  |     object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0, | 
					
						
							|  |  |  |                           qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK); | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, | 
					
						
							|  |  |  |                        qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0)); | 
					
						
							|  |  |  |     sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:42 +01:00
										 |  |  |     qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq); | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0, | 
					
						
							|  |  |  |                        qdev_get_gpio_in(DEVICE(&s->armv7m), 1)); | 
					
						
							|  |  |  |     sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq); | 
					
						
							|  |  |  |     object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_propagate(errp, err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0, | 
					
						
							|  |  |  |                        qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1)); | 
					
						
							|  |  |  |     sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { | 
					
						
							|  |  |  |         Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         object_property_set_int(splitter, 2, "num-lines", &err); | 
					
						
							|  |  |  |         if (err) { | 
					
						
							|  |  |  |             error_propagate(errp, err); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         object_property_set_bool(splitter, true, "realized", &err); | 
					
						
							|  |  |  |         if (err) { | 
					
						
							|  |  |  |             error_propagate(errp, err); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { | 
					
						
							|  |  |  |         char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         iotkit_forward_ppc(s, ppcname, i); | 
					
						
							|  |  |  |         g_free(ppcname); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { | 
					
						
							|  |  |  |         char *ppcname = g_strdup_printf("apb_ppcexp%d", i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         iotkit_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); | 
					
						
							|  |  |  |         g_free(ppcname); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) { | 
					
						
							|  |  |  |         /* Wire up IRQ splitter for internal PPCs */ | 
					
						
							|  |  |  |         DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]); | 
					
						
							|  |  |  |         char *gpioname = g_strdup_printf("apb_ppc%d_irq_status", | 
					
						
							|  |  |  |                                          i - NUM_EXTERNAL_PPCS); | 
					
						
							|  |  |  |         TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         qdev_connect_gpio_out(devs, 0, | 
					
						
							|  |  |  |                               qdev_get_gpio_in_named(dev_secctl, gpioname, 0)); | 
					
						
							|  |  |  |         qdev_connect_gpio_out(devs, 1, | 
					
						
							|  |  |  |                               qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); | 
					
						
							|  |  |  |         qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, | 
					
						
							|  |  |  |                                     qdev_get_gpio_in(devs, 0)); | 
					
						
							| 
									
										
										
										
											2018-05-10 18:10:56 +01:00
										 |  |  |         g_free(gpioname); | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-22 13:28:40 +01:00
										 |  |  |     /* Wire up the splitters for the MPC IRQs */ | 
					
						
							|  |  |  |     for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) { | 
					
						
							|  |  |  |         SplitIRQ *splitter = &s->mpc_irq_splitter[i]; | 
					
						
							|  |  |  |         DeviceState *dev_splitter = DEVICE(splitter); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         object_property_set_int(OBJECT(splitter), 2, "num-lines", &err); | 
					
						
							|  |  |  |         if (err) { | 
					
						
							|  |  |  |             error_propagate(errp, err); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         object_property_set_bool(OBJECT(splitter), true, "realized", &err); | 
					
						
							|  |  |  |         if (err) { | 
					
						
							|  |  |  |             error_propagate(errp, err); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (i < IOTS_NUM_EXP_MPC) { | 
					
						
							|  |  |  |             /* Splitter input is from GPIO input line */ | 
					
						
							|  |  |  |             s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); | 
					
						
							|  |  |  |             qdev_connect_gpio_out(dev_splitter, 0, | 
					
						
							|  |  |  |                                   qdev_get_gpio_in_named(dev_secctl, | 
					
						
							|  |  |  |                                                          "mpcexp_status", i)); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             /* Splitter input is from our own MPC */ | 
					
						
							|  |  |  |             qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0, | 
					
						
							|  |  |  |                                         qdev_get_gpio_in(dev_splitter, 0)); | 
					
						
							|  |  |  |             qdev_connect_gpio_out(dev_splitter, 0, | 
					
						
							|  |  |  |                                   qdev_get_gpio_in_named(dev_secctl, | 
					
						
							|  |  |  |                                                          "mpc_status", 0)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         qdev_connect_gpio_out(dev_splitter, 1, | 
					
						
							|  |  |  |                               qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* Create GPIO inputs which will pass the line state for our
 | 
					
						
							|  |  |  |      * mpcexp_irq inputs to the correct splitter devices. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status", | 
					
						
							|  |  |  |                             IOTS_NUM_EXP_MPC); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     iotkit_forward_sec_resp_cfg(s); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 13:17:44 +01:00
										 |  |  |     /* Forward the MSC related signals */ | 
					
						
							|  |  |  |     qdev_pass_gpios(dev_secctl, dev, "mscexp_status"); | 
					
						
							|  |  |  |     qdev_pass_gpios(dev_secctl, dev, "mscexp_clear"); | 
					
						
							|  |  |  |     qdev_pass_gpios(dev_secctl, dev, "mscexp_ns"); | 
					
						
							|  |  |  |     qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0, | 
					
						
							|  |  |  |                                 qdev_get_gpio_in(DEVICE(&s->armv7m), 11)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /*
 | 
					
						
							|  |  |  |      * Expose our container region to the board model; this corresponds | 
					
						
							|  |  |  |      * to the AHB Slave Expansion ports which allow bus master devices | 
					
						
							|  |  |  |      * (eg DMA controllers) in the board model to make transactions into | 
					
						
							|  |  |  |      * devices in the IoTKit. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 10:45:40 +00:00
										 |  |  |     system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void iotkit_idau_check(IDAUInterface *ii, uint32_t address, | 
					
						
							|  |  |  |                               int *iregion, bool *exempt, bool *ns, bool *nsc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* For IoTKit systems the IDAU responses are simple logical functions
 | 
					
						
							|  |  |  |      * of the address bits. The NSC attribute is guest-adjustable via the | 
					
						
							|  |  |  |      * NSCCFG register in the security controller. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     IoTKit *s = IOTKIT(ii); | 
					
						
							|  |  |  |     int region = extract32(address, 28, 4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *ns = !(region & 1); | 
					
						
							|  |  |  |     *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2)); | 
					
						
							|  |  |  |     /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ | 
					
						
							|  |  |  |     *exempt = (address & 0xeff00000) == 0xe0000000; | 
					
						
							|  |  |  |     *iregion = region; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const VMStateDescription iotkit_vmstate = { | 
					
						
							|  |  |  |     .name = "iotkit", | 
					
						
							|  |  |  |     .version_id = 1, | 
					
						
							|  |  |  |     .minimum_version_id = 1, | 
					
						
							|  |  |  |     .fields = (VMStateField[]) { | 
					
						
							|  |  |  |         VMSTATE_UINT32(nsccfg, IoTKit), | 
					
						
							|  |  |  |         VMSTATE_END_OF_LIST() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Property iotkit_properties[] = { | 
					
						
							|  |  |  |     DEFINE_PROP_LINK("memory", IoTKit, board_memory, TYPE_MEMORY_REGION, | 
					
						
							|  |  |  |                      MemoryRegion *), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("EXP_NUMIRQ", IoTKit, exp_numirq, 64), | 
					
						
							|  |  |  |     DEFINE_PROP_UINT32("MAINCLK", IoTKit, mainclk_frq, 0), | 
					
						
							|  |  |  |     DEFINE_PROP_END_OF_LIST() | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void iotkit_reset(DeviceState *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     IoTKit *s = IOTKIT(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s->nsccfg = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void iotkit_class_init(ObjectClass *klass, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DeviceClass *dc = DEVICE_CLASS(klass); | 
					
						
							|  |  |  |     IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dc->realize = iotkit_realize; | 
					
						
							|  |  |  |     dc->vmsd = &iotkit_vmstate; | 
					
						
							|  |  |  |     dc->props = iotkit_properties; | 
					
						
							|  |  |  |     dc->reset = iotkit_reset; | 
					
						
							|  |  |  |     iic->check = iotkit_idau_check; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const TypeInfo iotkit_info = { | 
					
						
							|  |  |  |     .name = TYPE_IOTKIT, | 
					
						
							|  |  |  |     .parent = TYPE_SYS_BUS_DEVICE, | 
					
						
							|  |  |  |     .instance_size = sizeof(IoTKit), | 
					
						
							|  |  |  |     .instance_init = iotkit_init, | 
					
						
							|  |  |  |     .class_init = iotkit_class_init, | 
					
						
							|  |  |  |     .interfaces = (InterfaceInfo[]) { | 
					
						
							|  |  |  |         { TYPE_IDAU_INTERFACE }, | 
					
						
							|  |  |  |         { } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void iotkit_register_types(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     type_register_static(&iotkit_info); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type_init(iotkit_register_types); |