From 4100d5e6dc28cdd89d3eec6e4ddeb9d1a159c330 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:38:57 +0800 Subject: hw/riscv: hart: Add a new 'resetvec' property RISC-V machines do not instantiate RISC-V CPUs directly, instead they do that via the hart array. Add a new property for the reset vector address to allow the value to be passed to the CPU, before CPU is realized. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-Id: <1598924352-89526-3-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- include/hw/riscv/riscv_hart.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h index c75856fa73..77aa4bc948 100644 --- a/include/hw/riscv/riscv_hart.h +++ b/include/hw/riscv/riscv_hart.h @@ -37,6 +37,7 @@ typedef struct RISCVHartArrayState { uint32_t num_harts; uint32_t hartid_base; char *cpu_type; + uint64_t resetvec; RISCVCPU *harts; } RISCVHartArrayState; -- cgit v1.2.3-55-g7522 From 56f6e31e7b7e06a66a0efd5464fa4257de7ec242 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:38:59 +0800 Subject: hw/riscv: Initial support for Microchip PolarFire SoC Icicle Kit board This is an initial support for Microchip PolarFire SoC Icicle Kit. The Icicle Kit board integrates a PolarFire SoC, with one SiFive's E51 plus four U54 cores and many on-chip peripherals and an FPGA. For more details about Microchip PolarFire Soc, please see: https://www.microsemi.com/product-directory/soc-fpgas/5498-polarfire-soc-fpga Unlike SiFive FU540, the RISC-V core resect vector is at 0x20220000. The following perepherals are created as an unimplemented device: - Bus Error Uint 0/1/2/3/4 - L2 cache controller - SYSREG - MPUCFG - IOSCBCFG More devices will be added later. The BIOS image used by this machine is hss.bin, aka Hart Software Services, which can be built from: https://github.com/polarfire-soc/hart-software-services To launch this machine: $ qemu-system-riscv64 -nographic -M microchip-icicle-kit The memory is set to 1 GiB by default to match the hardware. A sanity check on ram size is performed in the machine init routine to prompt user to increase the RAM size to > 1 GiB when less than 1 GiB ram is detected. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1598924352-89526-5-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 7 + default-configs/riscv64-softmmu.mak | 1 + hw/riscv/Kconfig | 6 + hw/riscv/meson.build | 1 + hw/riscv/microchip_pfsoc.c | 312 ++++++++++++++++++++++++++++++++++++ include/hw/riscv/microchip_pfsoc.h | 88 ++++++++++ 6 files changed, 415 insertions(+) create mode 100644 hw/riscv/microchip_pfsoc.c create mode 100644 include/hw/riscv/microchip_pfsoc.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 7d0a5e91e4..9641ed37e2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1316,6 +1316,13 @@ F: include/hw/riscv/opentitan.h F: include/hw/char/ibex_uart.h F: include/hw/intc/ibex_plic.h +Microchip PolarFire SoC Icicle Kit +M: Bin Meng +L: qemu-riscv@nongnu.org +S: Supported +F: hw/riscv/microchip_pfsoc.c +F: include/hw/riscv/microchip_pfsoc.h + RX Machines ----------- rx-gdbsim diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak index aaf6d735bb..76b6195648 100644 --- a/default-configs/riscv64-softmmu.mak +++ b/default-configs/riscv64-softmmu.mak @@ -10,3 +10,4 @@ CONFIG_SPIKE=y CONFIG_SIFIVE_E=y CONFIG_SIFIVE_U=y CONFIG_RISCV_VIRT=y +CONFIG_MICROCHIP_PFSOC=y diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 28947ef3e0..3292faef27 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -48,3 +48,9 @@ config RISCV_VIRT select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 select SIFIVE + +config MICROCHIP_PFSOC + bool + select HART + select SIFIVE + select UNIMP diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index fe2ea75f65..c29a48c3f1 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -16,5 +16,6 @@ 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..d946b2aae5 --- /dev/null +++ b/hw/riscv/microchip_pfsoc.c @@ -0,0 +1,312 @@ +/* + * QEMU RISC-V Board Compatible with Microchip PolarFire SoC Icicle Kit + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * 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) + * + * 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 . + */ + +#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 "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/sifive_clint.h" +#include "hw/riscv/sifive_plic.h" +#include "hw/riscv/microchip_pfsoc.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 + +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_L2LIM] = { 0x8000000, 0x2000000 }, + [MICROCHIP_PFSOC_PLIC] = { 0xc000000, 0x4000000 }, + [MICROCHIP_PFSOC_SYSREG] = { 0x20002000, 0x2000 }, + [MICROCHIP_PFSOC_MPUCFG] = { 0x20005000, 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); +} + +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; + 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, 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); + + /* 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); + + /* 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); + + /* 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); +} + +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/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h new file mode 100644 index 0000000000..1953ef1464 --- /dev/null +++ b/include/hw/riscv/microchip_pfsoc.h @@ -0,0 +1,88 @@ +/* + * Microchip PolarFire SoC machine interface + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * 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 . + */ + +#ifndef HW_MICROCHIP_PFSOC_H +#define HW_MICROCHIP_PFSOC_H + +typedef struct MicrochipPFSoCState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + CPUClusterState e_cluster; + CPUClusterState u_cluster; + RISCVHartArrayState e_cpus; + RISCVHartArrayState u_cpus; + DeviceState *plic; +} MicrochipPFSoCState; + +#define TYPE_MICROCHIP_PFSOC "microchip.pfsoc" +#define MICROCHIP_PFSOC(obj) \ + OBJECT_CHECK(MicrochipPFSoCState, (obj), TYPE_MICROCHIP_PFSOC) + +typedef struct MicrochipIcicleKitState { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + MicrochipPFSoCState soc; +} MicrochipIcicleKitState; + +#define TYPE_MICROCHIP_ICICLE_KIT_MACHINE \ + MACHINE_TYPE_NAME("microchip-icicle-kit") +#define MICROCHIP_ICICLE_KIT_MACHINE(obj) \ + OBJECT_CHECK(MicrochipIcicleKitState, (obj), \ + TYPE_MICROCHIP_ICICLE_KIT_MACHINE) + +enum { + MICROCHIP_PFSOC_DEBUG, + MICROCHIP_PFSOC_E51_DTIM, + MICROCHIP_PFSOC_BUSERR_UNIT0, + MICROCHIP_PFSOC_BUSERR_UNIT1, + MICROCHIP_PFSOC_BUSERR_UNIT2, + MICROCHIP_PFSOC_BUSERR_UNIT3, + MICROCHIP_PFSOC_BUSERR_UNIT4, + MICROCHIP_PFSOC_CLINT, + MICROCHIP_PFSOC_L2CC, + MICROCHIP_PFSOC_L2LIM, + MICROCHIP_PFSOC_PLIC, + MICROCHIP_PFSOC_SYSREG, + MICROCHIP_PFSOC_MPUCFG, + MICROCHIP_PFSOC_ENVM_CFG, + MICROCHIP_PFSOC_ENVM_DATA, + MICROCHIP_PFSOC_IOSCB_CFG, + MICROCHIP_PFSOC_DRAM, +}; + +#define MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT 1 +#define MICROCHIP_PFSOC_COMPUTE_CPU_COUNT 4 + +#define MICROCHIP_PFSOC_PLIC_HART_CONFIG "MS" +#define MICROCHIP_PFSOC_PLIC_NUM_SOURCES 185 +#define MICROCHIP_PFSOC_PLIC_NUM_PRIORITIES 7 +#define MICROCHIP_PFSOC_PLIC_PRIORITY_BASE 0x04 +#define MICROCHIP_PFSOC_PLIC_PENDING_BASE 0x1000 +#define MICROCHIP_PFSOC_PLIC_ENABLE_BASE 0x2000 +#define MICROCHIP_PFSOC_PLIC_ENABLE_STRIDE 0x80 +#define MICROCHIP_PFSOC_PLIC_CONTEXT_BASE 0x200000 +#define MICROCHIP_PFSOC_PLIC_CONTEXT_STRIDE 0x1000 + +#endif /* HW_MICROCHIP_PFSOC_H */ -- cgit v1.2.3-55-g7522 From a8fb0a500a695104cda5837b7aba93dee3abddde Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:00 +0800 Subject: hw/char: Add Microchip PolarFire SoC MMUART emulation Microchip PolarFire SoC MMUART is ns16550 compatible, with some additional registers. Create a simple MMUART model built on top of the existing ns16550 model. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1598924352-89526-6-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 2 + hw/char/Kconfig | 3 ++ hw/char/mchp_pfsoc_mmuart.c | 86 +++++++++++++++++++++++++++++++++++++ hw/char/meson.build | 1 + include/hw/char/mchp_pfsoc_mmuart.h | 61 ++++++++++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 hw/char/mchp_pfsoc_mmuart.c create mode 100644 include/hw/char/mchp_pfsoc_mmuart.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 9641ed37e2..d17b08fad9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1321,7 +1321,9 @@ M: Bin Meng L: qemu-riscv@nongnu.org S: Supported F: hw/riscv/microchip_pfsoc.c +F: hw/char/mchp_pfsoc_mmuart.c F: include/hw/riscv/microchip_pfsoc.h +F: include/hw/char/mchp_pfsoc_mmuart.h RX Machines ----------- diff --git a/hw/char/Kconfig b/hw/char/Kconfig index b7e0e4d5fa..1d645554c7 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -52,3 +52,6 @@ config RENESAS_SCI config AVR_USART bool + +config MCHP_PFSOC_MMUART + bool diff --git a/hw/char/mchp_pfsoc_mmuart.c b/hw/char/mchp_pfsoc_mmuart.c new file mode 100644 index 0000000000..8a002b0a19 --- /dev/null +++ b/hw/char/mchp_pfsoc_mmuart.c @@ -0,0 +1,86 @@ +/* + * Microchip PolarFire SoC MMUART emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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 . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "chardev/char.h" +#include "exec/address-spaces.h" +#include "hw/char/mchp_pfsoc_mmuart.h" + +static uint64_t mchp_pfsoc_mmuart_read(void *opaque, hwaddr addr, unsigned size) +{ + MchpPfSoCMMUartState *s = opaque; + + if (addr >= MCHP_PFSOC_MMUART_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: read: addr=0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + } + + return s->reg[addr / sizeof(uint32_t)]; +} + +static void mchp_pfsoc_mmuart_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + MchpPfSoCMMUartState *s = opaque; + uint32_t val32 = (uint32_t)value; + + if (addr >= MCHP_PFSOC_MMUART_REG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%" HWADDR_PRIx + " v=0x%x\n", __func__, addr, val32); + return; + } + + s->reg[addr / sizeof(uint32_t)] = val32; +} + +static const MemoryRegionOps mchp_pfsoc_mmuart_ops = { + .read = mchp_pfsoc_mmuart_read, + .write = mchp_pfsoc_mmuart_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +MchpPfSoCMMUartState *mchp_pfsoc_mmuart_create(MemoryRegion *sysmem, + hwaddr base, qemu_irq irq, Chardev *chr) +{ + MchpPfSoCMMUartState *s; + + s = g_new0(MchpPfSoCMMUartState, 1); + + memory_region_init_io(&s->iomem, NULL, &mchp_pfsoc_mmuart_ops, s, + "mchp.pfsoc.mmuart", 0x1000); + + s->base = base; + s->irq = irq; + + s->serial = serial_mm_init(sysmem, base, 2, irq, 399193, chr, + DEVICE_LITTLE_ENDIAN); + + memory_region_add_subregion(sysmem, base + 0x20, &s->iomem); + + return s; +} diff --git a/hw/char/meson.build b/hw/char/meson.build index e888215145..ae27932d00 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -32,6 +32,7 @@ softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) softmmu_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) softmmu_ss.add(when: 'CONFIG_SH4', if_true: files('sh_serial.c')) softmmu_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) +softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c')) specific_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-serial-bus.c')) diff --git a/include/hw/char/mchp_pfsoc_mmuart.h b/include/hw/char/mchp_pfsoc_mmuart.h new file mode 100644 index 0000000000..f61990215f --- /dev/null +++ b/include/hw/char/mchp_pfsoc_mmuart.h @@ -0,0 +1,61 @@ +/* + * Microchip PolarFire SoC MMUART emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_MCHP_PFSOC_MMUART_H +#define HW_MCHP_PFSOC_MMUART_H + +#include "hw/char/serial.h" + +#define MCHP_PFSOC_MMUART_REG_SIZE 52 + +typedef struct MchpPfSoCMMUartState { + MemoryRegion iomem; + hwaddr base; + qemu_irq irq; + + SerialMM *serial; + + uint32_t reg[MCHP_PFSOC_MMUART_REG_SIZE / sizeof(uint32_t)]; +} MchpPfSoCMMUartState; + +/** + * mchp_pfsoc_mmuart_create - Create a Microchip PolarFire SoC MMUART + * + * This is a helper routine for board to create a MMUART device that is + * compatible with Microchip PolarFire SoC. + * + * @sysmem: system memory region to map + * @base: base address of the MMUART registers + * @irq: IRQ number of the MMUART device + * @chr: character device to associate to + * + * @return: a pointer to the device specific control structure + */ +MchpPfSoCMMUartState *mchp_pfsoc_mmuart_create(MemoryRegion *sysmem, + hwaddr base, qemu_irq irq, Chardev *chr); + +#endif /* HW_MCHP_PFSOC_MMUART_H */ -- cgit v1.2.3-55-g7522 From 8f2ac39d5d307589faca1d00d55a1a8054d53b0e Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:01 +0800 Subject: hw/riscv: microchip_pfsoc: Connect 5 MMUARTs Microchip PolarFire SoC has 5 MMUARTs, and the Icicle Kit board wires 4 of them out. Let's connect all 5 MMUARTs. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1598924352-89526-7-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/Kconfig | 1 + hw/riscv/microchip_pfsoc.c | 30 ++++++++++++++++++++++++++++++ include/hw/riscv/microchip_pfsoc.h | 20 ++++++++++++++++++++ 3 files changed, 51 insertions(+) (limited to 'include') diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 3292faef27..ceb7c166a3 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -54,3 +54,4 @@ config MICROCHIP_PFSOC select HART select SIFIVE select UNIMP + select MCHP_PFSOC_MMUART diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index d946b2aae5..cee959a5e0 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -11,6 +11,7 @@ * 0) CLINT (Core Level Interruptor) * 1) PLIC (Platform Level Interrupt Controller) * 2) eNVM (Embedded Non-Volatile Memory) + * 3) MMUARTs (Multi-Mode UART) * * This board currently generates devicetree dynamically that indicates at least * two harts and up to five harts. @@ -38,6 +39,7 @@ #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" @@ -46,6 +48,7 @@ #include "hw/riscv/sifive_clint.h" #include "hw/riscv/sifive_plic.h" #include "hw/riscv/microchip_pfsoc.h" +#include "sysemu/sysemu.h" /* * The BIOS image used by this machine is called Hart Software Services (HSS). @@ -69,8 +72,13 @@ static const struct MemmapEntry { [MICROCHIP_PFSOC_L2CC] = { 0x2010000, 0x1000 }, [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_MMUART1] = { 0x20100000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART2] = { 0x20102000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART3] = { 0x20104000, 0x1000 }, + [MICROCHIP_PFSOC_MMUART4] = { 0x20106000, 0x1000 }, [MICROCHIP_PFSOC_ENVM_CFG] = { 0x20200000, 0x1000 }, [MICROCHIP_PFSOC_ENVM_DATA] = { 0x20220000, 0x20000 }, [MICROCHIP_PFSOC_IOSCB_CFG] = { 0x37080000, 0x1000 }, @@ -215,6 +223,28 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) memmap[MICROCHIP_PFSOC_MPUCFG].base, memmap[MICROCHIP_PFSOC_MPUCFG].size); + /* 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)); + /* eNVM */ memory_region_init_rom(envm_data, OBJECT(dev), "microchip.pfsoc.envm.data", memmap[MICROCHIP_PFSOC_ENVM_DATA].size, diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index 1953ef1464..a5efa1dcb8 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -22,6 +22,8 @@ #ifndef HW_MICROCHIP_PFSOC_H #define HW_MICROCHIP_PFSOC_H +#include "hw/char/mchp_pfsoc_mmuart.h" + typedef struct MicrochipPFSoCState { /*< private >*/ DeviceState parent_obj; @@ -32,6 +34,11 @@ typedef struct MicrochipPFSoCState { RISCVHartArrayState e_cpus; RISCVHartArrayState u_cpus; DeviceState *plic; + MchpPfSoCMMUartState *serial0; + MchpPfSoCMMUartState *serial1; + MchpPfSoCMMUartState *serial2; + MchpPfSoCMMUartState *serial3; + MchpPfSoCMMUartState *serial4; } MicrochipPFSoCState; #define TYPE_MICROCHIP_PFSOC "microchip.pfsoc" @@ -64,14 +71,27 @@ enum { MICROCHIP_PFSOC_L2CC, MICROCHIP_PFSOC_L2LIM, MICROCHIP_PFSOC_PLIC, + MICROCHIP_PFSOC_MMUART0, MICROCHIP_PFSOC_SYSREG, MICROCHIP_PFSOC_MPUCFG, + MICROCHIP_PFSOC_MMUART1, + MICROCHIP_PFSOC_MMUART2, + MICROCHIP_PFSOC_MMUART3, + MICROCHIP_PFSOC_MMUART4, MICROCHIP_PFSOC_ENVM_CFG, MICROCHIP_PFSOC_ENVM_DATA, MICROCHIP_PFSOC_IOSCB_CFG, MICROCHIP_PFSOC_DRAM, }; +enum { + MICROCHIP_PFSOC_MMUART0_IRQ = 90, + MICROCHIP_PFSOC_MMUART1_IRQ = 91, + MICROCHIP_PFSOC_MMUART2_IRQ = 92, + MICROCHIP_PFSOC_MMUART3_IRQ = 93, + MICROCHIP_PFSOC_MMUART4_IRQ = 94, +}; + #define MICROCHIP_PFSOC_MANAGEMENT_CPU_COUNT 1 #define MICROCHIP_PFSOC_COMPUTE_CPU_COUNT 4 -- cgit v1.2.3-55-g7522 From c696e1f2b392af19653e82da26df3c61b85ab5a2 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:02 +0800 Subject: hw/sd: Add Cadence SDHCI emulation Cadence SD/SDIO/eMMC Host Controller (SD4HC) is an SDHCI compatible controller. The SDHCI compatible registers start from offset 0x200, which are called Slot Register Set (SRS) in its datasheet. This creates a Cadence SDHCI model built on top of the existing generic SDHCI model. Cadence specific Host Register Set (HRS) is implemented to make guest software happy. Signed-off-by: Bin Meng Acked-by: Philippe Mathieu-Daudé Acked-by: Alistair Francis Message-Id: <1598924352-89526-8-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/sd/Kconfig | 4 + hw/sd/cadence_sdhci.c | 193 ++++++++++++++++++++++++++++++++++++++++++ hw/sd/meson.build | 1 + include/hw/sd/cadence_sdhci.h | 47 ++++++++++ 4 files changed, 245 insertions(+) create mode 100644 hw/sd/cadence_sdhci.c create mode 100644 include/hw/sd/cadence_sdhci.h (limited to 'include') diff --git a/hw/sd/Kconfig b/hw/sd/Kconfig index c5e1e5581c..633b9afec9 100644 --- a/hw/sd/Kconfig +++ b/hw/sd/Kconfig @@ -19,3 +19,7 @@ config SDHCI_PCI default y if PCI_DEVICES depends on PCI select SDHCI + +config CADENCE_SDHCI + bool + select SDHCI diff --git a/hw/sd/cadence_sdhci.c b/hw/sd/cadence_sdhci.c new file mode 100644 index 0000000000..0b371c843d --- /dev/null +++ b/hw/sd/cadence_sdhci.c @@ -0,0 +1,193 @@ +/* + * Cadence SDHCI emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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 . + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/irq.h" +#include "hw/sd/cadence_sdhci.h" +#include "sdhci-internal.h" + +/* HRS - Host Register Set (specific to Cadence) */ + +#define CADENCE_SDHCI_HRS00 0x00 /* general information */ +#define CADENCE_SDHCI_HRS00_SWR BIT(0) +#define CADENCE_SDHCI_HRS00_POR_VAL 0x00010000 + +#define CADENCE_SDHCI_HRS04 0x10 /* PHY access port */ +#define CADENCE_SDHCI_HRS04_WR BIT(24) +#define CADENCE_SDHCI_HRS04_RD BIT(25) +#define CADENCE_SDHCI_HRS04_ACK BIT(26) + +#define CADENCE_SDHCI_HRS06 0x18 /* eMMC control */ +#define CADENCE_SDHCI_HRS06_TUNE_UP BIT(15) + +/* SRS - Slot Register Set (SDHCI-compatible) */ + +#define CADENCE_SDHCI_SRS_BASE 0x200 + +#define TO_REG(addr) ((addr) / sizeof(uint32_t)) + +static void cadence_sdhci_instance_init(Object *obj) +{ + CadenceSDHCIState *s = CADENCE_SDHCI(obj); + + object_initialize_child(OBJECT(s), "generic-sdhci", + &s->sdhci, TYPE_SYSBUS_SDHCI); +} + +static void cadence_sdhci_reset(DeviceState *dev) +{ + CadenceSDHCIState *s = CADENCE_SDHCI(dev); + + memset(s->regs, 0, CADENCE_SDHCI_REG_SIZE); + s->regs[TO_REG(CADENCE_SDHCI_HRS00)] = CADENCE_SDHCI_HRS00_POR_VAL; + + device_cold_reset(DEVICE(&s->sdhci)); +} + +static uint64_t cadence_sdhci_read(void *opaque, hwaddr addr, unsigned int size) +{ + CadenceSDHCIState *s = opaque; + uint32_t val; + + val = s->regs[TO_REG(addr)]; + + return (uint64_t)val; +} + +static void cadence_sdhci_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + CadenceSDHCIState *s = opaque; + uint32_t val32 = (uint32_t)val; + + switch (addr) { + case CADENCE_SDHCI_HRS00: + /* + * The only writable bit is SWR (software reset) and it automatically + * clears to zero, so essentially this register remains unchanged. + */ + if (val32 & CADENCE_SDHCI_HRS00_SWR) { + cadence_sdhci_reset(DEVICE(s)); + } + + break; + case CADENCE_SDHCI_HRS04: + /* + * Only emulate the ACK bit behavior when read or write transaction + * are requested. + */ + if (val32 & (CADENCE_SDHCI_HRS04_WR | CADENCE_SDHCI_HRS04_RD)) { + val32 |= CADENCE_SDHCI_HRS04_ACK; + } else { + val32 &= ~CADENCE_SDHCI_HRS04_ACK; + } + + s->regs[TO_REG(addr)] = val32; + break; + case CADENCE_SDHCI_HRS06: + if (val32 & CADENCE_SDHCI_HRS06_TUNE_UP) { + val32 &= ~CADENCE_SDHCI_HRS06_TUNE_UP; + } + + s->regs[TO_REG(addr)] = val32; + break; + default: + s->regs[TO_REG(addr)] = val32; + break; + } +} + +static const MemoryRegionOps cadence_sdhci_ops = { + .read = cadence_sdhci_read, + .write = cadence_sdhci_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static void cadence_sdhci_realize(DeviceState *dev, Error **errp) +{ + CadenceSDHCIState *s = CADENCE_SDHCI(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + SysBusDevice *sbd_sdhci = SYS_BUS_DEVICE(&s->sdhci); + + memory_region_init(&s->container, OBJECT(s), + "cadence.sdhci-container", 0x1000); + sysbus_init_mmio(sbd, &s->container); + + memory_region_init_io(&s->iomem, OBJECT(s), &cadence_sdhci_ops, + s, TYPE_CADENCE_SDHCI, CADENCE_SDHCI_REG_SIZE); + memory_region_add_subregion(&s->container, 0, &s->iomem); + + sysbus_realize(sbd_sdhci, errp); + memory_region_add_subregion(&s->container, CADENCE_SDHCI_SRS_BASE, + sysbus_mmio_get_region(sbd_sdhci, 0)); + + /* propagate irq and "sd-bus" from generic-sdhci */ + sysbus_pass_irq(sbd, sbd_sdhci); + s->bus = qdev_get_child_bus(DEVICE(sbd_sdhci), "sd-bus"); +} + +static const VMStateDescription vmstate_cadence_sdhci = { + .name = TYPE_CADENCE_SDHCI, + .version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, CadenceSDHCIState, CADENCE_SDHCI_NUM_REGS), + VMSTATE_END_OF_LIST(), + }, +}; + +static void cadence_sdhci_class_init(ObjectClass *classp, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(classp); + + dc->desc = "Cadence SD/SDIO/eMMC Host Controller (SD4HC)"; + dc->realize = cadence_sdhci_realize; + dc->reset = cadence_sdhci_reset; + dc->vmsd = &vmstate_cadence_sdhci; +} + +static TypeInfo cadence_sdhci_info = { + .name = TYPE_CADENCE_SDHCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CadenceSDHCIState), + .instance_init = cadence_sdhci_instance_init, + .class_init = cadence_sdhci_class_init, +}; + +static void cadence_sdhci_register_types(void) +{ + type_register_static(&cadence_sdhci_info); +} + +type_init(cadence_sdhci_register_types) diff --git a/hw/sd/meson.build b/hw/sd/meson.build index b43e59bd00..9c29691e13 100644 --- a/hw/sd/meson.build +++ b/hw/sd/meson.build @@ -10,3 +10,4 @@ softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_mmci.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_sdhost.c')) softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sdhci.c')) softmmu_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-sdhost.c')) +softmmu_ss.add(when: 'CONFIG_CADENCE_SDHCI', if_true: files('cadence_sdhci.c')) diff --git a/include/hw/sd/cadence_sdhci.h b/include/hw/sd/cadence_sdhci.h new file mode 100644 index 0000000000..cd8288b7d8 --- /dev/null +++ b/include/hw/sd/cadence_sdhci.h @@ -0,0 +1,47 @@ +/* + * Cadence SDHCI emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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 . + */ + +#ifndef CADENCE_SDHCI_H +#define CADENCE_SDHCI_H + +#include "hw/sd/sdhci.h" + +#define CADENCE_SDHCI_REG_SIZE 0x100 +#define CADENCE_SDHCI_NUM_REGS (CADENCE_SDHCI_REG_SIZE / sizeof(uint32_t)) + +typedef struct CadenceSDHCIState { + SysBusDevice parent; + + MemoryRegion container; + MemoryRegion iomem; + BusState *bus; + + uint32_t regs[CADENCE_SDHCI_NUM_REGS]; + + SDHCIState sdhci; +} CadenceSDHCIState; + +#define TYPE_CADENCE_SDHCI "cadence.sdhci" +#define CADENCE_SDHCI(obj) OBJECT_CHECK(CadenceSDHCIState, (obj), \ + TYPE_CADENCE_SDHCI) + +#endif /* CADENCE_SDHCI_H */ -- cgit v1.2.3-55-g7522 From 898dc008e8cd474c21f98a63f151265673aea305 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:03 +0800 Subject: hw/riscv: microchip_pfsoc: Connect a Cadence SDHCI controller and an SD card Microchip PolarFire SoC integrates one Cadence SDHCI controller. On the Icicle Kit board, one eMMC chip and an external SD card connect to this controller depending on different configuration. As QEMU does not support eMMC yet, we just emulate the SD card configuration. To test this, the Hart Software Services (HSS) should choose the SD card configuration: $ cp boards/icicle-kit-es/def_config.sdcard .config $ make BOARD=icicle-kit-es The SD card image can be built from the Yocto BSP at: https://github.com/polarfire-soc/meta-polarfire-soc-yocto-bsp Note the generated SD card image should be resized before use: $ qemu-img resize /path/to/sdcard.img 4G Launch QEMU with the following command: $ qemu-system-riscv64 -nographic -M microchip-icicle-kit -sd sdcard.img Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-Id: <1598924352-89526-9-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/Kconfig | 1 + hw/riscv/microchip_pfsoc.c | 23 +++++++++++++++++++++++ include/hw/riscv/microchip_pfsoc.h | 4 ++++ 3 files changed, 28 insertions(+) (limited to 'include') diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index ceb7c166a3..7412db9ad7 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -55,3 +55,4 @@ config MICROCHIP_PFSOC select SIFIVE select UNIMP select MCHP_PFSOC_MMUART + select CADENCE_SDHCI diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index cee959a5e0..0b2e9ca175 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -12,6 +12,7 @@ * 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 * * This board currently generates devicetree dynamically that indicates at least * two harts and up to five harts. @@ -75,6 +76,7 @@ static const struct MemmapEntry { [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 }, @@ -111,6 +113,9 @@ static void microchip_pfsoc_soc_instance_init(Object *obj) 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, "sd-controller", &s->sdhci, + TYPE_CADENCE_SDHCI); } static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) @@ -223,6 +228,13 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) 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, @@ -290,6 +302,7 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) 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) { @@ -312,6 +325,16 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) /* 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) diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index a5efa1dcb8..d810ee8484 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -23,6 +23,7 @@ #define HW_MICROCHIP_PFSOC_H #include "hw/char/mchp_pfsoc_mmuart.h" +#include "hw/sd/cadence_sdhci.h" typedef struct MicrochipPFSoCState { /*< private >*/ @@ -39,6 +40,7 @@ typedef struct MicrochipPFSoCState { MchpPfSoCMMUartState *serial2; MchpPfSoCMMUartState *serial3; MchpPfSoCMMUartState *serial4; + CadenceSDHCIState sdhci; } MicrochipPFSoCState; #define TYPE_MICROCHIP_PFSOC "microchip.pfsoc" @@ -74,6 +76,7 @@ enum { MICROCHIP_PFSOC_MMUART0, MICROCHIP_PFSOC_SYSREG, MICROCHIP_PFSOC_MPUCFG, + MICROCHIP_PFSOC_EMMC_SD, MICROCHIP_PFSOC_MMUART1, MICROCHIP_PFSOC_MMUART2, MICROCHIP_PFSOC_MMUART3, @@ -85,6 +88,7 @@ enum { }; enum { + MICROCHIP_PFSOC_EMMC_SD_IRQ = 88, MICROCHIP_PFSOC_MMUART0_IRQ = 90, MICROCHIP_PFSOC_MMUART1_IRQ = 91, MICROCHIP_PFSOC_MMUART2_IRQ = 92, -- cgit v1.2.3-55-g7522 From 97ba42230b28636e02ab0af77738bb247e051dd4 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:04 +0800 Subject: hw/dma: Add SiFive platform DMA controller emulation Microchip PolarFire SoC integrates a DMA engine that supports: * Independent concurrent DMA transfers using 4 DMA channels * Generation of interrupts on various conditions during execution which is actually an IP reused from the SiFive FU540 chip. This creates a model to support both polling and interrupt modes. Signed-off-by: Bin Meng Acked-by: Alistair Francis Message-Id: <1598924352-89526-10-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/dma/Kconfig | 3 + hw/dma/meson.build | 1 + hw/dma/sifive_pdma.c | 313 +++++++++++++++++++++++++++++++++++++++++++ include/hw/dma/sifive_pdma.h | 57 ++++++++ 4 files changed, 374 insertions(+) create mode 100644 hw/dma/sifive_pdma.c create mode 100644 include/hw/dma/sifive_pdma.h (limited to 'include') diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig index 5c61b67bc0..d67492d36c 100644 --- a/hw/dma/Kconfig +++ b/hw/dma/Kconfig @@ -20,3 +20,6 @@ config ZYNQ_DEVCFG config STP2000 bool + +config SIFIVE_PDMA + bool diff --git a/hw/dma/meson.build b/hw/dma/meson.build index ff5bb3748f..b991d7698c 100644 --- a/hw/dma/meson.build +++ b/hw/dma/meson.build @@ -13,3 +13,4 @@ softmmu_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zdma.c')) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_dma.c', 'soc_dma.c')) softmmu_ss.add(when: 'CONFIG_PXA2XX', if_true: files('pxa2xx_dma.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_dma.c')) +softmmu_ss.add(when: 'CONFIG_SIFIVE_PDMA', if_true: files('sifive_pdma.c')) diff --git a/hw/dma/sifive_pdma.c b/hw/dma/sifive_pdma.c new file mode 100644 index 0000000000..e1f6fedbda --- /dev/null +++ b/hw/dma/sifive_pdma.c @@ -0,0 +1,313 @@ +/* + * SiFive Platform DMA emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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 . + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "migration/vmstate.h" +#include "sysemu/dma.h" +#include "hw/dma/sifive_pdma.h" + +#define DMA_CONTROL 0x000 +#define CONTROL_CLAIM BIT(0) +#define CONTROL_RUN BIT(1) +#define CONTROL_DONE_IE BIT(14) +#define CONTROL_ERR_IE BIT(15) +#define CONTROL_DONE BIT(30) +#define CONTROL_ERR BIT(31) + +#define DMA_NEXT_CONFIG 0x004 +#define CONFIG_REPEAT BIT(2) +#define CONFIG_ORDER BIT(3) +#define CONFIG_WRSZ_SHIFT 24 +#define CONFIG_RDSZ_SHIFT 28 +#define CONFIG_SZ_MASK 0xf + +#define DMA_NEXT_BYTES 0x008 +#define DMA_NEXT_DST 0x010 +#define DMA_NEXT_SRC 0x018 +#define DMA_EXEC_CONFIG 0x104 +#define DMA_EXEC_BYTES 0x108 +#define DMA_EXEC_DST 0x110 +#define DMA_EXEC_SRC 0x118 + +enum dma_chan_state { + DMA_CHAN_STATE_IDLE, + DMA_CHAN_STATE_STARTED, + DMA_CHAN_STATE_ERROR, + DMA_CHAN_STATE_DONE +}; + +static void sifive_pdma_run(SiFivePDMAState *s, int ch) +{ + uint64_t bytes = s->chan[ch].next_bytes; + uint64_t dst = s->chan[ch].next_dst; + uint64_t src = s->chan[ch].next_src; + uint32_t config = s->chan[ch].next_config; + int wsize, rsize, size; + uint8_t buf[64]; + int n; + + /* do nothing if bytes to transfer is zero */ + if (!bytes) { + goto error; + } + + /* + * The manual does not describe how the hardware behaviors when + * config.wsize and config.rsize are given different values. + * A common case is memory to memory DMA, and in this case they + * are normally the same. Abort if this expectation fails. + */ + wsize = (config >> CONFIG_WRSZ_SHIFT) & CONFIG_SZ_MASK; + rsize = (config >> CONFIG_RDSZ_SHIFT) & CONFIG_SZ_MASK; + if (wsize != rsize) { + goto error; + } + + /* + * Calculate the transaction size + * + * size field is base 2 logarithm of DMA transaction size, + * but there is an upper limit of 64 bytes per transaction. + */ + size = wsize; + if (size > 6) { + size = 6; + } + size = 1 << size; + + /* the bytes to transfer should be multiple of transaction size */ + if (bytes % size) { + goto error; + } + + /* indicate a DMA transfer is started */ + s->chan[ch].state = DMA_CHAN_STATE_STARTED; + s->chan[ch].control &= ~CONTROL_DONE; + s->chan[ch].control &= ~CONTROL_ERR; + + /* load the next_ registers into their exec_ counterparts */ + s->chan[ch].exec_config = config; + s->chan[ch].exec_bytes = bytes; + s->chan[ch].exec_dst = dst; + s->chan[ch].exec_src = src; + + for (n = 0; n < bytes / size; n++) { + cpu_physical_memory_read(s->chan[ch].exec_src, buf, size); + cpu_physical_memory_write(s->chan[ch].exec_dst, buf, size); + s->chan[ch].exec_src += size; + s->chan[ch].exec_dst += size; + s->chan[ch].exec_bytes -= size; + } + + /* indicate a DMA transfer is done */ + s->chan[ch].state = DMA_CHAN_STATE_DONE; + s->chan[ch].control &= ~CONTROL_RUN; + s->chan[ch].control |= CONTROL_DONE; + + /* reload exec_ registers if repeat is required */ + if (s->chan[ch].next_config & CONFIG_REPEAT) { + s->chan[ch].exec_bytes = bytes; + s->chan[ch].exec_dst = dst; + s->chan[ch].exec_src = src; + } + + return; + +error: + s->chan[ch].state = DMA_CHAN_STATE_ERROR; + s->chan[ch].control |= CONTROL_ERR; + return; +} + +static inline void sifive_pdma_update_irq(SiFivePDMAState *s, int ch) +{ + bool done_ie, err_ie; + + done_ie = !!(s->chan[ch].control & CONTROL_DONE_IE); + err_ie = !!(s->chan[ch].control & CONTROL_ERR_IE); + + if (done_ie && (s->chan[ch].control & CONTROL_DONE)) { + qemu_irq_raise(s->irq[ch * 2]); + } else { + qemu_irq_lower(s->irq[ch * 2]); + } + + if (err_ie && (s->chan[ch].control & CONTROL_ERR)) { + qemu_irq_raise(s->irq[ch * 2 + 1]); + } else { + qemu_irq_lower(s->irq[ch * 2 + 1]); + } + + s->chan[ch].state = DMA_CHAN_STATE_IDLE; +} + +static uint64_t sifive_pdma_read(void *opaque, hwaddr offset, unsigned size) +{ + SiFivePDMAState *s = opaque; + int ch = SIFIVE_PDMA_CHAN_NO(offset); + uint64_t val = 0; + + if (ch >= SIFIVE_PDMA_CHANS) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid channel no %d\n", + __func__, ch); + return 0; + } + + offset &= 0xfff; + switch (offset) { + case DMA_CONTROL: + val = s->chan[ch].control; + break; + case DMA_NEXT_CONFIG: + val = s->chan[ch].next_config; + break; + case DMA_NEXT_BYTES: + val = s->chan[ch].next_bytes; + break; + case DMA_NEXT_DST: + val = s->chan[ch].next_dst; + break; + case DMA_NEXT_SRC: + val = s->chan[ch].next_src; + break; + case DMA_EXEC_CONFIG: + val = s->chan[ch].exec_config; + break; + case DMA_EXEC_BYTES: + val = s->chan[ch].exec_bytes; + break; + case DMA_EXEC_DST: + val = s->chan[ch].exec_dst; + break; + case DMA_EXEC_SRC: + val = s->chan[ch].exec_src; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n", + __func__, offset); + break; + } + + return val; +} + +static void sifive_pdma_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + SiFivePDMAState *s = opaque; + int ch = SIFIVE_PDMA_CHAN_NO(offset); + + if (ch >= SIFIVE_PDMA_CHANS) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid channel no %d\n", + __func__, ch); + return; + } + + offset &= 0xfff; + switch (offset) { + case DMA_CONTROL: + s->chan[ch].control = value; + + if (value & CONTROL_RUN) { + sifive_pdma_run(s, ch); + } + + sifive_pdma_update_irq(s, ch); + break; + case DMA_NEXT_CONFIG: + s->chan[ch].next_config = value; + break; + case DMA_NEXT_BYTES: + s->chan[ch].next_bytes = value; + break; + case DMA_NEXT_DST: + s->chan[ch].next_dst = value; + break; + case DMA_NEXT_SRC: + s->chan[ch].next_src = value; + break; + case DMA_EXEC_CONFIG: + case DMA_EXEC_BYTES: + case DMA_EXEC_DST: + case DMA_EXEC_SRC: + /* these are read-only registers */ + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIX "\n", + __func__, offset); + break; + } +} + +static const MemoryRegionOps sifive_pdma_ops = { + .read = sifive_pdma_read, + .write = sifive_pdma_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* there are 32-bit and 64-bit wide registers */ + .impl = { + .min_access_size = 4, + .max_access_size = 8, + } +}; + +static void sifive_pdma_realize(DeviceState *dev, Error **errp) +{ + SiFivePDMAState *s = SIFIVE_PDMA(dev); + int i; + + memory_region_init_io(&s->iomem, OBJECT(dev), &sifive_pdma_ops, s, + TYPE_SIFIVE_PDMA, SIFIVE_PDMA_REG_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + + for (i = 0; i < SIFIVE_PDMA_IRQS; i++) { + sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); + } +} + +static void sifive_pdma_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "SiFive Platform DMA controller"; + dc->realize = sifive_pdma_realize; +} + +static const TypeInfo sifive_pdma_info = { + .name = TYPE_SIFIVE_PDMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SiFivePDMAState), + .class_init = sifive_pdma_class_init, +}; + +static void sifive_pdma_register_types(void) +{ + type_register_static(&sifive_pdma_info); +} + +type_init(sifive_pdma_register_types) diff --git a/include/hw/dma/sifive_pdma.h b/include/hw/dma/sifive_pdma.h new file mode 100644 index 0000000000..e319bbd6c4 --- /dev/null +++ b/include/hw/dma/sifive_pdma.h @@ -0,0 +1,57 @@ +/* + * SiFive Platform DMA emulation + * + * Copyright (c) 2020 Wind River Systems, Inc. + * + * Author: + * Bin Meng + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that 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 . + */ + +#ifndef SIFIVE_PDMA_H +#define SIFIVE_PDMA_H + +struct sifive_pdma_chan { + uint32_t control; + uint32_t next_config; + uint64_t next_bytes; + uint64_t next_dst; + uint64_t next_src; + uint32_t exec_config; + uint64_t exec_bytes; + uint64_t exec_dst; + uint64_t exec_src; + int state; +}; + +#define SIFIVE_PDMA_CHANS 4 +#define SIFIVE_PDMA_IRQS (SIFIVE_PDMA_CHANS * 2) +#define SIFIVE_PDMA_REG_SIZE 0x100000 +#define SIFIVE_PDMA_CHAN_NO(reg) ((reg & (SIFIVE_PDMA_REG_SIZE - 1)) >> 12) + +typedef struct SiFivePDMAState { + SysBusDevice parent; + MemoryRegion iomem; + qemu_irq irq[SIFIVE_PDMA_IRQS]; + + struct sifive_pdma_chan chan[SIFIVE_PDMA_CHANS]; +} SiFivePDMAState; + +#define TYPE_SIFIVE_PDMA "sifive.pdma" + +#define SIFIVE_PDMA(obj) \ + OBJECT_CHECK(SiFivePDMAState, (obj), TYPE_SIFIVE_PDMA) + +#endif /* SIFIVE_PDMA_H */ -- cgit v1.2.3-55-g7522 From 7124e27bb8ac16b6aae0e9cd30fb19385430dbb5 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:05 +0800 Subject: hw/riscv: microchip_pfsoc: Connect a DMA controller On the Icicle Kit board, the HSS firmware utilizes the on-chip DMA controller to move the 2nd stage bootloader in the system memory. Let's connect a DMA controller to Microchip PolarFire SoC. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1598924352-89526-11-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/Kconfig | 1 + hw/riscv/microchip_pfsoc.c | 15 +++++++++++++++ include/hw/riscv/microchip_pfsoc.h | 11 +++++++++++ 3 files changed, 27 insertions(+) (limited to 'include') diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 7412db9ad7..9032cb0cbd 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -55,4 +55,5 @@ config MICROCHIP_PFSOC select SIFIVE select UNIMP select MCHP_PFSOC_MMUART + select SIFIVE_PDMA select CADENCE_SDHCI diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 0b2e9ca175..d8ec973958 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -13,6 +13,7 @@ * 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) * * This board currently generates devicetree dynamically that indicates at least * two harts and up to five harts. @@ -71,6 +72,7 @@ static const struct MemmapEntry { [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 }, @@ -114,6 +116,9 @@ static void microchip_pfsoc_soc_instance_init(Object *obj) 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, "sd-controller", &s->sdhci, TYPE_CADENCE_SDHCI); } @@ -218,6 +223,16 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) 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, diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index d810ee8484..63e786052a 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -23,6 +23,7 @@ #define HW_MICROCHIP_PFSOC_H #include "hw/char/mchp_pfsoc_mmuart.h" +#include "hw/dma/sifive_pdma.h" #include "hw/sd/cadence_sdhci.h" typedef struct MicrochipPFSoCState { @@ -40,6 +41,7 @@ typedef struct MicrochipPFSoCState { MchpPfSoCMMUartState *serial2; MchpPfSoCMMUartState *serial3; MchpPfSoCMMUartState *serial4; + SiFivePDMAState dma; CadenceSDHCIState sdhci; } MicrochipPFSoCState; @@ -71,6 +73,7 @@ enum { MICROCHIP_PFSOC_BUSERR_UNIT4, MICROCHIP_PFSOC_CLINT, MICROCHIP_PFSOC_L2CC, + MICROCHIP_PFSOC_DMA, MICROCHIP_PFSOC_L2LIM, MICROCHIP_PFSOC_PLIC, MICROCHIP_PFSOC_MMUART0, @@ -88,6 +91,14 @@ enum { }; enum { + MICROCHIP_PFSOC_DMA_IRQ0 = 5, + MICROCHIP_PFSOC_DMA_IRQ1 = 6, + MICROCHIP_PFSOC_DMA_IRQ2 = 7, + MICROCHIP_PFSOC_DMA_IRQ3 = 8, + MICROCHIP_PFSOC_DMA_IRQ4 = 9, + MICROCHIP_PFSOC_DMA_IRQ5 = 10, + MICROCHIP_PFSOC_DMA_IRQ6 = 11, + MICROCHIP_PFSOC_DMA_IRQ7 = 12, MICROCHIP_PFSOC_EMMC_SD_IRQ = 88, MICROCHIP_PFSOC_MMUART0_IRQ = 90, MICROCHIP_PFSOC_MMUART1_IRQ = 91, -- cgit v1.2.3-55-g7522 From 64ac13633fd416541ea00ff4ae973489bdc33f7a Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:06 +0800 Subject: hw/net: cadence_gem: Add a new 'phy-addr' property At present the PHY address of the PHY connected to GEM is hard-coded to either 23 (BOARD_PHY_ADDRESS) or 0. This might not be the case for all boards. Add a new 'phy-addr' property so that board can specify the PHY address for each GEM instance. Signed-off-by: Bin Meng Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Reviewed-by: Alistair Francis Message-Id: <1598924352-89526-12-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/net/cadence_gem.c | 5 +++-- include/hw/net/cadence_gem.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index a93b5c07ce..d80096bbe8 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1446,7 +1446,7 @@ static uint64_t gem_read(void *opaque, hwaddr offset, unsigned size) uint32_t phy_addr, reg_num; phy_addr = (retval & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; - if (phy_addr == BOARD_PHY_ADDRESS || phy_addr == 0) { + if (phy_addr == s->phy_addr || phy_addr == 0) { reg_num = (retval & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; retval &= 0xFFFF0000; retval |= gem_phy_read(s, reg_num); @@ -1569,7 +1569,7 @@ static void gem_write(void *opaque, hwaddr offset, uint64_t val, uint32_t phy_addr, reg_num; phy_addr = (val & GEM_PHYMNTNC_ADDR) >> GEM_PHYMNTNC_ADDR_SHFT; - if (phy_addr == BOARD_PHY_ADDRESS || phy_addr == 0) { + if (phy_addr == s->phy_addr || phy_addr == 0) { reg_num = (val & GEM_PHYMNTNC_REG) >> GEM_PHYMNTNC_REG_SHIFT; gem_phy_write(s, reg_num, val); } @@ -1682,6 +1682,7 @@ static Property gem_properties[] = { DEFINE_NIC_PROPERTIES(CadenceGEMState, conf), DEFINE_PROP_UINT32("revision", CadenceGEMState, revision, GEM_MODID_VALUE), + DEFINE_PROP_UINT8("phy-addr", CadenceGEMState, phy_addr, BOARD_PHY_ADDRESS), DEFINE_PROP_UINT8("num-priority-queues", CadenceGEMState, num_priority_queues, 1), DEFINE_PROP_UINT8("num-type1-screeners", CadenceGEMState, diff --git a/include/hw/net/cadence_gem.h b/include/hw/net/cadence_gem.h index 54e646ff79..01c6189a24 100644 --- a/include/hw/net/cadence_gem.h +++ b/include/hw/net/cadence_gem.h @@ -73,6 +73,8 @@ typedef struct CadenceGEMState { /* Mask of register bits which are write 1 to clear */ uint32_t regs_w1c[CADENCE_GEM_MAXREG]; + /* PHY address */ + uint8_t phy_addr; /* PHY registers backing store */ uint16_t phy_regs[32]; -- cgit v1.2.3-55-g7522 From 47374b0761c1e3b6bff210dbb9d1a965e71c213e Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:08 +0800 Subject: hw/riscv: microchip_pfsoc: Connect 2 Cadence GEMs Microchip PolarFire SoC integrates 2 Candence GEMs to provide IEEE 802.3 standard-compliant 10/100/1000 Mbps ethernet interface. On the Icicle Kit board, GEM0 connects to a PHY at address 8 while GEM1 connects to a PHY at address 9. The 2nd stage bootloader (U-Boot) is using GEM1 by default, so we must specify 2 '-nic' options from the command line in order to get a working ethernet. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-Id: <1598924352-89526-14-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/microchip_pfsoc.c | 39 ++++++++++++++++++++++++++++++++++++++ include/hw/riscv/microchip_pfsoc.h | 7 +++++++ 2 files changed, 46 insertions(+) (limited to 'include') diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index d8ec973958..7f25609182 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -14,6 +14,7 @@ * 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. @@ -59,6 +60,9 @@ #define BIOS_FILENAME "hss.bin" #define RESET_VECTOR 0x20220000 +/* GEM version */ +#define GEM_REVISION 0x0107010c + static const struct MemmapEntry { hwaddr base; hwaddr size; @@ -83,6 +87,8 @@ static const struct MemmapEntry { [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_ENVM_CFG] = { 0x20200000, 0x1000 }, [MICROCHIP_PFSOC_ENVM_DATA] = { 0x20220000, 0x20000 }, [MICROCHIP_PFSOC_IOSCB_CFG] = { 0x37080000, 0x1000 }, @@ -119,6 +125,9 @@ static void microchip_pfsoc_soc_instance_init(Object *obj) 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); } @@ -134,6 +143,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) 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); @@ -272,6 +282,35 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) 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)); + /* eNVM */ memory_region_init_rom(envm_data, OBJECT(dev), "microchip.pfsoc.envm.data", memmap[MICROCHIP_PFSOC_ENVM_DATA].size, diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index 63e786052a..6d20853039 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -24,6 +24,7 @@ #include "hw/char/mchp_pfsoc_mmuart.h" #include "hw/dma/sifive_pdma.h" +#include "hw/net/cadence_gem.h" #include "hw/sd/cadence_sdhci.h" typedef struct MicrochipPFSoCState { @@ -42,6 +43,8 @@ typedef struct MicrochipPFSoCState { MchpPfSoCMMUartState *serial3; MchpPfSoCMMUartState *serial4; SiFivePDMAState dma; + CadenceGEMState gem0; + CadenceGEMState gem1; CadenceSDHCIState sdhci; } MicrochipPFSoCState; @@ -84,6 +87,8 @@ enum { MICROCHIP_PFSOC_MMUART2, MICROCHIP_PFSOC_MMUART3, MICROCHIP_PFSOC_MMUART4, + MICROCHIP_PFSOC_GEM0, + MICROCHIP_PFSOC_GEM1, MICROCHIP_PFSOC_ENVM_CFG, MICROCHIP_PFSOC_ENVM_DATA, MICROCHIP_PFSOC_IOSCB_CFG, @@ -99,6 +104,8 @@ enum { MICROCHIP_PFSOC_DMA_IRQ5 = 10, MICROCHIP_PFSOC_DMA_IRQ6 = 11, MICROCHIP_PFSOC_DMA_IRQ7 = 12, + MICROCHIP_PFSOC_GEM0_IRQ = 64, + MICROCHIP_PFSOC_GEM1_IRQ = 70, MICROCHIP_PFSOC_EMMC_SD_IRQ = 88, MICROCHIP_PFSOC_MMUART0_IRQ = 90, MICROCHIP_PFSOC_MMUART1_IRQ = 91, -- cgit v1.2.3-55-g7522 From ce908a2f6f6d6e1d8ede485ee3f9f7d36ee3533c Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:09 +0800 Subject: hw/riscv: microchip_pfsoc: Hook GPIO controllers Microchip PolarFire SoC integrates 3 GPIOs controllers. It seems enough to create unimplemented devices to cover their register spaces at this point. With this commit, QEMU can boot to U-Boot (2nd stage bootloader) all the way to the Linux shell login prompt, with a modified HSS (1st stage bootloader). For detailed instructions on how to create images for the Icicle Kit board, please check QEMU RISC-V WiKi page at: https://wiki.qemu.org/Documentation/Platforms/RISCV Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-Id: <1598924352-89526-15-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/microchip_pfsoc.c | 14 ++++++++++++++ include/hw/riscv/microchip_pfsoc.h | 3 +++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 7f25609182..11ebdd1aa8 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -89,6 +89,9 @@ static const struct MemmapEntry { [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 }, @@ -311,6 +314,17 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) 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, diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index 6d20853039..8bfc7e1a85 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -89,6 +89,9 @@ enum { MICROCHIP_PFSOC_MMUART4, MICROCHIP_PFSOC_GEM0, MICROCHIP_PFSOC_GEM1, + MICROCHIP_PFSOC_GPIO0, + MICROCHIP_PFSOC_GPIO1, + MICROCHIP_PFSOC_GPIO2, MICROCHIP_PFSOC_ENVM_CFG, MICROCHIP_PFSOC_ENVM_DATA, MICROCHIP_PFSOC_IOSCB_CFG, -- cgit v1.2.3-55-g7522 From a47ef6e93ab2ca1db8d5ecb61fda3c41f926a26b Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:10 +0800 Subject: hw/riscv: clint: Avoid using hard-coded timebase frequency At present the CLINT timestamp is using a hard-coded timebase frequency value SIFIVE_CLINT_TIMEBASE_FREQ. This might not be true for all boards. Add a new 'timebase-freq' property to the CLINT device, and update various functions to accept this as a parameter. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1598924352-89526-16-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/microchip_pfsoc.c | 6 +++++- hw/riscv/sifive_clint.c | 26 +++++++++++++++----------- hw/riscv/sifive_e.c | 3 ++- hw/riscv/sifive_u.c | 3 ++- hw/riscv/spike.c | 3 ++- hw/riscv/virt.c | 3 ++- include/hw/riscv/sifive_clint.h | 4 +++- target/riscv/cpu.h | 6 ++++-- target/riscv/cpu_helper.c | 4 +++- target/riscv/csr.c | 4 ++-- 10 files changed, 40 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 11ebdd1aa8..da6bd295ce 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -60,6 +60,9 @@ #define BIOS_FILENAME "hss.bin" #define RESET_VECTOR 0x20220000 +/* CLINT timebase frequency */ +#define CLINT_TIMEBASE_FREQ 1000000 + /* GEM version */ #define GEM_REVISION 0x0107010c @@ -187,7 +190,8 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) /* 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, false); + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + CLINT_TIMEBASE_FREQ, false); /* L2 cache controller */ create_unimplemented_device("microchip.pfsoc.l2cc", diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c index 15e13d5f7a..fa1ddf2ccd 100644 --- a/hw/riscv/sifive_clint.c +++ b/hw/riscv/sifive_clint.c @@ -29,22 +29,23 @@ #include "hw/riscv/sifive_clint.h" #include "qemu/timer.h" -static uint64_t cpu_riscv_read_rtc(void) +static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq) { return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - SIFIVE_CLINT_TIMEBASE_FREQ, NANOSECONDS_PER_SECOND); + 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) +static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value, + uint32_t timebase_freq) { uint64_t next; uint64_t diff; - uint64_t rtc_r = cpu_riscv_read_rtc(); + uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq); cpu->env.timecmp = value; if (cpu->env.timecmp <= rtc_r) { @@ -59,7 +60,7 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) 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); + muldiv64(diff, NANOSECONDS_PER_SECOND, timebase_freq); timer_mod(cpu->env.timer, next); } @@ -112,10 +113,10 @@ static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size) } } else if (addr == clint->time_base) { /* time_lo */ - return cpu_riscv_read_rtc() & 0xFFFFFFFF; + return cpu_riscv_read_rtc(clint->timebase_freq) & 0xFFFFFFFF; } else if (addr == clint->time_base + 4) { /* time_hi */ - return (cpu_riscv_read_rtc() >> 32) & 0xFFFFFFFF; + return (cpu_riscv_read_rtc(clint->timebase_freq) >> 32) & 0xFFFFFFFF; } error_report("clint: invalid read: %08x", (uint32_t)addr); @@ -153,13 +154,13 @@ static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value, /* timecmp_lo */ uint64_t timecmp_hi = env->timecmp >> 32; sifive_clint_write_timecmp(RISCV_CPU(cpu), - timecmp_hi << 32 | (value & 0xFFFFFFFF)); + timecmp_hi << 32 | (value & 0xFFFFFFFF), clint->timebase_freq); 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)); + value << 32 | (timecmp_lo & 0xFFFFFFFF), clint->timebase_freq); } else { error_report("clint: invalid timecmp write: %08x", (uint32_t)addr); } @@ -194,6 +195,7 @@ static Property sifive_clint_properties[] = { 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_UINT32("timebase-freq", SiFiveCLINTState, timebase_freq, 0), DEFINE_PROP_END_OF_LIST(), }; @@ -232,7 +234,8 @@ type_init(sifive_clint_register_types) */ 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) + uint32_t timecmp_base, uint32_t time_base, uint32_t timebase_freq, + bool provide_rdtime) { int i; for (i = 0; i < num_harts; i++) { @@ -242,7 +245,7 @@ DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, continue; } if (provide_rdtime) { - riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc); + riscv_cpu_set_rdtime_fn(env, cpu_riscv_read_rtc, timebase_freq); } env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &sifive_clint_timer_cb, cpu); @@ -256,6 +259,7 @@ DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, 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); + qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq); 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 cd7560d8ec..36ccfb2071 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -213,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_u.c b/hw/riscv/sifive_u.c index 404d5e6918..2bc3992989 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -706,7 +706,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; diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 56f5fe73c7..b54a396107 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -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/virt.c b/hw/riscv/virt.c index 6fca513ec9..c67a910e48 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -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 = diff --git a/include/hw/riscv/sifive_clint.h b/include/hw/riscv/sifive_clint.h index 9f5fb3d31d..a30be0f3d6 100644 --- a/include/hw/riscv/sifive_clint.h +++ b/include/hw/riscv/sifive_clint.h @@ -39,11 +39,13 @@ typedef struct SiFiveCLINTState { uint32_t timecmp_base; uint32_t time_base; uint32_t aperture_size; + uint32_t timebase_freq; } SiFiveCLINTState; 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); + uint32_t timecmp_base, uint32_t time_base, uint32_t timebase_freq, + bool provide_rdtime); enum { SIFIVE_SIP_BASE = 0x0, diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 0d1728a8cd..65daa73675 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -219,7 +219,8 @@ struct CPURISCVState { pmp_table_t pmp_state; /* machine specific rdtime callback */ - uint64_t (*rdtime_fn)(void); + uint64_t (*rdtime_fn)(uint32_t); + uint32_t rdtime_fn_arg; /* True if in debugger mode. */ bool debugger; @@ -350,7 +351,8 @@ void riscv_cpu_swap_hypervisor_regs(CPURISCVState *env); int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts); uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); #define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ -void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void)); +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t), + uint32_t arg); #endif void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 005880627e..f4c4111536 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -276,9 +276,11 @@ uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) return old; } -void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(void)) +void riscv_cpu_set_rdtime_fn(CPURISCVState *env, uint64_t (*fn)(uint32_t), + uint32_t arg) { env->rdtime_fn = fn; + env->rdtime_fn_arg = arg; } void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 200001de74..26ae347b4a 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -351,7 +351,7 @@ static int read_time(CPURISCVState *env, int csrno, target_ulong *val) return -RISCV_EXCP_ILLEGAL_INST; } - *val = env->rdtime_fn() + delta; + *val = env->rdtime_fn(env->rdtime_fn_arg) + delta; return 0; } @@ -364,7 +364,7 @@ static int read_timeh(CPURISCVState *env, int csrno, target_ulong *val) return -RISCV_EXCP_ILLEGAL_INST; } - *val = (env->rdtime_fn() + delta) >> 32; + *val = (env->rdtime_fn(env->rdtime_fn_arg) + delta) >> 32; return 0; } #endif -- cgit v1.2.3-55-g7522 From 834e027a3452e1c139c5400cae550c6c5a340b28 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Tue, 1 Sep 2020 09:39:11 +0800 Subject: hw/riscv: sifive_u: Connect a DMA controller SiFive FU540 SoC integrates a platform DMA controller with 4 DMA channels. This connects the exsiting SiFive PDMA model to the SoC, and adds its device tree data as well. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1598924352-89526-17-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/Kconfig | 1 + hw/riscv/sifive_u.c | 30 ++++++++++++++++++++++++++++++ include/hw/riscv/sifive_u.h | 11 +++++++++++ 3 files changed, 42 insertions(+) (limited to 'include') diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 9032cb0cbd..e53ab1efa5 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -22,6 +22,7 @@ config SIFIVE_U select CADENCE select HART select SIFIVE + select SIFIVE_PDMA select UNIMP config SPIKE diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 2bc3992989..79975372ef 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. @@ -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); @@ -627,6 +645,7 @@ static void sifive_u_soc_instance_init(Object *obj) 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) @@ -730,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/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index d3c0c00d10..793000a2ed 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -19,6 +19,7 @@ #ifndef HW_SIFIVE_U_H #define HW_SIFIVE_U_H +#include "hw/dma/sifive_pdma.h" #include "hw/net/cadence_gem.h" #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_cpu.h" @@ -43,6 +44,7 @@ typedef struct SiFiveUSoCState { SiFiveUPRCIState prci; SIFIVEGPIOState gpio; SiFiveUOTPState otp; + SiFivePDMAState dma; CadenceGEMState gem; uint32_t serial; @@ -72,6 +74,7 @@ enum { SIFIVE_U_MROM, SIFIVE_U_CLINT, SIFIVE_U_L2CC, + SIFIVE_U_PDMA, SIFIVE_U_L2LIM, SIFIVE_U_PLIC, SIFIVE_U_PRCI, @@ -108,6 +111,14 @@ enum { SIFIVE_U_GPIO_IRQ13 = 20, SIFIVE_U_GPIO_IRQ14 = 21, SIFIVE_U_GPIO_IRQ15 = 22, + SIFIVE_U_PDMA_IRQ0 = 23, + SIFIVE_U_PDMA_IRQ1 = 24, + SIFIVE_U_PDMA_IRQ2 = 25, + SIFIVE_U_PDMA_IRQ3 = 26, + SIFIVE_U_PDMA_IRQ4 = 27, + SIFIVE_U_PDMA_IRQ5 = 28, + SIFIVE_U_PDMA_IRQ6 = 29, + SIFIVE_U_PDMA_IRQ7 = 30, SIFIVE_U_GEM_IRQ = 0x35 }; -- cgit v1.2.3-55-g7522 From 89ece6f76f089bc415fc4b8c78f7dbe74113380c Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 3 Sep 2020 18:40:12 +0800 Subject: hw/riscv: Move sifive_e_prci model to hw/misc This is an effort to clean up the hw/riscv directory. Ideally it should only contain the RISC-V SoC / machine codes plus generic codes. Let's move sifive_e_prci model to hw/misc directory. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1599129623-68957-2-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/misc/Kconfig | 3 + hw/misc/meson.build | 3 + hw/misc/sifive_e_prci.c | 125 +++++++++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 1 + hw/riscv/meson.build | 1 - hw/riscv/sifive_e.c | 2 +- hw/riscv/sifive_e_prci.c | 125 --------------------------------------- include/hw/misc/sifive_e_prci.h | 71 ++++++++++++++++++++++ include/hw/riscv/sifive_e_prci.h | 71 ---------------------- 9 files changed, 204 insertions(+), 198 deletions(-) create mode 100644 hw/misc/sifive_e_prci.c delete mode 100644 hw/riscv/sifive_e_prci.c create mode 100644 include/hw/misc/sifive_e_prci.h delete mode 100644 include/hw/riscv/sifive_e_prci.h (limited to 'include') diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 92c397ca07..507398635b 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -134,4 +134,7 @@ config MAC_VIA config AVR_POWER bool +config SIFIVE_E_PRCI + bool + source macio/Kconfig diff --git a/hw/misc/meson.build b/hw/misc/meson.build index e1576b81cf..b6b2e5797f 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -21,6 +21,9 @@ softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c')) # Mac devices softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) +# RISC-V devices +softmmu_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) + # PKUnity SoC devices softmmu_ss.add(when: 'CONFIG_PUV3', if_true: files('puv3_pm.c')) diff --git a/hw/misc/sifive_e_prci.c b/hw/misc/sifive_e_prci.c new file mode 100644 index 0000000000..8ec4ee4b41 --- /dev/null +++ b/hw/misc/sifive_e_prci.c @@ -0,0 +1,125 @@ +/* + * 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 . + */ + +#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/misc/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/Kconfig b/hw/riscv/Kconfig index e53ab1efa5..5855e99aaa 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -15,6 +15,7 @@ config SIFIVE_E bool select HART select SIFIVE + select SIFIVE_E_PRCI select UNIMP config SIFIVE_U diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index c29a48c3f1..003994d1ea 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -10,7 +10,6 @@ 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')) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 36ccfb2071..7f43ed953a 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -43,8 +43,8 @@ #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/misc/sifive_e_prci.h" #include "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/sysemu.h" 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 . - */ - -#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/include/hw/misc/sifive_e_prci.h b/include/hw/misc/sifive_e_prci.h new file mode 100644 index 0000000000..698b0b451c --- /dev/null +++ b/include/hw/misc/sifive_e_prci.h @@ -0,0 +1,71 @@ +/* + * QEMU SiFive E PRCI (Power, Reset, Clock, Interrupt) interface + * + * Copyright (c) 2017 SiFive, Inc. + * + * 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 . + */ + +#ifndef HW_SIFIVE_E_PRCI_H +#define HW_SIFIVE_E_PRCI_H + +enum { + SIFIVE_E_PRCI_HFROSCCFG = 0x0, + SIFIVE_E_PRCI_HFXOSCCFG = 0x4, + SIFIVE_E_PRCI_PLLCFG = 0x8, + SIFIVE_E_PRCI_PLLOUTDIV = 0xC +}; + +enum { + SIFIVE_E_PRCI_HFROSCCFG_RDY = (1 << 31), + SIFIVE_E_PRCI_HFROSCCFG_EN = (1 << 30) +}; + +enum { + SIFIVE_E_PRCI_HFXOSCCFG_RDY = (1 << 31), + SIFIVE_E_PRCI_HFXOSCCFG_EN = (1 << 30) +}; + +enum { + SIFIVE_E_PRCI_PLLCFG_PLLSEL = (1 << 16), + SIFIVE_E_PRCI_PLLCFG_REFSEL = (1 << 17), + SIFIVE_E_PRCI_PLLCFG_BYPASS = (1 << 18), + SIFIVE_E_PRCI_PLLCFG_LOCK = (1 << 31) +}; + +enum { + SIFIVE_E_PRCI_PLLOUTDIV_DIV1 = (1 << 8) +}; + +#define SIFIVE_E_PRCI_REG_SIZE 0x1000 + +#define TYPE_SIFIVE_E_PRCI "riscv.sifive.e.prci" + +#define SIFIVE_E_PRCI(obj) \ + OBJECT_CHECK(SiFiveEPRCIState, (obj), TYPE_SIFIVE_E_PRCI) + +typedef struct SiFiveEPRCIState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + uint32_t hfrosccfg; + uint32_t hfxosccfg; + uint32_t pllcfg; + uint32_t plloutdiv; +} SiFiveEPRCIState; + +DeviceState *sifive_e_prci_create(hwaddr addr); + +#endif diff --git a/include/hw/riscv/sifive_e_prci.h b/include/hw/riscv/sifive_e_prci.h deleted file mode 100644 index 698b0b451c..0000000000 --- a/include/hw/riscv/sifive_e_prci.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * QEMU SiFive E PRCI (Power, Reset, Clock, Interrupt) interface - * - * Copyright (c) 2017 SiFive, Inc. - * - * 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 . - */ - -#ifndef HW_SIFIVE_E_PRCI_H -#define HW_SIFIVE_E_PRCI_H - -enum { - SIFIVE_E_PRCI_HFROSCCFG = 0x0, - SIFIVE_E_PRCI_HFXOSCCFG = 0x4, - SIFIVE_E_PRCI_PLLCFG = 0x8, - SIFIVE_E_PRCI_PLLOUTDIV = 0xC -}; - -enum { - SIFIVE_E_PRCI_HFROSCCFG_RDY = (1 << 31), - SIFIVE_E_PRCI_HFROSCCFG_EN = (1 << 30) -}; - -enum { - SIFIVE_E_PRCI_HFXOSCCFG_RDY = (1 << 31), - SIFIVE_E_PRCI_HFXOSCCFG_EN = (1 << 30) -}; - -enum { - SIFIVE_E_PRCI_PLLCFG_PLLSEL = (1 << 16), - SIFIVE_E_PRCI_PLLCFG_REFSEL = (1 << 17), - SIFIVE_E_PRCI_PLLCFG_BYPASS = (1 << 18), - SIFIVE_E_PRCI_PLLCFG_LOCK = (1 << 31) -}; - -enum { - SIFIVE_E_PRCI_PLLOUTDIV_DIV1 = (1 << 8) -}; - -#define SIFIVE_E_PRCI_REG_SIZE 0x1000 - -#define TYPE_SIFIVE_E_PRCI "riscv.sifive.e.prci" - -#define SIFIVE_E_PRCI(obj) \ - OBJECT_CHECK(SiFiveEPRCIState, (obj), TYPE_SIFIVE_E_PRCI) - -typedef struct SiFiveEPRCIState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - MemoryRegion mmio; - uint32_t hfrosccfg; - uint32_t hfxosccfg; - uint32_t pllcfg; - uint32_t plloutdiv; -} SiFiveEPRCIState; - -DeviceState *sifive_e_prci_create(hwaddr addr); - -#endif -- cgit v1.2.3-55-g7522 From 9fe640a53dd8ef33d32ab6e833fa9b6d1356cfae Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 3 Sep 2020 18:40:13 +0800 Subject: hw/riscv: Move sifive_u_prci model to hw/misc This is an effort to clean up the hw/riscv directory. Ideally it should only contain the RISC-V SoC / machine codes plus generic codes. Let's move sifive_u_prci model to hw/misc directory. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1599129623-68957-3-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/misc/Kconfig | 3 + hw/misc/meson.build | 1 + hw/misc/sifive_u_prci.c | 169 +++++++++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 1 + hw/riscv/meson.build | 1 - hw/riscv/sifive_u_prci.c | 169 --------------------------------------- include/hw/misc/sifive_u_prci.h | 91 +++++++++++++++++++++ include/hw/riscv/sifive_u.h | 2 +- include/hw/riscv/sifive_u_prci.h | 91 --------------------- 9 files changed, 266 insertions(+), 262 deletions(-) create mode 100644 hw/misc/sifive_u_prci.c delete mode 100644 hw/riscv/sifive_u_prci.c create mode 100644 include/hw/misc/sifive_u_prci.h delete mode 100644 include/hw/riscv/sifive_u_prci.h (limited to 'include') diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 507398635b..65f3fdd9e0 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -137,4 +137,7 @@ config AVR_POWER config SIFIVE_E_PRCI bool +config SIFIVE_U_PRCI + bool + source macio/Kconfig diff --git a/hw/misc/meson.build b/hw/misc/meson.build index b6b2e5797f..9e9550e30d 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -23,6 +23,7 @@ softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) # RISC-V devices softmmu_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) +softmmu_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) # PKUnity SoC devices softmmu_ss.add(when: 'CONFIG_PUV3', if_true: files('puv3_pm.c')) diff --git a/hw/misc/sifive_u_prci.c b/hw/misc/sifive_u_prci.c new file mode 100644 index 0000000000..5d9d446ee8 --- /dev/null +++ b/hw/misc/sifive_u_prci.c @@ -0,0 +1,169 @@ +/* + * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt) + * + * Copyright (c) 2019 Bin Meng + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/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/Kconfig b/hw/riscv/Kconfig index 5855e99aaa..109364b814 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -24,6 +24,7 @@ config SIFIVE_U select HART select SIFIVE select SIFIVE_PDMA + select SIFIVE_U_PRCI select UNIMP config SPIKE diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 003994d1ea..3462cb5a28 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -12,7 +12,6 @@ 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_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')) 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 - * - * 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 . - */ - -#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/include/hw/misc/sifive_u_prci.h b/include/hw/misc/sifive_u_prci.h new file mode 100644 index 0000000000..0a531fdadc --- /dev/null +++ b/include/hw/misc/sifive_u_prci.h @@ -0,0 +1,91 @@ +/* + * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt) interface + * + * Copyright (c) 2019 Bin Meng + * + * 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 . + */ + +#ifndef HW_SIFIVE_U_PRCI_H +#define HW_SIFIVE_U_PRCI_H + +#define SIFIVE_U_PRCI_HFXOSCCFG 0x00 +#define SIFIVE_U_PRCI_COREPLLCFG0 0x04 +#define SIFIVE_U_PRCI_DDRPLLCFG0 0x0C +#define SIFIVE_U_PRCI_DDRPLLCFG1 0x10 +#define SIFIVE_U_PRCI_GEMGXLPLLCFG0 0x1C +#define SIFIVE_U_PRCI_GEMGXLPLLCFG1 0x20 +#define SIFIVE_U_PRCI_CORECLKSEL 0x24 +#define SIFIVE_U_PRCI_DEVICESRESET 0x28 +#define SIFIVE_U_PRCI_CLKMUXSTATUS 0x2C + +/* + * Current FU540-C000 manual says ready bit is at bit 29, but + * freedom-u540-c000-bootloader codes (ux00prci.h) says it is at bit 31. + * We have to trust the actual code that works. + * + * see https://github.com/sifive/freedom-u540-c000-bootloader + */ + +#define SIFIVE_U_PRCI_HFXOSCCFG_EN (1 << 30) +#define SIFIVE_U_PRCI_HFXOSCCFG_RDY (1 << 31) + +/* xxxPLLCFG0 register bits */ +#define SIFIVE_U_PRCI_PLLCFG0_DIVR (1 << 0) +#define SIFIVE_U_PRCI_PLLCFG0_DIVF (31 << 6) +#define SIFIVE_U_PRCI_PLLCFG0_DIVQ (3 << 15) +#define SIFIVE_U_PRCI_PLLCFG0_FSE (1 << 25) +#define SIFIVE_U_PRCI_PLLCFG0_LOCK (1 << 31) + +/* xxxPLLCFG1 register bits */ +#define SIFIVE_U_PRCI_PLLCFG1_CKE (1 << 24) + +/* coreclksel register bits */ +#define SIFIVE_U_PRCI_CORECLKSEL_HFCLK (1 << 0) + + +#define SIFIVE_U_PRCI_REG_SIZE 0x1000 + +#define TYPE_SIFIVE_U_PRCI "riscv.sifive.u.prci" + +#define SIFIVE_U_PRCI(obj) \ + OBJECT_CHECK(SiFiveUPRCIState, (obj), TYPE_SIFIVE_U_PRCI) + +typedef struct SiFiveUPRCIState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + uint32_t hfxosccfg; + uint32_t corepllcfg0; + uint32_t ddrpllcfg0; + uint32_t ddrpllcfg1; + uint32_t gemgxlpllcfg0; + uint32_t gemgxlpllcfg1; + uint32_t coreclksel; + uint32_t devicesreset; + uint32_t clkmuxstatus; +} SiFiveUPRCIState; + +/* + * Clock indexes for use by Device Tree data and the PRCI driver. + * + * These values are from sifive-fu540-prci.h in the Linux kernel. + */ +#define PRCI_CLK_COREPLL 0 +#define PRCI_CLK_DDRPLL 1 +#define PRCI_CLK_GEMGXLPLL 2 +#define PRCI_CLK_TLCLK 3 + +#endif /* HW_SIFIVE_U_PRCI_H */ diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index 793000a2ed..cbeb2286d7 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -24,8 +24,8 @@ #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_cpu.h" #include "hw/riscv/sifive_gpio.h" -#include "hw/riscv/sifive_u_prci.h" #include "hw/riscv/sifive_u_otp.h" +#include "hw/misc/sifive_u_prci.h" #define TYPE_RISCV_U_SOC "riscv.sifive.u.soc" #define RISCV_U_SOC(obj) \ diff --git a/include/hw/riscv/sifive_u_prci.h b/include/hw/riscv/sifive_u_prci.h deleted file mode 100644 index 0a531fdadc..0000000000 --- a/include/hw/riscv/sifive_u_prci.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * QEMU SiFive U PRCI (Power, Reset, Clock, Interrupt) interface - * - * Copyright (c) 2019 Bin Meng - * - * 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 . - */ - -#ifndef HW_SIFIVE_U_PRCI_H -#define HW_SIFIVE_U_PRCI_H - -#define SIFIVE_U_PRCI_HFXOSCCFG 0x00 -#define SIFIVE_U_PRCI_COREPLLCFG0 0x04 -#define SIFIVE_U_PRCI_DDRPLLCFG0 0x0C -#define SIFIVE_U_PRCI_DDRPLLCFG1 0x10 -#define SIFIVE_U_PRCI_GEMGXLPLLCFG0 0x1C -#define SIFIVE_U_PRCI_GEMGXLPLLCFG1 0x20 -#define SIFIVE_U_PRCI_CORECLKSEL 0x24 -#define SIFIVE_U_PRCI_DEVICESRESET 0x28 -#define SIFIVE_U_PRCI_CLKMUXSTATUS 0x2C - -/* - * Current FU540-C000 manual says ready bit is at bit 29, but - * freedom-u540-c000-bootloader codes (ux00prci.h) says it is at bit 31. - * We have to trust the actual code that works. - * - * see https://github.com/sifive/freedom-u540-c000-bootloader - */ - -#define SIFIVE_U_PRCI_HFXOSCCFG_EN (1 << 30) -#define SIFIVE_U_PRCI_HFXOSCCFG_RDY (1 << 31) - -/* xxxPLLCFG0 register bits */ -#define SIFIVE_U_PRCI_PLLCFG0_DIVR (1 << 0) -#define SIFIVE_U_PRCI_PLLCFG0_DIVF (31 << 6) -#define SIFIVE_U_PRCI_PLLCFG0_DIVQ (3 << 15) -#define SIFIVE_U_PRCI_PLLCFG0_FSE (1 << 25) -#define SIFIVE_U_PRCI_PLLCFG0_LOCK (1 << 31) - -/* xxxPLLCFG1 register bits */ -#define SIFIVE_U_PRCI_PLLCFG1_CKE (1 << 24) - -/* coreclksel register bits */ -#define SIFIVE_U_PRCI_CORECLKSEL_HFCLK (1 << 0) - - -#define SIFIVE_U_PRCI_REG_SIZE 0x1000 - -#define TYPE_SIFIVE_U_PRCI "riscv.sifive.u.prci" - -#define SIFIVE_U_PRCI(obj) \ - OBJECT_CHECK(SiFiveUPRCIState, (obj), TYPE_SIFIVE_U_PRCI) - -typedef struct SiFiveUPRCIState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - MemoryRegion mmio; - uint32_t hfxosccfg; - uint32_t corepllcfg0; - uint32_t ddrpllcfg0; - uint32_t ddrpllcfg1; - uint32_t gemgxlpllcfg0; - uint32_t gemgxlpllcfg1; - uint32_t coreclksel; - uint32_t devicesreset; - uint32_t clkmuxstatus; -} SiFiveUPRCIState; - -/* - * Clock indexes for use by Device Tree data and the PRCI driver. - * - * These values are from sifive-fu540-prci.h in the Linux kernel. - */ -#define PRCI_CLK_COREPLL 0 -#define PRCI_CLK_DDRPLL 1 -#define PRCI_CLK_GEMGXLPLL 2 -#define PRCI_CLK_TLCLK 3 - -#endif /* HW_SIFIVE_U_PRCI_H */ -- cgit v1.2.3-55-g7522 From 0fa9e329454aaccc6dbb6a4f52ad0c88a060a3b6 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 3 Sep 2020 18:40:14 +0800 Subject: hw/riscv: Move sifive_u_otp model to hw/misc This is an effort to clean up the hw/riscv directory. Ideally it should only contain the RISC-V SoC / machine codes plus generic codes. Let's move sifive_u_otp model to hw/misc directory. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1599129623-68957-4-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/misc/Kconfig | 3 + hw/misc/meson.build | 1 + hw/misc/sifive_u_otp.c | 191 ++++++++++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 1 + hw/riscv/meson.build | 1 - hw/riscv/sifive_u_otp.c | 191 ---------------------------------------- include/hw/misc/sifive_u_otp.h | 80 +++++++++++++++++ include/hw/riscv/sifive_u.h | 2 +- include/hw/riscv/sifive_u_otp.h | 80 ----------------- 9 files changed, 277 insertions(+), 273 deletions(-) create mode 100644 hw/misc/sifive_u_otp.c delete mode 100644 hw/riscv/sifive_u_otp.c create mode 100644 include/hw/misc/sifive_u_otp.h delete mode 100644 include/hw/riscv/sifive_u_otp.h (limited to 'include') diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 65f3fdd9e0..fa3d0f4723 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -137,6 +137,9 @@ config AVR_POWER config SIFIVE_E_PRCI bool +config SIFIVE_U_OTP + bool + config SIFIVE_U_PRCI bool diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 9e9550e30d..018a88c670 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -23,6 +23,7 @@ softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) # RISC-V devices softmmu_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) +softmmu_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) softmmu_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) # PKUnity SoC devices diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c new file mode 100644 index 0000000000..c2f3c8e129 --- /dev/null +++ b/hw/misc/sifive_u_otp.c @@ -0,0 +1,191 @@ +/* + * QEMU SiFive U OTP (One-Time Programmable) Memory interface + * + * Copyright (c) 2019 Bin Meng + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "hw/qdev-properties.h" +#include "hw/sysbus.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "hw/misc/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/Kconfig b/hw/riscv/Kconfig index 109364b814..76eaf77a80 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -24,6 +24,7 @@ config SIFIVE_U select HART select SIFIVE select SIFIVE_PDMA + select SIFIVE_U_OTP select SIFIVE_U_PRCI select UNIMP diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 3462cb5a28..74a73b2a44 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -11,7 +11,6 @@ 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_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_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')) 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 - * - * 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 . - */ - -#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/include/hw/misc/sifive_u_otp.h b/include/hw/misc/sifive_u_otp.h new file mode 100644 index 0000000000..639297564a --- /dev/null +++ b/include/hw/misc/sifive_u_otp.h @@ -0,0 +1,80 @@ +/* + * QEMU SiFive U OTP (One-Time Programmable) Memory interface + * + * Copyright (c) 2019 Bin Meng + * + * 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 . + */ + +#ifndef HW_SIFIVE_U_OTP_H +#define HW_SIFIVE_U_OTP_H + +#define SIFIVE_U_OTP_PA 0x00 +#define SIFIVE_U_OTP_PAIO 0x04 +#define SIFIVE_U_OTP_PAS 0x08 +#define SIFIVE_U_OTP_PCE 0x0C +#define SIFIVE_U_OTP_PCLK 0x10 +#define SIFIVE_U_OTP_PDIN 0x14 +#define SIFIVE_U_OTP_PDOUT 0x18 +#define SIFIVE_U_OTP_PDSTB 0x1C +#define SIFIVE_U_OTP_PPROG 0x20 +#define SIFIVE_U_OTP_PTC 0x24 +#define SIFIVE_U_OTP_PTM 0x28 +#define SIFIVE_U_OTP_PTM_REP 0x2C +#define SIFIVE_U_OTP_PTR 0x30 +#define SIFIVE_U_OTP_PTRIM 0x34 +#define SIFIVE_U_OTP_PWE 0x38 + +#define SIFIVE_U_OTP_PCE_EN (1 << 0) + +#define SIFIVE_U_OTP_PDSTB_EN (1 << 0) + +#define SIFIVE_U_OTP_PTRIM_EN (1 << 0) + +#define SIFIVE_U_OTP_PA_MASK 0xfff +#define SIFIVE_U_OTP_NUM_FUSES 0x1000 +#define SIFIVE_U_OTP_SERIAL_ADDR 0xfc + +#define SIFIVE_U_OTP_REG_SIZE 0x1000 + +#define TYPE_SIFIVE_U_OTP "riscv.sifive.u.otp" + +#define SIFIVE_U_OTP(obj) \ + OBJECT_CHECK(SiFiveUOTPState, (obj), TYPE_SIFIVE_U_OTP) + +typedef struct SiFiveUOTPState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + uint32_t pa; + uint32_t paio; + uint32_t pas; + uint32_t pce; + uint32_t pclk; + uint32_t pdin; + uint32_t pdstb; + uint32_t pprog; + uint32_t ptc; + uint32_t ptm; + uint32_t ptm_rep; + uint32_t ptr; + uint32_t ptrim; + uint32_t pwe; + uint32_t fuse[SIFIVE_U_OTP_NUM_FUSES]; + /* config */ + uint32_t serial; +} SiFiveUOTPState; + +#endif /* HW_SIFIVE_U_OTP_H */ diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index cbeb2286d7..936a3bd0b1 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -24,7 +24,7 @@ #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_cpu.h" #include "hw/riscv/sifive_gpio.h" -#include "hw/riscv/sifive_u_otp.h" +#include "hw/misc/sifive_u_otp.h" #include "hw/misc/sifive_u_prci.h" #define TYPE_RISCV_U_SOC "riscv.sifive.u.soc" diff --git a/include/hw/riscv/sifive_u_otp.h b/include/hw/riscv/sifive_u_otp.h deleted file mode 100644 index 639297564a..0000000000 --- a/include/hw/riscv/sifive_u_otp.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * QEMU SiFive U OTP (One-Time Programmable) Memory interface - * - * Copyright (c) 2019 Bin Meng - * - * 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 . - */ - -#ifndef HW_SIFIVE_U_OTP_H -#define HW_SIFIVE_U_OTP_H - -#define SIFIVE_U_OTP_PA 0x00 -#define SIFIVE_U_OTP_PAIO 0x04 -#define SIFIVE_U_OTP_PAS 0x08 -#define SIFIVE_U_OTP_PCE 0x0C -#define SIFIVE_U_OTP_PCLK 0x10 -#define SIFIVE_U_OTP_PDIN 0x14 -#define SIFIVE_U_OTP_PDOUT 0x18 -#define SIFIVE_U_OTP_PDSTB 0x1C -#define SIFIVE_U_OTP_PPROG 0x20 -#define SIFIVE_U_OTP_PTC 0x24 -#define SIFIVE_U_OTP_PTM 0x28 -#define SIFIVE_U_OTP_PTM_REP 0x2C -#define SIFIVE_U_OTP_PTR 0x30 -#define SIFIVE_U_OTP_PTRIM 0x34 -#define SIFIVE_U_OTP_PWE 0x38 - -#define SIFIVE_U_OTP_PCE_EN (1 << 0) - -#define SIFIVE_U_OTP_PDSTB_EN (1 << 0) - -#define SIFIVE_U_OTP_PTRIM_EN (1 << 0) - -#define SIFIVE_U_OTP_PA_MASK 0xfff -#define SIFIVE_U_OTP_NUM_FUSES 0x1000 -#define SIFIVE_U_OTP_SERIAL_ADDR 0xfc - -#define SIFIVE_U_OTP_REG_SIZE 0x1000 - -#define TYPE_SIFIVE_U_OTP "riscv.sifive.u.otp" - -#define SIFIVE_U_OTP(obj) \ - OBJECT_CHECK(SiFiveUOTPState, (obj), TYPE_SIFIVE_U_OTP) - -typedef struct SiFiveUOTPState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - MemoryRegion mmio; - uint32_t pa; - uint32_t paio; - uint32_t pas; - uint32_t pce; - uint32_t pclk; - uint32_t pdin; - uint32_t pdstb; - uint32_t pprog; - uint32_t ptc; - uint32_t ptm; - uint32_t ptm_rep; - uint32_t ptr; - uint32_t ptrim; - uint32_t pwe; - uint32_t fuse[SIFIVE_U_OTP_NUM_FUSES]; - /* config */ - uint32_t serial; -} SiFiveUOTPState; - -#endif /* HW_SIFIVE_U_OTP_H */ -- cgit v1.2.3-55-g7522 From 4921a0ce86cecd03e6918832673db79de62e6fe1 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 3 Sep 2020 18:40:15 +0800 Subject: hw/riscv: Move sifive_gpio model to hw/gpio This is an effort to clean up the hw/riscv directory. Ideally it should only contain the RISC-V SoC / machine codes plus generic codes. Let's move sifive_gpio model to hw/gpio directory. Note this also removes the trace-events in the hw/riscv directory, since gpio is the only supported trace target in that directory. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1599129623-68957-5-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/gpio/Kconfig | 3 + hw/gpio/meson.build | 1 + hw/gpio/sifive_gpio.c | 397 +++++++++++++++++++++++++++++++++++++++++ hw/gpio/trace-events | 6 + hw/riscv/Kconfig | 2 + hw/riscv/meson.build | 1 - hw/riscv/sifive_gpio.c | 397 ----------------------------------------- hw/riscv/trace-events | 7 - hw/riscv/trace.h | 1 - include/hw/gpio/sifive_gpio.h | 76 ++++++++ include/hw/riscv/sifive_e.h | 2 +- include/hw/riscv/sifive_gpio.h | 76 -------- include/hw/riscv/sifive_u.h | 2 +- meson.build | 1 - 14 files changed, 487 insertions(+), 485 deletions(-) create mode 100644 hw/gpio/sifive_gpio.c delete mode 100644 hw/riscv/sifive_gpio.c delete mode 100644 hw/riscv/trace-events delete mode 100644 hw/riscv/trace.h create mode 100644 include/hw/gpio/sifive_gpio.h delete mode 100644 include/hw/riscv/sifive_gpio.h (limited to 'include') diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig index 9227cb5598..b6fdaa2586 100644 --- a/hw/gpio/Kconfig +++ b/hw/gpio/Kconfig @@ -7,3 +7,6 @@ config PL061 config GPIO_KEY bool + +config SIFIVE_GPIO + bool diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build index 6bcdfa6b1d..86cae9a0f3 100644 --- a/hw/gpio/meson.build +++ b/hw/gpio/meson.build @@ -10,3 +10,4 @@ softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_gpio.c')) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_gpio.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_gpio.c')) softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c')) +softmmu_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c')) diff --git a/hw/gpio/sifive_gpio.c b/hw/gpio/sifive_gpio.c new file mode 100644 index 0000000000..78bf29e996 --- /dev/null +++ b/hw/gpio/sifive_gpio.c @@ -0,0 +1,397 @@ +/* + * SiFive System-on-Chip general purpose input/output register definition + * + * Copyright 2019 AdaCore + * + * Base on nrf51_gpio.c: + * + * Copyright 2018 Steffen Görtz + * + * 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/gpio/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/gpio/trace-events b/hw/gpio/trace-events index c1271fdfb2..6e3f048745 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -5,3 +5,9 @@ nrf51_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PR nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 + +# 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/Kconfig b/hw/riscv/Kconfig index 76eaf77a80..5a8335bfec 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -15,6 +15,7 @@ config SIFIVE_E bool select HART select SIFIVE + select SIFIVE_GPIO select SIFIVE_E_PRCI select UNIMP @@ -23,6 +24,7 @@ config SIFIVE_U select CADENCE select HART select SIFIVE + select SIFIVE_GPIO select SIFIVE_PDMA select SIFIVE_U_OTP select SIFIVE_U_PRCI diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 74a73b2a44..90003793d4 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -5,7 +5,6 @@ riscv_ss.add(when: 'CONFIG_HART', if_true: 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')) 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 - * - * 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/trace-events b/hw/riscv/trace-events deleted file mode 100644 index 6d59233e23..0000000000 --- a/hw/riscv/trace-events +++ /dev/null @@ -1,7 +0,0 @@ -# See docs/devel/tracing.txt for syntax documentation. - -# hw/gpio/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/include/hw/gpio/sifive_gpio.h b/include/hw/gpio/sifive_gpio.h new file mode 100644 index 0000000000..cf12fcfd62 --- /dev/null +++ b/include/hw/gpio/sifive_gpio.h @@ -0,0 +1,76 @@ +/* + * SiFive System-on-Chip general purpose input/output register definition + * + * Copyright 2019 AdaCore + * + * Base on nrf51_gpio.c: + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef SIFIVE_GPIO_H +#define SIFIVE_GPIO_H + +#include "hw/sysbus.h" + +#define TYPE_SIFIVE_GPIO "sifive_soc.gpio" +#define SIFIVE_GPIO(obj) OBJECT_CHECK(SIFIVEGPIOState, (obj), TYPE_SIFIVE_GPIO) + +#define SIFIVE_GPIO_PINS 32 + +#define SIFIVE_GPIO_SIZE 0x100 + +#define SIFIVE_GPIO_REG_VALUE 0x000 +#define SIFIVE_GPIO_REG_INPUT_EN 0x004 +#define SIFIVE_GPIO_REG_OUTPUT_EN 0x008 +#define SIFIVE_GPIO_REG_PORT 0x00C +#define SIFIVE_GPIO_REG_PUE 0x010 +#define SIFIVE_GPIO_REG_DS 0x014 +#define SIFIVE_GPIO_REG_RISE_IE 0x018 +#define SIFIVE_GPIO_REG_RISE_IP 0x01C +#define SIFIVE_GPIO_REG_FALL_IE 0x020 +#define SIFIVE_GPIO_REG_FALL_IP 0x024 +#define SIFIVE_GPIO_REG_HIGH_IE 0x028 +#define SIFIVE_GPIO_REG_HIGH_IP 0x02C +#define SIFIVE_GPIO_REG_LOW_IE 0x030 +#define SIFIVE_GPIO_REG_LOW_IP 0x034 +#define SIFIVE_GPIO_REG_IOF_EN 0x038 +#define SIFIVE_GPIO_REG_IOF_SEL 0x03C +#define SIFIVE_GPIO_REG_OUT_XOR 0x040 + +typedef struct SIFIVEGPIOState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + qemu_irq irq[SIFIVE_GPIO_PINS]; + qemu_irq output[SIFIVE_GPIO_PINS]; + + uint32_t value; /* Actual value of the pin */ + uint32_t input_en; + uint32_t output_en; + uint32_t port; /* Pin value requested by the user */ + uint32_t pue; + uint32_t ds; + uint32_t rise_ie; + uint32_t rise_ip; + uint32_t fall_ie; + uint32_t fall_ip; + uint32_t high_ie; + uint32_t high_ip; + uint32_t low_ie; + uint32_t low_ip; + uint32_t iof_en; + uint32_t iof_sel; + uint32_t out_xor; + uint32_t in; + uint32_t in_mask; + + /* config */ + uint32_t ngpio; +} SIFIVEGPIOState; + +#endif /* SIFIVE_GPIO_H */ diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index 637414130b..b1400843c2 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -21,7 +21,7 @@ #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_cpu.h" -#include "hw/riscv/sifive_gpio.h" +#include "hw/gpio/sifive_gpio.h" #define TYPE_RISCV_E_SOC "riscv.sifive.e.soc" #define RISCV_E_SOC(obj) \ diff --git a/include/hw/riscv/sifive_gpio.h b/include/hw/riscv/sifive_gpio.h deleted file mode 100644 index cf12fcfd62..0000000000 --- a/include/hw/riscv/sifive_gpio.h +++ /dev/null @@ -1,76 +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 - * - * This code is licensed under the GPL version 2 or later. See - * the COPYING file in the top-level directory. - */ - -#ifndef SIFIVE_GPIO_H -#define SIFIVE_GPIO_H - -#include "hw/sysbus.h" - -#define TYPE_SIFIVE_GPIO "sifive_soc.gpio" -#define SIFIVE_GPIO(obj) OBJECT_CHECK(SIFIVEGPIOState, (obj), TYPE_SIFIVE_GPIO) - -#define SIFIVE_GPIO_PINS 32 - -#define SIFIVE_GPIO_SIZE 0x100 - -#define SIFIVE_GPIO_REG_VALUE 0x000 -#define SIFIVE_GPIO_REG_INPUT_EN 0x004 -#define SIFIVE_GPIO_REG_OUTPUT_EN 0x008 -#define SIFIVE_GPIO_REG_PORT 0x00C -#define SIFIVE_GPIO_REG_PUE 0x010 -#define SIFIVE_GPIO_REG_DS 0x014 -#define SIFIVE_GPIO_REG_RISE_IE 0x018 -#define SIFIVE_GPIO_REG_RISE_IP 0x01C -#define SIFIVE_GPIO_REG_FALL_IE 0x020 -#define SIFIVE_GPIO_REG_FALL_IP 0x024 -#define SIFIVE_GPIO_REG_HIGH_IE 0x028 -#define SIFIVE_GPIO_REG_HIGH_IP 0x02C -#define SIFIVE_GPIO_REG_LOW_IE 0x030 -#define SIFIVE_GPIO_REG_LOW_IP 0x034 -#define SIFIVE_GPIO_REG_IOF_EN 0x038 -#define SIFIVE_GPIO_REG_IOF_SEL 0x03C -#define SIFIVE_GPIO_REG_OUT_XOR 0x040 - -typedef struct SIFIVEGPIOState { - SysBusDevice parent_obj; - - MemoryRegion mmio; - - qemu_irq irq[SIFIVE_GPIO_PINS]; - qemu_irq output[SIFIVE_GPIO_PINS]; - - uint32_t value; /* Actual value of the pin */ - uint32_t input_en; - uint32_t output_en; - uint32_t port; /* Pin value requested by the user */ - uint32_t pue; - uint32_t ds; - uint32_t rise_ie; - uint32_t rise_ip; - uint32_t fall_ie; - uint32_t fall_ip; - uint32_t high_ie; - uint32_t high_ip; - uint32_t low_ie; - uint32_t low_ip; - uint32_t iof_en; - uint32_t iof_sel; - uint32_t out_xor; - uint32_t in; - uint32_t in_mask; - - /* config */ - uint32_t ngpio; -} SIFIVEGPIOState; - -#endif /* SIFIVE_GPIO_H */ diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index 936a3bd0b1..fe5c580845 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -23,7 +23,7 @@ #include "hw/net/cadence_gem.h" #include "hw/riscv/riscv_hart.h" #include "hw/riscv/sifive_cpu.h" -#include "hw/riscv/sifive_gpio.h" +#include "hw/gpio/sifive_gpio.h" #include "hw/misc/sifive_u_otp.h" #include "hw/misc/sifive_u_prci.h" diff --git a/meson.build b/meson.build index 5421eca66a..bc869c676a 100644 --- a/meson.build +++ b/meson.build @@ -773,7 +773,6 @@ if have_system 'hw/watchdog', 'hw/xen', 'hw/gpio', - 'hw/riscv', 'migration', 'net', 'ui', -- cgit v1.2.3-55-g7522 From 406fafd5d0f9a1c6a365ff1733c26a043b1c4877 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 3 Sep 2020 18:40:16 +0800 Subject: hw/riscv: Move sifive_clint model to hw/intc This is an effort to clean up the hw/riscv directory. Ideally it should only contain the RISC-V SoC / machine codes plus generic codes. Let's move sifive_clint model to hw/intc directory. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1599129623-68957-6-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/intc/Kconfig | 3 + hw/intc/meson.build | 1 + hw/intc/sifive_clint.c | 266 ++++++++++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 5 + hw/riscv/meson.build | 1 - hw/riscv/microchip_pfsoc.c | 2 +- hw/riscv/sifive_clint.c | 266 ---------------------------------------- hw/riscv/sifive_e.c | 2 +- hw/riscv/sifive_u.c | 2 +- hw/riscv/spike.c | 2 +- hw/riscv/virt.c | 2 +- include/hw/intc/sifive_clint.h | 60 +++++++++ include/hw/riscv/sifive_clint.h | 60 --------- 13 files changed, 340 insertions(+), 332 deletions(-) create mode 100644 hw/intc/sifive_clint.c delete mode 100644 hw/riscv/sifive_clint.c create mode 100644 include/hw/intc/sifive_clint.h delete mode 100644 include/hw/riscv/sifive_clint.h (limited to 'include') diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 2ae1e89497..f499d0f8df 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -67,3 +67,6 @@ config RX_ICU config LOONGSON_LIOINTC bool + +config SIFIVE_CLINT + bool diff --git a/hw/intc/meson.build b/hw/intc/meson.build index c16f7f036e..1e20daab77 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -47,6 +47,7 @@ specific_ss.add(when: 'CONFIG_RX_ICU', if_true: files('rx_icu.c')) specific_ss.add(when: 'CONFIG_S390_FLIC', if_true: files('s390_flic.c')) specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c')) specific_ss.add(when: 'CONFIG_SH4', if_true: files('sh_intc.c')) +specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c')) specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c')) specific_ss.add(when: 'CONFIG_XICS_KVM', if_true: files('xics_kvm.c')) specific_ss.add(when: 'CONFIG_XICS_SPAPR', if_true: files('xics_spapr.c')) diff --git a/hw/intc/sifive_clint.c b/hw/intc/sifive_clint.c new file mode 100644 index 0000000000..0f41e5ea1c --- /dev/null +++ b/hw/intc/sifive_clint.c @@ -0,0 +1,266 @@ +/* + * 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 . + */ + +#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/intc/sifive_clint.h" +#include "qemu/timer.h" + +static uint64_t cpu_riscv_read_rtc(uint32_t timebase_freq) +{ + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + 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, + uint32_t timebase_freq) +{ + uint64_t next; + uint64_t diff; + + uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq); + + 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, 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(clint->timebase_freq) & 0xFFFFFFFF; + } else if (addr == clint->time_base + 4) { + /* time_hi */ + return (cpu_riscv_read_rtc(clint->timebase_freq) >> 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), clint->timebase_freq); + 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), clint->timebase_freq); + } 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_UINT32("timebase-freq", SiFiveCLINTState, timebase_freq, 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, uint32_t timebase_freq, + 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, timebase_freq); + } + 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); + qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq); + 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/Kconfig b/hw/riscv/Kconfig index 5a8335bfec..f8bb7e7a05 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -15,6 +15,7 @@ config SIFIVE_E bool select HART select SIFIVE + select SIFIVE_CLINT select SIFIVE_GPIO select SIFIVE_E_PRCI select UNIMP @@ -24,6 +25,7 @@ config SIFIVE_U select CADENCE select HART select SIFIVE + select SIFIVE_CLINT select SIFIVE_GPIO select SIFIVE_PDMA select SIFIVE_U_OTP @@ -35,6 +37,7 @@ config SPIKE select HART select HTIF select SIFIVE + select SIFIVE_CLINT config OPENTITAN bool @@ -54,11 +57,13 @@ config RISCV_VIRT select PCI_EXPRESS_GENERIC_BRIDGE select PFLASH_CFI01 select SIFIVE + select SIFIVE_CLINT config MICROCHIP_PFSOC bool select HART select SIFIVE + select SIFIVE_CLINT select UNIMP select MCHP_PFSOC_MMUART select SIFIVE_PDMA diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 90003793d4..d0b4cafaec 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -4,7 +4,6 @@ riscv_ss.add(files('numa.c')) riscv_ss.add(when: 'CONFIG_HART', if_true: 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_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')) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index da6bd295ce..131eea1ef3 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -48,9 +48,9 @@ #include "hw/misc/unimp.h" #include "hw/riscv/boot.h" #include "hw/riscv/riscv_hart.h" -#include "hw/riscv/sifive_clint.h" #include "hw/riscv/sifive_plic.h" #include "hw/riscv/microchip_pfsoc.h" +#include "hw/intc/sifive_clint.h" #include "sysemu/sysemu.h" /* diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c deleted file mode 100644 index fa1ddf2ccd..0000000000 --- a/hw/riscv/sifive_clint.c +++ /dev/null @@ -1,266 +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 . - */ - -#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(uint32_t timebase_freq) -{ - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - 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, - uint32_t timebase_freq) -{ - uint64_t next; - uint64_t diff; - - uint64_t rtc_r = cpu_riscv_read_rtc(timebase_freq); - - 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, 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(clint->timebase_freq) & 0xFFFFFFFF; - } else if (addr == clint->time_base + 4) { - /* time_hi */ - return (cpu_riscv_read_rtc(clint->timebase_freq) >> 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), clint->timebase_freq); - 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), clint->timebase_freq); - } 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_UINT32("timebase-freq", SiFiveCLINTState, timebase_freq, 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, uint32_t timebase_freq, - 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, timebase_freq); - } - 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); - qdev_prop_set_uint32(dev, "timebase-freq", timebase_freq); - 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 7f43ed953a..3bdb16e697 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -40,10 +40,10 @@ #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/boot.h" +#include "hw/intc/sifive_clint.h" #include "hw/misc/sifive_e_prci.h" #include "chardev/char.h" #include "sysemu/arch_init.h" diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 79975372ef..7187d1ad17 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -47,10 +47,10 @@ #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/intc/sifive_clint.h" #include "chardev/char.h" #include "net/eth.h" #include "sysemu/arch_init.h" diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index b54a396107..59d9d87c56 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -33,10 +33,10 @@ #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/intc/sifive_clint.h" #include "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c67a910e48..bce2020d02 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -31,11 +31,11 @@ #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 "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" diff --git a/include/hw/intc/sifive_clint.h b/include/hw/intc/sifive_clint.h new file mode 100644 index 0000000000..a30be0f3d6 --- /dev/null +++ b/include/hw/intc/sifive_clint.h @@ -0,0 +1,60 @@ +/* + * SiFive CLINT (Core Local Interruptor) interface + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017 SiFive, Inc. + * + * 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 . + */ + +#ifndef HW_SIFIVE_CLINT_H +#define HW_SIFIVE_CLINT_H + +#include "hw/sysbus.h" + +#define TYPE_SIFIVE_CLINT "riscv.sifive.clint" + +#define SIFIVE_CLINT(obj) \ + OBJECT_CHECK(SiFiveCLINTState, (obj), TYPE_SIFIVE_CLINT) + +typedef struct SiFiveCLINTState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + uint32_t hartid_base; + uint32_t num_harts; + uint32_t sip_base; + uint32_t timecmp_base; + uint32_t time_base; + uint32_t aperture_size; + uint32_t timebase_freq; +} SiFiveCLINTState; + +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, uint32_t timebase_freq, + bool provide_rdtime); + +enum { + SIFIVE_SIP_BASE = 0x0, + SIFIVE_TIMECMP_BASE = 0x4000, + SIFIVE_TIME_BASE = 0xBFF8 +}; + +enum { + SIFIVE_CLINT_TIMEBASE_FREQ = 10000000 +}; + +#endif diff --git a/include/hw/riscv/sifive_clint.h b/include/hw/riscv/sifive_clint.h deleted file mode 100644 index a30be0f3d6..0000000000 --- a/include/hw/riscv/sifive_clint.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SiFive CLINT (Core Local Interruptor) interface - * - * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu - * Copyright (c) 2017 SiFive, Inc. - * - * 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 . - */ - -#ifndef HW_SIFIVE_CLINT_H -#define HW_SIFIVE_CLINT_H - -#include "hw/sysbus.h" - -#define TYPE_SIFIVE_CLINT "riscv.sifive.clint" - -#define SIFIVE_CLINT(obj) \ - OBJECT_CHECK(SiFiveCLINTState, (obj), TYPE_SIFIVE_CLINT) - -typedef struct SiFiveCLINTState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - MemoryRegion mmio; - uint32_t hartid_base; - uint32_t num_harts; - uint32_t sip_base; - uint32_t timecmp_base; - uint32_t time_base; - uint32_t aperture_size; - uint32_t timebase_freq; -} SiFiveCLINTState; - -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, uint32_t timebase_freq, - bool provide_rdtime); - -enum { - SIFIVE_SIP_BASE = 0x0, - SIFIVE_TIMECMP_BASE = 0x4000, - SIFIVE_TIME_BASE = 0xBFF8 -}; - -enum { - SIFIVE_CLINT_TIMEBASE_FREQ = 10000000 -}; - -#endif -- cgit v1.2.3-55-g7522 From 84fcf3c15111de9f0c72efbb6bc0def264555c46 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 3 Sep 2020 18:40:17 +0800 Subject: hw/riscv: Move sifive_plic model to hw/intc This is an effort to clean up the hw/riscv directory. Ideally it should only contain the RISC-V SoC / machine codes plus generic codes. Let's move sifive_plic model to hw/intc directory. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1599129623-68957-7-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/intc/Kconfig | 3 + hw/intc/meson.build | 1 + hw/intc/sifive_plic.c | 524 +++++++++++++++++++++++++++++++++++++++++ hw/intc/sifive_plic.h | 81 +++++++ hw/riscv/Kconfig | 5 + hw/riscv/meson.build | 1 - hw/riscv/microchip_pfsoc.c | 2 +- hw/riscv/sifive_e.c | 2 +- hw/riscv/sifive_plic.c | 524 ----------------------------------------- hw/riscv/sifive_u.c | 2 +- hw/riscv/virt.c | 2 +- include/hw/riscv/sifive_plic.h | 81 ------- 12 files changed, 618 insertions(+), 610 deletions(-) create mode 100644 hw/intc/sifive_plic.c create mode 100644 hw/intc/sifive_plic.h delete mode 100644 hw/riscv/sifive_plic.c delete mode 100644 include/hw/riscv/sifive_plic.h (limited to 'include') diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index f499d0f8df..d07954086a 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -70,3 +70,6 @@ config LOONGSON_LIOINTC config SIFIVE_CLINT bool + +config SIFIVE_PLIC + bool diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 1e20daab77..3f82cc230a 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -48,6 +48,7 @@ specific_ss.add(when: 'CONFIG_S390_FLIC', if_true: files('s390_flic.c')) specific_ss.add(when: 'CONFIG_S390_FLIC_KVM', if_true: files('s390_flic_kvm.c')) specific_ss.add(when: 'CONFIG_SH4', if_true: files('sh_intc.c')) specific_ss.add(when: 'CONFIG_SIFIVE_CLINT', if_true: files('sifive_clint.c')) +specific_ss.add(when: 'CONFIG_SIFIVE_PLIC', if_true: files('sifive_plic.c')) specific_ss.add(when: 'CONFIG_XICS', if_true: files('xics.c')) specific_ss.add(when: 'CONFIG_XICS_KVM', if_true: files('xics_kvm.c')) specific_ss.add(when: 'CONFIG_XICS_SPAPR', if_true: files('xics_spapr.c')) diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c new file mode 100644 index 0000000000..af611f8db8 --- /dev/null +++ b/hw/intc/sifive_plic.c @@ -0,0 +1,524 @@ +/* + * 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 . + */ + +#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 "hw/intc/sifive_plic.h" +#include "target/riscv/cpu.h" +#include "sysemu/sysemu.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/intc/sifive_plic.h b/hw/intc/sifive_plic.h new file mode 100644 index 0000000000..ace76d0f1b --- /dev/null +++ b/hw/intc/sifive_plic.h @@ -0,0 +1,81 @@ +/* + * SiFive PLIC (Platform Level Interrupt Controller) interface + * + * Copyright (c) 2017 SiFive, Inc. + * + * This provides a RISC-V PLIC device + * + * 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 . + */ + +#ifndef HW_SIFIVE_PLIC_H +#define HW_SIFIVE_PLIC_H + +#include "hw/sysbus.h" + +#define TYPE_SIFIVE_PLIC "riscv.sifive.plic" + +#define SIFIVE_PLIC(obj) \ + OBJECT_CHECK(SiFivePLICState, (obj), TYPE_SIFIVE_PLIC) + +typedef enum PLICMode { + PLICMode_U, + PLICMode_S, + PLICMode_H, + PLICMode_M +} PLICMode; + +typedef struct PLICAddr { + uint32_t addrid; + uint32_t hartid; + PLICMode mode; +} PLICAddr; + +typedef struct SiFivePLICState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; + uint32_t num_addrs; + uint32_t num_harts; + uint32_t bitfield_words; + PLICAddr *addr_config; + uint32_t *source_priority; + uint32_t *target_priority; + uint32_t *pending; + uint32_t *claimed; + uint32_t *enable; + + /* config */ + 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; +} SiFivePLICState; + +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); + +#endif diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index f8bb7e7a05..23b7027e11 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -17,6 +17,7 @@ config SIFIVE_E select SIFIVE select SIFIVE_CLINT select SIFIVE_GPIO + select SIFIVE_PLIC select SIFIVE_E_PRCI select UNIMP @@ -28,6 +29,7 @@ config SIFIVE_U select SIFIVE_CLINT select SIFIVE_GPIO select SIFIVE_PDMA + select SIFIVE_PLIC select SIFIVE_U_OTP select SIFIVE_U_PRCI select UNIMP @@ -38,6 +40,7 @@ config SPIKE select HTIF select SIFIVE select SIFIVE_CLINT + select SIFIVE_PLIC config OPENTITAN bool @@ -58,6 +61,7 @@ config RISCV_VIRT select PFLASH_CFI01 select SIFIVE select SIFIVE_CLINT + select SIFIVE_PLIC config MICROCHIP_PFSOC bool @@ -67,4 +71,5 @@ config MICROCHIP_PFSOC select UNIMP select MCHP_PFSOC_MMUART select SIFIVE_PDMA + select SIFIVE_PLIC select CADENCE_SDHCI diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index d0b4cafaec..df3f89d062 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -4,7 +4,6 @@ riscv_ss.add(files('numa.c')) riscv_ss.add(when: 'CONFIG_HART', if_true: 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_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')) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 131eea1ef3..4627179cd3 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -48,9 +48,9 @@ #include "hw/misc/unimp.h" #include "hw/riscv/boot.h" #include "hw/riscv/riscv_hart.h" -#include "hw/riscv/sifive_plic.h" #include "hw/riscv/microchip_pfsoc.h" #include "hw/intc/sifive_clint.h" +#include "hw/intc/sifive_plic.h" #include "sysemu/sysemu.h" /* diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 3bdb16e697..0ddcf1508d 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -39,11 +39,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_uart.h" #include "hw/riscv/sifive_e.h" #include "hw/riscv/boot.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" 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 . - */ - -#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_u.c b/hw/riscv/sifive_u.c index 7187d1ad17..faca2e829e 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -46,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_uart.h" #include "hw/riscv/sifive_u.h" #include "hw/riscv/boot.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" diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index bce2020d02..0caab8e050 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_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 "chardev/char.h" #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h deleted file mode 100644 index ace76d0f1b..0000000000 --- a/include/hw/riscv/sifive_plic.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * SiFive PLIC (Platform Level Interrupt Controller) interface - * - * Copyright (c) 2017 SiFive, Inc. - * - * This provides a RISC-V PLIC device - * - * 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 . - */ - -#ifndef HW_SIFIVE_PLIC_H -#define HW_SIFIVE_PLIC_H - -#include "hw/sysbus.h" - -#define TYPE_SIFIVE_PLIC "riscv.sifive.plic" - -#define SIFIVE_PLIC(obj) \ - OBJECT_CHECK(SiFivePLICState, (obj), TYPE_SIFIVE_PLIC) - -typedef enum PLICMode { - PLICMode_U, - PLICMode_S, - PLICMode_H, - PLICMode_M -} PLICMode; - -typedef struct PLICAddr { - uint32_t addrid; - uint32_t hartid; - PLICMode mode; -} PLICAddr; - -typedef struct SiFivePLICState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - MemoryRegion mmio; - uint32_t num_addrs; - uint32_t num_harts; - uint32_t bitfield_words; - PLICAddr *addr_config; - uint32_t *source_priority; - uint32_t *target_priority; - uint32_t *pending; - uint32_t *claimed; - uint32_t *enable; - - /* config */ - 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; -} SiFivePLICState; - -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); - -#endif -- cgit v1.2.3-55-g7522 From 70eb9f9cd1c0b519b31df8ab08ee2198b0e16176 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 3 Sep 2020 18:40:18 +0800 Subject: hw/riscv: Move riscv_htif model to hw/char This is an effort to clean up the hw/riscv directory. Ideally it should only contain the RISC-V SoC / machine codes plus generic codes. Let's move riscv_htif model to hw/char directory. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1599129623-68957-8-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/char/Kconfig | 3 + hw/char/meson.build | 1 + hw/char/riscv_htif.c | 261 ++++++++++++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 3 - hw/riscv/meson.build | 1 - hw/riscv/riscv_htif.c | 261 ------------------------------------------ hw/riscv/spike.c | 2 +- include/hw/char/riscv_htif.h | 59 ++++++++++ include/hw/riscv/riscv_htif.h | 59 ---------- 9 files changed, 325 insertions(+), 325 deletions(-) create mode 100644 hw/char/riscv_htif.c delete mode 100644 hw/riscv/riscv_htif.c create mode 100644 include/hw/char/riscv_htif.h delete mode 100644 include/hw/riscv/riscv_htif.h (limited to 'include') diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 1d645554c7..91da92f617 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -1,6 +1,9 @@ config ESCC bool +config HTIF + bool + config PARALLEL bool default y diff --git a/hw/char/meson.build b/hw/char/meson.build index ae27932d00..3db623eeec 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -34,6 +34,7 @@ softmmu_ss.add(when: 'CONFIG_SH4', if_true: files('sh_serial.c')) softmmu_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) +specific_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c')) specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c')) specific_ss.add(when: 'CONFIG_VIRTIO', if_true: files('virtio-serial-bus.c')) specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_vty.c')) diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c new file mode 100644 index 0000000000..ba1af1cfc4 --- /dev/null +++ b/hw/char/riscv_htif.c @@ -0,0 +1,261 @@ +/* + * 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 . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "hw/sysbus.h" +#include "hw/char/riscv_htif.h" +#include "hw/char/serial.h" +#include "chardev/char.h" +#include "chardev/char-fe.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/Kconfig b/hw/riscv/Kconfig index 23b7027e11..a0e256c344 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -1,6 +1,3 @@ -config HTIF - bool - config HART bool diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index df3f89d062..90df67acc7 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -8,7 +8,6 @@ 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_U', if_true: files('sifive_u.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')) 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 . - */ - -#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/spike.c b/hw/riscv/spike.c index 59d9d87c56..3fd152a035 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -31,11 +31,11 @@ #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/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" diff --git a/include/hw/char/riscv_htif.h b/include/hw/char/riscv_htif.h new file mode 100644 index 0000000000..fb9452cf51 --- /dev/null +++ b/include/hw/char/riscv_htif.h @@ -0,0 +1,59 @@ +/* + * QEMU RISCV Host Target Interface (HTIF) Emulation + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * + * 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 . + */ + +#ifndef HW_RISCV_HTIF_H +#define HW_RISCV_HTIF_H + +#include "chardev/char.h" +#include "chardev/char-fe.h" +#include "exec/memory.h" +#include "target/riscv/cpu.h" + +#define TYPE_HTIF_UART "riscv.htif.uart" + +typedef struct HTIFState { + int allow_tohost; + int fromhost_inprogress; + + hwaddr tohost_offset; + hwaddr fromhost_offset; + uint64_t tohost_size; + uint64_t fromhost_size; + MemoryRegion mmio; + MemoryRegion *address_space; + MemoryRegion *main_mem; + void *main_mem_ram_ptr; + + CPURISCVState *env; + CharBackend chr; + uint64_t pending_read; +} HTIFState; + +extern const VMStateDescription vmstate_htif; +extern const MemoryRegionOps htif_io_ops; + +/* HTIF symbol callback */ +void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, + uint64_t st_size); + +/* legacy pre qom */ +HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, + CPURISCVState *env, Chardev *chr); + +#endif diff --git a/include/hw/riscv/riscv_htif.h b/include/hw/riscv/riscv_htif.h deleted file mode 100644 index fb9452cf51..0000000000 --- a/include/hw/riscv/riscv_htif.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * QEMU RISCV Host Target Interface (HTIF) Emulation - * - * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu - * Copyright (c) 2017-2018 SiFive, Inc. - * - * 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 . - */ - -#ifndef HW_RISCV_HTIF_H -#define HW_RISCV_HTIF_H - -#include "chardev/char.h" -#include "chardev/char-fe.h" -#include "exec/memory.h" -#include "target/riscv/cpu.h" - -#define TYPE_HTIF_UART "riscv.htif.uart" - -typedef struct HTIFState { - int allow_tohost; - int fromhost_inprogress; - - hwaddr tohost_offset; - hwaddr fromhost_offset; - uint64_t tohost_size; - uint64_t fromhost_size; - MemoryRegion mmio; - MemoryRegion *address_space; - MemoryRegion *main_mem; - void *main_mem_ram_ptr; - - CPURISCVState *env; - CharBackend chr; - uint64_t pending_read; -} HTIFState; - -extern const VMStateDescription vmstate_htif; -extern const MemoryRegionOps htif_io_ops; - -/* HTIF symbol callback */ -void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, - uint64_t st_size); - -/* legacy pre qom */ -HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, - CPURISCVState *env, Chardev *chr); - -#endif -- cgit v1.2.3-55-g7522 From b609b7e3199912e16ef3b0447823f21fed73597e Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 3 Sep 2020 18:40:19 +0800 Subject: hw/riscv: Move sifive_uart model to hw/char This is an effort to clean up the hw/riscv directory. Ideally it should only contain the RISC-V SoC / machine codes plus generic codes. Let's move sifive_uart model to hw/char directory. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1599129623-68957-9-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/char/Kconfig | 3 + hw/char/meson.build | 1 + hw/char/sifive_uart.c | 194 +++++++++++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 2 + hw/riscv/meson.build | 1 - hw/riscv/sifive_e.c | 2 +- hw/riscv/sifive_u.c | 2 +- hw/riscv/sifive_uart.c | 194 ----------------------------------------- include/hw/char/sifive_uart.h | 77 ++++++++++++++++ include/hw/riscv/sifive_uart.h | 77 ---------------- 10 files changed, 279 insertions(+), 274 deletions(-) create mode 100644 hw/char/sifive_uart.c delete mode 100644 hw/riscv/sifive_uart.c create mode 100644 include/hw/char/sifive_uart.h delete mode 100644 include/hw/riscv/sifive_uart.h (limited to 'include') diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 91da92f617..939bc44758 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -58,3 +58,6 @@ config AVR_USART config MCHP_PFSOC_MMUART bool + +config SIFIVE_UART + bool diff --git a/hw/char/meson.build b/hw/char/meson.build index 3db623eeec..196ac91fa2 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -30,6 +30,7 @@ softmmu_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c')) softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c')) softmmu_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) softmmu_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) +softmmu_ss.add(when: 'CONFIG_SIFIVE_UART', if_true: files('sifive_uart.c')) softmmu_ss.add(when: 'CONFIG_SH4', if_true: files('sh_serial.c')) softmmu_ss.add(when: 'CONFIG_STM32F2XX_USART', if_true: files('stm32f2xx_usart.c')) softmmu_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c')) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c new file mode 100644 index 0000000000..3a00ba7f00 --- /dev/null +++ b/hw/char/sifive_uart.c @@ -0,0 +1,194 @@ +/* + * 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 . + */ + +#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/char/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/Kconfig b/hw/riscv/Kconfig index a0e256c344..a0461578a6 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -15,6 +15,7 @@ config SIFIVE_E select SIFIVE_CLINT select SIFIVE_GPIO select SIFIVE_PLIC + select SIFIVE_UART select SIFIVE_E_PRCI select UNIMP @@ -27,6 +28,7 @@ config SIFIVE_U select SIFIVE_GPIO select SIFIVE_PDMA select SIFIVE_PLIC + select SIFIVE_UART select SIFIVE_U_OTP select SIFIVE_U_PRCI select UNIMP diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 90df67acc7..967572d4f6 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -5,7 +5,6 @@ riscv_ss.add(when: 'CONFIG_HART', if_true: 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_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_U', if_true: files('sifive_u.c')) riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c')) diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 0ddcf1508d..40bbf530d4 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -39,9 +39,9 @@ #include "hw/misc/unimp.h" #include "target/riscv/cpu.h" #include "hw/riscv/riscv_hart.h" -#include "hw/riscv/sifive_uart.h" #include "hw/riscv/sifive_e.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" diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index faca2e829e..4f12a93188 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -46,9 +46,9 @@ #include "hw/misc/unimp.h" #include "target/riscv/cpu.h" #include "hw/riscv/riscv_hart.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" 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 . - */ - -#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/include/hw/char/sifive_uart.h b/include/hw/char/sifive_uart.h new file mode 100644 index 0000000000..65668825a3 --- /dev/null +++ b/include/hw/char/sifive_uart.h @@ -0,0 +1,77 @@ +/* + * SiFive UART interface + * + * Copyright (c) 2016 Stefan O'Rear + * Copyright (c) 2017 SiFive, Inc. + * + * 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 . + */ + +#ifndef HW_SIFIVE_UART_H +#define HW_SIFIVE_UART_H + +#include "chardev/char-fe.h" +#include "hw/sysbus.h" + +enum { + SIFIVE_UART_TXFIFO = 0, + SIFIVE_UART_RXFIFO = 4, + SIFIVE_UART_TXCTRL = 8, + SIFIVE_UART_TXMARK = 10, + SIFIVE_UART_RXCTRL = 12, + SIFIVE_UART_RXMARK = 14, + SIFIVE_UART_IE = 16, + SIFIVE_UART_IP = 20, + SIFIVE_UART_DIV = 24, + SIFIVE_UART_MAX = 32 +}; + +enum { + SIFIVE_UART_IE_TXWM = 1, /* Transmit watermark interrupt enable */ + SIFIVE_UART_IE_RXWM = 2 /* Receive watermark interrupt enable */ +}; + +enum { + SIFIVE_UART_IP_TXWM = 1, /* Transmit watermark interrupt pending */ + SIFIVE_UART_IP_RXWM = 2 /* Receive watermark interrupt pending */ +}; + +#define SIFIVE_UART_GET_TXCNT(txctrl) ((txctrl >> 16) & 0x7) +#define SIFIVE_UART_GET_RXCNT(rxctrl) ((rxctrl >> 16) & 0x7) + +#define TYPE_SIFIVE_UART "riscv.sifive.uart" + +#define SIFIVE_UART(obj) \ + OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART) + +typedef struct SiFiveUARTState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + qemu_irq irq; + MemoryRegion mmio; + CharBackend chr; + uint8_t rx_fifo[8]; + unsigned int rx_fifo_len; + uint32_t ie; + uint32_t ip; + uint32_t txctrl; + uint32_t rxctrl; + uint32_t div; +} SiFiveUARTState; + +SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, + Chardev *chr, qemu_irq irq); + +#endif diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h deleted file mode 100644 index 65668825a3..0000000000 --- a/include/hw/riscv/sifive_uart.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SiFive UART interface - * - * Copyright (c) 2016 Stefan O'Rear - * Copyright (c) 2017 SiFive, Inc. - * - * 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 . - */ - -#ifndef HW_SIFIVE_UART_H -#define HW_SIFIVE_UART_H - -#include "chardev/char-fe.h" -#include "hw/sysbus.h" - -enum { - SIFIVE_UART_TXFIFO = 0, - SIFIVE_UART_RXFIFO = 4, - SIFIVE_UART_TXCTRL = 8, - SIFIVE_UART_TXMARK = 10, - SIFIVE_UART_RXCTRL = 12, - SIFIVE_UART_RXMARK = 14, - SIFIVE_UART_IE = 16, - SIFIVE_UART_IP = 20, - SIFIVE_UART_DIV = 24, - SIFIVE_UART_MAX = 32 -}; - -enum { - SIFIVE_UART_IE_TXWM = 1, /* Transmit watermark interrupt enable */ - SIFIVE_UART_IE_RXWM = 2 /* Receive watermark interrupt enable */ -}; - -enum { - SIFIVE_UART_IP_TXWM = 1, /* Transmit watermark interrupt pending */ - SIFIVE_UART_IP_RXWM = 2 /* Receive watermark interrupt pending */ -}; - -#define SIFIVE_UART_GET_TXCNT(txctrl) ((txctrl >> 16) & 0x7) -#define SIFIVE_UART_GET_RXCNT(rxctrl) ((rxctrl >> 16) & 0x7) - -#define TYPE_SIFIVE_UART "riscv.sifive.uart" - -#define SIFIVE_UART(obj) \ - OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART) - -typedef struct SiFiveUARTState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - qemu_irq irq; - MemoryRegion mmio; - CharBackend chr; - uint8_t rx_fifo[8]; - unsigned int rx_fifo_len; - uint32_t ie; - uint32_t ip; - uint32_t txctrl; - uint32_t rxctrl; - uint32_t div; -} SiFiveUARTState; - -SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base, - Chardev *chr, qemu_irq irq); - -#endif -- cgit v1.2.3-55-g7522 From a4b84608ba0eecce1d4858181457dc26582e6d28 Mon Sep 17 00:00:00 2001 From: Bin Meng Date: Thu, 3 Sep 2020 18:40:20 +0800 Subject: hw/riscv: Move sifive_test model to hw/misc This is an effort to clean up the hw/riscv directory. Ideally it should only contain the RISC-V SoC / machine codes plus generic codes. Let's move sifive_test model to hw/misc directory. Signed-off-by: Bin Meng Reviewed-by: Alistair Francis Message-Id: <1599129623-68957-10-git-send-email-bmeng.cn@gmail.com> Signed-off-by: Alistair Francis --- hw/misc/Kconfig | 3 ++ hw/misc/meson.build | 1 + hw/misc/sifive_test.c | 100 +++++++++++++++++++++++++++++++++++++++++ hw/riscv/Kconfig | 1 + hw/riscv/meson.build | 1 - hw/riscv/sifive_test.c | 100 ----------------------------------------- hw/riscv/virt.c | 2 +- include/hw/misc/sifive_test.h | 45 +++++++++++++++++++ include/hw/riscv/sifive_test.h | 45 ------------------- 9 files changed, 151 insertions(+), 147 deletions(-) create mode 100644 hw/misc/sifive_test.c delete mode 100644 hw/riscv/sifive_test.c create mode 100644 include/hw/misc/sifive_test.h delete mode 100644 include/hw/riscv/sifive_test.h (limited to 'include') diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index fa3d0f4723..3185456110 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -134,6 +134,9 @@ config MAC_VIA config AVR_POWER bool +config SIFIVE_TEST + bool + config SIFIVE_E_PRCI bool diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 018a88c670..bd24132757 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -22,6 +22,7 @@ softmmu_ss.add(when: 'CONFIG_ARM11SCU', if_true: files('arm11scu.c')) softmmu_ss.add(when: 'CONFIG_MOS6522', if_true: files('mos6522.c')) # RISC-V devices +softmmu_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c')) softmmu_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c')) softmmu_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c')) softmmu_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c')) diff --git a/hw/misc/sifive_test.c b/hw/misc/sifive_test.c new file mode 100644 index 0000000000..2deb2072cc --- /dev/null +++ b/hw/misc/sifive_test.c @@ -0,0 +1,100 @@ +/* + * 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 . + */ + +#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/misc/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 = 2, + .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/Kconfig b/hw/riscv/Kconfig index a0461578a6..8e0710001b 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -61,6 +61,7 @@ config RISCV_VIRT select SIFIVE select SIFIVE_CLINT select SIFIVE_PLIC + select SIFIVE_TEST config MICROCHIP_PFSOC bool diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index 967572d4f6..f762623288 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -4,7 +4,6 @@ riscv_ss.add(files('numa.c')) riscv_ss.add(when: 'CONFIG_HART', if_true: 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_test.c')) riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c')) riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c')) riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c')) diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c deleted file mode 100644 index 8c70dd69df..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 . - */ - -#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 = 2, - .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/virt.c b/hw/riscv/virt.c index 0caab8e050..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_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" diff --git a/include/hw/misc/sifive_test.h b/include/hw/misc/sifive_test.h new file mode 100644 index 0000000000..1ec416ac1b --- /dev/null +++ b/include/hw/misc/sifive_test.h @@ -0,0 +1,45 @@ +/* + * QEMU Test Finisher interface + * + * Copyright (c) 2018 SiFive, Inc. + * + * 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 . + */ + +#ifndef HW_SIFIVE_TEST_H +#define HW_SIFIVE_TEST_H + +#include "hw/sysbus.h" + +#define TYPE_SIFIVE_TEST "riscv.sifive.test" + +#define SIFIVE_TEST(obj) \ + OBJECT_CHECK(SiFiveTestState, (obj), TYPE_SIFIVE_TEST) + +typedef struct SiFiveTestState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion mmio; +} SiFiveTestState; + +enum { + FINISHER_FAIL = 0x3333, + FINISHER_PASS = 0x5555, + FINISHER_RESET = 0x7777 +}; + +DeviceState *sifive_test_create(hwaddr addr); + +#endif diff --git a/include/hw/riscv/sifive_test.h b/include/hw/riscv/sifive_test.h deleted file mode 100644 index 1ec416ac1b..0000000000 --- a/include/hw/riscv/sifive_test.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * QEMU Test Finisher interface - * - * Copyright (c) 2018 SiFive, Inc. - * - * 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 . - */ - -#ifndef HW_SIFIVE_TEST_H -#define HW_SIFIVE_TEST_H - -#include "hw/sysbus.h" - -#define TYPE_SIFIVE_TEST "riscv.sifive.test" - -#define SIFIVE_TEST(obj) \ - OBJECT_CHECK(SiFiveTestState, (obj), TYPE_SIFIVE_TEST) - -typedef struct SiFiveTestState { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - MemoryRegion mmio; -} SiFiveTestState; - -enum { - FINISHER_FAIL = 0x3333, - FINISHER_PASS = 0x5555, - FINISHER_RESET = 0x7777 -}; - -DeviceState *sifive_test_create(hwaddr addr); - -#endif -- cgit v1.2.3-55-g7522