diff options
author | Daniel Baumann <mail@daniel-baumann.ch> | 2025-06-06 10:05:23 +0000 |
---|---|---|
committer | Daniel Baumann <mail@daniel-baumann.ch> | 2025-06-06 10:05:23 +0000 |
commit | 755cc582a2473d06f3a2131d506d0311cc70e9f9 (patch) | |
tree | 3efb1ddb8d57bbb4539ac0d229b384871c57820f /hw/riscv/microchip_pfsoc.c | |
parent | Initial commit. (diff) | |
download | qemu-upstream.tar.xz qemu-upstream.zip |
Adding upstream version 1:7.2+dfsg.upstream/1%7.2+dfsgupstream
Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>
Diffstat (limited to 'hw/riscv/microchip_pfsoc.c')
-rw-r--r-- | hw/riscv/microchip_pfsoc.c | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c new file mode 100644 index 00000000..a821263d --- /dev/null +++ b/hw/riscv/microchip_pfsoc.c @@ -0,0 +1,679 @@ +/* + * QEMU RISC-V Board Compatible with Microchip PolarFire SoC Icicle Kit + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng <bin.meng@windriver.com> + * + * Provides a board compatible with the Microchip PolarFire SoC Icicle Kit + * + * 0) CLINT (Core Level Interruptor) + * 1) PLIC (Platform Level Interrupt Controller) + * 2) eNVM (Embedded Non-Volatile Memory) + * 3) MMUARTs (Multi-Mode UART) + * 4) Cadence eMMC/SDHC controller and an SD card connected to it + * 5) SiFive Platform DMA (Direct Memory Access Controller) + * 6) GEM (Gigabit Ethernet MAC Controller) + * 7) DMC (DDR Memory Controller) + * 8) IOSCB modules + * + * This board currently generates devicetree dynamically that indicates at least + * two harts and up to five harts. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/units.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "chardev/char.h" +#include "hw/cpu/cluster.h" +#include "target/riscv/cpu.h" +#include "hw/misc/unimp.h" +#include "hw/riscv/boot.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/microchip_pfsoc.h" +#include "hw/intc/riscv_aclint.h" +#include "hw/intc/sifive_plic.h" +#include "sysemu/device_tree.h" +#include "sysemu/sysemu.h" + +/* + * The BIOS image used by this machine is called Hart Software Services (HSS). + * See https://github.com/polarfire-soc/hart-software-services + */ +#define BIOS_FILENAME "hss.bin" +#define RESET_VECTOR 0x20220000 + +/* CLINT timebase frequency */ +#define CLINT_TIMEBASE_FREQ 1000000 + +/* GEM version */ +#define GEM_REVISION 0x0107010c + +/* + * The complete description of the whole PolarFire SoC memory map is scattered + * in different documents. There are several places to look at for memory maps: + * + * 1 Chapter 11 "MSS Memory Map", in the doc "UG0880: PolarFire SoC FPGA + * Microprocessor Subsystem (MSS) User Guide", which can be downloaded from + * https://www.microsemi.com/document-portal/doc_download/ + * 1244570-ug0880-polarfire-soc-fpga-microprocessor-subsystem-mss-user-guide, + * describes the whole picture of the PolarFire SoC memory map. + * + * 2 A zip file for PolarFire soC memory map, which can be downloaded from + * https://www.microsemi.com/document-portal/doc_download/ + * 1244581-polarfire-soc-register-map, contains the following 2 major parts: + * - Register Map/PF_SoC_RegMap_V1_1/pfsoc_regmap.htm + * describes the complete integrated peripherals memory map + * - Register Map/PF_SoC_RegMap_V1_1/MPFS250T/mpfs250t_ioscb_memmap_dri.htm + * describes the complete IOSCB modules memory maps + */ +static const MemMapEntry microchip_pfsoc_memmap[] = { + [MICROCHIP_PFSOC_RSVD0] = { 0x0, 0x100 }, + [MICROCHIP_PFSOC_DEBUG] = { 0x100, 0xf00 }, + [MICROCHIP_PFSOC_E51_DTIM] = { 0x1000000, 0x2000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT0] = { 0x1700000, 0x1000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT1] = { 0x1701000, 0x1000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT2] = { 0x1702000, 0x1000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT3] = { 0x1703000, 0x1000 }, + [MICROCHIP_PFSOC_BUSERR_UNIT4] = { 0x1704000, 0x1000 }, + [MICROCHIP_PFSOC_CLINT] = { 0x2000000, 0x10000 }, + [MICROCHIP_PFSOC_L2CC] = { 0x2010000, 0x1000 }, + [MICROCHIP_PFSOC_DMA] = { 0x3000000, 0x100000 }, + [MICROCHIP_PFSOC_L2LIM] = { 0x8000000, 0x2000000 }, + [MICROCHIP_PFSOC_PLIC] = { 0xc000000, 0x4000000 }, + [MICROCHIP_PFSOC_MMUART0] = { 0x20000000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG0] = { 0x20001000, 0x1000 }, + [MICROCHIP_PFSOC_SYSREG] = { 0x20002000, 0x2000 }, + [MICROCHIP_PFSOC_AXISW] = { 0x20004000, 0x1000 }, + [MICROCHIP_PFSOC_MPUCFG] = { 0x20005000, 0x1000 }, + [MICROCHIP_PFSOC_FMETER] = { 0x20006000, 0x1000 }, + [MICROCHIP_PFSOC_DDR_SGMII_PHY] = { 0x20007000, 0x1000 }, + [MICROCHIP_PFSOC_EMMC_SD] = { 0x20008000, 0x1000 }, + [MICROCHIP_PFSOC_DDR_CFG] = { 0x20080000, 0x40000 }, + [MICROCHIP_PFSOC_MMUART1] = { 0x20100000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART2] = { 0x20102000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART3] = { 0x20104000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART4] = { 0x20106000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG1] = { 0x20101000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG2] = { 0x20103000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG3] = { 0x20105000, 0x1000 }, + [MICROCHIP_PFSOC_WDOG4] = { 0x20106000, 0x1000 }, + [MICROCHIP_PFSOC_SPI0] = { 0x20108000, 0x1000 }, + [MICROCHIP_PFSOC_SPI1] = { 0x20109000, 0x1000 }, + [MICROCHIP_PFSOC_I2C0] = { 0x2010a000, 0x1000 }, + [MICROCHIP_PFSOC_I2C1] = { 0x2010b000, 0x1000 }, + [MICROCHIP_PFSOC_CAN0] = { 0x2010c000, 0x1000 }, + [MICROCHIP_PFSOC_CAN1] = { 0x2010d000, 0x1000 }, + [MICROCHIP_PFSOC_GEM0] = { 0x20110000, 0x2000 }, + [MICROCHIP_PFSOC_GEM1] = { 0x20112000, 0x2000 }, + [MICROCHIP_PFSOC_GPIO0] = { 0x20120000, 0x1000 }, + [MICROCHIP_PFSOC_GPIO1] = { 0x20121000, 0x1000 }, + [MICROCHIP_PFSOC_GPIO2] = { 0x20122000, 0x1000 }, + [MICROCHIP_PFSOC_RTC] = { 0x20124000, 0x1000 }, + [MICROCHIP_PFSOC_ENVM_CFG] = { 0x20200000, 0x1000 }, + [MICROCHIP_PFSOC_ENVM_DATA] = { 0x20220000, 0x20000 }, + [MICROCHIP_PFSOC_USB] = { 0x20201000, 0x1000 }, + [MICROCHIP_PFSOC_QSPI_XIP] = { 0x21000000, 0x1000000 }, + [MICROCHIP_PFSOC_IOSCB] = { 0x30000000, 0x10000000 }, + [MICROCHIP_PFSOC_FABRIC_FIC3] = { 0x40000000, 0x20000000 }, + [MICROCHIP_PFSOC_DRAM_LO] = { 0x80000000, 0x40000000 }, + [MICROCHIP_PFSOC_DRAM_LO_ALIAS] = { 0xc0000000, 0x40000000 }, + [MICROCHIP_PFSOC_DRAM_HI] = { 0x1000000000, 0x0 }, + [MICROCHIP_PFSOC_DRAM_HI_ALIAS] = { 0x1400000000, 0x0 }, +}; + +static void microchip_pfsoc_soc_instance_init(Object *obj) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + MicrochipPFSoCState *s = MICROCHIP_PFSOC(obj); + + object_initialize_child(obj, "e-cluster", &s->e_cluster, TYPE_CPU_CLUSTER); + qdev_prop_set_uint32(DEVICE(&s->e_cluster), "cluster-id", 0); + + object_initialize_child(OBJECT(&s->e_cluster), "e-cpus", &s->e_cpus, + TYPE_RISCV_HART_ARRAY); + qdev_prop_set_uint32(DEVICE(&s->e_cpus), "num-harts", 1); + qdev_prop_set_uint32(DEVICE(&s->e_cpus), "hartid-base", 0); + qdev_prop_set_string(DEVICE(&s->e_cpus), "cpu-type", + TYPE_RISCV_CPU_SIFIVE_E51); + qdev_prop_set_uint64(DEVICE(&s->e_cpus), "resetvec", RESET_VECTOR); + + object_initialize_child(obj, "u-cluster", &s->u_cluster, TYPE_CPU_CLUSTER); + qdev_prop_set_uint32(DEVICE(&s->u_cluster), "cluster-id", 1); + + object_initialize_child(OBJECT(&s->u_cluster), "u-cpus", &s->u_cpus, + TYPE_RISCV_HART_ARRAY); + qdev_prop_set_uint32(DEVICE(&s->u_cpus), "num-harts", ms->smp.cpus - 1); + qdev_prop_set_uint32(DEVICE(&s->u_cpus), "hartid-base", 1); + qdev_prop_set_string(DEVICE(&s->u_cpus), "cpu-type", + TYPE_RISCV_CPU_SIFIVE_U54); + qdev_prop_set_uint64(DEVICE(&s->u_cpus), "resetvec", RESET_VECTOR); + + object_initialize_child(obj, "dma-controller", &s->dma, + TYPE_SIFIVE_PDMA); + + object_initialize_child(obj, "sysreg", &s->sysreg, + TYPE_MCHP_PFSOC_SYSREG); + + object_initialize_child(obj, "ddr-sgmii-phy", &s->ddr_sgmii_phy, + TYPE_MCHP_PFSOC_DDR_SGMII_PHY); + object_initialize_child(obj, "ddr-cfg", &s->ddr_cfg, + TYPE_MCHP_PFSOC_DDR_CFG); + + object_initialize_child(obj, "gem0", &s->gem0, TYPE_CADENCE_GEM); + object_initialize_child(obj, "gem1", &s->gem1, TYPE_CADENCE_GEM); + + object_initialize_child(obj, "sd-controller", &s->sdhci, + TYPE_CADENCE_SDHCI); + + object_initialize_child(obj, "ioscb", &s->ioscb, TYPE_MCHP_PFSOC_IOSCB); +} + +static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + MicrochipPFSoCState *s = MICROCHIP_PFSOC(dev); + const MemMapEntry *memmap = microchip_pfsoc_memmap; + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *rsvd0_mem = g_new(MemoryRegion, 1); + MemoryRegion *e51_dtim_mem = g_new(MemoryRegion, 1); + MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1); + MemoryRegion *envm_data = g_new(MemoryRegion, 1); + MemoryRegion *qspi_xip_mem = g_new(MemoryRegion, 1); + char *plic_hart_config; + NICInfo *nd; + int i; + + sysbus_realize(SYS_BUS_DEVICE(&s->e_cpus), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&s->u_cpus), &error_abort); + /* + * The cluster must be realized after the RISC-V hart array container, + * as the container's CPU object is only created on realize, and the + * CPU must exist and have been parented into the cluster before the + * cluster is realized. + */ + qdev_realize(DEVICE(&s->e_cluster), NULL, &error_abort); + qdev_realize(DEVICE(&s->u_cluster), NULL, &error_abort); + + /* Reserved Memory at address 0 */ + memory_region_init_ram(rsvd0_mem, NULL, "microchip.pfsoc.rsvd0_mem", + memmap[MICROCHIP_PFSOC_RSVD0].size, &error_fatal); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_RSVD0].base, + rsvd0_mem); + + /* E51 DTIM */ + memory_region_init_ram(e51_dtim_mem, NULL, "microchip.pfsoc.e51_dtim_mem", + memmap[MICROCHIP_PFSOC_E51_DTIM].size, &error_fatal); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_E51_DTIM].base, + e51_dtim_mem); + + /* Bus Error Units */ + create_unimplemented_device("microchip.pfsoc.buserr_unit0_mem", + memmap[MICROCHIP_PFSOC_BUSERR_UNIT0].base, + memmap[MICROCHIP_PFSOC_BUSERR_UNIT0].size); + create_unimplemented_device("microchip.pfsoc.buserr_unit1_mem", + memmap[MICROCHIP_PFSOC_BUSERR_UNIT1].base, + memmap[MICROCHIP_PFSOC_BUSERR_UNIT1].size); + create_unimplemented_device("microchip.pfsoc.buserr_unit2_mem", + memmap[MICROCHIP_PFSOC_BUSERR_UNIT2].base, + memmap[MICROCHIP_PFSOC_BUSERR_UNIT2].size); + create_unimplemented_device("microchip.pfsoc.buserr_unit3_mem", + memmap[MICROCHIP_PFSOC_BUSERR_UNIT3].base, + memmap[MICROCHIP_PFSOC_BUSERR_UNIT3].size); + create_unimplemented_device("microchip.pfsoc.buserr_unit4_mem", + memmap[MICROCHIP_PFSOC_BUSERR_UNIT4].base, + memmap[MICROCHIP_PFSOC_BUSERR_UNIT4].size); + + /* CLINT */ + riscv_aclint_swi_create(memmap[MICROCHIP_PFSOC_CLINT].base, + 0, ms->smp.cpus, false); + riscv_aclint_mtimer_create( + memmap[MICROCHIP_PFSOC_CLINT].base + RISCV_ACLINT_SWI_SIZE, + RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus, + RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, + CLINT_TIMEBASE_FREQ, false); + + /* L2 cache controller */ + create_unimplemented_device("microchip.pfsoc.l2cc", + memmap[MICROCHIP_PFSOC_L2CC].base, memmap[MICROCHIP_PFSOC_L2CC].size); + + /* + * Add L2-LIM at reset size. + * This should be reduced in size as the L2 Cache Controller WayEnable + * register is incremented. Unfortunately I don't see a nice (or any) way + * to handle reducing or blocking out the L2 LIM while still allowing it + * be re returned to all enabled after a reset. For the time being, just + * leave it enabled all the time. This won't break anything, but will be + * too generous to misbehaving guests. + */ + memory_region_init_ram(l2lim_mem, NULL, "microchip.pfsoc.l2lim", + memmap[MICROCHIP_PFSOC_L2LIM].size, &error_fatal); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_L2LIM].base, + l2lim_mem); + + /* create PLIC hart topology configuration string */ + plic_hart_config = riscv_plic_hart_config_string(ms->smp.cpus); + + /* PLIC */ + s->plic = sifive_plic_create(memmap[MICROCHIP_PFSOC_PLIC].base, + plic_hart_config, ms->smp.cpus, 0, + MICROCHIP_PFSOC_PLIC_NUM_SOURCES, + MICROCHIP_PFSOC_PLIC_NUM_PRIORITIES, + MICROCHIP_PFSOC_PLIC_PRIORITY_BASE, + MICROCHIP_PFSOC_PLIC_PENDING_BASE, + MICROCHIP_PFSOC_PLIC_ENABLE_BASE, + MICROCHIP_PFSOC_PLIC_ENABLE_STRIDE, + MICROCHIP_PFSOC_PLIC_CONTEXT_BASE, + MICROCHIP_PFSOC_PLIC_CONTEXT_STRIDE, + memmap[MICROCHIP_PFSOC_PLIC].size); + g_free(plic_hart_config); + + /* DMA */ + sysbus_realize(SYS_BUS_DEVICE(&s->dma), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dma), 0, + memmap[MICROCHIP_PFSOC_DMA].base); + for (i = 0; i < SIFIVE_PDMA_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), i, + qdev_get_gpio_in(DEVICE(s->plic), + MICROCHIP_PFSOC_DMA_IRQ0 + i)); + } + + /* SYSREG */ + sysbus_realize(SYS_BUS_DEVICE(&s->sysreg), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysreg), 0, + memmap[MICROCHIP_PFSOC_SYSREG].base); + + /* AXISW */ + create_unimplemented_device("microchip.pfsoc.axisw", + memmap[MICROCHIP_PFSOC_AXISW].base, + memmap[MICROCHIP_PFSOC_AXISW].size); + + /* MPUCFG */ + create_unimplemented_device("microchip.pfsoc.mpucfg", + memmap[MICROCHIP_PFSOC_MPUCFG].base, + memmap[MICROCHIP_PFSOC_MPUCFG].size); + + /* FMETER */ + create_unimplemented_device("microchip.pfsoc.fmeter", + memmap[MICROCHIP_PFSOC_FMETER].base, + memmap[MICROCHIP_PFSOC_FMETER].size); + + /* DDR SGMII PHY */ + sysbus_realize(SYS_BUS_DEVICE(&s->ddr_sgmii_phy), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_sgmii_phy), 0, + memmap[MICROCHIP_PFSOC_DDR_SGMII_PHY].base); + + /* DDR CFG */ + sysbus_realize(SYS_BUS_DEVICE(&s->ddr_cfg), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ddr_cfg), 0, + memmap[MICROCHIP_PFSOC_DDR_CFG].base); + + /* SDHCI */ + sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0, + memmap[MICROCHIP_PFSOC_EMMC_SD].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0, + qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_EMMC_SD_IRQ)); + + /* MMUARTs */ + s->serial0 = mchp_pfsoc_mmuart_create(system_memory, + memmap[MICROCHIP_PFSOC_MMUART0].base, + qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_MMUART0_IRQ), + serial_hd(0)); + s->serial1 = mchp_pfsoc_mmuart_create(system_memory, + memmap[MICROCHIP_PFSOC_MMUART1].base, + qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_MMUART1_IRQ), + serial_hd(1)); + s->serial2 = mchp_pfsoc_mmuart_create(system_memory, + memmap[MICROCHIP_PFSOC_MMUART2].base, + qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_MMUART2_IRQ), + serial_hd(2)); + s->serial3 = mchp_pfsoc_mmuart_create(system_memory, + memmap[MICROCHIP_PFSOC_MMUART3].base, + qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_MMUART3_IRQ), + serial_hd(3)); + s->serial4 = mchp_pfsoc_mmuart_create(system_memory, + memmap[MICROCHIP_PFSOC_MMUART4].base, + qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_MMUART4_IRQ), + serial_hd(4)); + + /* Watchdogs */ + create_unimplemented_device("microchip.pfsoc.watchdog0", + memmap[MICROCHIP_PFSOC_WDOG0].base, + memmap[MICROCHIP_PFSOC_WDOG0].size); + create_unimplemented_device("microchip.pfsoc.watchdog1", + memmap[MICROCHIP_PFSOC_WDOG1].base, + memmap[MICROCHIP_PFSOC_WDOG1].size); + create_unimplemented_device("microchip.pfsoc.watchdog2", + memmap[MICROCHIP_PFSOC_WDOG2].base, + memmap[MICROCHIP_PFSOC_WDOG2].size); + create_unimplemented_device("microchip.pfsoc.watchdog3", + memmap[MICROCHIP_PFSOC_WDOG3].base, + memmap[MICROCHIP_PFSOC_WDOG3].size); + create_unimplemented_device("microchip.pfsoc.watchdog4", + memmap[MICROCHIP_PFSOC_WDOG4].base, + memmap[MICROCHIP_PFSOC_WDOG4].size); + + /* SPI */ + create_unimplemented_device("microchip.pfsoc.spi0", + memmap[MICROCHIP_PFSOC_SPI0].base, + memmap[MICROCHIP_PFSOC_SPI0].size); + create_unimplemented_device("microchip.pfsoc.spi1", + memmap[MICROCHIP_PFSOC_SPI1].base, + memmap[MICROCHIP_PFSOC_SPI1].size); + + /* I2C */ + create_unimplemented_device("microchip.pfsoc.i2c0", + memmap[MICROCHIP_PFSOC_I2C0].base, + memmap[MICROCHIP_PFSOC_I2C0].size); + create_unimplemented_device("microchip.pfsoc.i2c1", + memmap[MICROCHIP_PFSOC_I2C1].base, + memmap[MICROCHIP_PFSOC_I2C1].size); + + /* CAN */ + create_unimplemented_device("microchip.pfsoc.can0", + memmap[MICROCHIP_PFSOC_CAN0].base, + memmap[MICROCHIP_PFSOC_CAN0].size); + create_unimplemented_device("microchip.pfsoc.can1", + memmap[MICROCHIP_PFSOC_CAN1].base, + memmap[MICROCHIP_PFSOC_CAN1].size); + + /* USB */ + create_unimplemented_device("microchip.pfsoc.usb", + memmap[MICROCHIP_PFSOC_USB].base, + memmap[MICROCHIP_PFSOC_USB].size); + + /* GEMs */ + + nd = &nd_table[0]; + if (nd->used) { + qemu_check_nic_model(nd, TYPE_CADENCE_GEM); + qdev_set_nic_properties(DEVICE(&s->gem0), nd); + } + nd = &nd_table[1]; + if (nd->used) { + qemu_check_nic_model(nd, TYPE_CADENCE_GEM); + qdev_set_nic_properties(DEVICE(&s->gem1), nd); + } + + object_property_set_int(OBJECT(&s->gem0), "revision", GEM_REVISION, errp); + object_property_set_int(OBJECT(&s->gem0), "phy-addr", 8, errp); + sysbus_realize(SYS_BUS_DEVICE(&s->gem0), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem0), 0, + memmap[MICROCHIP_PFSOC_GEM0].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem0), 0, + qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_GEM0_IRQ)); + + object_property_set_int(OBJECT(&s->gem1), "revision", GEM_REVISION, errp); + object_property_set_int(OBJECT(&s->gem1), "phy-addr", 9, errp); + sysbus_realize(SYS_BUS_DEVICE(&s->gem1), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem1), 0, + memmap[MICROCHIP_PFSOC_GEM1].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem1), 0, + qdev_get_gpio_in(DEVICE(s->plic), MICROCHIP_PFSOC_GEM1_IRQ)); + + /* GPIOs */ + create_unimplemented_device("microchip.pfsoc.gpio0", + memmap[MICROCHIP_PFSOC_GPIO0].base, + memmap[MICROCHIP_PFSOC_GPIO0].size); + create_unimplemented_device("microchip.pfsoc.gpio1", + memmap[MICROCHIP_PFSOC_GPIO1].base, + memmap[MICROCHIP_PFSOC_GPIO1].size); + create_unimplemented_device("microchip.pfsoc.gpio2", + memmap[MICROCHIP_PFSOC_GPIO2].base, + memmap[MICROCHIP_PFSOC_GPIO2].size); + + /* eNVM */ + memory_region_init_rom(envm_data, OBJECT(dev), "microchip.pfsoc.envm.data", + memmap[MICROCHIP_PFSOC_ENVM_DATA].size, + &error_fatal); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_ENVM_DATA].base, + envm_data); + + /* IOSCB */ + sysbus_realize(SYS_BUS_DEVICE(&s->ioscb), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ioscb), 0, + memmap[MICROCHIP_PFSOC_IOSCB].base); + + /* FPGA Fabric */ + create_unimplemented_device("microchip.pfsoc.fabricfic3", + memmap[MICROCHIP_PFSOC_FABRIC_FIC3].base, + memmap[MICROCHIP_PFSOC_FABRIC_FIC3].size); + + /* QSPI Flash */ + memory_region_init_rom(qspi_xip_mem, OBJECT(dev), + "microchip.pfsoc.qspi_xip", + memmap[MICROCHIP_PFSOC_QSPI_XIP].size, + &error_fatal); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_QSPI_XIP].base, + qspi_xip_mem); +} + +static void microchip_pfsoc_soc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = microchip_pfsoc_soc_realize; + /* Reason: Uses serial_hds in realize function, thus can't be used twice */ + dc->user_creatable = false; +} + +static const TypeInfo microchip_pfsoc_soc_type_info = { + .name = TYPE_MICROCHIP_PFSOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(MicrochipPFSoCState), + .instance_init = microchip_pfsoc_soc_instance_init, + .class_init = microchip_pfsoc_soc_class_init, +}; + +static void microchip_pfsoc_soc_register_types(void) +{ + type_register_static(µchip_pfsoc_soc_type_info); +} + +type_init(microchip_pfsoc_soc_register_types) + +static void microchip_icicle_kit_machine_init(MachineState *machine) +{ + MachineClass *mc = MACHINE_GET_CLASS(machine); + const MemMapEntry *memmap = microchip_pfsoc_memmap; + MicrochipIcicleKitState *s = MICROCHIP_ICICLE_KIT_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *mem_low = g_new(MemoryRegion, 1); + MemoryRegion *mem_low_alias = g_new(MemoryRegion, 1); + MemoryRegion *mem_high = g_new(MemoryRegion, 1); + MemoryRegion *mem_high_alias = g_new(MemoryRegion, 1); + uint64_t mem_low_size, mem_high_size; + hwaddr firmware_load_addr; + const char *firmware_name; + bool kernel_as_payload = false; + target_ulong firmware_end_addr, kernel_start_addr; + uint64_t kernel_entry; + uint32_t fdt_load_addr; + DriveInfo *dinfo = drive_get(IF_SD, 0, 0); + + /* Sanity check on RAM size */ + if (machine->ram_size < mc->default_ram_size) { + char *sz = size_to_str(mc->default_ram_size); + error_report("Invalid RAM size, should be bigger than %s", sz); + g_free(sz); + exit(EXIT_FAILURE); + } + + /* Initialize SoC */ + object_initialize_child(OBJECT(machine), "soc", &s->soc, + TYPE_MICROCHIP_PFSOC); + qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + + /* Split RAM into low and high regions using aliases to machine->ram */ + mem_low_size = memmap[MICROCHIP_PFSOC_DRAM_LO].size; + mem_high_size = machine->ram_size - mem_low_size; + memory_region_init_alias(mem_low, NULL, + "microchip.icicle.kit.ram_low", machine->ram, + 0, mem_low_size); + memory_region_init_alias(mem_high, NULL, + "microchip.icicle.kit.ram_high", machine->ram, + mem_low_size, mem_high_size); + + /* Register RAM */ + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_DRAM_LO].base, + mem_low); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_DRAM_HI].base, + mem_high); + + /* Create aliases for the low and high RAM regions */ + memory_region_init_alias(mem_low_alias, NULL, + "microchip.icicle.kit.ram_low.alias", + mem_low, 0, mem_low_size); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_DRAM_LO_ALIAS].base, + mem_low_alias); + memory_region_init_alias(mem_high_alias, NULL, + "microchip.icicle.kit.ram_high.alias", + mem_high, 0, mem_high_size); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_DRAM_HI_ALIAS].base, + mem_high_alias); + + /* Attach an SD card */ + if (dinfo) { + CadenceSDHCIState *sdhci = &(s->soc.sdhci); + DeviceState *card = qdev_new(TYPE_SD_CARD); + + qdev_prop_set_drive_err(card, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); + qdev_realize_and_unref(card, sdhci->bus, &error_fatal); + } + + /* + * We follow the following table to select which payload we execute. + * + * -bios | -kernel | payload + * -------+------------+-------- + * N | N | HSS + * Y | don't care | HSS + * N | Y | kernel + * + * This ensures backwards compatibility with how we used to expose -bios + * to users but allows them to run through direct kernel booting as well. + * + * When -kernel is used for direct boot, -dtb must be present to provide + * a valid device tree for the board, as we don't generate device tree. + */ + + if (machine->kernel_filename && machine->dtb) { + int fdt_size; + machine->fdt = load_device_tree(machine->dtb, &fdt_size); + if (!machine->fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + + firmware_name = RISCV64_BIOS_BIN; + firmware_load_addr = memmap[MICROCHIP_PFSOC_DRAM_LO].base; + kernel_as_payload = true; + } + + if (!kernel_as_payload) { + firmware_name = BIOS_FILENAME; + firmware_load_addr = RESET_VECTOR; + } + + /* Load the firmware */ + firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, + firmware_load_addr, NULL); + + if (kernel_as_payload) { + kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, + firmware_end_addr); + + kernel_entry = riscv_load_kernel(machine->kernel_filename, + kernel_start_addr, NULL); + + if (machine->initrd_filename) { + hwaddr start; + hwaddr end = riscv_load_initrd(machine->initrd_filename, + machine->ram_size, kernel_entry, + &start); + qemu_fdt_setprop_cell(machine->fdt, "/chosen", + "linux,initrd-start", start); + qemu_fdt_setprop_cell(machine->fdt, "/chosen", + "linux,initrd-end", end); + } + + if (machine->kernel_cmdline && *machine->kernel_cmdline) { + qemu_fdt_setprop_string(machine->fdt, "/chosen", + "bootargs", machine->kernel_cmdline); + } + + /* Compute the fdt load address in dram */ + fdt_load_addr = riscv_load_fdt(memmap[MICROCHIP_PFSOC_DRAM_LO].base, + machine->ram_size, machine->fdt); + /* Load the reset vector */ + riscv_setup_rom_reset_vec(machine, &s->soc.u_cpus, firmware_load_addr, + memmap[MICROCHIP_PFSOC_ENVM_DATA].base, + memmap[MICROCHIP_PFSOC_ENVM_DATA].size, + kernel_entry, fdt_load_addr); + } +} + +static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Microchip PolarFire SoC Icicle Kit"; + mc->init = microchip_icicle_kit_machine_init; + mc->max_cpus = MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT + + MICROCHIP_PFSOC_COMPUTE_CPU_COUNT; + mc->min_cpus = MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT + 1; + mc->default_cpus = mc->min_cpus; + mc->default_ram_id = "microchip.icicle.kit.ram"; + + /* + * Map 513 MiB high memory, the mimimum required high memory size, because + * HSS will do memory test against the high memory address range regardless + * of physical memory installed. + * + * See memory_tests() in mss_ddr.c in the HSS source code. + */ + mc->default_ram_size = 1537 * MiB; +} + +static const TypeInfo microchip_icicle_kit_machine_typeinfo = { + .name = MACHINE_TYPE_NAME("microchip-icicle-kit"), + .parent = TYPE_MACHINE, + .class_init = microchip_icicle_kit_machine_class_init, + .instance_size = sizeof(MicrochipIcicleKitState), +}; + +static void microchip_icicle_kit_machine_init_register_types(void) +{ + type_register_static(µchip_icicle_kit_machine_typeinfo); +} + +type_init(microchip_icicle_kit_machine_init_register_types) |