qemu/hw/riscv/sifive_u.c
Conor Dooley 1c8e491c45 target/riscv: support new isa extension detection devicetree properties
A few months ago I submitted a patch to various lists, deprecating
"riscv,isa" with a lengthy commit message [0] that is now commit
aeb71e42caae ("dt-bindings: riscv: deprecate riscv,isa") in the Linux
kernel tree. Primarily, the goal was to replace "riscv,isa" with a new
set of properties that allowed for strictly defining the meaning of
various extensions, where "riscv,isa" was tied to whatever definitions
inflicted upon us by the ISA manual, which have seen some variance over
time.

Two new properties were introduced: "riscv,isa-base" and
"riscv,isa-extensions". The former is a simple string to communicate the
base ISA implemented by a hart and the latter an array of strings used
to communicate the set of ISA extensions supported, per the definitions
of each substring in extensions.yaml [1]. A beneficial side effect was
also the ability to define vendor extensions in a more "official" way,
as the ISA manual and other RVI specifications only covered the format
for vendor extensions in the ISA string, but not the meaning of vendor
extensions, for obvious reasons.

Add support for setting these two new properties in the devicetrees for
the various devicetree platforms supported by QEMU for RISC-V. The Linux
kernel already supports parsing ISA extensions from these new
properties, and documenting them in the dt-binding is a requirement for
new extension detection being added to the kernel.

A side effect of the implementation is that the meaning for elements in
"riscv,isa" and in "riscv,isa-extensions" are now tied together as they
are constructed from the same source. The same applies to the ISA string
provided in ACPI tables, but there does not appear to be any strict
definitions of meanings in ACPI land either.

Link: https://lore.kernel.org/qemu-riscv/20230702-eats-scorebook-c951f170d29f@spud/ [0]
Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/riscv/extensions.yaml [1]
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Message-ID: <20240124-unvarying-foothold-9dde2aaf95d4@spud>
[ Changes by AF:
 - Rebase on recent changes
]
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
2024-02-09 20:43:14 +10:00

969 lines
40 KiB
C

