diff options
Diffstat (limited to 'hw/riscv')
| -rw-r--r-- | hw/riscv/Kconfig | 70 | ||||
| -rw-r--r-- | hw/riscv/meson.build | 12 | ||||
| -rw-r--r-- | hw/riscv/microchip_pfsoc.c | 437 | ||||
| -rw-r--r-- | hw/riscv/opentitan.c | 1 | ||||
| -rw-r--r-- | hw/riscv/riscv_hart.c | 3 | ||||
| -rw-r--r-- | hw/riscv/riscv_htif.c | 261 | ||||
| -rw-r--r-- | hw/riscv/sifive_clint.c | 262 | ||||
| -rw-r--r-- | hw/riscv/sifive_e.c | 12 | ||||
| -rw-r--r-- | hw/riscv/sifive_e_prci.c | 125 | ||||
| -rw-r--r-- | hw/riscv/sifive_gpio.c | 397 | ||||
| -rw-r--r-- | hw/riscv/sifive_plic.c | 524 | ||||
| -rw-r--r-- | hw/riscv/sifive_test.c | 100 | ||||
| -rw-r--r-- | hw/riscv/sifive_u.c | 41 | ||||
| -rw-r--r-- | hw/riscv/sifive_u_otp.c | 191 | ||||
| -rw-r--r-- | hw/riscv/sifive_u_prci.c | 169 | ||||
| -rw-r--r-- | hw/riscv/sifive_uart.c | 194 | ||||
| -rw-r--r-- | hw/riscv/spike.c | 7 | ||||
| -rw-r--r-- | hw/riscv/trace-events | 7 | ||||
| -rw-r--r-- | hw/riscv/trace.h | 1 | ||||
| -rw-r--r-- | hw/riscv/virt.c | 9 |
20 files changed, 537 insertions, 2286 deletions
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 28947ef3e0..2df978fe8d 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -1,50 +1,62 @@ -config HTIF +config IBEX bool -config HART +config MICROCHIP_PFSOC bool + select CADENCE_SDHCI + select MCHP_PFSOC_MMUART + select MSI_NONBROKEN + select SIFIVE_CLINT + select SIFIVE_PDMA + select SIFIVE_PLIC + select UNIMP -config IBEX +config OPENTITAN bool + select IBEX + select UNIMP -config SIFIVE +config RISCV_VIRT bool + imply PCI_DEVICES + imply TEST_DEVICES + select GOLDFISH_RTC select MSI_NONBROKEN + select PCI + select PCI_EXPRESS_GENERIC_BRIDGE + select PFLASH_CFI01 + select SERIAL + select SIFIVE_CLINT + select SIFIVE_PLIC + select SIFIVE_TEST + select VIRTIO_MMIO config SIFIVE_E bool - select HART - select SIFIVE + select MSI_NONBROKEN + select SIFIVE_CLINT + select SIFIVE_GPIO + select SIFIVE_PLIC + select SIFIVE_UART + select SIFIVE_E_PRCI select UNIMP config SIFIVE_U bool select CADENCE - select HART - select SIFIVE + select MSI_NONBROKEN + select SIFIVE_CLINT + select SIFIVE_GPIO + select SIFIVE_PDMA + select SIFIVE_PLIC + select SIFIVE_UART + select SIFIVE_U_OTP + select SIFIVE_U_PRCI select UNIMP config SPIKE bool - select HART select HTIF - select SIFIVE - -config OPENTITAN - bool - select IBEX - select HART - select UNIMP - -config RISCV_VIRT - bool - imply PCI_DEVICES - imply TEST_DEVICES - select PCI - select HART - select SERIAL - select GOLDFISH_RTC - select VIRTIO_MMIO - select PCI_EXPRESS_GENERIC_BRIDGE - select PFLASH_CFI01 - select SIFIVE + select MSI_NONBROKEN + select SIFIVE_CLINT + select SIFIVE_PLIC diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index fe2ea75f65..275c0f7eb7 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -1,20 +1,12 @@ riscv_ss = ss.source_set() riscv_ss.add(files('boot.c'), fdt) riscv_ss.add(files('numa.c')) -riscv_ss.add(when: 'CONFIG_HART', if_true: files('riscv_hart.c')) +riscv_ss.add(files('riscv_hart.c')) riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c')) riscv_ss.add(when: 'CONFIG_RISCV_VIRT', if_true: files('virt.c')) -riscv_ss.add(when: 'CONFIG_SIFIVE', if_true: files('sifive_clint.c')) -riscv_ss.add(when: 'CONFIG_SIFIVE', if_true: files('sifive_gpio.c')) -riscv_ss.add(when: 'CONFIG_SIFIVE', if_true: files('sifive_plic.c')) -riscv_ss.add(when: 'CONFIG_SIFIVE', if_true: files('sifive_test.c')) -riscv_ss.add(when: 'CONFIG_SIFIVE', if_true: files('sifive_uart.c')) riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c')) -riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e_prci.c')) riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c')) -riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u_otp.c')) -riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u_prci.c')) -riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('riscv_htif.c')) riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c')) +riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c new file mode 100644 index 0000000000..4627179cd3 --- /dev/null +++ b/hw/riscv/microchip_pfsoc.c @@ -0,0 +1,437 @@ +/* + * 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) + * + * 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/log.h" +#include "qemu/units.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/irq.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/sifive_clint.h" +#include "hw/intc/sifive_plic.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 + +static const struct MemmapEntry { + hwaddr base; + hwaddr size; +} microchip_pfsoc_memmap[] = { + [MICROCHIP_PFSOC_DEBUG] = { 0x0, 0x1000 }, + [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_SYSREG] = { 0x20002000, 0x2000 }, + [MICROCHIP_PFSOC_MPUCFG] = { 0x20005000, 0x1000 }, + [MICROCHIP_PFSOC_EMMC_SD] = { 0x20008000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART1] = { 0x20100000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART2] = { 0x20102000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART3] = { 0x20104000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART4] = { 0x20106000, 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_ENVM_CFG] = { 0x20200000, 0x1000 }, + [MICROCHIP_PFSOC_ENVM_DATA] = { 0x20220000, 0x20000 }, + [MICROCHIP_PFSOC_IOSCB_CFG] = { 0x37080000, 0x1000 }, + [MICROCHIP_PFSOC_DRAM] = { 0x80000000, 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, "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); +} + +static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + MicrochipPFSoCState *s = MICROCHIP_PFSOC(dev); + const struct MemmapEntry *memmap = microchip_pfsoc_memmap; + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *e51_dtim_mem = g_new(MemoryRegion, 1); + MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1); + MemoryRegion *envm_data = g_new(MemoryRegion, 1); + char *plic_hart_config; + size_t plic_hart_config_len; + 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); + + /* 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 */ + sifive_clint_create(memmap[MICROCHIP_PFSOC_CLINT].base, + memmap[MICROCHIP_PFSOC_CLINT].size, 0, ms->smp.cpus, + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + 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_len = (strlen(MICROCHIP_PFSOC_PLIC_HART_CONFIG) + 1) * + ms->smp.cpus; + plic_hart_config = g_malloc0(plic_hart_config_len); + for (i = 0; i < ms->smp.cpus; i++) { + if (i != 0) { + strncat(plic_hart_config, "," MICROCHIP_PFSOC_PLIC_HART_CONFIG, + plic_hart_config_len); + } else { + strncat(plic_hart_config, "M", plic_hart_config_len); + } + plic_hart_config_len -= (strlen(MICROCHIP_PFSOC_PLIC_HART_CONFIG) + 1); + } + + /* PLIC */ + s->plic = sifive_plic_create(memmap[MICROCHIP_PFSOC_PLIC].base, + plic_hart_config, 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 */ + create_unimplemented_device("microchip.pfsoc.sysreg", + memmap[MICROCHIP_PFSOC_SYSREG].base, + memmap[MICROCHIP_PFSOC_SYSREG].size); + + /* MPUCFG */ + create_unimplemented_device("microchip.pfsoc.mpucfg", + memmap[MICROCHIP_PFSOC_MPUCFG].base, + memmap[MICROCHIP_PFSOC_MPUCFG].size); + + /* 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)); + + /* 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); + + /* IOSCBCFG */ + create_unimplemented_device("microchip.pfsoc.ioscb.cfg", + memmap[MICROCHIP_PFSOC_IOSCB_CFG].base, + memmap[MICROCHIP_PFSOC_IOSCB_CFG].size); +} + +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 struct MemmapEntry *memmap = microchip_pfsoc_memmap; + MicrochipIcicleKitState *s = MICROCHIP_ICICLE_KIT_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + DriveInfo *dinfo = drive_get_next(IF_SD); + + /* 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_abort); + + /* Register RAM */ + memory_region_init_ram(main_mem, NULL, "microchip.icicle.kit.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(system_memory, + memmap[MICROCHIP_PFSOC_DRAM].base, main_mem); + + /* Load the firmware */ + riscv_find_and_load_firmware(machine, BIOS_FILENAME, RESET_VECTOR, NULL); + + /* 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); + } +} + +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_size = 1 * GiB; +} + +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) diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 23ba3b4bfc..0531bd879b 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -111,6 +111,7 @@ static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp) &error_abort); object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, &error_abort); + object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x8090, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_abort); /* Boot ROM */ diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index f59fe52f0f..613ea2aaa0 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -31,6 +31,8 @@ static Property riscv_harts_props[] = { DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1), DEFINE_PROP_UINT32("hartid-base", RISCVHartArrayState, hartid_base, 0), DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type), + DEFINE_PROP_UINT64("resetvec", RISCVHartArrayState, resetvec, + DEFAULT_RSTVEC), DEFINE_PROP_END_OF_LIST(), }; @@ -44,6 +46,7 @@ static bool riscv_hart_realize(RISCVHartArrayState *s, int idx, char *cpu_type, Error **errp) { object_initialize_child(OBJECT(s), "harts[*]", &s->harts[idx], cpu_type); + qdev_prop_set_uint64(DEVICE(&s->harts[idx]), "resetvec", s->resetvec); s->harts[idx].env.mhartid = s->hartid_base + idx; qemu_register_reset(riscv_harts_cpu_reset, &s->harts[idx]); return qdev_realize(DEVICE(&s->harts[idx]), NULL, errp); diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c deleted file mode 100644 index ca87a5cf9f..0000000000 --- a/hw/riscv/riscv_htif.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * QEMU RISC-V Host Target Interface (HTIF) Emulation - * - * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu - * Copyright (c) 2017-2018 SiFive, Inc. - * - * This provides HTIF device emulation for QEMU. At the moment this allows - * for identical copies of bbl/linux to run on both spike and QEMU. - * - * 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 "qapi/error.h" -#include "qemu/log.h" -#include "hw/sysbus.h" -#include "hw/char/serial.h" -#include "chardev/char.h" -#include "chardev/char-fe.h" -#include "hw/riscv/riscv_htif.h" -#include "qemu/timer.h" -#include "qemu/error-report.h" - -#define RISCV_DEBUG_HTIF 0 -#define HTIF_DEBUG(fmt, ...) \ - do { \ - if (RISCV_DEBUG_HTIF) { \ - qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ - } \ - } while (0) - -static uint64_t fromhost_addr, tohost_addr; -static int address_symbol_set; - -void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, - uint64_t st_size) -{ - if (strcmp("fromhost", st_name) == 0) { - address_symbol_set |= 1; - fromhost_addr = st_value; - if (st_size != 8) { - error_report("HTIF fromhost must be 8 bytes"); - exit(1); - } - } else if (strcmp("tohost", st_name) == 0) { - address_symbol_set |= 2; - tohost_addr = st_value; - if (st_size != 8) { - error_report("HTIF tohost must be 8 bytes"); - exit(1); - } - } -} - -/* - * Called by the char dev to see if HTIF is ready to accept input. - */ -static int htif_can_recv(void *opaque) -{ - return 1; -} - -/* - * Called by the char dev to supply input to HTIF console. - * We assume that we will receive one character at a time. - */ -static void htif_recv(void *opaque, const uint8_t *buf, int size) -{ - HTIFState *htifstate = opaque; - - if (size != 1) { - return; - } - - /* TODO - we need to check whether mfromhost is zero which indicates - the device is ready to receive. The current implementation - will drop characters */ - - uint64_t val_written = htifstate->pending_read; - uint64_t resp = 0x100 | *buf; - - htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); -} - -/* - * Called by the char dev to supply special events to the HTIF console. - * Not used for HTIF. - */ -static void htif_event(void *opaque, QEMUChrEvent event) -{ - -} - -static int htif_be_change(void *opaque) -{ - HTIFState *s = opaque; - - qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, - htif_be_change, s, NULL, true); - - return 0; -} - -static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written) -{ - uint8_t device = val_written >> 56; - uint8_t cmd = val_written >> 48; - uint64_t payload = val_written & 0xFFFFFFFFFFFFULL; - int resp = 0; - - HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64 - " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload); - - /* - * Currently, there is a fixed mapping of devices: - * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy) - * 1: Console - */ - if (unlikely(device == 0x0)) { - /* frontend syscall handler, shutdown and exit code support */ - if (cmd == 0x0) { - if (payload & 0x1) { - /* exit code */ - int exit_code = payload >> 1; - exit(exit_code); - } else { - qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n"); - } - } else { - qemu_log("HTIF device %d: unknown command\n", device); - } - } else if (likely(device == 0x1)) { - /* HTIF Console */ - if (cmd == 0x0) { - /* this should be a queue, but not yet implemented as such */ - htifstate->pending_read = val_written; - htifstate->env->mtohost = 0; /* clear to indicate we read */ - return; - } else if (cmd == 0x1) { - qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1); - resp = 0x100 | (uint8_t)payload; - } else { - qemu_log("HTIF device %d: unknown command\n", device); - } - } else { - qemu_log("HTIF unknown device or command\n"); - HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64 - " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload); - } - /* - * - latest bbl does not set fromhost to 0 if there is a value in tohost - * - with this code enabled, qemu hangs waiting for fromhost to go to 0 - * - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10 - * - HTIF needs protocol documentation and a more complete state machine - - while (!htifstate->fromhost_inprogress && - htifstate->env->mfromhost != 0x0) { - } - */ - htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16); - htifstate->env->mtohost = 0; /* clear to indicate we read */ -} - -#define TOHOST_OFFSET1 (htifstate->tohost_offset) -#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4) -#define FROMHOST_OFFSET1 (htifstate->fromhost_offset) -#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4) - -/* CPU wants to read an HTIF register */ -static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size) -{ - HTIFState *htifstate = opaque; - if (addr == TOHOST_OFFSET1) { - return htifstate->env->mtohost & 0xFFFFFFFF; - } else if (addr == TOHOST_OFFSET2) { - return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF; - } else if (addr == FROMHOST_OFFSET1) { - return htifstate->env->mfromhost & 0xFFFFFFFF; - } else if (addr == FROMHOST_OFFSET2) { - return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF; - } else { - qemu_log("Invalid htif read: address %016" PRIx64 "\n", - (uint64_t)addr); - return 0; - } -} - -/* CPU wrote to an HTIF register */ -static void htif_mm_write(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - HTIFState *htifstate = opaque; - if (addr == TOHOST_OFFSET1) { - if (htifstate->env->mtohost == 0x0) { - htifstate->allow_tohost = 1; - htifstate->env->mtohost = value & 0xFFFFFFFF; - } else { - htifstate->allow_tohost = 0; - } - } else if (addr == TOHOST_OFFSET2) { - if (htifstate->allow_tohost) { - htifstate->env->mtohost |= value << 32; - htif_handle_tohost_write(htifstate, htifstate->env->mtohost); - } - } else if (addr == FROMHOST_OFFSET1) { - htifstate->fromhost_inprogress = 1; - htifstate->env->mfromhost = value & 0xFFFFFFFF; - } else if (addr == FROMHOST_OFFSET2) { - htifstate->env->mfromhost |= value << 32; - htifstate->fromhost_inprogress = 0; - } else { - qemu_log("Invalid htif write: address %016" PRIx64 "\n", - (uint64_t)addr); - } -} - -static const MemoryRegionOps htif_mm_ops = { - .read = htif_mm_read, - .write = htif_mm_write, -}; - -HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, - CPURISCVState *env, Chardev *chr) -{ - uint64_t base = MIN(tohost_addr, fromhost_addr); - uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base; - uint64_t tohost_offset = tohost_addr - base; - uint64_t fromhost_offset = fromhost_addr - base; - - HTIFState *s = g_malloc0(sizeof(HTIFState)); - s->address_space = address_space; - s->main_mem = main_mem; - s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem); - s->env = env; - s->tohost_offset = tohost_offset; - s->fromhost_offset = fromhost_offset; - s->pending_read = 0; - s->allow_tohost = 0; - s->fromhost_inprogress = 0; - qemu_chr_fe_init(&s->chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event, - htif_be_change, s, NULL, true); - if (address_symbol_set == 3) { - memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s, - TYPE_HTIF_UART, size); - memory_region_add_subregion_overlap(address_space, base, - &s->mmio, 1); - } - - return s; -} diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c deleted file mode 100644 index 15e13d5f7a..0000000000 --- a/hw/riscv/sifive_clint.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * SiFive CLINT (Core Local Interruptor) - * - * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu - * Copyright (c) 2017 SiFive, Inc. - * - * This provides real-time clock, timer and interprocessor interrupts. - * - * 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 "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include "hw/sysbus.h" -#include "target/riscv/cpu.h" -#include "hw/qdev-properties.h" -#include "hw/riscv/sifive_clint.h" -#include "qemu/timer.h" - -static uint64_t cpu_riscv_read_rtc(void) -{ - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - SIFIVE_CLINT_TIMEBASE_FREQ, NANOSECONDS_PER_SECOND); -} - -/* - * Called when timecmp is written to update the QEMU timer or immediately - * trigger timer interrupt if mtimecmp <= current timer value. - */ -static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) -{ - uint64_t next; - uint64_t diff; - - uint64_t rtc_r = cpu_riscv_read_rtc(); - - cpu->env.timecmp = value; - if (cpu->env.timecmp <= rtc_r) { - /* if we're setting an MTIMECMP value in the "past", - immediately raise the timer interrupt */ - riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); - return; - } - - /* otherwise, set up the future timer interrupt */ - riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0)); - diff = cpu->env.timecmp - rtc_r; - /* back to ns (note args switched in muldiv64) */ - next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(diff, NANOSECONDS_PER_SECOND, SIFIVE_CLINT_TIMEBASE_FREQ); - timer_mod(cpu->env.timer, next); -} - -/* - * Callback used when the timer set using timer_mod expires. - * Should raise the timer interrupt line - */ -static void sifive_clint_timer_cb(void *opaque) -{ - RISCVCPU *cpu = opaque; - riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); -} - -/* CPU wants to read rtc or timecmp register */ -static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size) -{ - SiFiveCLINTState *clint = opaque; - if (addr >= clint->sip_base && - addr < clint->sip_base + (clint->num_harts << 2)) { - size_t hartid = clint->hartid_base + ((addr - clint->sip_base) >> 2); - CPUState *cpu = qemu_get_cpu(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; - if (!env) { - error_report("clint: invalid timecmp hartid: %zu", hartid); - } else if ((addr & 0x3) == 0) { - return (env->mip & MIP_MSIP) > 0; - } else { - error_report("clint: invalid read: %08x", (uint32_t)addr); - return 0; - } - } else if (addr >= clint->timecmp_base && - addr < clint->timecmp_base + (clint->num_harts << 3)) { - size_t hartid = clint->hartid_base + - ((addr - clint->timecmp_base) >> 3); - CPUState *cpu = qemu_get_cpu(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; - if (!env) { - error_report("clint: invalid timecmp hartid: %zu", hartid); - } else if ((addr & 0x7) == 0) { - /* timecmp_lo */ - uint64_t timecmp = env->timecmp; - return timecmp & 0xFFFFFFFF; - } else if ((addr & 0x7) == 4) { - /* timecmp_hi */ - uint64_t timecmp = env->timecmp; - return (timecmp >> 32) & 0xFFFFFFFF; - } else { - error_report("clint: invalid read: %08x", (uint32_t)addr); - return 0; - } - } else if (addr == clint->time_base) { - /* time_lo */ - return cpu_riscv_read_rtc() & 0xFFFFFFFF; - } else if (addr == clint->time_base + 4) { - /* time_hi */ - return (cpu_riscv_read_rtc() >> 32) & 0xFFFFFFFF; - } - - error_report("clint: invalid read: %08x", (uint32_t)addr); - return 0; -} - -/* CPU wrote to rtc or timecmp register */ -static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - SiFiveCLINTState *clint = opaque; - - if (addr >= clint->sip_base && - addr < clint->sip_base + (clint->num_harts << 2)) { - size_t hartid = clint->hartid_base + ((addr - clint->sip_base) >> 2); - CPUState *cpu = qemu_get_cpu(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; - if (!env) { - error_report("clint: invalid timecmp hartid: %zu", hartid); - } else if ((addr & 0x3) == 0) { - riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MSIP, BOOL_TO_MASK(value)); - } else { - error_report("clint: invalid sip write: %08x", (uint32_t)addr); - } - return; - } else if (addr >= clint->timecmp_base && - addr < clint->timecmp_base + (clint->num_harts << 3)) { - size_t hartid = clint->hartid_base + - ((addr - clint->timecmp_base) >> 3); - CPUState *cpu = qemu_get_cpu(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; - if (!env) { - error_report("clint: invalid timecmp hartid: %zu", hartid); - } else if ((addr & 0x7) == 0) { - /* timecmp_lo */ - uint64_t timecmp_hi = env->timecmp >> 32; - sifive_clint_write_timecmp(RISCV_CPU(cpu), - timecmp_hi << 32 | (value & 0xFFFFFFFF)); - return; - } else if ((addr & 0x7) == 4) { - /* timecmp_hi */ - uint64_t timecmp_lo = env->timecmp; - sifive_clint_write_timecmp(RISCV_CPU(cpu), - value << 32 | (timecmp_lo & 0xFFFFFFFF)); - } else { - error_report("clint: invalid timecmp write: %08x", (uint32_t)addr); - } - return; - } else if (addr == clint->time_base) { - /* time_lo */ - error_report("clint: time_lo write not implemented"); - return; - } else if (addr == clint->time_base + 4) { - /* time_hi */ - error_report("clint: time_hi write not implemented"); - return; - } - - error_report("clint: invalid write: %08x", (uint32_t)addr); -} - -static const MemoryRegionOps sifive_clint_ops = { - .read = sifive_clint_read, - .write = sifive_clint_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 8 - } -}; - -static Property sifive_clint_properties[] = { - DEFINE_PROP_UINT32("hartid-base", SiFiveCLINTState, hartid_base, 0), - DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0), - DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0), - DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0), - DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0), - DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sifive_clint_realize(DeviceState *dev, Error **errp) -{ - SiFiveCLINTState *s = SIFIVE_CLINT(dev); - memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s, - TYPE_SIFIVE_CLINT, s->aperture_size); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); -} - -static void sifive_clint_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - dc->realize = sifive_clint_realize; - device_class_set_props(dc, sifive_clint_properties); -} - -static const TypeInfo sifive_clint_info = { - .name = TYPE_SIFIVE_CLINT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFiveCLINTState), - .class_init = sifive_clint_class_init, -}; - -static void sifive_clint_register_types(void) -{ - type_register_static(&sifive_clint_info); -} - -type_init(sifive_clint_register_types) - - -/* - * Create CLINT device. - */ -DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, - uint32_t hartid_base, uint32_t num_harts, uint32_t sip_base, - uint32_t timecmp_base, uint32_t time_base, bool provide_rdtime) -{ - int i; - for (i = 0; i < num_harts; i++) { - CPUState *cpu = qemu_get_cpu(hartid_base + i); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; - if (!env) { - continue; - } - if (provide_rdtime) { - riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc); - } - env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - &sifive_clint_timer_cb, cpu); - env->timecmp = 0; - } - - DeviceState *dev = qdev_new(TYPE_SIFIVE_CLINT); - qdev_prop_set_uint32(dev, "hartid-base", hartid_base); - qdev_prop_set_uint32(dev, "num-harts", num_harts); - qdev_prop_set_uint32(dev, "sip-base", sip_base); - qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base); - qdev_prop_set_uint32(dev, "time-base", time_base); - qdev_prop_set_uint32(dev, "aperture-size", size); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); - return dev; -} diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index ca55cc438a..40bbf530d4 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -39,12 +39,12 @@ #include "hw/misc/unimp.h" #include "target/riscv/cpu.h" #include "hw/riscv/riscv_hart.h" -#include "hw/riscv/sifive_plic.h" -#include "hw/riscv/sifive_clint.h" -#include "hw/riscv/sifive_uart.h" #include "hw/riscv/sifive_e.h" -#include "hw/riscv/sifive_e_prci.h" #include "hw/riscv/boot.h" +#include "hw/char/sifive_uart.h" +#include "hw/intc/sifive_clint.h" +#include "hw/intc/sifive_plic.h" +#include "hw/misc/sifive_e_prci.h" #include "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/sysemu.h" @@ -177,6 +177,7 @@ static void sifive_e_soc_init(Object *obj) object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY); object_property_set_int(OBJECT(&s->cpus), "num-harts", ms->smp.cpus, &error_abort); + object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x1004, &error_abort); object_initialize_child(obj, "riscv.sifive.e.gpio0", &s->gpio, TYPE_SIFIVE_GPIO); } @@ -212,7 +213,8 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp) memmap[SIFIVE_E_PLIC].size); sifive_clint_create(memmap[SIFIVE_E_CLINT].base, memmap[SIFIVE_E_CLINT].size, 0, ms->smp.cpus, - SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, false); + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + SIFIVE_CLINT_TIMEBASE_FREQ, false); create_unimplemented_device("riscv.sifive.e.aon", memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size); sifive_e_prci_create(memmap[SIFIVE_E_PRCI].base); diff --git a/hw/riscv/sifive_e_prci.c b/hw/riscv/sifive_e_prci.c deleted file mode 100644 index 17dfa74715..0000000000 --- a/hw/riscv/sifive_e_prci.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * QEMU SiFive E PRCI (Power, Reset, Clock, Interrupt) - * - * Copyright (c) 2017 SiFive, Inc. - * - * Simple model of the PRCI to emulate register reads made by the SDK BSP - * - * 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 "hw/sysbus.h" -#include "qapi/error.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "hw/hw.h" -#include "hw/riscv/sifive_e_prci.h" - -static uint64_t sifive_e_prci_read(void *opaque, hwaddr addr, unsigned int size) -{ - SiFiveEPRCIState *s = opaque; - switch (addr) { - case SIFIVE_E_PRCI_HFROSCCFG: - return s->hfrosccfg; - case SIFIVE_E_PRCI_HFXOSCCFG: - return s->hfxosccfg; - case SIFIVE_E_PRCI_PLLCFG: - return s->pllcfg; - case SIFIVE_E_PRCI_PLLOUTDIV: - return s->plloutdiv; - } - qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%x\n", - __func__, (int)addr); - return 0; -} - -static void sifive_e_prci_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - SiFiveEPRCIState *s = opaque; - switch (addr) { - case SIFIVE_E_PRCI_HFROSCCFG: - s->hfrosccfg = (uint32_t) val64; - /* OSC stays ready */ - s->hfrosccfg |= SIFIVE_E_PRCI_HFROSCCFG_RDY; - break; - case SIFIVE_E_PRCI_HFXOSCCFG: - s->hfxosccfg = (uint32_t) val64; - /* OSC stays ready */ - s->hfxosccfg |= SIFIVE_E_PRCI_HFXOSCCFG_RDY; - break; - case SIFIVE_E_PRCI_PLLCFG: - s->pllcfg = (uint32_t) val64; - /* PLL stays locked */ - s->pllcfg |= SIFIVE_E_PRCI_PLLCFG_LOCK; - break; - case SIFIVE_E_PRCI_PLLOUTDIV: - s->plloutdiv = (uint32_t) val64; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n", - __func__, (int)addr, (int)val64); - } -} - -static const MemoryRegionOps sifive_e_prci_ops = { - .read = sifive_e_prci_read, - .write = sifive_e_prci_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void sifive_e_prci_init(Object *obj) -{ - SiFiveEPRCIState *s = SIFIVE_E_PRCI(obj); - - memory_region_init_io(&s->mmio, obj, &sifive_e_prci_ops, s, - TYPE_SIFIVE_E_PRCI, SIFIVE_E_PRCI_REG_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); - - s->hfrosccfg = (SIFIVE_E_PRCI_HFROSCCFG_RDY | SIFIVE_E_PRCI_HFROSCCFG_EN); - s->hfxosccfg = (SIFIVE_E_PRCI_HFXOSCCFG_RDY | SIFIVE_E_PRCI_HFXOSCCFG_EN); - s->pllcfg = (SIFIVE_E_PRCI_PLLCFG_REFSEL | SIFIVE_E_PRCI_PLLCFG_BYPASS | - SIFIVE_E_PRCI_PLLCFG_LOCK); - s->plloutdiv = SIFIVE_E_PRCI_PLLOUTDIV_DIV1; -} - -static const TypeInfo sifive_e_prci_info = { - .name = TYPE_SIFIVE_E_PRCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFiveEPRCIState), - .instance_init = sifive_e_prci_init, -}; - -static void sifive_e_prci_register_types(void) -{ - type_register_static(&sifive_e_prci_info); -} - -type_init(sifive_e_prci_register_types) - - -/* - * Create PRCI device. - */ -DeviceState *sifive_e_prci_create(hwaddr addr) -{ - DeviceState *dev = qdev_new(TYPE_SIFIVE_E_PRCI); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); - return dev; -} diff --git a/hw/riscv/sifive_gpio.c b/hw/riscv/sifive_gpio.c deleted file mode 100644 index aac6b44cac..0000000000 --- a/hw/riscv/sifive_gpio.c +++ /dev/null @@ -1,397 +0,0 @@ -/* - * SiFive System-on-Chip general purpose input/output register definition - * - * Copyright 2019 AdaCore - * - * Base on nrf51_gpio.c: - * - * Copyright 2018 Steffen Görtz <contrib@steffen-goertz.de> - * - * This code is licensed under the GPL version 2 or later. See - * the COPYING file in the top-level directory. - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "hw/riscv/sifive_gpio.h" -#include "migration/vmstate.h" -#include "trace.h" - -static void update_output_irq(SIFIVEGPIOState *s) -{ - uint32_t pending; - uint32_t pin; - - pending = s->high_ip & s->high_ie; - pending |= s->low_ip & s->low_ie; - pending |= s->rise_ip & s->rise_ie; - pending |= s->fall_ip & s->fall_ie; - - for (int i = 0; i < s->ngpio; i++) { - pin = 1 << i; - qemu_set_irq(s->irq[i], (pending & pin) != 0); - trace_sifive_gpio_update_output_irq(i, (pending & pin) != 0); - } -} - -static void update_state(SIFIVEGPIOState *s) -{ - size_t i; - bool prev_ival, in, in_mask, port, out_xor, pull, output_en, input_en, - rise_ip, fall_ip, low_ip, high_ip, oval, actual_value, ival; - - for (i = 0; i < s->ngpio; i++) { - - prev_ival = extract32(s->value, i, 1); - in = extract32(s->in, i, 1); - in_mask = extract32(s->in_mask, i, 1); - port = extract32(s->port, i, 1); - out_xor = extract32(s->out_xor, i, 1); - pull = extract32(s->pue, i, 1); - output_en = extract32(s->output_en, i, 1); - input_en = extract32(s->input_en, i, 1); - rise_ip = extract32(s->rise_ip, i, 1); - fall_ip = extract32(s->fall_ip, i, 1); - low_ip = extract32(s->low_ip, i, 1); - high_ip = extract32(s->high_ip, i, 1); - - /* Output value (IOF not supported) */ - oval = output_en && (port ^ out_xor); - - /* Pin both driven externally and internally */ - if (output_en && in_mask) { - qemu_log_mask(LOG_GUEST_ERROR, "GPIO pin %zu short circuited\n", i); - } - - if (in_mask) { - /* The pin is driven by external device */ - actual_value = in; - } else if (output_en) { - /* The pin is driven by internal circuit */ - actual_value = oval; - } else { - /* Floating? Apply pull-up resistor */ - actual_value = pull; - } - - if (output_en) { - qemu_set_irq(s->output[i], actual_value); - } - - /* Input value */ - ival = input_en && actual_value; - - /* Interrupts */ - high_ip = high_ip || ival; - s->high_ip = deposit32(s->high_ip, i, 1, high_ip); - - low_ip = low_ip || !ival; - s->low_ip = deposit32(s->low_ip, i, 1, low_ip); - - rise_ip = rise_ip || (ival && !prev_ival); - s->rise_ip = deposit32(s->rise_ip, i, 1, rise_ip); - - fall_ip = fall_ip || (!ival && prev_ival); - s->fall_ip = deposit32(s->fall_ip, i, 1, fall_ip); - - /* Update value */ - s->value = deposit32(s->value, i, 1, ival); - } - update_output_irq(s); -} - -static uint64_t sifive_gpio_read(void *opaque, hwaddr offset, unsigned int size) -{ - SIFIVEGPIOState *s = SIFIVE_GPIO(opaque); - uint64_t r = 0; - - switch (offset) { - case SIFIVE_GPIO_REG_VALUE: - r = s->value; - break; - - case SIFIVE_GPIO_REG_INPUT_EN: - r = s->input_en; - break; - - case SIFIVE_GPIO_REG_OUTPUT_EN: - r = s->output_en; - break; - - case SIFIVE_GPIO_REG_PORT: - r = s->port; - break; - - case SIFIVE_GPIO_REG_PUE: - r = s->pue; - break; - - case SIFIVE_GPIO_REG_DS: - r = s->ds; - break; - - case SIFIVE_GPIO_REG_RISE_IE: - r = s->rise_ie; - break; - - case SIFIVE_GPIO_REG_RISE_IP: - r = s->rise_ip; - break; - - case SIFIVE_GPIO_REG_FALL_IE: - r = s->fall_ie; - break; - - case SIFIVE_GPIO_REG_FALL_IP: - r = s->fall_ip; - break; - - case SIFIVE_GPIO_REG_HIGH_IE: - r = s->high_ie; - break; - - case SIFIVE_GPIO_REG_HIGH_IP: - r = s->high_ip; - break; - - case SIFIVE_GPIO_REG_LOW_IE: - r = s->low_ie; - break; - - case SIFIVE_GPIO_REG_LOW_IP: - r = s->low_ip; - break; - - case SIFIVE_GPIO_REG_IOF_EN: - r = s->iof_en; - break; - - case SIFIVE_GPIO_REG_IOF_SEL: - r = s->iof_sel; - break; - - case SIFIVE_GPIO_REG_OUT_XOR: - r = s->out_xor; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: bad read offset 0x%" HWADDR_PRIx "\n", - __func__, offset); - } - - trace_sifive_gpio_read(offset, r); - - return r; -} - -static void sifive_gpio_write(void *opaque, hwaddr offset, - uint64_t value, unsigned int size) -{ - SIFIVEGPIOState *s = SIFIVE_GPIO(opaque); - - trace_sifive_gpio_write(offset, value); - - switch (offset) { - - case SIFIVE_GPIO_REG_INPUT_EN: - s->input_en = value; - break; - - case SIFIVE_GPIO_REG_OUTPUT_EN: - s->output_en = value; - break; - - case SIFIVE_GPIO_REG_PORT: - s->port = value; - break; - - case SIFIVE_GPIO_REG_PUE: - s->pue = value; - break; - - case SIFIVE_GPIO_REG_DS: - s->ds = value; - break; - - case SIFIVE_GPIO_REG_RISE_IE: - s->rise_ie = value; - break; - - case SIFIVE_GPIO_REG_RISE_IP: - /* Write 1 to clear */ - s->rise_ip &= ~value; - break; - - case SIFIVE_GPIO_REG_FALL_IE: - s->fall_ie = value; - break; - - case SIFIVE_GPIO_REG_FALL_IP: - /* Write 1 to clear */ - s->fall_ip &= ~value; - break; - - case SIFIVE_GPIO_REG_HIGH_IE: - s->high_ie = value; - break; - - case SIFIVE_GPIO_REG_HIGH_IP: - /* Write 1 to clear */ - s->high_ip &= ~value; - break; - - case SIFIVE_GPIO_REG_LOW_IE: - s->low_ie = value; - break; - - case SIFIVE_GPIO_REG_LOW_IP: - /* Write 1 to clear */ - s->low_ip &= ~value; - break; - - case SIFIVE_GPIO_REG_IOF_EN: - s->iof_en = value; - break; - - case SIFIVE_GPIO_REG_IOF_SEL: - s->iof_sel = value; - break; - - case SIFIVE_GPIO_REG_OUT_XOR: - s->out_xor = value; - break; - - default: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: bad write offset 0x%" HWADDR_PRIx "\n", - __func__, offset); - } - - update_state(s); -} - -static const MemoryRegionOps gpio_ops = { - .read = sifive_gpio_read, - .write = sifive_gpio_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .impl.min_access_size = 4, - .impl.max_access_size = 4, -}; - -static void sifive_gpio_set(void *opaque, int line, int value) -{ - SIFIVEGPIOState *s = SIFIVE_GPIO(opaque); - - trace_sifive_gpio_set(line, value); - - assert(line >= 0 && line < SIFIVE_GPIO_PINS); - - s->in_mask = deposit32(s->in_mask, line, 1, value >= 0); - if (value >= 0) { - s->in = deposit32(s->in, line, 1, value != 0); - } - - update_state(s); -} - -static void sifive_gpio_reset(DeviceState *dev) -{ - SIFIVEGPIOState *s = SIFIVE_GPIO(dev); - - s->value = 0; - s->input_en = 0; - s->output_en = 0; - s->port = 0; - s->pue = 0; - s->ds = 0; - s->rise_ie = 0; - s->rise_ip = 0; - s->fall_ie = 0; - s->fall_ip = 0; - s->high_ie = 0; - s->high_ip = 0; - s->low_ie = 0; - s->low_ip = 0; - s->iof_en = 0; - s->iof_sel = 0; - s->out_xor = 0; - s->in = 0; - s->in_mask = 0; -} - -static const VMStateDescription vmstate_sifive_gpio = { - .name = TYPE_SIFIVE_GPIO, - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(value, SIFIVEGPIOState), - VMSTATE_UINT32(input_en, SIFIVEGPIOState), - VMSTATE_UINT32(output_en, SIFIVEGPIOState), - VMSTATE_UINT32(port, SIFIVEGPIOState), - VMSTATE_UINT32(pue, SIFIVEGPIOState), - VMSTATE_UINT32(rise_ie, SIFIVEGPIOState), - VMSTATE_UINT32(rise_ip, SIFIVEGPIOState), - VMSTATE_UINT32(fall_ie, SIFIVEGPIOState), - VMSTATE_UINT32(fall_ip, SIFIVEGPIOState), - VMSTATE_UINT32(high_ie, SIFIVEGPIOState), - VMSTATE_UINT32(high_ip, SIFIVEGPIOState), - VMSTATE_UINT32(low_ie, SIFIVEGPIOState), - VMSTATE_UINT32(low_ip, SIFIVEGPIOState), - VMSTATE_UINT32(iof_en, SIFIVEGPIOState), - VMSTATE_UINT32(iof_sel, SIFIVEGPIOState), - VMSTATE_UINT32(out_xor, SIFIVEGPIOState), - VMSTATE_UINT32(in, SIFIVEGPIOState), - VMSTATE_UINT32(in_mask, SIFIVEGPIOState), - VMSTATE_END_OF_LIST() - } -}; - -static Property sifive_gpio_properties[] = { - DEFINE_PROP_UINT32("ngpio", SIFIVEGPIOState, ngpio, SIFIVE_GPIO_PINS), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sifive_gpio_realize(DeviceState *dev, Error **errp) -{ - SIFIVEGPIOState *s = SIFIVE_GPIO(dev); - - memory_region_init_io(&s->mmio, OBJECT(dev), &gpio_ops, s, - TYPE_SIFIVE_GPIO, SIFIVE_GPIO_SIZE); - - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); - - for (int i = 0; i < s->ngpio; i++) { - sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); - } - - qdev_init_gpio_in(DEVICE(s), sifive_gpio_set, s->ngpio); - qdev_init_gpio_out(DEVICE(s), s->output, s->ngpio); -} - -static void sifive_gpio_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - device_class_set_props(dc, sifive_gpio_properties); - dc->vmsd = &vmstate_sifive_gpio; - dc->realize = sifive_gpio_realize; - dc->reset = sifive_gpio_reset; - dc->desc = "SiFive GPIO"; -} - -static const TypeInfo sifive_gpio_info = { - .name = TYPE_SIFIVE_GPIO, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SIFIVEGPIOState), - .class_init = sifive_gpio_class_init -}; - -static void sifive_gpio_register_types(void) -{ - type_register_static(&sifive_gpio_info); -} - -type_init(sifive_gpio_register_types) diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c deleted file mode 100644 index 11ef147606..0000000000 --- a/hw/riscv/sifive_plic.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * SiFive PLIC (Platform Level Interrupt Controller) - * - * Copyright (c) 2017 SiFive, Inc. - * - * This provides a parameterizable interrupt controller based on SiFive's PLIC. - * - * 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 "qapi/error.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "qemu/error-report.h" -#include "hw/sysbus.h" -#include "hw/pci/msi.h" -#include "hw/boards.h" -#include "hw/qdev-properties.h" -#include "target/riscv/cpu.h" -#include "sysemu/sysemu.h" -#include "hw/riscv/sifive_plic.h" - -#define RISCV_DEBUG_PLIC 0 - -static PLICMode char_to_mode(char c) -{ - switch (c) { - case 'U': return PLICMode_U; - case 'S': return PLICMode_S; - case 'H': return PLICMode_H; - case 'M': return PLICMode_M; - default: - error_report("plic: invalid mode '%c'", c); - exit(1); - } -} - -static char mode_to_char(PLICMode m) -{ - switch (m) { - case PLICMode_U: return 'U'; - case PLICMode_S: return 'S'; - case PLICMode_H: return 'H'; - case PLICMode_M: return 'M'; - default: return '?'; - } -} - -static void sifive_plic_print_state(SiFivePLICState *plic) -{ - int i; - int addrid; - - /* pending */ - qemu_log("pending : "); - for (i = plic->bitfield_words - 1; i >= 0; i--) { - qemu_log("%08x", plic->pending[i]); - } - qemu_log("\n"); - - /* pending */ - qemu_log("claimed : "); - for (i = plic->bitfield_words - 1; i >= 0; i--) { - qemu_log("%08x", plic->claimed[i]); - } - qemu_log("\n"); - - for (addrid = 0; addrid < plic->num_addrs; addrid++) { - qemu_log("hart%d-%c enable: ", - plic->addr_config[addrid].hartid, - mode_to_char(plic->addr_config[addrid].mode)); - for (i = plic->bitfield_words - 1; i >= 0; i--) { - qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]); - } - qemu_log("\n"); - } -} - -static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value) -{ - uint32_t old, new, cmp = atomic_read(a); - - do { - old = cmp; - new = (old & ~mask) | (value & mask); - cmp = atomic_cmpxchg(a, old, new); - } while (old != cmp); - - return old; -} - -static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level) -{ - atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level); -} - -static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level) -{ - atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level); -} - -static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid) -{ - int i, j; - for (i = 0; i < plic->bitfield_words; i++) { - uint32_t pending_enabled_not_claimed = - (plic->pending[i] & ~plic->claimed[i]) & - plic->enable[addrid * plic->bitfield_words + i]; - if (!pending_enabled_not_claimed) { - continue; - } - for (j = 0; j < 32; j++) { - int irq = (i << 5) + j; - uint32_t prio = plic->source_priority[irq]; - int enabled = pending_enabled_not_claimed & (1 << j); - if (enabled && prio > plic->target_priority[addrid]) { - return 1; - } - } - } - return 0; -} - -static void sifive_plic_update(SiFivePLICState *plic) -{ - int addrid; - - /* raise irq on harts where this irq is enabled */ - for (addrid = 0; addrid < plic->num_addrs; addrid++) { - uint32_t hartid = plic->addr_config[addrid].hartid; - PLICMode mode = plic->addr_config[addrid].mode; - CPUState *cpu = qemu_get_cpu(hartid); - CPURISCVState *env = cpu ? cpu->env_ptr : NULL; - if (!env) { - continue; - } - int level = sifive_plic_irqs_pending(plic, addrid); - switch (mode) { - case PLICMode_M: - riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level)); - break; - case PLICMode_S: - riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_SEIP, BOOL_TO_MASK(level)); - break; - default: - break; - } - } - - if (RISCV_DEBUG_PLIC) { - sifive_plic_print_state(plic); - } -} - -static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid) -{ - int i, j; - uint32_t max_irq = 0; - uint32_t max_prio = plic->target_priority[addrid]; - - for (i = 0; i < plic->bitfield_words; i++) { - uint32_t pending_enabled_not_claimed = - (plic->pending[i] & ~plic->claimed[i]) & - plic->enable[addrid * plic->bitfield_words + i]; - if (!pending_enabled_not_claimed) { - continue; - } - for (j = 0; j < 32; j++) { - int irq = (i << 5) + j; - uint32_t prio = plic->source_priority[irq]; - int enabled = pending_enabled_not_claimed & (1 << j); - if (enabled && prio > max_prio) { - max_irq = irq; - max_prio = prio; - } - } - } - - if (max_irq) { - sifive_plic_set_pending(plic, max_irq, false); - sifive_plic_set_claimed(plic, max_irq, true); - } - return max_irq; -} - -static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) -{ - SiFivePLICState *plic = opaque; - - /* writes must be 4 byte words */ - if ((addr & 0x3) != 0) { - goto err; - } - - if (addr >= plic->priority_base && /* 4 bytes per source */ - addr < plic->priority_base + (plic->num_sources << 2)) - { - uint32_t irq = ((addr - plic->priority_base) >> 2) + 1; - if (RISCV_DEBUG_PLIC) { - qemu_log("plic: read priority: irq=%d priority=%d\n", - irq, plic->source_priority[irq]); - } - return plic->source_priority[irq]; - } else if (addr >= plic->pending_base && /* 1 bit per source */ - addr < plic->pending_base + (plic->num_sources >> 3)) - { - uint32_t word = (addr - plic->pending_base) >> 2; - if (RISCV_DEBUG_PLIC) { - qemu_log("plic: read pending: word=%d value=%d\n", - word, plic->pending[word]); - } - return plic->pending[word]; - } else if (addr >= plic->enable_base && /* 1 bit per source */ - addr < plic->enable_base + plic->num_addrs * plic->enable_stride) - { - uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; - uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; - if (wordid < plic->bitfield_words) { - if (RISCV_DEBUG_PLIC) { - qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n", - plic->addr_config[addrid].hartid, - mode_to_char(plic->addr_config[addrid].mode), wordid, - plic->enable[addrid * plic->bitfield_words + wordid]); - } - return plic->enable[addrid * plic->bitfield_words + wordid]; - } - } else if (addr >= plic->context_base && /* 1 bit per source */ - addr < plic->context_base + plic->num_addrs * plic->context_stride) - { - uint32_t addrid = (addr - plic->context_base) / plic->context_stride; - uint32_t contextid = (addr & (plic->context_stride - 1)); - if (contextid == 0) { - if (RISCV_DEBUG_PLIC) { - qemu_log("plic: read priority: hart%d-%c priority=%x\n", - plic->addr_config[addrid].hartid, - mode_to_char(plic->addr_config[addrid].mode), - plic->target_priority[addrid]); - } - return plic->target_priority[addrid]; - } else if (contextid == 4) { - uint32_t value = sifive_plic_claim(plic, addrid); - if (RISCV_DEBUG_PLIC) { - qemu_log("plic: read claim: hart%d-%c irq=%x\n", - plic->addr_config[addrid].hartid, - mode_to_char(plic->addr_config[addrid].mode), - value); - } - sifive_plic_update(plic); - return value; - } - } - -err: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Invalid register read 0x%" HWADDR_PRIx "\n", - __func__, addr); - return 0; -} - -static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - SiFivePLICState *plic = opaque; - - /* writes must be 4 byte words */ - if ((addr & 0x3) != 0) { - goto err; - } - - if (addr >= plic->priority_base && /* 4 bytes per source */ - addr < plic->priority_base + (plic->num_sources << 2)) - { - uint32_t irq = ((addr - plic->priority_base) >> 2) + 1; - plic->source_priority[irq] = value & 7; - if (RISCV_DEBUG_PLIC) { - qemu_log("plic: write priority: irq=%d priority=%d\n", - irq, plic->source_priority[irq]); - } - sifive_plic_update(plic); - return; - } else if (addr >= plic->pending_base && /* 1 bit per source */ - addr < plic->pending_base + (plic->num_sources >> 3)) - { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: invalid pending write: 0x%" HWADDR_PRIx "", - __func__, addr); - return; - } else if (addr >= plic->enable_base && /* 1 bit per source */ - addr < plic->enable_base + plic->num_addrs * plic->enable_stride) - { - uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride; - uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2; - if (wordid < plic->bitfield_words) { - plic->enable[addrid * plic->bitfield_words + wordid] = value; - if (RISCV_DEBUG_PLIC) { - qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n", - plic->addr_config[addrid].hartid, - mode_to_char(plic->addr_config[addrid].mode), wordid, - plic->enable[addrid * plic->bitfield_words + wordid]); - } - return; - } - } else if (addr >= plic->context_base && /* 4 bytes per reg */ - addr < plic->context_base + plic->num_addrs * plic->context_stride) - { - uint32_t addrid = (addr - plic->context_base) / plic->context_stride; - uint32_t contextid = (addr & (plic->context_stride - 1)); - if (contextid == 0) { - if (RISCV_DEBUG_PLIC) { - qemu_log("plic: write priority: hart%d-%c priority=%x\n", - plic->addr_config[addrid].hartid, - mode_to_char(plic->addr_config[addrid].mode), - plic->target_priority[addrid]); - } - if (value <= plic->num_priorities) { - plic->target_priority[addrid] = value; - sifive_plic_update(plic); - } - return; - } else if (contextid == 4) { - if (RISCV_DEBUG_PLIC) { - qemu_log("plic: write claim: hart%d-%c irq=%x\n", - plic->addr_config[addrid].hartid, - mode_to_char(plic->addr_config[addrid].mode), - (uint32_t)value); - } - if (value < plic->num_sources) { - sifive_plic_set_claimed(plic, value, false); - sifive_plic_update(plic); - } - return; - } - } - -err: - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Invalid register write 0x%" HWADDR_PRIx "\n", - __func__, addr); -} - -static const MemoryRegionOps sifive_plic_ops = { - .read = sifive_plic_read, - .write = sifive_plic_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static Property sifive_plic_properties[] = { - DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config), - DEFINE_PROP_UINT32("hartid-base", SiFivePLICState, hartid_base, 0), - DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0), - DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0), - DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0), - DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0), - DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0), - DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0), - DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0), - DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0), - DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -/* - * parse PLIC hart/mode address offset config - * - * "M" 1 hart with M mode - * "MS,MS" 2 harts, 0-1 with M and S mode - * "M,MS,MS,MS,MS" 5 harts, 0 with M mode, 1-5 with M and S mode - */ -static void parse_hart_config(SiFivePLICState *plic) -{ - int addrid, hartid, modes; - const char *p; - char c; - - /* count and validate hart/mode combinations */ - addrid = 0, hartid = 0, modes = 0; - p = plic->hart_config; - while ((c = *p++)) { - if (c == ',') { - addrid += ctpop8(modes); - modes = 0; - hartid++; - } else { - int m = 1 << char_to_mode(c); - if (modes == (modes | m)) { - error_report("plic: duplicate mode '%c' in config: %s", - c, plic->hart_config); - exit(1); - } - modes |= m; - } - } - if (modes) { - addrid += ctpop8(modes); - } - hartid++; - - plic->num_addrs = addrid; - plic->num_harts = hartid; - - /* store hart/mode combinations */ - plic->addr_config = g_new(PLICAddr, plic->num_addrs); - addrid = 0, hartid = plic->hartid_base; - p = plic->hart_config; - while ((c = *p++)) { - if (c == ',') { - hartid++; - } else { - plic->addr_config[addrid].addrid = addrid; - plic->addr_config[addrid].hartid = hartid; - plic->addr_config[addrid].mode = char_to_mode(c); - addrid++; - } - } -} - -static void sifive_plic_irq_request(void *opaque, int irq, int level) -{ - SiFivePLICState *plic = opaque; - if (RISCV_DEBUG_PLIC) { - qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level); - } - sifive_plic_set_pending(plic, irq, level > 0); - sifive_plic_update(plic); -} - -static void sifive_plic_realize(DeviceState *dev, Error **errp) -{ - SiFivePLICState *plic = SIFIVE_PLIC(dev); - int i; - - memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, - TYPE_SIFIVE_PLIC, plic->aperture_size); - parse_hart_config(plic); - plic->bitfield_words = (plic->num_sources + 31) >> 5; - plic->source_priority = g_new0(uint32_t, plic->num_sources); - plic->target_priority = g_new(uint32_t, plic->num_addrs); - plic->pending = g_new0(uint32_t, plic->bitfield_words); - plic->claimed = g_new0(uint32_t, plic->bitfield_words); - plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); - qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); - - /* We can't allow the supervisor to control SEIP as this would allow the - * supervisor to clear a pending external interrupt which will result in - * lost a interrupt in the case a PLIC is attached. The SEIP bit must be - * hardware controlled when a PLIC is attached. - */ - for (i = 0; i < plic->num_harts; i++) { - RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(plic->hartid_base + i)); - if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { - error_report("SEIP already claimed"); - exit(1); - } - } - - msi_nonbroken = true; -} - -static void sifive_plic_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - device_class_set_props(dc, sifive_plic_properties); - dc->realize = sifive_plic_realize; -} - -static const TypeInfo sifive_plic_info = { - .name = TYPE_SIFIVE_PLIC, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFivePLICState), - .class_init = sifive_plic_class_init, -}; - -static void sifive_plic_register_types(void) -{ - type_register_static(&sifive_plic_info); -} - -type_init(sifive_plic_register_types) - -/* - * Create PLIC device. - */ -DeviceState *sifive_plic_create(hwaddr addr, char *hart_config, - uint32_t hartid_base, uint32_t num_sources, - uint32_t num_priorities, uint32_t priority_base, - uint32_t pending_base, uint32_t enable_base, - uint32_t enable_stride, uint32_t context_base, - uint32_t context_stride, uint32_t aperture_size) -{ - DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC); - assert(enable_stride == (enable_stride & -enable_stride)); - assert(context_stride == (context_stride & -context_stride)); - qdev_prop_set_string(dev, "hart-config", hart_config); - qdev_prop_set_uint32(dev, "hartid-base", hartid_base); - qdev_prop_set_uint32(dev, "num-sources", num_sources); - qdev_prop_set_uint32(dev, "num-priorities", num_priorities); - qdev_prop_set_uint32(dev, "priority-base", priority_base); - qdev_prop_set_uint32(dev, "pending-base", pending_base); - qdev_prop_set_uint32(dev, "enable-base", enable_base); - qdev_prop_set_uint32(dev, "enable-stride", enable_stride); - qdev_prop_set_uint32(dev, "context-base", context_base); - qdev_prop_set_uint32(dev, "context-stride", context_stride); - qdev_prop_set_uint32(dev, "aperture-size", aperture_size); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); - return dev; -} diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c deleted file mode 100644 index 0c78fb2c93..0000000000 --- a/hw/riscv/sifive_test.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * QEMU SiFive Test Finisher - * - * Copyright (c) 2018 SiFive, Inc. - * - * Test finisher memory mapped device used to exit simulation - * - * 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 "hw/sysbus.h" -#include "qapi/error.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "sysemu/runstate.h" -#include "hw/hw.h" -#include "hw/riscv/sifive_test.h" - -static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size) -{ - return 0; -} - -static void sifive_test_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - if (addr == 0) { - int status = val64 & 0xffff; - int code = (val64 >> 16) & 0xffff; - switch (status) { - case FINISHER_FAIL: - exit(code); - case FINISHER_PASS: - exit(0); - case FINISHER_RESET: - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - return; - default: - break; - } - } - qemu_log_mask(LOG_GUEST_ERROR, "%s: write: addr=0x%x val=0x%016" PRIx64 "\n", - __func__, (int)addr, val64); -} - -static const MemoryRegionOps sifive_test_ops = { - .read = sifive_test_read, - .write = sifive_test_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void sifive_test_init(Object *obj) -{ - SiFiveTestState *s = SIFIVE_TEST(obj); - - memory_region_init_io(&s->mmio, obj, &sifive_test_ops, s, - TYPE_SIFIVE_TEST, 0x1000); - sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); -} - -static const TypeInfo sifive_test_info = { - .name = TYPE_SIFIVE_TEST, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFiveTestState), - .instance_init = sifive_test_init, -}; - -static void sifive_test_register_types(void) -{ - type_register_static(&sifive_test_info); -} - -type_init(sifive_test_register_types) - - -/* - * Create Test device. - */ -DeviceState *sifive_test_create(hwaddr addr) -{ - DeviceState *dev = qdev_new(TYPE_SIFIVE_TEST); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr); - return dev; -} diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index a48046c6a0..4f12a93188 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -14,6 +14,7 @@ * 4) GPIO (General Purpose Input/Output Controller) * 5) OTP (One-Time Programmable) memory with stored serial number * 6) GEM (Gigabit Ethernet Controller) and management block + * 7) DMA (Direct Memory Access Controller) * * This board currently generates devicetree dynamically that indicates at least * two harts and up to five harts. @@ -45,11 +46,11 @@ #include "hw/misc/unimp.h" #include "target/riscv/cpu.h" #include "hw/riscv/riscv_hart.h" -#include "hw/riscv/sifive_plic.h" -#include "hw/riscv/sifive_clint.h" -#include "hw/riscv/sifive_uart.h" #include "hw/riscv/sifive_u.h" #include "hw/riscv/boot.h" +#include "hw/char/sifive_uart.h" +#include "hw/intc/sifive_clint.h" +#include "hw/intc/sifive_plic.h" #include "chardev/char.h" #include "net/eth.h" #include "sysemu/arch_init.h" @@ -73,6 +74,7 @@ static const struct MemmapEntry { [SIFIVE_U_MROM] = { 0x1000, 0xf000 }, [SIFIVE_U_CLINT] = { 0x2000000, 0x10000 }, [SIFIVE_U_L2CC] = { 0x2010000, 0x1000 }, + [SIFIVE_U_PDMA] = { 0x3000000, 0x100000 }, [SIFIVE_U_L2LIM] = { 0x8000000, 0x2000000 }, [SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 }, [SIFIVE_U_PRCI] = { 0x10000000, 0x1000 }, @@ -303,6 +305,22 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "compatible", "gpio-restart"); g_free(nodename); + nodename = g_strdup_printf("/soc/dma@%lx", + (long)memmap[SIFIVE_U_PDMA].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#dma-cells", 1); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", + SIFIVE_U_PDMA_IRQ0, SIFIVE_U_PDMA_IRQ1, SIFIVE_U_PDMA_IRQ2, + SIFIVE_U_PDMA_IRQ3, SIFIVE_U_PDMA_IRQ4, SIFIVE_U_PDMA_IRQ5, + SIFIVE_U_PDMA_IRQ6, SIFIVE_U_PDMA_IRQ7); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_PDMA].base, + 0x0, memmap[SIFIVE_U_PDMA].size); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "sifive,fu540-c000-pdma"); + g_free(nodename); + nodename = g_strdup_printf("/soc/cache-controller@%lx", (long)memmap[SIFIVE_U_L2CC].base); qemu_fdt_add_subnode(fdt, nodename); @@ -611,6 +629,7 @@ static void sifive_u_soc_instance_init(Object *obj) 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", SIFIVE_E_CPU); + qdev_prop_set_uint64(DEVICE(&s->e_cpus), "resetvec", 0x1004); object_initialize_child(obj, "u-cluster", &s->u_cluster, TYPE_CPU_CLUSTER); qdev_prop_set_uint32(DEVICE(&s->u_cluster), "cluster-id", 1); @@ -620,11 +639,13 @@ static void sifive_u_soc_instance_init(Object *obj) 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", SIFIVE_U_CPU); + qdev_prop_set_uint64(DEVICE(&s->u_cpus), "resetvec", 0x1004); object_initialize_child(obj, "prci", &s->prci, TYPE_SIFIVE_U_PRCI); object_initialize_child(obj, "otp", &s->otp, TYPE_SIFIVE_U_OTP); object_initialize_child(obj, "gem", &s->gem, TYPE_CADENCE_GEM); object_initialize_child(obj, "gpio", &s->gpio, TYPE_SIFIVE_GPIO); + object_initialize_child(obj, "pdma", &s->dma, TYPE_SIFIVE_PDMA); } static void sifive_u_soc_realize(DeviceState *dev, Error **errp) @@ -704,7 +725,8 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART1_IRQ)); sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, 0, ms->smp.cpus, - SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, false); + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + SIFIVE_CLINT_TIMEBASE_FREQ, false); if (!sysbus_realize(SYS_BUS_DEVICE(&s->prci), errp)) { return; @@ -727,6 +749,17 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp) SIFIVE_U_GPIO_IRQ0 + i)); } + /* PDMA */ + sysbus_realize(SYS_BUS_DEVICE(&s->dma), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dma), 0, memmap[SIFIVE_U_PDMA].base); + + /* Connect PDMA interrupts to the PLIC */ + for (i = 0; i < SIFIVE_PDMA_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), i, + qdev_get_gpio_in(DEVICE(s->plic), + SIFIVE_U_PDMA_IRQ0 + i)); + } + qdev_prop_set_uint32(DEVICE(&s->otp), "serial", s->serial); if (!sysbus_realize(SYS_BUS_DEVICE(&s->otp), errp)) { return; diff --git a/hw/riscv/sifive_u_otp.c b/hw/riscv/sifive_u_otp.c deleted file mode 100644 index f6ecbaa2ca..0000000000 --- a/hw/riscv/sifive_u_otp.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * QEMU SiFive U OTP (One-Time Programmable) Memory interface - * - * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com> - * - * Simple model of the OTP to emulate register reads made by the SDK BSP - * - * 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 "hw/qdev-properties.h" -#include "hw/sysbus.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "hw/riscv/sifive_u_otp.h" - -static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size) -{ - SiFiveUOTPState *s = opaque; - - switch (addr) { - case SIFIVE_U_OTP_PA: - return s->pa; - case SIFIVE_U_OTP_PAIO: - return s->paio; - case SIFIVE_U_OTP_PAS: - return s->pas; - case SIFIVE_U_OTP_PCE: - return s->pce; - case SIFIVE_U_OTP_PCLK: - return s->pclk; - case SIFIVE_U_OTP_PDIN: - return s->pdin; - case SIFIVE_U_OTP_PDOUT: - if ((s->pce & SIFIVE_U_OTP_PCE_EN) && - (s->pdstb & SIFIVE_U_OTP_PDSTB_EN) && - (s->ptrim & SIFIVE_U_OTP_PTRIM_EN)) { - return s->fuse[s->pa & SIFIVE_U_OTP_PA_MASK]; - } else { - return 0xff; - } - case SIFIVE_U_OTP_PDSTB: - return s->pdstb; - case SIFIVE_U_OTP_PPROG: - return s->pprog; - case SIFIVE_U_OTP_PTC: - return s->ptc; - case SIFIVE_U_OTP_PTM: - return s->ptm; - case SIFIVE_U_OTP_PTM_REP: - return s->ptm_rep; - case SIFIVE_U_OTP_PTR: - return s->ptr; - case SIFIVE_U_OTP_PTRIM: - return s->ptrim; - case SIFIVE_U_OTP_PWE: - return s->pwe; - } - - qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n", - __func__, addr); - return 0; -} - -static void sifive_u_otp_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - SiFiveUOTPState *s = opaque; - uint32_t val32 = (uint32_t)val64; - - switch (addr) { - case SIFIVE_U_OTP_PA: - s->pa = val32 & SIFIVE_U_OTP_PA_MASK; - break; - case SIFIVE_U_OTP_PAIO: - s->paio = val32; - break; - case SIFIVE_U_OTP_PAS: - s->pas = val32; - break; - case SIFIVE_U_OTP_PCE: - s->pce = val32; - break; - case SIFIVE_U_OTP_PCLK: - s->pclk = val32; - break; - case SIFIVE_U_OTP_PDIN: - s->pdin = val32; - break; - case SIFIVE_U_OTP_PDOUT: - /* read-only */ - break; - case SIFIVE_U_OTP_PDSTB: - s->pdstb = val32; - break; - case SIFIVE_U_OTP_PPROG: - s->pprog = val32; - break; - case SIFIVE_U_OTP_PTC: - s->ptc = val32; - break; - case SIFIVE_U_OTP_PTM: - s->ptm = val32; - break; - case SIFIVE_U_OTP_PTM_REP: - s->ptm_rep = val32; - break; - case SIFIVE_U_OTP_PTR: - s->ptr = val32; - break; - case SIFIVE_U_OTP_PTRIM: - s->ptrim = val32; - break; - case SIFIVE_U_OTP_PWE: - s->pwe = val32; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx - " v=0x%x\n", __func__, addr, val32); - } -} - -static const MemoryRegionOps sifive_u_otp_ops = { - .read = sifive_u_otp_read, - .write = sifive_u_otp_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static Property sifive_u_otp_properties[] = { - DEFINE_PROP_UINT32("serial", SiFiveUOTPState, serial, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sifive_u_otp_realize(DeviceState *dev, Error **errp) -{ - SiFiveUOTPState *s = SIFIVE_U_OTP(dev); - - memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_otp_ops, s, - TYPE_SIFIVE_U_OTP, SIFIVE_U_OTP_REG_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); -} - -static void sifive_u_otp_reset(DeviceState *dev) -{ - SiFiveUOTPState *s = SIFIVE_U_OTP(dev); - - /* Initialize all fuses' initial value to 0xFFs */ - memset(s->fuse, 0xff, sizeof(s->fuse)); - - /* Make a valid content of serial number */ - s->fuse[SIFIVE_U_OTP_SERIAL_ADDR] = s->serial; - s->fuse[SIFIVE_U_OTP_SERIAL_ADDR + 1] = ~(s->serial); -} - -static void sifive_u_otp_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - device_class_set_props(dc, sifive_u_otp_properties); - dc->realize = sifive_u_otp_realize; - dc->reset = sifive_u_otp_reset; -} - -static const TypeInfo sifive_u_otp_info = { - .name = TYPE_SIFIVE_U_OTP, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFiveUOTPState), - .class_init = sifive_u_otp_class_init, -}; - -static void sifive_u_otp_register_types(void) -{ - type_register_static(&sifive_u_otp_info); -} - -type_init(sifive_u_otp_register_types) diff --git a/hw/riscv/sifive_u_prci.c b/hw/riscv/sifive_u_prci.c deleted file mode 100644 index 4fa590c064..0000000000 --- a/hw/riscv/sifive_u_prci.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt) - * - * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com> - * - * Simple model of the PRCI to emulate register reads made by the SDK BSP - * - * 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 "hw/sysbus.h" -#include "qemu/log.h" -#include "qemu/module.h" -#include "hw/riscv/sifive_u_prci.h" - -static uint64_t sifive_u_prci_read(void *opaque, hwaddr addr, unsigned int size) -{ - SiFiveUPRCIState *s = opaque; - - switch (addr) { - case SIFIVE_U_PRCI_HFXOSCCFG: - return s->hfxosccfg; - case SIFIVE_U_PRCI_COREPLLCFG0: - return s->corepllcfg0; - case SIFIVE_U_PRCI_DDRPLLCFG0: - return s->ddrpllcfg0; - case SIFIVE_U_PRCI_DDRPLLCFG1: - return s->ddrpllcfg1; - case SIFIVE_U_PRCI_GEMGXLPLLCFG0: - return s->gemgxlpllcfg0; - case SIFIVE_U_PRCI_GEMGXLPLLCFG1: - return s->gemgxlpllcfg1; - case SIFIVE_U_PRCI_CORECLKSEL: - return s->coreclksel; - case SIFIVE_U_PRCI_DEVICESRESET: - return s->devicesreset; - case SIFIVE_U_PRCI_CLKMUXSTATUS: - return s->clkmuxstatus; - } - - qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n", - __func__, addr); - - return 0; -} - -static void sifive_u_prci_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - SiFiveUPRCIState *s = opaque; - uint32_t val32 = (uint32_t)val64; - - switch (addr) { - case SIFIVE_U_PRCI_HFXOSCCFG: - s->hfxosccfg = val32; - /* OSC stays ready */ - s->hfxosccfg |= SIFIVE_U_PRCI_HFXOSCCFG_RDY; - break; - case SIFIVE_U_PRCI_COREPLLCFG0: - s->corepllcfg0 = val32; - /* internal feedback */ - s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; - /* PLL stays locked */ - s->corepllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; - break; - case SIFIVE_U_PRCI_DDRPLLCFG0: - s->ddrpllcfg0 = val32; - /* internal feedback */ - s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; - /* PLL stays locked */ - s->ddrpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; - break; - case SIFIVE_U_PRCI_DDRPLLCFG1: - s->ddrpllcfg1 = val32; - break; - case SIFIVE_U_PRCI_GEMGXLPLLCFG0: - s->gemgxlpllcfg0 = val32; - /* internal feedback */ - s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_FSE; - /* PLL stays locked */ - s->gemgxlpllcfg0 |= SIFIVE_U_PRCI_PLLCFG0_LOCK; - break; - case SIFIVE_U_PRCI_GEMGXLPLLCFG1: - s->gemgxlpllcfg1 = val32; - break; - case SIFIVE_U_PRCI_CORECLKSEL: - s->coreclksel = val32; - break; - case SIFIVE_U_PRCI_DEVICESRESET: - s->devicesreset = val32; - break; - case SIFIVE_U_PRCI_CLKMUXSTATUS: - s->clkmuxstatus = val32; - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx - " v=0x%x\n", __func__, addr, val32); - } -} - -static const MemoryRegionOps sifive_u_prci_ops = { - .read = sifive_u_prci_read, - .write = sifive_u_prci_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void sifive_u_prci_realize(DeviceState *dev, Error **errp) -{ - SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev); - - memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_u_prci_ops, s, - TYPE_SIFIVE_U_PRCI, SIFIVE_U_PRCI_REG_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio); -} - -static void sifive_u_prci_reset(DeviceState *dev) -{ - SiFiveUPRCIState *s = SIFIVE_U_PRCI(dev); - - /* Initialize register to power-on-reset values */ - s->hfxosccfg = SIFIVE_U_PRCI_HFXOSCCFG_RDY | SIFIVE_U_PRCI_HFXOSCCFG_EN; - s->corepllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | - SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | - SIFIVE_U_PRCI_PLLCFG0_LOCK; - s->ddrpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | - SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | - SIFIVE_U_PRCI_PLLCFG0_LOCK; - s->gemgxlpllcfg0 = SIFIVE_U_PRCI_PLLCFG0_DIVR | SIFIVE_U_PRCI_PLLCFG0_DIVF | - SIFIVE_U_PRCI_PLLCFG0_DIVQ | SIFIVE_U_PRCI_PLLCFG0_FSE | - SIFIVE_U_PRCI_PLLCFG0_LOCK; - s->coreclksel = SIFIVE_U_PRCI_CORECLKSEL_HFCLK; -} - -static void sifive_u_prci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = sifive_u_prci_realize; - dc->reset = sifive_u_prci_reset; -} - -static const TypeInfo sifive_u_prci_info = { - .name = TYPE_SIFIVE_U_PRCI, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFiveUPRCIState), - .class_init = sifive_u_prci_class_init, -}; - -static void sifive_u_prci_register_types(void) -{ - type_register_static(&sifive_u_prci_info); -} - -type_init(sifive_u_prci_register_types) diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c deleted file mode 100644 index 9350482662..0000000000 --- a/hw/riscv/sifive_uart.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * QEMU model of the UART on the SiFive E300 and U500 series SOCs. - * - * Copyright (c) 2016 Stefan O'Rear - * - * 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 "qapi/error.h" -#include "qemu/log.h" -#include "hw/sysbus.h" -#include "chardev/char.h" -#include "chardev/char-fe.h" -#include "hw/hw.h" -#include "hw/irq.h" -#include "hw/riscv/sifive_uart.h" - -/* - * Not yet implemented: - * - * Transmit FIFO using "qemu/fifo8.h" - */ - -/* Returns the state of the IP (interrupt pending) register */ -static uint64_t uart_ip(SiFiveUARTState *s) -{ - uint64_t ret = 0; - - uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl); - uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl); - - if (txcnt != 0) { - ret |= SIFIVE_UART_IP_TXWM; - } - if (s->rx_fifo_len > rxcnt) { - ret |= SIFIVE_UART_IP_RXWM; - } - - return ret; -} - -static void update_irq(SiFiveUARTState *s) -{ - int cond = 0; - if ((s->ie & SIFIVE_UART_IE_TXWM) || - ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len)) { - cond = 1; - } - if (cond) { - qemu_irq_raise(s->irq); - } else { - qemu_irq_lower(s->irq); - } -} - -static uint64_t -uart_read(void *opaque, hwaddr addr, unsigned int size) -{ - SiFiveUARTState *s = opaque; - unsigned char r; - switch (addr) { - case SIFIVE_UART_RXFIFO: - if (s->rx_fifo_len) { - r = s->rx_fifo[0]; - memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1); - s->rx_fifo_len--; - qemu_chr_fe_accept_input(&s->chr); - update_irq(s); - return r; - } - return 0x80000000; - - case SIFIVE_UART_TXFIFO: - return 0; /* Should check tx fifo */ - case SIFIVE_UART_IE: - return s->ie; - case SIFIVE_UART_IP: - return uart_ip(s); - case SIFIVE_UART_TXCTRL: - return s->txctrl; - case SIFIVE_UART_RXCTRL: - return s->rxctrl; - case SIFIVE_UART_DIV: - return s->div; - } - - qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n", - __func__, (int)addr); - return 0; -} - -static void -uart_write(void *opaque, hwaddr addr, - uint64_t val64, unsigned int size) -{ - SiFiveUARTState *s = opaque; - uint32_t value = val64; - unsigned char ch = value; - - switch (addr) { - case SIFIVE_UART_TXFIFO: - qemu_chr_fe_write(&s->chr, &ch, 1); - update_irq(s); - return; - case SIFIVE_UART_IE: - s->ie = val64; - update_irq(s); - return; - case SIFIVE_UART_TXCTRL: - s->txctrl = val64; - return; - case SIFIVE_UART_RXCTRL: - s->rxctrl = val64; - return; - case SIFIVE_UART_DIV: - s->div = val64; - return; - } - qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n", - __func__, (int)addr, (int)value); -} - -static const MemoryRegionOps uart_ops = { - .read = uart_read, - .write = uart_write, - .endianness = DEVICE_NATIVE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4 - } -}; - -static void uart_rx(void *opaque, const uint8_t *buf, int size) -{ - SiFiveUARTState *s = opaque; - - /* Got a byte. */ - if (s->rx_fifo_len >= sizeof(s->rx_fifo)) { - printf("WARNING: UART dropped char.\n"); - return; - } - s->rx_fifo[s->rx_fifo_len++] = *buf; - - update_irq(s); -} - -static int uart_can_rx(void *opaque) -{ - SiFiveUARTState *s = opaque; - - return s->rx_fifo_len < sizeof(s->rx_fifo); -} - -static void uart_event(void *opaque, QEMUChrEvent event) -{ -} - -static int uart_be_change(void *opaque) -{ - SiFiveUARTState *s = opaque; - - qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, - uart_be_change, s, NULL, true); - - return 0; -} - -/* - * Create UART device. - */ -SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, - Chardev *chr, qemu_irq irq) -{ - SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState)); - s->irq = irq; - qemu_chr_fe_init(&s->chr, chr, &error_abort); - qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event, - uart_be_change, s, NULL, true); - memory_region_init_io(&s->mmio, NULL, &uart_ops, s, - TYPE_SIFIVE_UART, SIFIVE_UART_MAX); - memory_region_add_subregion(address_space, base, &s->mmio); - return s; -} diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 56f5fe73c7..3fd152a035 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -31,12 +31,12 @@ #include "hw/loader.h" #include "hw/sysbus.h" #include "target/riscv/cpu.h" -#include "hw/riscv/riscv_htif.h" #include "hw/riscv/riscv_hart.h" -#include "hw/riscv/sifive_clint.h" #include "hw/riscv/spike.h" #include "hw/riscv/boot.h" #include "hw/riscv/numa.h" +#include "hw/char/riscv_htif.h" +#include "hw/intc/sifive_clint.h" #include "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" @@ -242,7 +242,8 @@ static void spike_board_init(MachineState *machine) sifive_clint_create( memmap[SPIKE_CLINT].base + i * memmap[SPIKE_CLINT].size, memmap[SPIKE_CLINT].size, base_hartid, hart_count, - SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, false); + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + SIFIVE_CLINT_TIMEBASE_FREQ, false); } /* register system main memory (actual RAM) */ diff --git a/hw/riscv/trace-events b/hw/riscv/trace-events deleted file mode 100644 index b819878963..0000000000 --- a/hw/riscv/trace-events +++ /dev/null @@ -1,7 +0,0 @@ -# See docs/devel/tracing.txt for syntax documentation. - -# sifive_gpio.c -sifive_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64 -sifive_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 -sifive_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 -sifive_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 diff --git a/hw/riscv/trace.h b/hw/riscv/trace.h deleted file mode 100644 index 8c0e3ca1f3..0000000000 --- a/hw/riscv/trace.h +++ /dev/null @@ -1 +0,0 @@ -#include "trace/trace-hw_riscv.h" diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 6fca513ec9..41bd2f38ba 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -30,12 +30,12 @@ #include "hw/char/serial.h" #include "target/riscv/cpu.h" #include "hw/riscv/riscv_hart.h" -#include "hw/riscv/sifive_plic.h" -#include "hw/riscv/sifive_clint.h" -#include "hw/riscv/sifive_test.h" #include "hw/riscv/virt.h" #include "hw/riscv/boot.h" #include "hw/riscv/numa.h" +#include "hw/intc/sifive_clint.h" +#include "hw/intc/sifive_plic.h" +#include "hw/misc/sifive_test.h" #include "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" @@ -541,7 +541,8 @@ static void virt_machine_init(MachineState *machine) sifive_clint_create( memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size, memmap[VIRT_CLINT].size, base_hartid, hart_count, - SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, true); + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + SIFIVE_CLINT_TIMEBASE_FREQ, true); /* Per-socket PLIC hart topology configuration string */ plic_hart_config_len = |