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" 204690bf4eSAlistair Francis #include "qemu-common.h" 21133d23b3SAlistair Francis #include "exec/address-spaces.h" 224690bf4eSAlistair Francis #include "hw/boards.h" 23133d23b3SAlistair Francis #include "hw/qdev-properties.h" 244690bf4eSAlistair Francis #include "cpu.h" 25133d23b3SAlistair Francis #include "boot.h" 264690bf4eSAlistair Francis 2707b30201SAlistair Francis #include "hw/intc/xlnx-zynqmp-ipi.h" 28633a91b6SAlistair Francis #include "hw/intc/xlnx-pmu-iomod-intc.h" 29633a91b6SAlistair Francis 304690bf4eSAlistair Francis /* Define the PMU device */ 314690bf4eSAlistair Francis 324690bf4eSAlistair Francis #define TYPE_XLNX_ZYNQMP_PMU_SOC "xlnx,zynqmp-pmu-soc" 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 514690bf4eSAlistair Francis typedef struct XlnxZynqMPPMUSoCState { 524690bf4eSAlistair Francis /*< private >*/ 534690bf4eSAlistair Francis DeviceState parent_obj; 544690bf4eSAlistair Francis 554690bf4eSAlistair Francis /*< public >*/ 56133d23b3SAlistair Francis MicroBlazeCPU cpu; 57633a91b6SAlistair Francis XlnxPMUIOIntc intc; 584690bf4eSAlistair Francis } XlnxZynqMPPMUSoCState; 594690bf4eSAlistair Francis 60633a91b6SAlistair Francis 614690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_init(Object *obj) 624690bf4eSAlistair Francis { 63133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *s = XLNX_ZYNQMP_PMU_SOC(obj); 644690bf4eSAlistair Francis 65*7b6d7b84SThomas Huth object_initialize_child(obj, "pmu-cpu", &s->cpu, sizeof(s->cpu), 66*7b6d7b84SThomas Huth TYPE_MICROBLAZE_CPU, &error_abort, NULL); 67633a91b6SAlistair Francis 68*7b6d7b84SThomas Huth sysbus_init_child_obj(obj, "intc", &s->intc, sizeof(s->intc), 69*7b6d7b84SThomas Huth TYPE_XLNX_PMU_IO_INTC); 704690bf4eSAlistair Francis } 714690bf4eSAlistair Francis 724690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_realize(DeviceState *dev, Error **errp) 734690bf4eSAlistair Francis { 74133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *s = XLNX_ZYNQMP_PMU_SOC(dev); 75133d23b3SAlistair Francis Error *err = NULL; 764690bf4eSAlistair Francis 77133d23b3SAlistair Francis object_property_set_uint(OBJECT(&s->cpu), XLNX_ZYNQMP_PMU_ROM_ADDR, 78133d23b3SAlistair Francis "base-vectors", &error_abort); 79133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "use-stack-protection", 80133d23b3SAlistair Francis &error_abort); 81133d23b3SAlistair Francis object_property_set_uint(OBJECT(&s->cpu), 0, "use-fpu", &error_abort); 82133d23b3SAlistair Francis object_property_set_uint(OBJECT(&s->cpu), 0, "use-hw-mul", &error_abort); 83133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "use-barrel", 84133d23b3SAlistair Francis &error_abort); 85133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "use-msr-instr", 86133d23b3SAlistair Francis &error_abort); 87133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "use-pcmp-instr", 88133d23b3SAlistair Francis &error_abort); 89133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), false, "use-mmu", &error_abort); 90133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "endianness", 91133d23b3SAlistair Francis &error_abort); 92133d23b3SAlistair Francis object_property_set_str(OBJECT(&s->cpu), "8.40.b", "version", 93133d23b3SAlistair Francis &error_abort); 94133d23b3SAlistair Francis object_property_set_uint(OBJECT(&s->cpu), 0, "pvr", &error_abort); 95133d23b3SAlistair Francis object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); 96133d23b3SAlistair Francis if (err) { 97133d23b3SAlistair Francis error_propagate(errp, err); 98133d23b3SAlistair Francis return; 99133d23b3SAlistair Francis } 100633a91b6SAlistair Francis 101633a91b6SAlistair Francis object_property_set_uint(OBJECT(&s->intc), 0x10, "intc-intr-size", 102633a91b6SAlistair Francis &error_abort); 103633a91b6SAlistair Francis object_property_set_uint(OBJECT(&s->intc), 0x0, "intc-level-edge", 104633a91b6SAlistair Francis &error_abort); 105633a91b6SAlistair Francis object_property_set_uint(OBJECT(&s->intc), 0xffff, "intc-positive", 106633a91b6SAlistair Francis &error_abort); 107633a91b6SAlistair Francis object_property_set_bool(OBJECT(&s->intc), true, "realized", &err); 108633a91b6SAlistair Francis if (err) { 109633a91b6SAlistair Francis error_propagate(errp, err); 110633a91b6SAlistair Francis return; 111633a91b6SAlistair Francis } 112633a91b6SAlistair Francis sysbus_mmio_map(SYS_BUS_DEVICE(&s->intc), 0, XLNX_ZYNQMP_PMU_INTC_ADDR); 113633a91b6SAlistair Francis sysbus_connect_irq(SYS_BUS_DEVICE(&s->intc), 0, 114633a91b6SAlistair Francis qdev_get_gpio_in(DEVICE(&s->cpu), MB_CPU_IRQ)); 1154690bf4eSAlistair Francis } 1164690bf4eSAlistair Francis 1174690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, void *data) 1184690bf4eSAlistair Francis { 1194690bf4eSAlistair Francis DeviceClass *dc = DEVICE_CLASS(oc); 1204690bf4eSAlistair Francis 1214690bf4eSAlistair Francis dc->realize = xlnx_zynqmp_pmu_soc_realize; 1224690bf4eSAlistair Francis } 1234690bf4eSAlistair Francis 1244690bf4eSAlistair Francis static const TypeInfo xlnx_zynqmp_pmu_soc_type_info = { 1254690bf4eSAlistair Francis .name = TYPE_XLNX_ZYNQMP_PMU_SOC, 1264690bf4eSAlistair Francis .parent = TYPE_DEVICE, 1274690bf4eSAlistair Francis .instance_size = sizeof(XlnxZynqMPPMUSoCState), 1284690bf4eSAlistair Francis .instance_init = xlnx_zynqmp_pmu_soc_init, 1294690bf4eSAlistair Francis .class_init = xlnx_zynqmp_pmu_soc_class_init, 1304690bf4eSAlistair Francis }; 1314690bf4eSAlistair Francis 1324690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_soc_register_types(void) 1334690bf4eSAlistair Francis { 1344690bf4eSAlistair Francis type_register_static(&xlnx_zynqmp_pmu_soc_type_info); 1354690bf4eSAlistair Francis } 1364690bf4eSAlistair Francis 1374690bf4eSAlistair Francis type_init(xlnx_zynqmp_pmu_soc_register_types) 1384690bf4eSAlistair Francis 1394690bf4eSAlistair Francis /* Define the PMU Machine */ 1404690bf4eSAlistair Francis 1414690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_init(MachineState *machine) 1424690bf4eSAlistair Francis { 143133d23b3SAlistair Francis XlnxZynqMPPMUSoCState *pmu = g_new0(XlnxZynqMPPMUSoCState, 1); 144133d23b3SAlistair Francis MemoryRegion *address_space_mem = get_system_memory(); 145133d23b3SAlistair Francis MemoryRegion *pmu_rom = g_new(MemoryRegion, 1); 146133d23b3SAlistair Francis MemoryRegion *pmu_ram = g_new(MemoryRegion, 1); 14707b30201SAlistair Francis XlnxZynqMPIPI *ipi[XLNX_ZYNQMP_PMU_NUM_IPIS]; 14807b30201SAlistair Francis qemu_irq irq[32]; 14907b30201SAlistair Francis int i; 1504690bf4eSAlistair Francis 151133d23b3SAlistair Francis /* Create the ROM */ 152133d23b3SAlistair Francis memory_region_init_rom(pmu_rom, NULL, "xlnx-zynqmp-pmu.rom", 153133d23b3SAlistair Francis XLNX_ZYNQMP_PMU_ROM_SIZE, &error_fatal); 154133d23b3SAlistair Francis memory_region_add_subregion(address_space_mem, XLNX_ZYNQMP_PMU_ROM_ADDR, 155133d23b3SAlistair Francis pmu_rom); 156133d23b3SAlistair Francis 157133d23b3SAlistair Francis /* Create the RAM */ 158133d23b3SAlistair Francis memory_region_init_ram(pmu_ram, NULL, "xlnx-zynqmp-pmu.ram", 159133d23b3SAlistair Francis machine->ram_size, &error_fatal); 160133d23b3SAlistair Francis memory_region_add_subregion(address_space_mem, XLNX_ZYNQMP_PMU_RAM_ADDR, 161133d23b3SAlistair Francis pmu_ram); 162133d23b3SAlistair Francis 163133d23b3SAlistair Francis /* Create the PMU device */ 164133d23b3SAlistair Francis object_initialize(pmu, sizeof(XlnxZynqMPPMUSoCState), TYPE_XLNX_ZYNQMP_PMU_SOC); 165133d23b3SAlistair Francis object_property_add_child(OBJECT(machine), "pmu", OBJECT(pmu), 166133d23b3SAlistair Francis &error_abort); 167133d23b3SAlistair Francis object_property_set_bool(OBJECT(pmu), true, "realized", &error_fatal); 168133d23b3SAlistair Francis 16907b30201SAlistair Francis for (i = 0; i < 32; i++) { 17007b30201SAlistair Francis irq[i] = qdev_get_gpio_in(DEVICE(&pmu->intc), i); 17107b30201SAlistair Francis } 17207b30201SAlistair Francis 17307b30201SAlistair Francis /* Create and connect the IPI device */ 17407b30201SAlistair Francis for (i = 0; i < XLNX_ZYNQMP_PMU_NUM_IPIS; i++) { 17507b30201SAlistair Francis ipi[i] = g_new0(XlnxZynqMPIPI, 1); 17607b30201SAlistair Francis object_initialize(ipi[i], sizeof(XlnxZynqMPIPI), TYPE_XLNX_ZYNQMP_IPI); 17707b30201SAlistair Francis qdev_set_parent_bus(DEVICE(ipi[i]), sysbus_get_default()); 17807b30201SAlistair Francis } 17907b30201SAlistair Francis 18007b30201SAlistair Francis for (i = 0; i < XLNX_ZYNQMP_PMU_NUM_IPIS; i++) { 18107b30201SAlistair Francis object_property_set_bool(OBJECT(ipi[i]), true, "realized", 18207b30201SAlistair Francis &error_abort); 18307b30201SAlistair Francis sysbus_mmio_map(SYS_BUS_DEVICE(ipi[i]), 0, ipi_addr[i]); 18407b30201SAlistair Francis sysbus_connect_irq(SYS_BUS_DEVICE(ipi[i]), 0, irq[ipi_irq[i]]); 18507b30201SAlistair Francis } 18607b30201SAlistair Francis 187133d23b3SAlistair Francis /* Load the kernel */ 188133d23b3SAlistair Francis microblaze_load_kernel(&pmu->cpu, XLNX_ZYNQMP_PMU_RAM_ADDR, 189133d23b3SAlistair Francis machine->ram_size, 190133d23b3SAlistair Francis machine->initrd_filename, 191133d23b3SAlistair Francis machine->dtb, 192133d23b3SAlistair Francis NULL); 1934690bf4eSAlistair Francis } 1944690bf4eSAlistair Francis 1954690bf4eSAlistair Francis static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) 1964690bf4eSAlistair Francis { 1974690bf4eSAlistair Francis mc->desc = "Xilinx ZynqMP PMU machine"; 1984690bf4eSAlistair Francis mc->init = xlnx_zynqmp_pmu_init; 1994690bf4eSAlistair Francis } 2004690bf4eSAlistair Francis 2014690bf4eSAlistair Francis DEFINE_MACHINE("xlnx-zynqmp-pmu", xlnx_zynqmp_pmu_machine_init) 2024690bf4eSAlistair Francis 203