/*
* 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 <bmeng.cn@gmail.com>
*
* 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
* 10) PWM0 and PWM1
*
* This board currently generates devicetree dynamically that indicates at least
* two harts and up to five harts.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "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/sd/sd.h"
#include "hw/ssi/ssi.h"
#include "target/riscv/cpu.h"
#include "hw/riscv/riscv_hart.h"
#include "hw/riscv/sifive_u.h"
#include "hw/riscv/boot.h"
#include "hw/char/sifive_uart.h"
#include "hw/intc/riscv_aclint.h"
#include "hw/intc/sifive_plic.h"
#include "chardev/char.h"
#include "net/eth.h"
#include "sysemu/device_tree.h"
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
#include <libfdt.h>
/* CLINT timebase frequency */
#define CLINT_TIMEBASE_FREQ 1000000
static const MemMapEntry sifive_u_memmap[] = {
[SIFIVE_U_DEV_DEBUG] = { 0x0, 0x100 },
[SIFIVE_U_DEV_MROM] = { 0x1000, 0xf000 },
[SIFIVE_U_DEV_CLINT] = { 0x2000000, 0x10000 },
[SIFIVE_U_DEV_L2CC] = { 0x2010000, 0x1000 },
[SIFIVE_U_DEV_PDMA] = { 0x3000000, 0x100000 },
[SIFIVE_U_DEV_L2LIM] = { 0x8000000, 0x2000000 },
[SIFIVE_U_DEV_PLIC] = { 0xc000000, 0x4000000 },
[SIFIVE_U_DEV_PRCI] = { 0x10000000, 0x1000 },
[SIFIVE_U_DEV_UART0] = { 0x10010000, 0x1000 },
[SIFIVE_U_DEV_UART1] = { 0x10011000, 0x1000 },
[SIFIVE_U_DEV_PWM0] = { 0x10020000, 0x1000 },
[SIFIVE_U_DEV_PWM1] = { 0x10021000, 0x1000 },
[SIFIVE_U_DEV_QSPI0] = { 0x10040000, 0x1000 },
[SIFIVE_U_DEV_QSPI2] = { 0x10050000, 0x1000 },
[SIFIVE_U_DEV_GPIO] = { 0x10060000, 0x1000 },
[SIFIVE_U_DEV_OTP] = { 0x10070000, 0x1000 },
[SIFIVE_U_DEV_GEM] = { 0x10090000, 0x2000 },
[SIFIVE_U_DEV_GEM_MGMT] = { 0x100a0000, 0x1000 },
[SIFIVE_U_DEV_DMC] = { 0x100b0000, 0x10000 },
[SIFIVE_U_DEV_FLASH0] = { 0x20000000, 0x10000000 },
[SIFIVE_U_DEV_DRAM] = { 0x80000000, 0x0 },
};
#define OTP_SERIAL 1
#define GEM_REVISION 0x10070109
static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
bool is_32_bit)
{
MachineState *ms = MACHINE(s);
uint64_t mem_size = ms->ram_size;
void *fdt;
int cpu;
uint32_t *cells;
char *nodename;
uint32_t plic_phandle, prci_phandle, gpio_phandle, phandle = 1;
uint32_t hfclk_phandle, rtcclk_phandle, phy_phandle;
static const char * const ethclk_names[2] = { "pclk", "hclk" };
static const char * const clint_compat[2] = {
"sifive,clint0", "riscv,clint0"
};
static const char * const plic_compat[2] = {
"sifive,plic-1.0.0", "riscv,plic0"
};
fdt = ms->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",
SIFIVE_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",
SIFIVE_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[SIFIVE_U_DEV_DRAM].base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_cells(fdt, nodename, "reg",
memmap[SIFIVE_U_DEV_DRAM].base >> 32, memmap[SIFIVE_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",
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);
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");
}
riscv_isa_write_fdt(&s->soc.u_cpus.harts[cpu - 1], fdt, nodename);
} else {
riscv_isa_write_fdt(&s->soc.e_cpus.harts[0], fdt, nodename);
}
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(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[SIFIVE_U_DEV_CLINT].base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_string_array(fdt, nodename, "compatible",
(char **)&clint_compat, ARRAY_SIZE(clint_compat));
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_DEV_CLINT].base,
0x0, memmap[SIFIVE_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[SIFIVE_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[SIFIVE_U_DEV_OTP].base,
0x0, memmap[SIFIVE_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[SIFIVE_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[SIFIVE_U_DEV_PRCI].base,
0x0, memmap[SIFIVE_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[SIFIVE_U_DEV_PLIC].base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
qemu_fdt_setprop_string_array(fdt, nodename, "compatible",
(char **)&plic_compat, ARRAY_SIZE(plic_compat));
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[SIFIVE_U_DEV_PLIC].base,
0x0, memmap[SIFIVE_U_DEV_PLIC].size);
qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev",
SIFIVE_U_PLIC_NUM_SOURCES - 1);
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[SIFIVE_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[SIFIVE_U_DEV_GPIO].base,
0x0, memmap[SIFIVE_U_DEV_GPIO].size);
qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_GPIO_IRQ0,
SIFIVE_U_GPIO_IRQ1, SIFIVE_U_GPIO_IRQ2, SIFIVE_U_GPIO_IRQ3,
SIFIVE_U_GPIO_IRQ4, SIFIVE_U_GPIO_IRQ5, SIFIVE_U_GPIO_IRQ6,
SIFIVE_U_GPIO_IRQ7, SIFIVE_U_GPIO_IRQ8, SIFIVE_U_GPIO_IRQ9,
SIFIVE_U_GPIO_IRQ10, SIFIVE_U_GPIO_IRQ11, SIFIVE_U_GPIO_IRQ12,
SIFIVE_U_GPIO_IRQ13, SIFIVE_U_GPIO_IRQ14, SIFIVE_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[SIFIVE_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",
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_DEV_PDMA].base,
0x0, memmap[SIFIVE_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[SIFIVE_U_DEV_L2CC].base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_DEV_L2CC].base,
0x0, memmap[SIFIVE_U_DEV_L2CC].size);
qemu_fdt_setprop_cells(fdt, nodename, "interrupts",
SIFIVE_U_L2CC_IRQ0, SIFIVE_U_L2CC_IRQ1, SIFIVE_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[SIFIVE_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", SIFIVE_U_QSPI2_IRQ);
qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_DEV_QSPI2].base,
0x0, memmap[SIFIVE_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[SIFIVE_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[SIFIVE_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", SIFIVE_U_QSPI0_IRQ);
qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_DEV_QSPI0].base,
0x0, memmap[SIFIVE_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[SIFIVE_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[SIFIVE_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[SIFIVE_U_DEV_GEM].base,
0x0, memmap[SIFIVE_U_DEV_GEM].size,
0x0, memmap[SIFIVE_U_DEV_GEM_MGMT].base,
0x0, memmap[SIFIVE_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", SIFIVE_U_GEM_IRQ);
qemu_fdt_setprop_cells(fdt, nodename, "clocks",
prci_phandle, PRCI_CLK_GEMGXLPLL, prci_phandle, PRCI_CLK_GEMGXLPLL);
qemu_fdt_setprop_string_array(fdt, nodename, "clock-names",
(char **)&ethclk_names, ARRAY_SIZE(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[SIFIVE_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/pwm@%lx",
(long)memmap[SIFIVE_U_DEV_PWM0].base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,pwm0");
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_DEV_PWM0].base,
0x0, memmap[SIFIVE_U_DEV_PWM0].size);
qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
qemu_fdt_setprop_cells(fdt, nodename, "interrupts",
SIFIVE_U_PWM0_IRQ0, SIFIVE_U_PWM0_IRQ1,
SIFIVE_U_PWM0_IRQ2, SIFIVE_U_PWM0_IRQ3);
qemu_fdt_setprop_cells(fdt, nodename, "clocks",
prci_phandle, PRCI_CLK_TLCLK);
qemu_fdt_setprop_cell(fdt, nodename, "#pwm-cells", 0);
g_free(nodename);
nodename = g_strdup_printf("/soc/pwm@%lx",
(long)memmap[SIFIVE_U_DEV_PWM1].base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,pwm0");
qemu_fdt_setprop_cells(fdt, nodename, "reg",
0x0, memmap[SIFIVE_U_DEV_PWM1].base,
0x0, memmap[SIFIVE_U_DEV_PWM1].size);
qemu_fdt_setprop_cell(fdt, nodename, "interrupt-parent", plic_phandle);
qemu_fdt_setprop_cells(fdt, nodename, "interrupts",
SIFIVE_U_PWM1_IRQ0, SIFIVE_U_PWM1_IRQ1,
SIFIVE_U_PWM1_IRQ2, SIFIVE_U_PWM1_IRQ3);
qemu_fdt_setprop_cells(fdt, nodename, "clocks",
prci_phandle, PRCI_CLK_TLCLK);
qemu_fdt_setprop_cell(fdt, nodename, "#pwm-cells", 0);
g_free(nodename);
nodename = g_strdup_printf("/soc/serial@%lx",
(long)memmap[SIFIVE_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[SIFIVE_U_DEV_UART1].base,
0x0, memmap[SIFIVE_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", SIFIVE_U_UART1_IRQ);
qemu_fdt_setprop_string(fdt, "/aliases", "serial1", nodename);
g_free(nodename);
nodename = g_strdup_printf("/soc/serial@%lx",
(long)memmap[SIFIVE_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[SIFIVE_U_DEV_UART0].base,
0x0, memmap[SIFIVE_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", SIFIVE_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);
}
static void sifive_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 sifive_u_machine_init(MachineState *machine)
{
const MemMapEntry *memmap = sifive_u_memmap;
SiFiveUState *s = RISCV_U_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *flash0 = g_new(MemoryRegion, 1);
target_ulong start_addr = memmap[SIFIVE_U_DEV_DRAM].base;
target_ulong firmware_end_addr, kernel_start_addr;
const char *firmware_name;
uint32_t start_addr_hi32 = 0x00000000;
int i;
uint32_t fdt_load_addr;
uint64_t kernel_entry;
DriveInfo *dinfo;
BlockBackend *blk;
DeviceState *flash_dev, *sd_dev, *card_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_fatal);
/* register RAM */
memory_region_add_subregion(system_memory, memmap[SIFIVE_U_DEV_DRAM].base,
machine->ram);
/* register QSPI0 Flash */
memory_region_init_ram(flash0, NULL, "riscv.sifive.u.flash0",
memmap[SIFIVE_U_DEV_FLASH0].size, &error_fatal);
memory_region_add_subregion(system_memory, memmap[SIFIVE_U_DEV_FLASH0].base,
flash0);
/* register gpio-restart */
qdev_connect_gpio_out(DEVICE(&(s->soc.gpio)), 10,
qemu_allocate_irq(sifive_u_machine_reset, NULL, 0));
/* load/create device tree */
if (machine->dtb) {
machine->fdt = load_device_tree(machine->dtb, &s->fdt_size);
if (!machine->fdt) {
error_report("load_device_tree() failed");
exit(1);
}
} else {
create_fdt(s, memmap, 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[SIFIVE_U_DEV_FLASH0].base;
break;
case MSEL_L2LIM_QSPI0_FLASH:
case MSEL_L2LIM_QSPI2_SD:
start_addr = memmap[SIFIVE_U_DEV_L2LIM].base;
break;
default:
start_addr = memmap[SIFIVE_U_DEV_DRAM].base;
break;
}
firmware_name = riscv_default_firmware_name(&s->soc.u_cpus);
firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name,
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, &s->soc.u_cpus,
kernel_start_addr, true, NULL);
} else {
/*
* If dynamic firmware is used, it doesn't know where is the next mode
* if kernel argument is not set.
*/
kernel_entry = 0;
}
fdt_load_addr = riscv_compute_fdt_addr(memmap[SIFIVE_U_DEV_DRAM].base,
memmap[SIFIVE_U_DEV_DRAM].size,
machine);
riscv_load_fdt(fdt_load_addr, machine->fdt);
if (!riscv_is_32bit(&s->soc.u_cpus)) {
start_addr_hi32 = (uint64_t)start_addr >> 32;
}
/* reset vector */
uint32_t reset_vec[12] = {
s->msel, /* MSEL pin state */
0x00000297, /* 1: auipc t0, %pcrel_hi(fw_dyn) */
0x02c28613, /* 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,
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[SIFIVE_U_DEV_MROM].base, &address_space_memory);
riscv_rom_copy_firmware_info(machine, memmap[SIFIVE_U_DEV_MROM].base,
memmap[SIFIVE_U_DEV_MROM].size,
sizeof(reset_vec), kernel_entry);
/* Connect an SPI flash to SPI0 */
flash_dev = qdev_new("is25wp256");
dinfo = drive_get(IF_MTD, 0, 0);
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);
dinfo = drive_get(IF_SD, 0, 0);
blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
card_dev = qdev_new(TYPE_SD_CARD_SPI);
qdev_prop_set_drive_err(card_dev, "drive", blk, &error_fatal);
qdev_realize_and_unref(card_dev,
qdev_get_child_bus(sd_dev, "sd-bus"),
&error_fatal);
}
static bool sifive_u_machine_get_start_in_flash(Object *obj, Error **errp)
{
SiFiveUState *s = RISCV_U_MACHINE(obj);
return s->start_in_flash;
}
static void sifive_u_machine_set_start_in_flash(Object *obj, bool value, Error **errp)
{
SiFiveUState *s = RISCV_U_MACHINE(obj);
s->start_in_flash = value;
}
static void sifive_u_machine_instance_init(Object *obj)
{
SiFiveUState *s = RISCV_U_MACHINE(obj);
s->start_in_flash = false;
s->msel = 0;
object_property_add_uint32_ptr(obj, "msel", &s->msel,
OBJ_PROP_FLAG_READWRITE);
object_property_set_description(obj, "msel",
"Mode Select (MSEL[3:0]) pin state");
s->serial = OTP_SERIAL;
object_property_add_uint32_ptr(obj, "serial", &s->serial,
OBJ_PROP_FLAG_READWRITE);
object_property_set_description(obj, "serial", "Board serial number");
}
static void sifive_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 = sifive_u_machine_init;
mc->max_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + SIFIVE_U_COMPUTE_CPU_COUNT;
mc->min_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + 1;
mc->default_cpu_type = SIFIVE_U_CPU;
mc->default_cpus = mc->min_cpus;
mc->default_ram_id = "riscv.sifive.u.ram";
object_class_property_add_bool(oc, "start-in-flash",
sifive_u_machine_get_start_in_flash,
sifive_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 sifive_u_machine_typeinfo = {
.name = MACHINE_TYPE_NAME("sifive_u"),
.parent = TYPE_MACHINE,
.class_init = sifive_u_machine_class_init,
.instance_init = sifive_u_machine_instance_init,
.instance_size = sizeof(SiFiveUState),
};
static void sifive_u_machine_init_register_types(void)
{
type_register_static(&sifive_u_machine_typeinfo);
}
type_init(sifive_u_machine_init_register_types)
static void sifive_u_soc_instance_init(Object *obj)
{
SiFiveUSoCState *s = RISCV_U_SOC(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", SIFIVE_E_CPU);
qdev_prop_set_uint64(DEVICE(&s->e_cpus), "resetvec", 0x1004);
object_initialize_child(obj, "u-cluster", &s->u_cluster, TYPE_CPU_CLUSTER);
qdev_prop_set_uint32(DEVICE(&s->u_cluster), "cluster-id", 1);
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);
object_initialize_child(obj, "pwm0", &s->pwm[0], TYPE_SIFIVE_PWM);
object_initialize_child(obj, "pwm1", &s->pwm[1], TYPE_SIFIVE_PWM);
}
static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
SiFiveUSoCState *s = RISCV_U_SOC(dev);
const MemMapEntry *memmap = sifive_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;
int i, j;
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", s->cpu_type);
qdev_prop_set_uint64(DEVICE(&s->u_cpus), "resetvec", 0x1004);
sysbus_realize(SYS_BUS_DEVICE(&s->e_cpus), &error_fatal);
sysbus_realize(SYS_BUS_DEVICE(&s->u_cpus), &error_fatal);
/*
* 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);
/* boot rom */
memory_region_init_rom(mask_rom, OBJECT(dev), "riscv.sifive.u.mrom",
memmap[SIFIVE_U_DEV_MROM].size, &error_fatal);
memory_region_add_subregion(system_memory, memmap[SIFIVE_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.sifive.u.l2lim",
memmap[SIFIVE_U_DEV_L2LIM].size, &error_fatal);
memory_region_add_subregion(system_memory, memmap[SIFIVE_U_DEV_L2LIM].base,
l2lim_mem);
/* create PLIC hart topology configuration string */
plic_hart_config = riscv_plic_hart_config_string(ms->smp.cpus);
/* MMIO */
s->plic = sifive_plic_create(memmap[SIFIVE_U_DEV_PLIC].base,
plic_hart_config, ms->smp.cpus, 0,
SIFIVE_U_PLIC_NUM_SOURCES,
SIFIVE_U_PLIC_NUM_PRIORITIES,
SIFIVE_U_PLIC_PRIORITY_BASE,
SIFIVE_U_PLIC_PENDING_BASE,
SIFIVE_U_PLIC_ENABLE_BASE,
SIFIVE_U_PLIC_ENABLE_STRIDE,
SIFIVE_U_PLIC_CONTEXT_BASE,
SIFIVE_U_PLIC_CONTEXT_STRIDE,
memmap[SIFIVE_U_DEV_PLIC].size);
g_free(plic_hart_config);
sifive_uart_create(system_memory, memmap[SIFIVE_U_DEV_UART0].base,
serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ));
sifive_uart_create(system_memory, memmap[SIFIVE_U_DEV_UART1].base,
serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART1_IRQ));
riscv_aclint_swi_create(memmap[SIFIVE_U_DEV_CLINT].base, 0,
ms->smp.cpus, false);
riscv_aclint_mtimer_create(memmap[SIFIVE_U_DEV_CLINT].base +
RISCV_ACLINT_SWI_SIZE,
RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
CLINT_TIMEBASE_FREQ, false);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->prci), errp)) {
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->prci), 0, memmap[SIFIVE_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[SIFIVE_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),
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_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),
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;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->otp), 0, memmap[SIFIVE_U_DEV_OTP].base);
qemu_configure_nic_device(DEVICE(&s->gem), true, NULL);
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[SIFIVE_U_DEV_GEM].base);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0,
qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_GEM_IRQ));
/* PWM */
for (i = 0; i < 2; i++) {
if (!sysbus_realize(SYS_BUS_DEVICE(&s->pwm[i]), errp)) {
return;
}
sysbus_mmio_map(SYS_BUS_DEVICE(&s->pwm[i]), 0,
memmap[SIFIVE_U_DEV_PWM0].base + (0x1000 * i));
/* Connect PWM interrupts to the PLIC */
for (j = 0; j < SIFIVE_PWM_IRQS; j++) {
sysbus_connect_irq(SYS_BUS_DEVICE(&s->pwm[i]), j,
qdev_get_gpio_in(DEVICE(s->plic),
SIFIVE_U_PWM0_IRQ0 + (i * 4) + j));
}
}
create_unimplemented_device("riscv.sifive.u.gem-mgmt",
memmap[SIFIVE_U_DEV_GEM_MGMT].base, memmap[SIFIVE_U_DEV_GEM_MGMT].size);
create_unimplemented_device("riscv.sifive.u.dmc",
memmap[SIFIVE_U_DEV_DMC].base, memmap[SIFIVE_U_DEV_DMC].size);
create_unimplemented_device("riscv.sifive.u.l2cc",
memmap[SIFIVE_U_DEV_L2CC].base, memmap[SIFIVE_U_DEV_L2CC].size);
sysbus_realize(SYS_BUS_DEVICE(&s->spi0), errp);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi0), 0,
memmap[SIFIVE_U_DEV_QSPI0].base);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi0), 0,
qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_QSPI0_IRQ));
sysbus_realize(SYS_BUS_DEVICE(&s->spi2), errp);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi2), 0,
memmap[SIFIVE_U_DEV_QSPI2].base);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi2), 0,
qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_QSPI2_IRQ));
}
static Property sifive_u_soc_props[] = {
DEFINE_PROP_UINT32("serial", SiFiveUSoCState, serial, OTP_SERIAL),
DEFINE_PROP_STRING("cpu-type", SiFiveUSoCState, cpu_type),
DEFINE_PROP_END_OF_LIST()
};
static void sifive_u_soc_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
device_class_set_props(dc, sifive_u_soc_props);
dc->realize = sifive_u_soc_realize;
/* Reason: Uses serial_hds in realize function, thus can't be used twice */
dc->user_creatable = false;
}
static const TypeInfo sifive_u_soc_type_info = {
.name = TYPE_RISCV_U_SOC,
.parent = TYPE_DEVICE,
.instance_size = sizeof(SiFiveUSoCState),
.instance_init = sifive_u_soc_instance_init,
.class_init = sifive_u_soc_class_init,
};
static void sifive_u_soc_register_types(void)
{
type_register_static(&sifive_u_soc_type_info);
}
type_init(sifive_u_soc_register_types)