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 649fc7fc4dSMarkus Armbruster object_initialize_child(obj, "pmu-cpu", &s->cpu, TYPE_MICROBLAZE_CPU); 65633a91b6SAlistair Francis 66db873cc5SMarkus Armbruster object_initialize_child(obj, "intc", &s->intc, TYPE_XLNX_PMU_IO_INTC); 67da4aeff9SPhilippe Mathieu-Daudé 68da4aeff9SPhilippe Mathieu-Daudé /* Create the IPI device */ 69da4aeff9SPhilippe Mathieu-Daudé for (int i = 0; i < XLNX_ZYNQMP_PMU_NUM_IPIS; i++) { 70ff5d4dc9SPhilippe Mathieu-Daudé char *name = g_strdup_printf("ipi%d", i); 71db873cc5SMarkus Armbruster object_initialize_child(obj, name, &s->ipi[i], TYPE_XLNX_ZYNQMP_IPI); 72ff5d4dc9SPhilippe Mathieu-Daudé g_free(name); 73da4aeff9SPhilippe Mathieu-Daudé } 744690bf4eSAlistair Francis } 754690bf4eSAlistair Francis 764690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_realize(DeviceState *dev, Error **errp) 774690bf4eSAlistair Francis { 78133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *s = XLNX_ZYNQMP_PMU_SOC(dev); 794690bf4eSAlistair Francis 805325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->cpu), "base-vectors", 815325cc34SMarkus Armbruster XLNX_ZYNQMP_PMU_ROM_ADDR, &error_abort); 825325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-stack-protection", true, 83133d23b3SAlistair Francis &error_abort); 845325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->cpu), "use-fpu", 0, &error_abort); 855325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->cpu), "use-hw-mul", 0, &error_abort); 865325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-barrel", true, 87133d23b3SAlistair Francis &error_abort); 885325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-msr-instr", true, 89133d23b3SAlistair Francis &error_abort); 905325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-pcmp-instr", true, 91133d23b3SAlistair Francis &error_abort); 925325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "use-mmu", false, &error_abort); 935325cc34SMarkus Armbruster object_property_set_bool(OBJECT(&s->cpu), "endianness", true, 94133d23b3SAlistair Francis &error_abort); 955325cc34SMarkus Armbruster object_property_set_str(OBJECT(&s->cpu), "version", "8.40.b", 96133d23b3SAlistair Francis &error_abort); 975325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->cpu), "pvr", 0, &error_abort); 98*668f62ecSMarkus Armbruster if (!qdev_realize(DEVICE(&s->cpu), NULL, errp)) { 99133d23b3SAlistair Francis return; 100133d23b3SAlistair Francis } 101633a91b6SAlistair Francis 1025325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->intc), "intc-intr-size", 0x10, 103633a91b6SAlistair Francis &error_abort); 1045325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->intc), "intc-level-edge", 0x0, 105633a91b6SAlistair Francis &error_abort); 1065325cc34SMarkus Armbruster object_property_set_uint(OBJECT(&s->intc), "intc-positive", 0xffff, 107633a91b6SAlistair Francis &error_abort); 108*668f62ecSMarkus Armbruster if (!sysbus_realize(SYS_BUS_DEVICE(&s->intc), errp)) { 109633a91b6SAlistair Francis return; 110633a91b6SAlistair Francis } 111633a91b6SAlistair Francis sysbus_mmio_map(SYS_BUS_DEVICE(&s->intc), 0, XLNX_ZYNQMP_PMU_INTC_ADDR); 112633a91b6SAlistair Francis sysbus_connect_irq(SYS_BUS_DEVICE(&s->intc), 0, 113633a91b6SAlistair Francis qdev_get_gpio_in(DEVICE(&s->cpu), MB_CPU_IRQ)); 114da4aeff9SPhilippe Mathieu-Daudé 115da4aeff9SPhilippe Mathieu-Daudé /* Connect the IPI device */ 116da4aeff9SPhilippe Mathieu-Daudé for (int i = 0; i < XLNX_ZYNQMP_PMU_NUM_IPIS; i++) { 117db873cc5SMarkus Armbruster sysbus_realize(SYS_BUS_DEVICE(&s->ipi[i]), &error_abort); 118da4aeff9SPhilippe Mathieu-Daudé sysbus_mmio_map(SYS_BUS_DEVICE(&s->ipi[i]), 0, ipi_addr[i]); 119da4aeff9SPhilippe Mathieu-Daudé sysbus_connect_irq(SYS_BUS_DEVICE(&s->ipi[i]), 0, 120da4aeff9SPhilippe Mathieu-Daudé qdev_get_gpio_in(DEVICE(&s->intc), ipi_irq[i])); 121da4aeff9SPhilippe Mathieu-Daudé } 1224690bf4eSAlistair Francis } 1234690bf4eSAlistair Francis 1244690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, void *data) 1254690bf4eSAlistair Francis { 1264690bf4eSAlistair Francis DeviceClass *dc = DEVICE_CLASS(oc); 1274690bf4eSAlistair Francis 1284690bf4eSAlistair Francis dc->realize = xlnx_zynqmp_pmu_soc_realize; 1294690bf4eSAlistair Francis } 1304690bf4eSAlistair Francis 1314690bf4eSAlistair Francis static const TypeInfo xlnx_zynqmp_pmu_soc_type_info = { 1324690bf4eSAlistair Francis .name = TYPE_XLNX_ZYNQMP_PMU_SOC, 1334690bf4eSAlistair Francis .parent = TYPE_DEVICE, 1344690bf4eSAlistair Francis .instance_size = sizeof(XlnxZynqMPPMUSoCState), 1354690bf4eSAlistair Francis .instance_init = xlnx_zynqmp_pmu_soc_init, 1364690bf4eSAlistair Francis .class_init = xlnx_zynqmp_pmu_soc_class_init, 1374690bf4eSAlistair Francis }; 1384690bf4eSAlistair Francis 1394690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_register_types(void) 1404690bf4eSAlistair Francis { 1414690bf4eSAlistair Francis type_register_static(&xlnx_zynqmp_pmu_soc_type_info); 1424690bf4eSAlistair Francis } 1434690bf4eSAlistair Francis 1444690bf4eSAlistair Francis type_init(xlnx_zynqmp_pmu_soc_register_types) 1454690bf4eSAlistair Francis 1464690bf4eSAlistair Francis /* Define the PMU Machine */ 1474690bf4eSAlistair Francis 1484690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_init(MachineState *machine) 1494690bf4eSAlistair Francis { 150133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *pmu = g_new0(XlnxZynqMPPMUSoCState, 1); 151133d23b3SAlistair Francis MemoryRegion *address_space_mem = get_system_memory(); 152133d23b3SAlistair Francis MemoryRegion *pmu_rom = g_new(MemoryRegion, 1); 153133d23b3SAlistair Francis MemoryRegion *pmu_ram = g_new(MemoryRegion, 1); 1544690bf4eSAlistair Francis 155133d23b3SAlistair Francis /* Create the ROM */ 156133d23b3SAlistair Francis memory_region_init_rom(pmu_rom, NULL, "xlnx-zynqmp-pmu.rom", 157133d23b3SAlistair Francis XLNX_ZYNQMP_PMU_ROM_SIZE, &error_fatal); 158133d23b3SAlistair Francis memory_region_add_subregion(address_space_mem, XLNX_ZYNQMP_PMU_ROM_ADDR, 159133d23b3SAlistair Francis pmu_rom); 160133d23b3SAlistair Francis 161133d23b3SAlistair Francis /* Create the RAM */ 162133d23b3SAlistair Francis memory_region_init_ram(pmu_ram, NULL, "xlnx-zynqmp-pmu.ram", 163133d23b3SAlistair Francis machine->ram_size, &error_fatal); 164133d23b3SAlistair Francis memory_region_add_subregion(address_space_mem, XLNX_ZYNQMP_PMU_RAM_ADDR, 165133d23b3SAlistair Francis pmu_ram); 166133d23b3SAlistair Francis 167133d23b3SAlistair Francis /* Create the PMU device */ 1689fc7fc4dSMarkus Armbruster object_initialize_child(OBJECT(machine), "pmu", pmu, 1699fc7fc4dSMarkus Armbruster TYPE_XLNX_ZYNQMP_PMU_SOC); 170ce189ab2SMarkus Armbruster qdev_realize(DEVICE(pmu), NULL, &error_fatal); 171133d23b3SAlistair Francis 172133d23b3SAlistair Francis /* Load the kernel */ 173133d23b3SAlistair Francis microblaze_load_kernel(&pmu->cpu, XLNX_ZYNQMP_PMU_RAM_ADDR, 174133d23b3SAlistair Francis machine->ram_size, 175133d23b3SAlistair Francis machine->initrd_filename, 176133d23b3SAlistair Francis machine->dtb, 177133d23b3SAlistair Francis NULL); 1784690bf4eSAlistair Francis } 1794690bf4eSAlistair Francis 1804690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) 1814690bf4eSAlistair Francis { 1824690bf4eSAlistair Francis mc->desc = "Xilinx ZynqMP PMU machine"; 1834690bf4eSAlistair Francis mc->init = xlnx_zynqmp_pmu_init; 1844690bf4eSAlistair Francis } 1854690bf4eSAlistair Francis 1864690bf4eSAlistair Francis DEFINE_MACHINE("xlnx-zynqmp-pmu", xlnx_zynqmp_pmu_machine_init) 1874690bf4eSAlistair Francis 188