14690bf4eSAlistair Francis /* 24690bf4eSAlistair Francis * Xilinx Zynq MPSoC PMU (Power Management Unit) emulation 34690bf4eSAlistair Francis * 44690bf4eSAlistair Francis * Copyright (C) 2017 Xilinx Inc 54690bf4eSAlistair Francis * Written by Alistair Francis <alistair.francis@xilinx.com> 64690bf4eSAlistair Francis * 74690bf4eSAlistair Francis * This program is free software; you can redistribute it and/or modify it 84690bf4eSAlistair Francis * under the terms of the GNU General Public License as published by the 94690bf4eSAlistair Francis * Free Software Foundation; either version 2 of the License, or 104690bf4eSAlistair Francis * (at your option) any later version. 114690bf4eSAlistair Francis * 124690bf4eSAlistair Francis * This program is distributed in the hope that it will be useful, but WITHOUT 134690bf4eSAlistair Francis * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 144690bf4eSAlistair Francis * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 154690bf4eSAlistair Francis * for more details. 164690bf4eSAlistair Francis */ 174690bf4eSAlistair Francis 184690bf4eSAlistair Francis #include "qemu/osdep.h" 194690bf4eSAlistair Francis #include "qapi/error.h" 20133d23b3SAlistair Francis #include "exec/address-spaces.h" 214690bf4eSAlistair Francis #include "hw/boards.h" 224690bf4eSAlistair Francis #include "cpu.h" 23133d23b3SAlistair Francis #include "boot.h" 244690bf4eSAlistair Francis 2507b30201SAlistair Francis #include "hw/intc/xlnx-zynqmp-ipi.h" 26633a91b6SAlistair Francis #include "hw/intc/xlnx-pmu-iomod-intc.h" 27633a91b6SAlistair Francis 284690bf4eSAlistair Francis /* Define the PMU device */ 294690bf4eSAlistair Francis 304690bf4eSAlistair Francis #define TYPE_XLNX_ZYNQMP_PMU_SOC "xlnx,zynqmp-pmu-soc" 314690bf4eSAlistair Francis #define XLNX_ZYNQMP_PMU_SOC(obj) OBJECT_CHECK(XlnxZynqMPPMUSoCState, (obj), \ 324690bf4eSAlistair Francis TYPE_XLNX_ZYNQMP_PMU_SOC) 334690bf4eSAlistair Francis 34133d23b3SAlistair Francis #define XLNX_ZYNQMP_PMU_ROM_SIZE 0x8000 35133d23b3SAlistair Francis #define XLNX_ZYNQMP_PMU_ROM_ADDR 0xFFD00000 36133d23b3SAlistair Francis #define XLNX_ZYNQMP_PMU_RAM_ADDR 0xFFDC0000 37133d23b3SAlistair Francis 38633a91b6SAlistair Francis #define XLNX_ZYNQMP_PMU_INTC_ADDR 0xFFD40000 39633a91b6SAlistair Francis 4007b30201SAlistair Francis #define XLNX_ZYNQMP_PMU_NUM_IPIS 4 4107b30201SAlistair Francis 4207b30201SAlistair Francis static const uint64_t ipi_addr[XLNX_ZYNQMP_PMU_NUM_IPIS] = { 4307b30201SAlistair Francis 0xFF340000, 0xFF350000, 0xFF360000, 0xFF370000, 4407b30201SAlistair Francis }; 4507b30201SAlistair Francis static const uint64_t ipi_irq[XLNX_ZYNQMP_PMU_NUM_IPIS] = { 4607b30201SAlistair Francis 19, 20, 21, 22, 4707b30201SAlistair Francis }; 4807b30201SAlistair Francis 494690bf4eSAlistair Francis typedef struct XlnxZynqMPPMUSoCState { 504690bf4eSAlistair Francis /*< private >*/ 514690bf4eSAlistair Francis DeviceState parent_obj; 524690bf4eSAlistair Francis 534690bf4eSAlistair Francis /*< public >*/ 54133d23b3SAlistair Francis MicroBlazeCPU cpu; 55633a91b6SAlistair Francis XlnxPMUIOIntc intc; 56a8ae92e0SPhilippe Mathieu-Daudé XlnxZynqMPIPI ipi[XLNX_ZYNQMP_PMU_NUM_IPIS]; 574690bf4eSAlistair Francis } XlnxZynqMPPMUSoCState; 584690bf4eSAlistair Francis 59633a91b6SAlistair Francis 604690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_init(Object *obj) 614690bf4eSAlistair Francis { 62133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *s = XLNX_ZYNQMP_PMU_SOC(obj); 634690bf4eSAlistair Francis 64*9fc7fc4dSMarkus Armbruster object_initialize_child(obj, "pmu-cpu", &s->cpu, TYPE_MICROBLAZE_CPU); 65633a91b6SAlistair Francis 667b6d7b84SThomas Huth sysbus_init_child_obj(obj, "intc", &s->intc, sizeof(s->intc), 677b6d7b84SThomas Huth TYPE_XLNX_PMU_IO_INTC); 68da4aeff9SPhilippe Mathieu-Daudé 69da4aeff9SPhilippe Mathieu-Daudé /* Create the IPI device */ 70da4aeff9SPhilippe Mathieu-Daudé for (int i = 0; i < XLNX_ZYNQMP_PMU_NUM_IPIS; i++) { 71ff5d4dc9SPhilippe Mathieu-Daudé char *name = g_strdup_printf("ipi%d", i); 72ff5d4dc9SPhilippe Mathieu-Daudé sysbus_init_child_obj(obj, name, &s->ipi[i], 73ff5d4dc9SPhilippe Mathieu-Daudé sizeof(XlnxZynqMPIPI), TYPE_XLNX_ZYNQMP_IPI); 74ff5d4dc9SPhilippe Mathieu-Daudé g_free(name); 75da4aeff9SPhilippe Mathieu-Daudé } 764690bf4eSAlistair Francis } 774690bf4eSAlistair Francis 784690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_realize(DeviceState *dev, Error **errp) 794690bf4eSAlistair Francis { 80133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *s = XLNX_ZYNQMP_PMU_SOC(dev); 81133d23b3SAlistair Francis Error *err = NULL; 824690bf4eSAlistair Francis 83133d23b3SAlistair Francis object_property_set_uint(OBJECT(&s->cpu), XLNX_ZYNQMP_PMU_ROM_ADDR, 84133d23b3SAlistair Francis "base-vectors", &error_abort); 85133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "use-stack-protection", 86133d23b3SAlistair Francis &error_abort); 87133d23b3SAlistair Francis object_property_set_uint(OBJECT(&s->cpu), 0, "use-fpu", &error_abort); 88133d23b3SAlistair Francis object_property_set_uint(OBJECT(&s->cpu), 0, "use-hw-mul", &error_abort); 89133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "use-barrel", 90133d23b3SAlistair Francis &error_abort); 91133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "use-msr-instr", 92133d23b3SAlistair Francis &error_abort); 93133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "use-pcmp-instr", 94133d23b3SAlistair Francis &error_abort); 95133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), false, "use-mmu", &error_abort); 96133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "endianness", 97133d23b3SAlistair Francis &error_abort); 98133d23b3SAlistair Francis object_property_set_str(OBJECT(&s->cpu), "8.40.b", "version", 99133d23b3SAlistair Francis &error_abort); 100133d23b3SAlistair Francis object_property_set_uint(OBJECT(&s->cpu), 0, "pvr", &error_abort); 101133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); 102133d23b3SAlistair Francis if (err) { 103133d23b3SAlistair Francis error_propagate(errp, err); 104133d23b3SAlistair Francis return; 105133d23b3SAlistair Francis } 106633a91b6SAlistair Francis 107633a91b6SAlistair Francis object_property_set_uint(OBJECT(&s->intc), 0x10, "intc-intr-size", 108633a91b6SAlistair Francis &error_abort); 109633a91b6SAlistair Francis object_property_set_uint(OBJECT(&s->intc), 0x0, "intc-level-edge", 110633a91b6SAlistair Francis &error_abort); 111633a91b6SAlistair Francis object_property_set_uint(OBJECT(&s->intc), 0xffff, "intc-positive", 112633a91b6SAlistair Francis &error_abort); 113633a91b6SAlistair Francis object_property_set_bool(OBJECT(&s->intc), true, "realized", &err); 114633a91b6SAlistair Francis if (err) { 115633a91b6SAlistair Francis error_propagate(errp, err); 116633a91b6SAlistair Francis return; 117633a91b6SAlistair Francis } 118633a91b6SAlistair Francis sysbus_mmio_map(SYS_BUS_DEVICE(&s->intc), 0, XLNX_ZYNQMP_PMU_INTC_ADDR); 119633a91b6SAlistair Francis sysbus_connect_irq(SYS_BUS_DEVICE(&s->intc), 0, 120633a91b6SAlistair Francis qdev_get_gpio_in(DEVICE(&s->cpu), MB_CPU_IRQ)); 121da4aeff9SPhilippe Mathieu-Daudé 122da4aeff9SPhilippe Mathieu-Daudé /* Connect the IPI device */ 123da4aeff9SPhilippe Mathieu-Daudé for (int i = 0; i < XLNX_ZYNQMP_PMU_NUM_IPIS; i++) { 124da4aeff9SPhilippe Mathieu-Daudé object_property_set_bool(OBJECT(&s->ipi[i]), true, "realized", 125da4aeff9SPhilippe Mathieu-Daudé &error_abort); 126da4aeff9SPhilippe Mathieu-Daudé sysbus_mmio_map(SYS_BUS_DEVICE(&s->ipi[i]), 0, ipi_addr[i]); 127da4aeff9SPhilippe Mathieu-Daudé sysbus_connect_irq(SYS_BUS_DEVICE(&s->ipi[i]), 0, 128da4aeff9SPhilippe Mathieu-Daudé qdev_get_gpio_in(DEVICE(&s->intc), ipi_irq[i])); 129da4aeff9SPhilippe Mathieu-Daudé } 1304690bf4eSAlistair Francis } 1314690bf4eSAlistair Francis 1324690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, void *data) 1334690bf4eSAlistair Francis { 1344690bf4eSAlistair Francis DeviceClass *dc = DEVICE_CLASS(oc); 1354690bf4eSAlistair Francis 1364690bf4eSAlistair Francis dc->realize = xlnx_zynqmp_pmu_soc_realize; 1374690bf4eSAlistair Francis } 1384690bf4eSAlistair Francis 1394690bf4eSAlistair Francis static const TypeInfo xlnx_zynqmp_pmu_soc_type_info = { 1404690bf4eSAlistair Francis .name = TYPE_XLNX_ZYNQMP_PMU_SOC, 1414690bf4eSAlistair Francis .parent = TYPE_DEVICE, 1424690bf4eSAlistair Francis .instance_size = sizeof(XlnxZynqMPPMUSoCState), 1434690bf4eSAlistair Francis .instance_init = xlnx_zynqmp_pmu_soc_init, 1444690bf4eSAlistair Francis .class_init = xlnx_zynqmp_pmu_soc_class_init, 1454690bf4eSAlistair Francis }; 1464690bf4eSAlistair Francis 1474690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_register_types(void) 1484690bf4eSAlistair Francis { 1494690bf4eSAlistair Francis type_register_static(&xlnx_zynqmp_pmu_soc_type_info); 1504690bf4eSAlistair Francis } 1514690bf4eSAlistair Francis 1524690bf4eSAlistair Francis type_init(xlnx_zynqmp_pmu_soc_register_types) 1534690bf4eSAlistair Francis 1544690bf4eSAlistair Francis /* Define the PMU Machine */ 1554690bf4eSAlistair Francis 1564690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_init(MachineState *machine) 1574690bf4eSAlistair Francis { 158133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *pmu = g_new0(XlnxZynqMPPMUSoCState, 1); 159133d23b3SAlistair Francis MemoryRegion *address_space_mem = get_system_memory(); 160133d23b3SAlistair Francis MemoryRegion *pmu_rom = g_new(MemoryRegion, 1); 161133d23b3SAlistair Francis MemoryRegion *pmu_ram = g_new(MemoryRegion, 1); 1624690bf4eSAlistair Francis 163133d23b3SAlistair Francis /* Create the ROM */ 164133d23b3SAlistair Francis memory_region_init_rom(pmu_rom, NULL, "xlnx-zynqmp-pmu.rom", 165133d23b3SAlistair Francis XLNX_ZYNQMP_PMU_ROM_SIZE, &error_fatal); 166133d23b3SAlistair Francis memory_region_add_subregion(address_space_mem, XLNX_ZYNQMP_PMU_ROM_ADDR, 167133d23b3SAlistair Francis pmu_rom); 168133d23b3SAlistair Francis 169133d23b3SAlistair Francis /* Create the RAM */ 170133d23b3SAlistair Francis memory_region_init_ram(pmu_ram, NULL, "xlnx-zynqmp-pmu.ram", 171133d23b3SAlistair Francis machine->ram_size, &error_fatal); 172133d23b3SAlistair Francis memory_region_add_subregion(address_space_mem, XLNX_ZYNQMP_PMU_RAM_ADDR, 173133d23b3SAlistair Francis pmu_ram); 174133d23b3SAlistair Francis 175133d23b3SAlistair Francis /* Create the PMU device */ 176*9fc7fc4dSMarkus Armbruster object_initialize_child(OBJECT(machine), "pmu", pmu, 177*9fc7fc4dSMarkus Armbruster TYPE_XLNX_ZYNQMP_PMU_SOC); 178133d23b3SAlistair Francis object_property_set_bool(OBJECT(pmu), true, "realized", &error_fatal); 179133d23b3SAlistair Francis 180133d23b3SAlistair Francis /* Load the kernel */ 181133d23b3SAlistair Francis microblaze_load_kernel(&pmu->cpu, XLNX_ZYNQMP_PMU_RAM_ADDR, 182133d23b3SAlistair Francis machine->ram_size, 183133d23b3SAlistair Francis machine->initrd_filename, 184133d23b3SAlistair Francis machine->dtb, 185133d23b3SAlistair Francis NULL); 1864690bf4eSAlistair Francis } 1874690bf4eSAlistair Francis 1884690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) 1894690bf4eSAlistair Francis { 1904690bf4eSAlistair Francis mc->desc = "Xilinx ZynqMP PMU machine"; 1914690bf4eSAlistair Francis mc->init = xlnx_zynqmp_pmu_init; 1924690bf4eSAlistair Francis } 1934690bf4eSAlistair Francis 1944690bf4eSAlistair Francis DEFINE_MACHINE("xlnx-zynqmp-pmu", xlnx_zynqmp_pmu_machine_init) 1954690bf4eSAlistair Francis 196