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" 27*db1015e9SEduardo Habkost #include "qom/object.h" 28633a91b6SAlistair Francis 294690bf4eSAlistair Francis /* Define the PMU device */ 304690bf4eSAlistair Francis 314690bf4eSAlistair Francis #define TYPE_XLNX_ZYNQMP_PMU_SOC "xlnx,zynqmp-pmu-soc" 32*db1015e9SEduardo Habkost typedef struct XlnxZynqMPPMUSoCState XlnxZynqMPPMUSoCState; 334690bf4eSAlistair Francis #define XLNX_ZYNQMP_PMU_SOC(obj) OBJECT_CHECK(XlnxZynqMPPMUSoCState, (obj), \ 344690bf4eSAlistair Francis TYPE_XLNX_ZYNQMP_PMU_SOC) 354690bf4eSAlistair Francis 36133d23b3SAlistair Francis #define XLNX_ZYNQMP_PMU_ROM_SIZE 0x8000 37133d23b3SAlistair Francis #define XLNX_ZYNQMP_PMU_ROM_ADDR 0xFFD00000 38133d23b3SAlistair Francis #define XLNX_ZYNQMP_PMU_RAM_ADDR 0xFFDC0000 39133d23b3SAlistair Francis 40633a91b6SAlistair Francis #define XLNX_ZYNQMP_PMU_INTC_ADDR 0xFFD40000 41633a91b6SAlistair Francis 4207b30201SAlistair Francis #define XLNX_ZYNQMP_PMU_NUM_IPIS 4 4307b30201SAlistair Francis 4407b30201SAlistair Francis static const uint64_t ipi_addr[XLNX_ZYNQMP_PMU_NUM_IPIS] = { 4507b30201SAlistair Francis 0xFF340000, 0xFF350000, 0xFF360000, 0xFF370000, 4607b30201SAlistair Francis }; 4707b30201SAlistair Francis static const uint64_t ipi_irq[XLNX_ZYNQMP_PMU_NUM_IPIS] = { 4807b30201SAlistair Francis 19, 20, 21, 22, 4907b30201SAlistair Francis }; 5007b30201SAlistair Francis 51*db1015e9SEduardo Habkost struct XlnxZynqMPPMUSoCState { 524690bf4eSAlistair Francis /*< private >*/ 534690bf4eSAlistair Francis DeviceState parent_obj; 544690bf4eSAlistair Francis 554690bf4eSAlistair Francis /*< public >*/ 56133d23b3SAlistair Francis MicroBlazeCPU cpu; 57633a91b6SAlistair Francis XlnxPMUIOIntc intc; 58a8ae92e0SPhilippe Mathieu-Daudé XlnxZynqMPIPI ipi[XLNX_ZYNQMP_PMU_NUM_IPIS]; 59*db1015e9SEduardo Habkost }; 604690bf4eSAlistair Francis 61633a91b6SAlistair Francis 624690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_init(Object *obj) 634690bf4eSAlistair Francis { 64133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *s = XLNX_ZYNQMP_PMU_SOC(obj); 654690bf4eSAlistair Francis 669fc7fc4dSMarkus Armbruster object_initialize_child(obj, "pmu-cpu", &s->cpu, TYPE_MICROBLAZE_CPU); 67633a91b6SAlistair Francis 68db873cc5SMarkus Armbruster object_initialize_child(obj, "intc", &s->intc, TYPE_XLNX_PMU_IO_INTC); 69da4aeff9SPhilippe Mathieu-Daudé 70da4aeff9SPhilippe Mathieu-Daudé /* Create the IPI device */ 71da4aeff9SPhilippe Mathieu-Daudé for (int i = 0; i < XLNX_ZYNQMP_PMU_NUM_IPIS; i++) { 72ff5d4dc9SPhilippe Mathieu-Daudé char *name = g_strdup_printf("ipi%d", i); 73db873cc5SMarkus Armbruster object_initialize_child(obj, name, &s->ipi[i], 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); 814690bf4eSAlistair Francis 825325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->cpu), "base-vectors", 835325cc34SMarkus Armbruster XLNX_ZYNQMP_PMU_ROM_ADDR, &error_abort); 845325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-stack-protection", true, 85133d23b3SAlistair Francis &error_abort); 865325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->cpu), "use-fpu", 0, &error_abort); 875325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->cpu), "use-hw-mul", 0, &error_abort); 885325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-barrel", true, 89133d23b3SAlistair Francis &error_abort); 905325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-msr-instr", true, 91133d23b3SAlistair Francis &error_abort); 925325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-pcmp-instr", true, 93133d23b3SAlistair Francis &error_abort); 945325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-mmu", false, &error_abort); 955325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "endianness", true, 96133d23b3SAlistair Francis &error_abort); 975325cc34SMarkus Armbruster object_property_set_str(OBJECT(&s->cpu), "version", "8.40.b", 98133d23b3SAlistair Francis &error_abort); 995325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->cpu), "pvr", 0, &error_abort); 100668f62ecSMarkus Armbruster if (!qdev_realize(DEVICE(&s->cpu), NULL, errp)) { 101133d23b3SAlistair Francis return; 102133d23b3SAlistair Francis } 103633a91b6SAlistair Francis 1045325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->intc), "intc-intr-size", 0x10, 105633a91b6SAlistair Francis &error_abort); 1065325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->intc), "intc-level-edge", 0x0, 107633a91b6SAlistair Francis &error_abort); 1085325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->intc), "intc-positive", 0xffff, 109633a91b6SAlistair Francis &error_abort); 110668f62ecSMarkus Armbruster if (!sysbus_realize(SYS_BUS_DEVICE(&s->intc), errp)) { 111633a91b6SAlistair Francis return; 112633a91b6SAlistair Francis } 113633a91b6SAlistair Francis sysbus_mmio_map(SYS_BUS_DEVICE(&s->intc), 0, XLNX_ZYNQMP_PMU_INTC_ADDR); 114633a91b6SAlistair Francis sysbus_connect_irq(SYS_BUS_DEVICE(&s->intc), 0, 115633a91b6SAlistair Francis qdev_get_gpio_in(DEVICE(&s->cpu), MB_CPU_IRQ)); 116da4aeff9SPhilippe Mathieu-Daudé 117da4aeff9SPhilippe Mathieu-Daudé /* Connect the IPI device */ 118da4aeff9SPhilippe Mathieu-Daudé for (int i = 0; i < XLNX_ZYNQMP_PMU_NUM_IPIS; i++) { 119db873cc5SMarkus Armbruster sysbus_realize(SYS_BUS_DEVICE(&s->ipi[i]), &error_abort); 120da4aeff9SPhilippe Mathieu-Daudé sysbus_mmio_map(SYS_BUS_DEVICE(&s->ipi[i]), 0, ipi_addr[i]); 121da4aeff9SPhilippe Mathieu-Daudé sysbus_connect_irq(SYS_BUS_DEVICE(&s->ipi[i]), 0, 122da4aeff9SPhilippe Mathieu-Daudé qdev_get_gpio_in(DEVICE(&s->intc), ipi_irq[i])); 123da4aeff9SPhilippe Mathieu-Daudé } 1244690bf4eSAlistair Francis } 1254690bf4eSAlistair Francis 1264690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, void *data) 1274690bf4eSAlistair Francis { 1284690bf4eSAlistair Francis DeviceClass *dc = DEVICE_CLASS(oc); 1294690bf4eSAlistair Francis 1304690bf4eSAlistair Francis dc->realize = xlnx_zynqmp_pmu_soc_realize; 1314690bf4eSAlistair Francis } 1324690bf4eSAlistair Francis 1334690bf4eSAlistair Francis static const TypeInfo xlnx_zynqmp_pmu_soc_type_info = { 1344690bf4eSAlistair Francis .name = TYPE_XLNX_ZYNQMP_PMU_SOC, 1354690bf4eSAlistair Francis .parent = TYPE_DEVICE, 1364690bf4eSAlistair Francis .instance_size = sizeof(XlnxZynqMPPMUSoCState), 1374690bf4eSAlistair Francis .instance_init = xlnx_zynqmp_pmu_soc_init, 1384690bf4eSAlistair Francis .class_init = xlnx_zynqmp_pmu_soc_class_init, 1394690bf4eSAlistair Francis }; 1404690bf4eSAlistair Francis 1414690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_register_types(void) 1424690bf4eSAlistair Francis { 1434690bf4eSAlistair Francis type_register_static(&xlnx_zynqmp_pmu_soc_type_info); 1444690bf4eSAlistair Francis } 1454690bf4eSAlistair Francis 1464690bf4eSAlistair Francis type_init(xlnx_zynqmp_pmu_soc_register_types) 1474690bf4eSAlistair Francis 1484690bf4eSAlistair Francis /* Define the PMU Machine */ 1494690bf4eSAlistair Francis 1504690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_init(MachineState *machine) 1514690bf4eSAlistair Francis { 152133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *pmu = g_new0(XlnxZynqMPPMUSoCState, 1); 153133d23b3SAlistair Francis MemoryRegion *address_space_mem = get_system_memory(); 154133d23b3SAlistair Francis MemoryRegion *pmu_rom = g_new(MemoryRegion, 1); 155133d23b3SAlistair Francis MemoryRegion *pmu_ram = g_new(MemoryRegion, 1); 1564690bf4eSAlistair Francis 157133d23b3SAlistair Francis /* Create the ROM */ 158133d23b3SAlistair Francis memory_region_init_rom(pmu_rom, NULL, "xlnx-zynqmp-pmu.rom", 159133d23b3SAlistair Francis XLNX_ZYNQMP_PMU_ROM_SIZE, &error_fatal); 160133d23b3SAlistair Francis memory_region_add_subregion(address_space_mem, XLNX_ZYNQMP_PMU_ROM_ADDR, 161133d23b3SAlistair Francis pmu_rom); 162133d23b3SAlistair Francis 163133d23b3SAlistair Francis /* Create the RAM */ 164133d23b3SAlistair Francis memory_region_init_ram(pmu_ram, NULL, "xlnx-zynqmp-pmu.ram", 165133d23b3SAlistair Francis machine->ram_size, &error_fatal); 166133d23b3SAlistair Francis memory_region_add_subregion(address_space_mem, XLNX_ZYNQMP_PMU_RAM_ADDR, 167133d23b3SAlistair Francis pmu_ram); 168133d23b3SAlistair Francis 169133d23b3SAlistair Francis /* Create the PMU device */ 1709fc7fc4dSMarkus Armbruster object_initialize_child(OBJECT(machine), "pmu", pmu, 1719fc7fc4dSMarkus Armbruster TYPE_XLNX_ZYNQMP_PMU_SOC); 172ce189ab2SMarkus Armbruster qdev_realize(DEVICE(pmu), NULL, &error_fatal); 173133d23b3SAlistair Francis 174133d23b3SAlistair Francis /* Load the kernel */ 175133d23b3SAlistair Francis microblaze_load_kernel(&pmu->cpu, XLNX_ZYNQMP_PMU_RAM_ADDR, 176133d23b3SAlistair Francis machine->ram_size, 177133d23b3SAlistair Francis machine->initrd_filename, 178133d23b3SAlistair Francis machine->dtb, 179133d23b3SAlistair Francis NULL); 1804690bf4eSAlistair Francis } 1814690bf4eSAlistair Francis 1824690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) 1834690bf4eSAlistair Francis { 1844690bf4eSAlistair Francis mc->desc = "Xilinx ZynqMP PMU machine"; 1854690bf4eSAlistair Francis mc->init = xlnx_zynqmp_pmu_init; 1864690bf4eSAlistair Francis } 1874690bf4eSAlistair Francis 1884690bf4eSAlistair Francis DEFINE_MACHINE("xlnx-zynqmp-pmu", xlnx_zynqmp_pmu_machine_init) 1894690bf4eSAlistair Francis 190