From de231c5356b11a911bd02de36c0848f7765ddb37 Mon Sep 17 00:00:00 2001 From: "demin.han" Date: Fri, 15 Apr 2022 15:50:43 +0800 Subject: [PATCH 3/6] duhhe bare-metal-sdk support based on sifive_u 1. remove e cluster 2. add test dev 3. change UART0 memmap --- configs/devices/riscv64-softmmu/default.mak | 1 + hw/riscv/Kconfig | 16 + hw/riscv/meson.build | 1 + hw/riscv/starfive_u.c | 939 ++++++++++++++++++++ include/hw/riscv/starfive_u.h | 156 ++++ 5 files changed, 1113 insertions(+) create mode 100644 hw/riscv/starfive_u.c create mode 100644 include/hw/riscv/starfive_u.h diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak index bc69301fa4..4c336b78c5 100644 --- a/configs/devices/riscv64-softmmu/default.mak +++ b/configs/devices/riscv64-softmmu/default.mak @@ -14,3 +14,4 @@ CONFIG_SIFIVE_U=y CONFIG_RISCV_VIRT=y CONFIG_MICROCHIP_PFSOC=y CONFIG_SHAKTI_C=y +CONFIG_STARFIVE_U=y diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 79ff61c464..369495f2fe 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -85,3 +85,19 @@ config SPIKE select MSI_NONBROKEN select RISCV_ACLINT select SIFIVE_PLIC + +config STARFIVE_U + bool + select CADENCE + select MSI_NONBROKEN + select SIFIVE_CLINT + select SIFIVE_GPIO + select SIFIVE_PDMA + select SIFIVE_PLIC + select SIFIVE_SPI + select SIFIVE_UART + select SIFIVE_U_OTP + select SIFIVE_U_PRCI + select SSI_M25P80 + select SSI_SD + select UNIMP \ No newline at end of file diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index ab6cae57ea..80e9f4d077 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -9,5 +9,6 @@ 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')) riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c')) +riscv_ss.add(when: 'CONFIG_STARFIVE_U', if_true: files('starfive_u.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/starfive_u.c b/hw/riscv/starfive_u.c new file mode 100644 index 0000000000..c8e27ab805 --- /dev/null +++ b/hw/riscv/starfive_u.c @@ -0,0 +1,939 @@ +/* + * QEMU RISC-V Board Compatible with SiFive Freedom U SDK + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017 SiFive, Inc. + * Copyright (c) 2019 Bin Meng + * + * Provides a board compatible with the SiFive Freedom U SDK: + * + * 0) UART + * 1) CLINT (Core Level Interruptor) + * 2) PLIC (Platform Level Interrupt Controller) + * 3) PRCI (Power, Reset, Clock, Interrupt) + * 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) + * 8) SPI0 connected to an SPI flash + * 9) SPI2 connected to an SD card + * + * 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/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "hw/boards.h" +#include "hw/irq.h" +#include "hw/loader.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "hw/cpu/cluster.h" +#include "hw/misc/unimp.h" +#include "hw/ssi/ssi.h" +#include "target/riscv/cpu.h" +#include "hw/riscv/riscv_hart.h" +#include "hw/riscv/starfive_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 "hw/misc/sifive_test.h" +#include "chardev/char.h" +#include "net/eth.h" +#include "sysemu/arch_init.h" +#include "sysemu/device_tree.h" +#include "sysemu/runstate.h" +#include "sysemu/sysemu.h" + +#include + +static const MemMapEntry starfive_u_memmap[] = { + [STARFIVE_U_DEV_DEBUG] = { 0x0, 0x100 }, + [STARFIVE_U_DEV_MROM] = { 0x1000, 0xf000 }, + [STARFIVE_U_DEV_TEST] = { 0x100000, 0x1000 }, + [STARFIVE_U_DEV_CLINT] = { 0x2000000, 0x10000 }, + [STARFIVE_U_DEV_L2CC] = { 0x2010000, 0x1000 }, + [STARFIVE_U_DEV_PDMA] = { 0x3000000, 0x100000 }, + [STARFIVE_U_DEV_L2LIM] = { 0x8000000, 0x2000000 }, + [STARFIVE_U_DEV_PLIC] = { 0xc000000, 0x4000000 }, + [STARFIVE_U_DEV_PRCI] = { 0x10000000, 0x1000 }, + [STARFIVE_U_DEV_UART0] = { 0x10012000, 0x1000 }, + [STARFIVE_U_DEV_UART1] = { 0x10011000, 0x1000 }, + [STARFIVE_U_DEV_QSPI0] = { 0x10040000, 0x1000 }, + [STARFIVE_U_DEV_QSPI2] = { 0x10050000, 0x1000 }, + [STARFIVE_U_DEV_GPIO] = { 0x10060000, 0x1000 }, + [STARFIVE_U_DEV_OTP] = { 0x10070000, 0x1000 }, + [STARFIVE_U_DEV_GEM] = { 0x10090000, 0x2000 }, + [STARFIVE_U_DEV_GEM_MGMT] = { 0x100a0000, 0x1000 }, + [STARFIVE_U_DEV_DMC] = { 0x100b0000, 0x10000 }, + [STARFIVE_U_DEV_FLASH0] = { 0x20000000, 0x10000000 }, + [STARFIVE_U_DEV_DRAM] = { 0x80000000, 0x0 }, +}; + +#define OTP_SERIAL 1 +#define GEM_REVISION 0x10070109 + +static void create_fdt(StarFiveUState *s, const MemMapEntry *memmap, + uint64_t mem_size, const char *cmdline, bool is_32_bit) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + void *fdt; + int cpu; + uint32_t *cells; + char *nodename; + char ethclk_names[] = "pclk\0hclk"; + uint32_t plic_phandle, prci_phandle, gpio_phandle, phandle = 1; + uint32_t hfclk_phandle, rtcclk_phandle, phy_phandle; + + if (ms->dtb) { + fdt = s->fdt = load_device_tree(ms->dtb, &s->fdt_size); + if (!fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + goto update_bootargs; + } else { + fdt = s->fdt = create_device_tree(&s->fdt_size); + if (!fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + } + + qemu_fdt_setprop_string(fdt, "/", "model", "SiFive HiFive Unleashed A00"); + qemu_fdt_setprop_string(fdt, "/", "compatible", + "sifive,hifive-unleashed-a00"); + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); + + qemu_fdt_add_subnode(fdt, "/soc"); + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); + + hfclk_phandle = phandle++; + nodename = g_strdup_printf("/hfclk"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", hfclk_phandle); + qemu_fdt_setprop_string(fdt, nodename, "clock-output-names", "hfclk"); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + STARFIVE_U_HFCLK_FREQ); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "fixed-clock"); + qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x0); + g_free(nodename); + + rtcclk_phandle = phandle++; + nodename = g_strdup_printf("/rtcclk"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", rtcclk_phandle); + qemu_fdt_setprop_string(fdt, nodename, "clock-output-names", "rtcclk"); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + STARFIVE_U_RTCCLK_FREQ); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "fixed-clock"); + qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x0); + g_free(nodename); + + nodename = g_strdup_printf("/memory@%lx", + (long)memmap[STARFIVE_U_DEV_DRAM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + memmap[STARFIVE_U_DEV_DRAM].base >> 32, memmap[STARFIVE_U_DEV_DRAM].base, + mem_size >> 32, mem_size); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + g_free(nodename); + + qemu_fdt_add_subnode(fdt, "/cpus"); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", + SIFIVE_CLINT_TIMEBASE_FREQ); + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); + + for (cpu = ms->smp.cpus - 1; cpu >= 0; cpu--) { + int cpu_phandle = phandle++; + nodename = g_strdup_printf("/cpus/cpu@%d", cpu); + char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + char *isa; + qemu_fdt_add_subnode(fdt, nodename); + /* cpu 0 is the management hart that does not have mmu */ + if (cpu != 0) { + if (is_32_bit) { + qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv32"); + } else { + qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); + } + isa = riscv_isa_string(&s->soc.u_cpus.harts[cpu - 1]); + } else { + isa = riscv_isa_string(&s->soc.u_cpus.harts[cpu]); + } + qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); + qemu_fdt_setprop_string(fdt, nodename, "status", "okay"); + qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu"); + qemu_fdt_add_subnode(fdt, intc); + qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle); + qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc"); + qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1); + g_free(isa); + g_free(intc); + g_free(nodename); + } + + cells = g_new0(uint32_t, ms->smp.cpus * 4); + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { + nodename = + g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); + cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT); + cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); + g_free(nodename); + } + nodename = g_strdup_printf("/soc/clint@%lx", + (long)memmap[STARFIVE_U_DEV_CLINT].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_CLINT].base, + 0x0, memmap[STARFIVE_U_DEV_CLINT].size); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, ms->smp.cpus * sizeof(uint32_t) * 4); + g_free(cells); + g_free(nodename); + + nodename = g_strdup_printf("/soc/otp@%lx", + (long)memmap[STARFIVE_U_DEV_OTP].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "fuse-count", SIFIVE_U_OTP_REG_SIZE); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_OTP].base, + 0x0, memmap[STARFIVE_U_DEV_OTP].size); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "sifive,fu540-c000-otp"); + g_free(nodename); + + prci_phandle = phandle++; + nodename = g_strdup_printf("/soc/clock-controller@%lx", + (long)memmap[STARFIVE_U_DEV_PRCI].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", prci_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x1); + qemu_fdt_setprop_cells(fdt, nodename, "clocks", + hfclk_phandle, rtcclk_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_PRCI].base, + 0x0, memmap[STARFIVE_U_DEV_PRCI].size); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "sifive,fu540-c000-prci"); + g_free(nodename); + + plic_phandle = phandle++; + cells = g_new0(uint32_t, ms->smp.cpus * 4 - 2); + for (cpu = 0; cpu < ms->smp.cpus; cpu++) { + nodename = + g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); + uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); + /* cpu 0 is the management hart that does not have S-mode */ + if (cpu == 0) { + cells[0] = cpu_to_be32(intc_phandle); + cells[1] = cpu_to_be32(IRQ_M_EXT); + } else { + cells[cpu * 4 - 2] = cpu_to_be32(intc_phandle); + cells[cpu * 4 - 1] = cpu_to_be32(IRQ_M_EXT); + cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle); + cells[cpu * 4 + 1] = cpu_to_be32(IRQ_S_EXT); + } + g_free(nodename); + } + nodename = g_strdup_printf("/soc/interrupt-controller@%lx", + (long)memmap[STARFIVE_U_DEV_PLIC].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0"); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop(fdt, nodename, "interrupts-extended", + cells, (ms->smp.cpus * 4 - 2) * sizeof(uint32_t)); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_PLIC].base, + 0x0, memmap[STARFIVE_U_DEV_PLIC].size); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 0x35); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle); + plic_phandle = qemu_fdt_get_phandle(fdt, nodename); + g_free(cells); + g_free(nodename); + + gpio_phandle = phandle++; + nodename = g_strdup_printf("/soc/gpio@%lx", + (long)memmap[STARFIVE_U_DEV_GPIO].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", gpio_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "clocks", + prci_phandle, PRCI_CLK_TLCLK); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 2); + qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(fdt, nodename, "#gpio-cells", 2); + qemu_fdt_setprop(fdt, nodename, "gpio-controller", NULL, 0); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_GPIO].base, + 0x0, memmap[STARFIVE_U_DEV_GPIO].size); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", STARFIVE_U_GPIO_IRQ0, + STARFIVE_U_GPIO_IRQ1, STARFIVE_U_GPIO_IRQ2, STARFIVE_U_GPIO_IRQ3, + STARFIVE_U_GPIO_IRQ4, STARFIVE_U_GPIO_IRQ5, STARFIVE_U_GPIO_IRQ6, + STARFIVE_U_GPIO_IRQ7, STARFIVE_U_GPIO_IRQ8, STARFIVE_U_GPIO_IRQ9, + STARFIVE_U_GPIO_IRQ10, STARFIVE_U_GPIO_IRQ11, STARFIVE_U_GPIO_IRQ12, + STARFIVE_U_GPIO_IRQ13, STARFIVE_U_GPIO_IRQ14, STARFIVE_U_GPIO_IRQ15); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,gpio0"); + g_free(nodename); + + nodename = g_strdup_printf("/gpio-restart"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "gpios", gpio_phandle, 10, 1); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "gpio-restart"); + g_free(nodename); + + nodename = g_strdup_printf("/soc/dma@%lx", + (long)memmap[STARFIVE_U_DEV_PDMA].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#dma-cells", 1); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", + STARFIVE_U_PDMA_IRQ0, STARFIVE_U_PDMA_IRQ1, STARFIVE_U_PDMA_IRQ2, + STARFIVE_U_PDMA_IRQ3, STARFIVE_U_PDMA_IRQ4, STARFIVE_U_PDMA_IRQ5, + STARFIVE_U_PDMA_IRQ6, STARFIVE_U_PDMA_IRQ7); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_PDMA].base, + 0x0, memmap[STARFIVE_U_DEV_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[STARFIVE_U_DEV_L2CC].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_L2CC].base, + 0x0, memmap[STARFIVE_U_DEV_L2CC].size); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", + STARFIVE_U_L2CC_IRQ0, STARFIVE_U_L2CC_IRQ1, STARFIVE_U_L2CC_IRQ2); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop(fdt, nodename, "cache-unified", NULL, 0); + qemu_fdt_setprop_cell(fdt, nodename, "cache-size", 2097152); + qemu_fdt_setprop_cell(fdt, nodename, "cache-sets", 1024); + qemu_fdt_setprop_cell(fdt, nodename, "cache-level", 2); + qemu_fdt_setprop_cell(fdt, nodename, "cache-block-size", 64); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "sifive,fu540-c000-ccache"); + g_free(nodename); + + nodename = g_strdup_printf("/soc/spi@%lx", + (long)memmap[STARFIVE_U_DEV_QSPI2].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#size-cells", 0); + qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", 1); + qemu_fdt_setprop_cells(fdt, nodename, "clocks", + prci_phandle, PRCI_CLK_TLCLK); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", STARFIVE_U_QSPI2_IRQ); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_QSPI2].base, + 0x0, memmap[STARFIVE_U_DEV_QSPI2].size); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,spi0"); + g_free(nodename); + + nodename = g_strdup_printf("/soc/spi@%lx/mmc@0", + (long)memmap[STARFIVE_U_DEV_QSPI2].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop(fdt, nodename, "disable-wp", NULL, 0); + qemu_fdt_setprop_cells(fdt, nodename, "voltage-ranges", 3300, 3300); + qemu_fdt_setprop_cell(fdt, nodename, "spi-max-frequency", 20000000); + qemu_fdt_setprop_cell(fdt, nodename, "reg", 0); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "mmc-spi-slot"); + g_free(nodename); + + nodename = g_strdup_printf("/soc/spi@%lx", + (long)memmap[STARFIVE_U_DEV_QSPI0].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "#size-cells", 0); + qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", 1); + qemu_fdt_setprop_cells(fdt, nodename, "clocks", + prci_phandle, PRCI_CLK_TLCLK); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", STARFIVE_U_QSPI0_IRQ); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_QSPI0].base, + 0x0, memmap[STARFIVE_U_DEV_QSPI0].size); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,spi0"); + g_free(nodename); + + nodename = g_strdup_printf("/soc/spi@%lx/flash@0", + (long)memmap[STARFIVE_U_DEV_QSPI0].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "spi-rx-bus-width", 4); + qemu_fdt_setprop_cell(fdt, nodename, "spi-tx-bus-width", 4); + qemu_fdt_setprop(fdt, nodename, "m25p,fast-read", NULL, 0); + qemu_fdt_setprop_cell(fdt, nodename, "spi-max-frequency", 50000000); + qemu_fdt_setprop_cell(fdt, nodename, "reg", 0); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "jedec,spi-nor"); + g_free(nodename); + + phy_phandle = phandle++; + nodename = g_strdup_printf("/soc/ethernet@%lx", + (long)memmap[STARFIVE_U_DEV_GEM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "sifive,fu540-c000-gem"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_GEM].base, + 0x0, memmap[STARFIVE_U_DEV_GEM].size, + 0x0, memmap[STARFIVE_U_DEV_GEM_MGMT].base, + 0x0, memmap[STARFIVE_U_DEV_GEM_MGMT].size); + qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); + qemu_fdt_setprop_string(fdt, nodename, "phy-mode", "gmii"); + qemu_fdt_setprop_cell(fdt, nodename, "phy-handle", phy_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", STARFIVE_U_GEM_IRQ); + qemu_fdt_setprop_cells(fdt, nodename, "clocks", + prci_phandle, PRCI_CLK_GEMGXLPLL, prci_phandle, PRCI_CLK_GEMGXLPLL); + qemu_fdt_setprop(fdt, nodename, "clock-names", ethclk_names, + sizeof(ethclk_names)); + qemu_fdt_setprop(fdt, nodename, "local-mac-address", + s->soc.gem.conf.macaddr.a, ETH_ALEN); + qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", 1); + qemu_fdt_setprop_cell(fdt, nodename, "#size-cells", 0); + + qemu_fdt_add_subnode(fdt, "/aliases"); + qemu_fdt_setprop_string(fdt, "/aliases", "ethernet0", nodename); + + g_free(nodename); + + nodename = g_strdup_printf("/soc/ethernet@%lx/ethernet-phy@0", + (long)memmap[STARFIVE_U_DEV_GEM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", phy_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "reg", 0x0); + g_free(nodename); + + nodename = g_strdup_printf("/soc/serial@%lx", + (long)memmap[STARFIVE_U_DEV_UART1].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_UART1].base, + 0x0, memmap[STARFIVE_U_DEV_UART1].size); + qemu_fdt_setprop_cells(fdt, nodename, "clocks", + prci_phandle, PRCI_CLK_TLCLK); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", STARFIVE_U_UART1_IRQ); + + qemu_fdt_setprop_string(fdt, "/aliases", "serial1", nodename); + g_free(nodename); + + nodename = g_strdup_printf("/soc/serial@%lx", + (long)memmap[STARFIVE_U_DEV_UART0].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[STARFIVE_U_DEV_UART0].base, + 0x0, memmap[STARFIVE_U_DEV_UART0].size); + qemu_fdt_setprop_cells(fdt, nodename, "clocks", + prci_phandle, PRCI_CLK_TLCLK); + qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cell(fdt, nodename, "interrupts", STARFIVE_U_UART0_IRQ); + + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); + qemu_fdt_setprop_string(fdt, "/aliases", "serial0", nodename); + + g_free(nodename); + +update_bootargs: + if (cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } +} + +static void starfive_u_machine_reset(void *opaque, int n, int level) +{ + /* gpio pin active low triggers reset */ + if (!level) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } +} + +static void starfive_u_machine_init(MachineState *machine) +{ + const MemMapEntry *memmap = starfive_u_memmap; + StarFiveUState *s = RISCV_U_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *main_mem = g_new(MemoryRegion, 1); + MemoryRegion *flash0 = g_new(MemoryRegion, 1); + target_ulong start_addr = memmap[STARFIVE_U_DEV_DRAM].base; + target_ulong firmware_end_addr, kernel_start_addr; + uint32_t start_addr_hi32 = 0x00000000; + int i; + uint32_t fdt_load_addr; + uint64_t kernel_entry; + DriveInfo *dinfo; + DeviceState *flash_dev, *sd_dev; + qemu_irq flash_cs, sd_cs; + + /* Initialize SoC */ + object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_RISCV_U_SOC); + object_property_set_uint(OBJECT(&s->soc), "serial", s->serial, + &error_abort); + object_property_set_str(OBJECT(&s->soc), "cpu-type", machine->cpu_type, + &error_abort); + qdev_realize(DEVICE(&s->soc), NULL, &error_abort); + + /* register RAM */ + memory_region_init_ram(main_mem, NULL, "riscv.starfive.u.ram", + machine->ram_size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[STARFIVE_U_DEV_DRAM].base, + main_mem); + + /* register QSPI0 Flash */ + memory_region_init_ram(flash0, NULL, "riscv.starfive.u.flash0", + memmap[STARFIVE_U_DEV_FLASH0].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[STARFIVE_U_DEV_FLASH0].base, + flash0); + + /* register gpio-restart */ + qdev_connect_gpio_out(DEVICE(&(s->soc.gpio)), 10, + qemu_allocate_irq(starfive_u_machine_reset, NULL, 0)); + + /* create device tree */ + create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline, + riscv_is_32bit(&s->soc.u_cpus)); + + if (s->start_in_flash) { + /* + * If start_in_flash property is given, assign s->msel to a value + * that representing booting from QSPI0 memory-mapped flash. + * + * This also means that when both start_in_flash and msel properties + * are given, start_in_flash takes the precedence over msel. + * + * Note this is to keep backward compatibility not to break existing + * users that use start_in_flash property. + */ + s->msel = MSEL_MEMMAP_QSPI0_FLASH; + } + + switch (s->msel) { + case MSEL_MEMMAP_QSPI0_FLASH: + start_addr = memmap[STARFIVE_U_DEV_FLASH0].base; + break; + case MSEL_L2LIM_QSPI0_FLASH: + case MSEL_L2LIM_QSPI2_SD: + start_addr = memmap[STARFIVE_U_DEV_L2LIM].base; + break; + default: + start_addr = memmap[STARFIVE_U_DEV_DRAM].base; + break; + } + + if (riscv_is_32bit(&s->soc.u_cpus)) { + firmware_end_addr = riscv_find_and_load_firmware(machine, + "opensbi-riscv32-generic-fw_dynamic.bin", + start_addr, NULL); + } else { + firmware_end_addr = riscv_find_and_load_firmware(machine, + "opensbi-riscv64-generic-fw_dynamic.bin", + start_addr, NULL); + } + + if (machine->kernel_filename) { + kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, + firmware_end_addr); + + kernel_entry = riscv_load_kernel(machine->kernel_filename, + kernel_start_addr, NULL); + + if (machine->initrd_filename) { + hwaddr start; + hwaddr end = riscv_load_initrd(machine->initrd_filename, + machine->ram_size, kernel_entry, + &start); + qemu_fdt_setprop_cell(s->fdt, "/chosen", + "linux,initrd-start", start); + qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end", + end); + } + } else { + /* + * If dynamic firmware is used, it doesn't know where is the next mode + * if kernel argument is not set. + */ + kernel_entry = 0; + } + + /* Compute the fdt load address in dram */ + fdt_load_addr = riscv_load_fdt(memmap[STARFIVE_U_DEV_DRAM].base, + machine->ram_size, s->fdt); + if (!riscv_is_32bit(&s->soc.u_cpus)) { + start_addr_hi32 = (uint64_t)start_addr >> 32; + } + + /* reset vector */ + uint32_t reset_vec[11] = { + s->msel, /* MSEL pin state */ + 0x00000297, /* 1: auipc t0, %pcrel_hi(fw_dyn) */ + 0x02828613, /* addi a2, t0, %pcrel_lo(1b) */ + 0xf1402573, /* csrr a0, mhartid */ + 0, + 0, + 0x00028067, /* jr t0 */ + start_addr, /* start: .dword */ + start_addr_hi32, + fdt_load_addr, /* fdt_laddr: .dword */ + 0x00000000, + /* fw_dyn: */ + }; + if (riscv_is_32bit(&s->soc.u_cpus)) { + reset_vec[4] = 0x0202a583; /* lw a1, 32(t0) */ + reset_vec[5] = 0x0182a283; /* lw t0, 24(t0) */ + } else { + reset_vec[4] = 0x0202b583; /* ld a1, 32(t0) */ + reset_vec[5] = 0x0182b283; /* ld t0, 24(t0) */ + } + + + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < ARRAY_SIZE(reset_vec); i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[STARFIVE_U_DEV_MROM].base, &address_space_memory); + + riscv_rom_copy_firmware_info(machine, memmap[STARFIVE_U_DEV_MROM].base, + memmap[STARFIVE_U_DEV_MROM].size, + sizeof(reset_vec), kernel_entry); + + /* Connect an SPI flash to SPI0 */ + flash_dev = qdev_new("is25wp256"); + dinfo = drive_get_next(IF_MTD); + if (dinfo) { + qdev_prop_set_drive_err(flash_dev, "drive", + blk_by_legacy_dinfo(dinfo), + &error_fatal); + } + qdev_realize_and_unref(flash_dev, BUS(s->soc.spi0.spi), &error_fatal); + + flash_cs = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi0), 1, flash_cs); + + /* Connect an SD card to SPI2 */ + sd_dev = ssi_create_peripheral(s->soc.spi2.spi, "ssi-sd"); + + sd_cs = qdev_get_gpio_in_named(sd_dev, SSI_GPIO_CS, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi2), 1, sd_cs); +} + +static bool starfive_u_machine_get_start_in_flash(Object *obj, Error **errp) +{ + StarFiveUState *s = RISCV_U_MACHINE(obj); + + return s->start_in_flash; +} + +static void starfive_u_machine_set_start_in_flash(Object *obj, bool value, Error **errp) +{ + StarFiveUState *s = RISCV_U_MACHINE(obj); + + s->start_in_flash = value; +} + +static void starfive_u_machine_get_uint32_prop(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + visit_type_uint32(v, name, (uint32_t *)opaque, errp); +} + +static void starfive_u_machine_set_uint32_prop(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + visit_type_uint32(v, name, (uint32_t *)opaque, errp); +} + +static void starfive_u_machine_instance_init(Object *obj) +{ + StarFiveUState *s = RISCV_U_MACHINE(obj); + + s->start_in_flash = false; + s->msel = 0; + object_property_add(obj, "msel", "uint32", + starfive_u_machine_get_uint32_prop, + starfive_u_machine_set_uint32_prop, NULL, &s->msel); + object_property_set_description(obj, "msel", + "Mode Select (MSEL[3:0]) pin state"); + + s->serial = OTP_SERIAL; + object_property_add(obj, "serial", "uint32", + starfive_u_machine_get_uint32_prop, + starfive_u_machine_set_uint32_prop, NULL, &s->serial); + object_property_set_description(obj, "serial", "Board serial number"); +} + +static void starfive_u_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "RISC-V Board compatible with SiFive U SDK"; + mc->init = starfive_u_machine_init; + mc->max_cpus = STARFIVE_U_MANAGEMENT_CPU_COUNT + STARFIVE_U_COMPUTE_CPU_COUNT; + mc->min_cpus = STARFIVE_U_MANAGEMENT_CPU_COUNT; + mc->default_cpu_type = SIFIVE_U_CPU; + mc->default_cpus = mc->min_cpus; + + object_class_property_add_bool(oc, "start-in-flash", + starfive_u_machine_get_start_in_flash, + starfive_u_machine_set_start_in_flash); + object_class_property_set_description(oc, "start-in-flash", + "Set on to tell QEMU's ROM to jump to " + "flash. Otherwise QEMU will jump to DRAM " + "or L2LIM depending on the msel value"); +} + +static const TypeInfo starfive_u_machine_typeinfo = { + .name = MACHINE_TYPE_NAME("starfive_u"), + .parent = TYPE_MACHINE, + .class_init = starfive_u_machine_class_init, + .instance_init = starfive_u_machine_instance_init, + .instance_size = sizeof(StarFiveUState), +}; + +static void starfive_u_machine_init_register_types(void) +{ + type_register_static(&starfive_u_machine_typeinfo); +} + +type_init(starfive_u_machine_init_register_types) + +static void starfive_u_soc_instance_init(Object *obj) +{ + StarFiveUSoCState *s = RISCV_U_SOC(obj); + object_initialize_child(obj, "u-cluster", &s->u_cluster, TYPE_CPU_CLUSTER); + qdev_prop_set_uint32(DEVICE(&s->u_cluster), "cluster-id", 0); + + object_initialize_child(OBJECT(&s->u_cluster), "u-cpus", &s->u_cpus, + TYPE_RISCV_HART_ARRAY); + + object_initialize_child(obj, "prci", &s->prci, TYPE_SIFIVE_U_PRCI); + object_initialize_child(obj, "otp", &s->otp, TYPE_SIFIVE_U_OTP); + object_initialize_child(obj, "gem", &s->gem, TYPE_CADENCE_GEM); + object_initialize_child(obj, "gpio", &s->gpio, TYPE_SIFIVE_GPIO); + object_initialize_child(obj, "pdma", &s->dma, TYPE_SIFIVE_PDMA); + object_initialize_child(obj, "spi0", &s->spi0, TYPE_SIFIVE_SPI); + object_initialize_child(obj, "spi2", &s->spi2, TYPE_SIFIVE_SPI); +} + +static void starfive_u_soc_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + StarFiveUSoCState *s = RISCV_U_SOC(dev); + const MemMapEntry *memmap = starfive_u_memmap; + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + MemoryRegion *l2lim_mem = g_new(MemoryRegion, 1); + char *plic_hart_config; + size_t plic_hart_config_len; + int i; + NICInfo *nd = &nd_table[0]; + + qdev_prop_set_uint32(DEVICE(&s->u_cpus), "num-harts", ms->smp.cpus); + qdev_prop_set_uint32(DEVICE(&s->u_cpus), "hartid-base", 0); + qdev_prop_set_string(DEVICE(&s->u_cpus), "cpu-type", s->cpu_type); + qdev_prop_set_uint64(DEVICE(&s->u_cpus), "resetvec", 0x1004); + + 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->u_cluster), NULL, &error_abort); + + /* boot rom */ + memory_region_init_rom(mask_rom, OBJECT(dev), "riscv.starfive.u.mrom", + memmap[STARFIVE_U_DEV_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[STARFIVE_U_DEV_MROM].base, + mask_rom); + + /* + * 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, "riscv.starfive.u.l2lim", + memmap[STARFIVE_U_DEV_L2LIM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[STARFIVE_U_DEV_L2LIM].base, + l2lim_mem); + + /* create PLIC hart topology configuration string */ + plic_hart_config_len = (strlen(STARFIVE_U_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, "," STARFIVE_U_PLIC_HART_CONFIG, + plic_hart_config_len); + } else { + strncat(plic_hart_config, "M", plic_hart_config_len); + } + plic_hart_config_len -= (strlen(STARFIVE_U_PLIC_HART_CONFIG) + 1); + } + + /* MMIO */ + s->plic = sifive_plic_create(memmap[STARFIVE_U_DEV_PLIC].base, + plic_hart_config, 0, + STARFIVE_U_PLIC_NUM_SOURCES, + STARFIVE_U_PLIC_NUM_PRIORITIES, + STARFIVE_U_PLIC_PRIORITY_BASE, + STARFIVE_U_PLIC_PENDING_BASE, + STARFIVE_U_PLIC_ENABLE_BASE, + STARFIVE_U_PLIC_ENABLE_STRIDE, + STARFIVE_U_PLIC_CONTEXT_BASE, + STARFIVE_U_PLIC_CONTEXT_STRIDE, + memmap[STARFIVE_U_DEV_PLIC].size); + g_free(plic_hart_config); + sifive_uart_create(system_memory, memmap[STARFIVE_U_DEV_UART0].base, + serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), STARFIVE_U_UART0_IRQ)); + sifive_uart_create(system_memory, memmap[STARFIVE_U_DEV_UART1].base, + serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), STARFIVE_U_UART1_IRQ)); + sifive_clint_create(memmap[STARFIVE_U_DEV_CLINT].base, + memmap[STARFIVE_U_DEV_CLINT].size, 0, ms->smp.cpus, + SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE, + SIFIVE_CLINT_TIMEBASE_FREQ, false); + sifive_test_create(memmap[STARFIVE_U_DEV_TEST].base); + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->prci), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->prci), 0, memmap[STARFIVE_U_DEV_PRCI].base); + + qdev_prop_set_uint32(DEVICE(&s->gpio), "ngpio", 16); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, memmap[STARFIVE_U_DEV_GPIO].base); + + /* Pass all GPIOs to the SOC layer so they are available to the board */ + qdev_pass_gpios(DEVICE(&s->gpio), dev, NULL); + + /* Connect GPIO interrupts to the PLIC */ + for (i = 0; i < 16; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), i, + qdev_get_gpio_in(DEVICE(s->plic), + STARFIVE_U_GPIO_IRQ0 + i)); + } + + /* PDMA */ + sysbus_realize(SYS_BUS_DEVICE(&s->dma), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dma), 0, memmap[STARFIVE_U_DEV_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), + STARFIVE_U_PDMA_IRQ0 + i)); + } + + qdev_prop_set_uint32(DEVICE(&s->otp), "serial", s->serial); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->otp), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->otp), 0, memmap[STARFIVE_U_DEV_OTP].base); + + /* FIXME use qdev NIC properties instead of nd_table[] */ + if (nd->used) { + qemu_check_nic_model(nd, TYPE_CADENCE_GEM); + qdev_set_nic_properties(DEVICE(&s->gem), nd); + } + object_property_set_int(OBJECT(&s->gem), "revision", GEM_REVISION, + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->gem), errp)) { + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[STARFIVE_U_DEV_GEM].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0, + qdev_get_gpio_in(DEVICE(s->plic), STARFIVE_U_GEM_IRQ)); + + create_unimplemented_device("riscv.starfive.u.gem-mgmt", + memmap[STARFIVE_U_DEV_GEM_MGMT].base, memmap[STARFIVE_U_DEV_GEM_MGMT].size); + + create_unimplemented_device("riscv.starfive.u.dmc", + memmap[STARFIVE_U_DEV_DMC].base, memmap[STARFIVE_U_DEV_DMC].size); + + create_unimplemented_device("riscv.starfive.u.l2cc", + memmap[STARFIVE_U_DEV_L2CC].base, memmap[STARFIVE_U_DEV_L2CC].size); + + sysbus_realize(SYS_BUS_DEVICE(&s->spi0), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi0), 0, + memmap[STARFIVE_U_DEV_QSPI0].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi0), 0, + qdev_get_gpio_in(DEVICE(s->plic), STARFIVE_U_QSPI0_IRQ)); + sysbus_realize(SYS_BUS_DEVICE(&s->spi2), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi2), 0, + memmap[STARFIVE_U_DEV_QSPI2].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi2), 0, + qdev_get_gpio_in(DEVICE(s->plic), STARFIVE_U_QSPI2_IRQ)); +} + +static Property starfive_u_soc_props[] = { + DEFINE_PROP_UINT32("serial", StarFiveUSoCState, serial, OTP_SERIAL), + DEFINE_PROP_STRING("cpu-type", StarFiveUSoCState, cpu_type), + DEFINE_PROP_END_OF_LIST() +}; + +static void starfive_u_soc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + device_class_set_props(dc, starfive_u_soc_props); + dc->realize = starfive_u_soc_realize; + /* Reason: Uses serial_hds in realize function, thus can't be used twice */ + dc->user_creatable = false; +} + +static const TypeInfo starfive_u_soc_type_info = { + .name = TYPE_RISCV_U_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(StarFiveUSoCState), + .instance_init = starfive_u_soc_instance_init, + .class_init = starfive_u_soc_class_init, +}; + +static void starfive_u_soc_register_types(void) +{ + type_register_static(&starfive_u_soc_type_info); +} + +type_init(starfive_u_soc_register_types) diff --git a/include/hw/riscv/starfive_u.h b/include/hw/riscv/starfive_u.h new file mode 100644 index 0000000000..3007480aa7 --- /dev/null +++ b/include/hw/riscv/starfive_u.h @@ -0,0 +1,156 @@ +/* + * SiFive U series machine 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_STARFIVE_U_H +#define HW_STARFIVE_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" +#include "hw/gpio/sifive_gpio.h" +#include "hw/misc/sifive_u_otp.h" +#include "hw/misc/sifive_u_prci.h" +#include "hw/ssi/sifive_spi.h" + +#define TYPE_RISCV_U_SOC "riscv.starfive.u.soc" +#define RISCV_U_SOC(obj) \ + OBJECT_CHECK(StarFiveUSoCState, (obj), TYPE_RISCV_U_SOC) + +typedef struct StarFiveUSoCState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + CPUClusterState u_cluster; + RISCVHartArrayState u_cpus; + DeviceState *plic; + SiFiveUPRCIState prci; + SIFIVEGPIOState gpio; + SiFiveUOTPState otp; + SiFivePDMAState dma; + SiFiveSPIState spi0; + SiFiveSPIState spi2; + CadenceGEMState gem; + + uint32_t serial; + char *cpu_type; +} StarFiveUSoCState; + +#define TYPE_RISCV_U_MACHINE MACHINE_TYPE_NAME("starfive_u") +#define RISCV_U_MACHINE(obj) \ + OBJECT_CHECK(StarFiveUState, (obj), TYPE_RISCV_U_MACHINE) + +typedef struct StarFiveUState { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + StarFiveUSoCState soc; + + void *fdt; + int fdt_size; + + bool start_in_flash; + uint32_t msel; + uint32_t serial; +} StarFiveUState; + +enum { + STARFIVE_U_DEV_DEBUG, + STARFIVE_U_DEV_MROM, + STARFIVE_U_DEV_TEST, + STARFIVE_U_DEV_CLINT, + STARFIVE_U_DEV_L2CC, + STARFIVE_U_DEV_PDMA, + STARFIVE_U_DEV_L2LIM, + STARFIVE_U_DEV_PLIC, + STARFIVE_U_DEV_PRCI, + STARFIVE_U_DEV_UART0, + STARFIVE_U_DEV_UART1, + STARFIVE_U_DEV_GPIO, + STARFIVE_U_DEV_QSPI0, + STARFIVE_U_DEV_QSPI2, + STARFIVE_U_DEV_OTP, + STARFIVE_U_DEV_DMC, + STARFIVE_U_DEV_FLASH0, + STARFIVE_U_DEV_DRAM, + STARFIVE_U_DEV_GEM, + STARFIVE_U_DEV_GEM_MGMT +}; + +enum { + STARFIVE_U_L2CC_IRQ0 = 1, + STARFIVE_U_L2CC_IRQ1 = 2, + STARFIVE_U_L2CC_IRQ2 = 3, + STARFIVE_U_UART0_IRQ = 4, + STARFIVE_U_UART1_IRQ = 5, + STARFIVE_U_QSPI2_IRQ = 6, + STARFIVE_U_GPIO_IRQ0 = 7, + STARFIVE_U_GPIO_IRQ1 = 8, + STARFIVE_U_GPIO_IRQ2 = 9, + STARFIVE_U_GPIO_IRQ3 = 10, + STARFIVE_U_GPIO_IRQ4 = 11, + STARFIVE_U_GPIO_IRQ5 = 12, + STARFIVE_U_GPIO_IRQ6 = 13, + STARFIVE_U_GPIO_IRQ7 = 14, + STARFIVE_U_GPIO_IRQ8 = 15, + STARFIVE_U_GPIO_IRQ9 = 16, + STARFIVE_U_GPIO_IRQ10 = 17, + STARFIVE_U_GPIO_IRQ11 = 18, + STARFIVE_U_GPIO_IRQ12 = 19, + STARFIVE_U_GPIO_IRQ13 = 20, + STARFIVE_U_GPIO_IRQ14 = 21, + STARFIVE_U_GPIO_IRQ15 = 22, + STARFIVE_U_PDMA_IRQ0 = 23, + STARFIVE_U_PDMA_IRQ1 = 24, + STARFIVE_U_PDMA_IRQ2 = 25, + STARFIVE_U_PDMA_IRQ3 = 26, + STARFIVE_U_PDMA_IRQ4 = 27, + STARFIVE_U_PDMA_IRQ5 = 28, + STARFIVE_U_PDMA_IRQ6 = 29, + STARFIVE_U_PDMA_IRQ7 = 30, + STARFIVE_U_QSPI0_IRQ = 51, + STARFIVE_U_GEM_IRQ = 53 +}; + +enum { + STARFIVE_U_HFCLK_FREQ = 33333333, + STARFIVE_U_RTCCLK_FREQ = 1000000 +}; + +enum { + MSEL_MEMMAP_QSPI0_FLASH = 1, + MSEL_L2LIM_QSPI0_FLASH = 6, + MSEL_L2LIM_QSPI2_SD = 11 +}; + +#define STARFIVE_U_MANAGEMENT_CPU_COUNT 1 +#define STARFIVE_U_COMPUTE_CPU_COUNT 4 + +#define STARFIVE_U_PLIC_HART_CONFIG "MS" +#define STARFIVE_U_PLIC_NUM_SOURCES 54 +#define STARFIVE_U_PLIC_NUM_PRIORITIES 7 +#define STARFIVE_U_PLIC_PRIORITY_BASE 0x04 +#define STARFIVE_U_PLIC_PENDING_BASE 0x1000 +#define STARFIVE_U_PLIC_ENABLE_BASE 0x2000 +#define STARFIVE_U_PLIC_ENABLE_STRIDE 0x80 +#define STARFIVE_U_PLIC_CONTEXT_BASE 0x200000 +#define STARFIVE_U_PLIC_CONTEXT_STRIDE 0x1000 + +#endif -- 2.25.1