summaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/block/m25p80.c57
-rw-r--r--hw/misc/sifive_u_otp.c13
-rw-r--r--hw/riscv/Kconfig3
-rw-r--r--hw/riscv/microchip_pfsoc.c9
-rw-r--r--hw/riscv/opentitan.c9
-rw-r--r--hw/riscv/sifive_e.c9
-rw-r--r--hw/riscv/sifive_u.c102
-rw-r--r--hw/riscv/spike.c9
-rw-r--r--hw/riscv/virt.c68
-rw-r--r--hw/rtc/goldfish_rtc.c2
-rw-r--r--hw/ssi/Kconfig4
-rw-r--r--hw/ssi/meson.build1
-rw-r--r--hw/ssi/sifive_spi.c358
13 files changed, 589 insertions, 55 deletions
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 0412d3e7f4..5f9471d83c 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -210,6 +210,19 @@ static const FlashPartInfo known_devices[] = {
{ INFO("640s33b", 0x898913, 0, 64 << 10, 128, 0) },
{ INFO("n25q064", 0x20ba17, 0, 64 << 10, 128, 0) },
+ /* ISSI */
+ { INFO("is25lq040b", 0x9d4013, 0, 64 << 10, 8, ER_4K) },
+ { INFO("is25lp080d", 0x9d6014, 0, 64 << 10, 16, ER_4K) },
+ { INFO("is25lp016d", 0x9d6015, 0, 64 << 10, 32, ER_4K) },
+ { INFO("is25lp032", 0x9d6016, 0, 64 << 10, 64, ER_4K) },
+ { INFO("is25lp064", 0x9d6017, 0, 64 << 10, 128, ER_4K) },
+ { INFO("is25lp128", 0x9d6018, 0, 64 << 10, 256, ER_4K) },
+ { INFO("is25lp256", 0x9d6019, 0, 64 << 10, 512, ER_4K) },
+ { INFO("is25wp032", 0x9d7016, 0, 64 << 10, 64, ER_4K) },
+ { INFO("is25wp064", 0x9d7017, 0, 64 << 10, 128, ER_4K) },
+ { INFO("is25wp128", 0x9d7018, 0, 64 << 10, 256, ER_4K) },
+ { INFO("is25wp256", 0x9d7019, 0, 64 << 10, 512, ER_4K) },
+
/* Macronix */
{ INFO("mx25l2005a", 0xc22012, 0, 64 << 10, 4, ER_4K) },
{ INFO("mx25l4005a", 0xc22013, 0, 64 << 10, 8, ER_4K) },
@@ -412,6 +425,7 @@ typedef enum {
MAN_NUMONYX,
MAN_WINBOND,
MAN_SST,
+ MAN_ISSI,
MAN_GENERIC,
} Manufacturer;
@@ -487,6 +501,8 @@ static inline Manufacturer get_man(Flash *s)
return MAN_MACRONIX;
case 0xBF:
return MAN_SST;
+ case 0x9D:
+ return MAN_ISSI;
default:
return MAN_GENERIC;
}
@@ -706,6 +722,9 @@ static void complete_collecting_data(Flash *s)
case MAN_SPANSION:
s->quad_enable = !!(s->data[1] & 0x02);
break;
+ case MAN_ISSI:
+ s->quad_enable = extract32(s->data[0], 6, 1);
+ break;
case MAN_MACRONIX:
s->quad_enable = extract32(s->data[0], 6, 1);
if (s->len > 1) {
@@ -895,6 +914,19 @@ static void decode_fast_read_cmd(Flash *s)
SPANSION_DUMMY_CLK_LEN
);
break;
+ case MAN_ISSI:
+ /*
+ * The Fast Read instruction code is followed by address bytes and
+ * dummy cycles, transmitted via the SI line.
+ *
+ * The number of dummy cycles is configurable but this is currently
+ * unmodeled, hence the default value 8 is used.
+ *
+ * QPI (Quad Peripheral Interface) mode has different default value
+ * of dummy cycles, but this is unsupported at the time being.
+ */
+ s->needed_bytes += 1;
+ break;
default:
break;
}
@@ -934,6 +966,16 @@ static void decode_dio_read_cmd(Flash *s)
break;
}
break;
+ case MAN_ISSI:
+ /*
+ * The Fast Read Dual I/O instruction code is followed by address bytes
+ * and dummy cycles, transmitted via the IO1 and IO0 line.
+ *
+ * The number of dummy cycles is configurable but this is currently
+ * unmodeled, hence the default value 4 is used.
+ */
+ s->needed_bytes += 1;
+ break;
default:
break;
}
@@ -974,6 +1016,19 @@ static void decode_qio_read_cmd(Flash *s)
break;
}
break;
+ case MAN_ISSI:
+ /*
+ * The Fast Read Quad I/O instruction code is followed by address bytes
+ * and dummy cycles, transmitted via the IO3, IO2, IO1 and IO0 line.
+ *
+ * The number of dummy cycles is configurable but this is currently
+ * unmodeled, hence the default value 6 is used.
+ *
+ * QPI (Quad Peripheral Interface) mode has different default value
+ * of dummy cycles, but this is unsupported at the time being.
+ */
+ s->needed_bytes += 3;
+ break;
default:
break;
}
@@ -1132,7 +1187,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
case RDSR:
s->data[0] = (!!s->write_enable) << 1;
- if (get_man(s) == MAN_MACRONIX) {
+ if (get_man(s) == MAN_MACRONIX || get_man(s) == MAN_ISSI) {
s->data[0] |= (!!s->quad_enable) << 6;
}
if (get_man(s) == MAN_SST) {
diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c
index b8e8b9eebe..18aa0bd55d 100644
--- a/hw/misc/sifive_u_otp.c
+++ b/hw/misc/sifive_u_otp.c
@@ -23,6 +23,7 @@
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/sysbus.h"
+#include "qemu/error-report.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "hw/misc/sifive_u_otp.h"
@@ -65,8 +66,7 @@ static uint64_t sifive_u_otp_read(void *opaque, hwaddr addr, unsigned int size)
if (blk_pread(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD, &buf,
SIFIVE_U_OTP_FUSE_WORD) < 0) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "read error index<%d>\n", s->pa);
+ error_report("read error index<%d>", s->pa);
return 0xff;
}
@@ -169,8 +169,7 @@ static void sifive_u_otp_write(void *opaque, hwaddr addr,
if (blk_pwrite(s->blk, s->pa * SIFIVE_U_OTP_FUSE_WORD,
&s->fuse[s->pa], SIFIVE_U_OTP_FUSE_WORD,
0) < 0) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "write error index<%d>\n", s->pa);
+ error_report("write error index<%d>", s->pa);
}
}
@@ -260,15 +259,13 @@ static void sifive_u_otp_reset(DeviceState *dev)
serial_data = s->serial;
if (blk_pwrite(s->blk, index * SIFIVE_U_OTP_FUSE_WORD,
&serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "write error index<%d>\n", index);
+ error_report("write error index<%d>", index);
}
serial_data = ~(s->serial);
if (blk_pwrite(s->blk, (index + 1) * SIFIVE_U_OTP_FUSE_WORD,
&serial_data, SIFIVE_U_OTP_FUSE_WORD, 0) < 0) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "write error index<%d>\n", index + 1);
+ error_report("write error index<%d>", index + 1);
}
}
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index facb0cbacc..d139074b02 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -52,9 +52,12 @@ config SIFIVE_U
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
config SPIKE
diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c
index e952b49e8c..266f1c3342 100644
--- a/hw/riscv/microchip_pfsoc.c
+++ b/hw/riscv/microchip_pfsoc.c
@@ -86,10 +86,7 @@
* - Register Map/PF_SoC_RegMap_V1_1/MPFS250T/mpfs250t_ioscb_memmap_dri.htm
* describes the complete IOSCB modules memory maps
*/
-static const struct MemmapEntry {
- hwaddr base;
- hwaddr size;
-} microchip_pfsoc_memmap[] = {
+static const MemMapEntry microchip_pfsoc_memmap[] = {
[MICROCHIP_PFSOC_RSVD0] = { 0x0, 0x100 },
[MICROCHIP_PFSOC_DEBUG] = { 0x100, 0xf00 },
[MICROCHIP_PFSOC_E51_DTIM] = { 0x1000000, 0x2000 },
@@ -182,7 +179,7 @@ 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;
+ const MemMapEntry *memmap = microchip_pfsoc_memmap;
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *rsvd0_mem = g_new(MemoryRegion, 1);
MemoryRegion *e51_dtim_mem = g_new(MemoryRegion, 1);
@@ -451,7 +448,7 @@ 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;
+ const MemMapEntry *memmap = microchip_pfsoc_memmap;
MicrochipIcicleKitState *s = MICROCHIP_ICICLE_KIT_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *mem_low = g_new(MemoryRegion, 1);
diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c
index af3456932f..e168bffe69 100644
--- a/hw/riscv/opentitan.c
+++ b/hw/riscv/opentitan.c
@@ -28,10 +28,7 @@
#include "qemu/units.h"
#include "sysemu/sysemu.h"
-static const struct MemmapEntry {
- hwaddr base;
- hwaddr size;
-} ibex_memmap[] = {
+static const MemMapEntry ibex_memmap[] = {
[IBEX_DEV_ROM] = { 0x00008000, 16 * KiB },
[IBEX_DEV_RAM] = { 0x10000000, 0x10000 },
[IBEX_DEV_FLASH] = { 0x20000000, 0x80000 },
@@ -66,7 +63,7 @@ static const struct MemmapEntry {
static void opentitan_board_init(MachineState *machine)
{
- const struct MemmapEntry *memmap = ibex_memmap;
+ const MemMapEntry *memmap = ibex_memmap;
OpenTitanState *s = g_new0(OpenTitanState, 1);
MemoryRegion *sys_mem = get_system_memory();
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
@@ -114,7 +111,7 @@ static void lowrisc_ibex_soc_init(Object *obj)
static void lowrisc_ibex_soc_realize(DeviceState *dev_soc, Error **errp)
{
- const struct MemmapEntry *memmap = ibex_memmap;
+ const MemMapEntry *memmap = ibex_memmap;
MachineState *ms = MACHINE(qdev_get_machine());
LowRISCIbexSoCState *s = RISCV_IBEX_SOC(dev_soc);
MemoryRegion *sys_mem = get_system_memory();
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
index 59bac4cc9a..f939bcf9ea 100644
--- a/hw/riscv/sifive_e.c
+++ b/hw/riscv/sifive_e.c
@@ -50,10 +50,7 @@
#include "sysemu/sysemu.h"
#include "exec/address-spaces.h"
-static const struct MemmapEntry {
- hwaddr base;
- hwaddr size;
-} sifive_e_memmap[] = {
+static MemMapEntry sifive_e_memmap[] = {
[SIFIVE_E_DEV_DEBUG] = { 0x0, 0x1000 },
[SIFIVE_E_DEV_MROM] = { 0x1000, 0x2000 },
[SIFIVE_E_DEV_OTP] = { 0x20000, 0x2000 },
@@ -77,7 +74,7 @@ static const struct MemmapEntry {
static void sifive_e_machine_init(MachineState *machine)
{
- const struct MemmapEntry *memmap = sifive_e_memmap;
+ const MemMapEntry *memmap = sifive_e_memmap;
SiFiveEState *s = RISCV_E_MACHINE(machine);
MemoryRegion *sys_mem = get_system_memory();
@@ -187,7 +184,7 @@ static void sifive_e_soc_init(Object *obj)
static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
- const struct MemmapEntry *memmap = sifive_e_memmap;
+ const MemMapEntry *memmap = sifive_e_memmap;
SiFiveESoCState *s = RISCV_E_SOC(dev);
MemoryRegion *sys_mem = get_system_memory();
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 59b61cea01..7b59942369 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -15,6 +15,8 @@
* 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.
@@ -44,6 +46,7 @@
#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/sifive_u.h"
@@ -60,10 +63,7 @@
#include <libfdt.h>
-static const struct MemmapEntry {
- hwaddr base;
- hwaddr size;
-} sifive_u_memmap[] = {
+static const MemMapEntry sifive_u_memmap[] = {
[SIFIVE_U_DEV_DEBUG] = { 0x0, 0x100 },
[SIFIVE_U_DEV_MROM] = { 0x1000, 0xf000 },
[SIFIVE_U_DEV_CLINT] = { 0x2000000, 0x10000 },
@@ -74,6 +74,8 @@ static const struct MemmapEntry {
[SIFIVE_U_DEV_PRCI] = { 0x10000000, 0x1000 },
[SIFIVE_U_DEV_UART0] = { 0x10010000, 0x1000 },
[SIFIVE_U_DEV_UART1] = { 0x10011000, 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 },
@@ -86,7 +88,7 @@ static const struct MemmapEntry {
#define OTP_SERIAL 1
#define GEM_REVISION 0x10070109
-static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
+static void create_fdt(SiFiveUState *s, const MemMapEntry *memmap,
uint64_t mem_size, const char *cmdline, bool is_32_bit)
{
MachineState *ms = MACHINE(qdev_get_machine());
@@ -342,6 +344,57 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
"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);
@@ -428,7 +481,7 @@ static void sifive_u_machine_reset(void *opaque, int n, int level)
static void sifive_u_machine_init(MachineState *machine)
{
- const struct MemmapEntry *memmap = sifive_u_memmap;
+ const MemMapEntry *memmap = sifive_u_memmap;
SiFiveUState *s = RISCV_U_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
@@ -439,6 +492,9 @@ static void sifive_u_machine_init(MachineState *machine)
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);
@@ -571,6 +627,25 @@ static void sifive_u_machine_init(MachineState *machine)
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_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 sifive_u_machine_get_start_in_flash(Object *obj, Error **errp)
@@ -680,13 +755,15 @@ static void sifive_u_soc_instance_init(Object *obj)
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 sifive_u_soc_realize(DeviceState *dev, Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
SiFiveUSoCState *s = RISCV_U_SOC(dev);
- const struct MemmapEntry *memmap = sifive_u_memmap;
+ 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);
@@ -827,6 +904,17 @@ static void sifive_u_soc_realize(DeviceState *dev, Error **errp)
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[] = {
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
index 56986ecfe0..ed4ca9808e 100644
--- a/hw/riscv/spike.c
+++ b/hw/riscv/spike.c
@@ -43,16 +43,13 @@
#include "sysemu/qtest.h"
#include "sysemu/sysemu.h"
-static const struct MemmapEntry {
- hwaddr base;
- hwaddr size;
-} spike_memmap[] = {
+static const MemMapEntry spike_memmap[] = {
[SPIKE_MROM] = { 0x1000, 0xf000 },
[SPIKE_CLINT] = { 0x2000000, 0x10000 },
[SPIKE_DRAM] = { 0x80000000, 0x0 },
};
-static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
+static void create_fdt(SpikeState *s, const MemMapEntry *memmap,
uint64_t mem_size, const char *cmdline, bool is_32_bit)
{
void *fdt;
@@ -179,7 +176,7 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
static void spike_board_init(MachineState *machine)
{
- const struct MemmapEntry *memmap = spike_memmap;
+ const MemMapEntry *memmap = spike_memmap;
SpikeState *s = SPIKE_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 2299b3a6be..4f0c2fbca0 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -43,10 +43,7 @@
#include "hw/pci/pci.h"
#include "hw/pci-host/gpex.h"
-static const struct MemmapEntry {
- hwaddr base;
- hwaddr size;
-} virt_memmap[] = {
+static const MemMapEntry virt_memmap[] = {
[VIRT_DEBUG] = { 0x0, 0x100 },
[VIRT_MROM] = { 0x1000, 0xf000 },
[VIRT_TEST] = { 0x100000, 0x1000 },
@@ -62,6 +59,15 @@ static const struct MemmapEntry {
[VIRT_DRAM] = { 0x80000000, 0x0 },
};
+/* PCIe high mmio is fixed for RV32 */
+#define VIRT32_HIGH_PCIE_MMIO_BASE 0x300000000ULL
+#define VIRT32_HIGH_PCIE_MMIO_SIZE (4 * GiB)
+
+/* PCIe high mmio for RV64, size is fixed but base depends on top of RAM */
+#define VIRT64_HIGH_PCIE_MMIO_SIZE (16 * GiB)
+
+static MemMapEntry virt_high_pcie_memmap;
+
#define VIRT_FLASH_SECTOR_SIZE (256 * KiB)
static PFlashCFI01 *virt_flash_create1(RISCVVirtState *s,
@@ -170,7 +176,7 @@ static void create_pcie_irq_map(void *fdt, char *nodename,
0x1800, 0, 0, 0x7);
}
-static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
+static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap,
uint64_t mem_size, const char *cmdline, bool is_32_bit)
{
void *fdt;
@@ -374,7 +380,11 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
2, memmap[VIRT_PCIE_PIO].base, 2, memmap[VIRT_PCIE_PIO].size,
1, FDT_PCI_RANGE_MMIO,
2, memmap[VIRT_PCIE_MMIO].base,
- 2, memmap[VIRT_PCIE_MMIO].base, 2, memmap[VIRT_PCIE_MMIO].size);
+ 2, memmap[VIRT_PCIE_MMIO].base, 2, memmap[VIRT_PCIE_MMIO].size,
+ 1, FDT_PCI_RANGE_MMIO_64BIT,
+ 2, virt_high_pcie_memmap.base,
+ 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size);
+
create_pcie_irq_map(fdt, name, plic_pcie_phandle);
g_free(name);
@@ -451,12 +461,14 @@ update_bootargs:
static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
hwaddr ecam_base, hwaddr ecam_size,
hwaddr mmio_base, hwaddr mmio_size,
+ hwaddr high_mmio_base,
+ hwaddr high_mmio_size,
hwaddr pio_base,
- DeviceState *plic, bool link_up)
+ DeviceState *plic)
{
DeviceState *dev;
MemoryRegion *ecam_alias, *ecam_reg;
- MemoryRegion *mmio_alias, *mmio_reg;
+ MemoryRegion *mmio_alias, *high_mmio_alias, *mmio_reg;
qemu_irq irq;
int i;
@@ -476,6 +488,13 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
mmio_reg, mmio_base, mmio_size);
memory_region_add_subregion(get_system_memory(), mmio_base, mmio_alias);
+ /* Map high MMIO space */
+ high_mmio_alias = g_new0(MemoryRegion, 1);
+ memory_region_init_alias(high_mmio_alias, OBJECT(dev), "pcie-mmio-high",
+ mmio_reg, high_mmio_base, high_mmio_size);
+ memory_region_add_subregion(get_system_memory(), high_mmio_base,
+ high_mmio_alias);
+
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base);
for (i = 0; i < GPEX_NUM_IRQS; i++) {
@@ -490,7 +509,7 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem,
static void virt_machine_init(MachineState *machine)
{
- const struct MemmapEntry *memmap = virt_memmap;
+ const MemMapEntry *memmap = virt_memmap;
RISCVVirtState *s = RISCV_VIRT_MACHINE(machine);
MemoryRegion *system_memory = get_system_memory();
MemoryRegion *main_mem = g_new(MemoryRegion, 1);
@@ -593,6 +612,23 @@ static void virt_machine_init(MachineState *machine)
}
}
+ if (riscv_is_32bit(&s->soc[0])) {
+#if HOST_LONG_BITS == 64
+ /* limit RAM size in a 32-bit system */
+ if (machine->ram_size > 10 * GiB) {
+ machine->ram_size = 10 * GiB;
+ error_report("Limiting RAM size to 10 GiB");
+ }
+#endif
+ virt_high_pcie_memmap.base = VIRT32_HIGH_PCIE_MMIO_BASE;
+ virt_high_pcie_memmap.size = VIRT32_HIGH_PCIE_MMIO_SIZE;
+ } else {
+ virt_high_pcie_memmap.size = VIRT64_HIGH_PCIE_MMIO_SIZE;
+ virt_high_pcie_memmap.base = memmap[VIRT_DRAM].base + machine->ram_size;
+ virt_high_pcie_memmap.base =
+ ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size);
+ }
+
/* register system main memory (actual RAM) */
memory_region_init_ram(main_mem, NULL, "riscv_virt_board.ram",
machine->ram_size, &error_fatal);
@@ -672,12 +708,14 @@ static void virt_machine_init(MachineState *machine)
}
gpex_pcie_init(system_memory,
- memmap[VIRT_PCIE_ECAM].base,
- memmap[VIRT_PCIE_ECAM].size,
- memmap[VIRT_PCIE_MMIO].base,
- memmap[VIRT_PCIE_MMIO].size,
- memmap[VIRT_PCIE_PIO].base,
- DEVICE(pcie_plic), true);
+ memmap[VIRT_PCIE_ECAM].base,
+ memmap[VIRT_PCIE_ECAM].size,
+ memmap[VIRT_PCIE_MMIO].base,
+ memmap[VIRT_PCIE_MMIO].size,
+ virt_high_pcie_memmap.base,
+ virt_high_pcie_memmap.size,
+ memmap[VIRT_PCIE_PIO].base,
+ DEVICE(pcie_plic));
serial_mm_init(system_memory, memmap[VIRT_UART0].base,
0, qdev_get_gpio_in(DEVICE(mmio_plic), UART0_IRQ), 399193,
diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c
index 0f4e8185a7..e07ff0164e 100644
--- a/hw/rtc/goldfish_rtc.c
+++ b/hw/rtc/goldfish_rtc.c
@@ -211,6 +211,8 @@ static int goldfish_rtc_post_load(void *opaque, int version_id)
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
s->tick_offset = s->tick_offset_vmstate - delta;
+ goldfish_rtc_set_alarm(s);
+
return 0;
}
diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig
index 9e54a0c8dd..7d90a02181 100644
--- a/hw/ssi/Kconfig
+++ b/hw/ssi/Kconfig
@@ -2,6 +2,10 @@ config PL022
bool
select SSI
+config SIFIVE_SPI
+ bool
+ select SSI
+
config SSI
bool
diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
index dee00c0da6..3d6bc82ab1 100644
--- a/hw/ssi/meson.build
+++ b/hw/ssi/meson.build
@@ -2,6 +2,7 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c'))
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c'))
softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c'))
softmmu_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c'))
+softmmu_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c'))
softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c'))
softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c'))
softmmu_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c'))
diff --git a/hw/ssi/sifive_spi.c b/hw/ssi/sifive_spi.c
new file mode 100644
index 0000000000..0c9ebca3c8
--- /dev/null
+++ b/hw/ssi/sifive_spi.c
@@ -0,0 +1,358 @@
+/*
+ * QEMU model of the SiFive SPI Controller
+ *
+ * Copyright (c) 2021 Wind River Systems, Inc.
+ *
+ * Author:
+ * Bin Meng <bin.meng@windriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "hw/ssi/ssi.h"
+#include "sysemu/sysemu.h"
+#include "qemu/fifo8.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/ssi/sifive_spi.h"
+
+#define R_SCKDIV (0x00 / 4)
+#define R_SCKMODE (0x04 / 4)
+#define R_CSID (0x10 / 4)
+#define R_CSDEF (0x14 / 4)
+#define R_CSMODE (0x18 / 4)
+#define R_DELAY0 (0x28 / 4)
+#define R_DELAY1 (0x2C / 4)
+#define R_FMT (0x40 / 4)
+#define R_TXDATA (0x48 / 4)
+#define R_RXDATA (0x4C / 4)
+#define R_TXMARK (0x50 / 4)
+#define R_RXMARK (0x54 / 4)
+#define R_FCTRL (0x60 / 4)
+#define R_FFMT (0x64 / 4)
+#define R_IE (0x70 / 4)
+#define R_IP (0x74 / 4)
+
+#define FMT_DIR (1 << 3)
+
+#define TXDATA_FULL (1 << 31)
+#define RXDATA_EMPTY (1 << 31)
+
+#define IE_TXWM (1 << 0)
+#define IE_RXWM (1 << 1)
+
+#define IP_TXWM (1 << 0)
+#define IP_RXWM (1 << 1)
+
+#define FIFO_CAPACITY 8
+
+static void sifive_spi_txfifo_reset(SiFiveSPIState *s)
+{
+ fifo8_reset(&s->tx_fifo);
+
+ s->regs[R_TXDATA] &= ~TXDATA_FULL;
+ s->regs[R_IP] &= ~IP_TXWM;
+}
+
+static void sifive_spi_rxfifo_reset(SiFiveSPIState *s)
+{
+ fifo8_reset(&s->rx_fifo);
+
+ s->regs[R_RXDATA] |= RXDATA_EMPTY;
+ s->regs[R_IP] &= ~IP_RXWM;
+}
+
+static void sifive_spi_update_cs(SiFiveSPIState *s)
+{
+ int i;
+
+ for (i = 0; i < s->num_cs; i++) {
+ if (s->regs[R_CSDEF] & (1 << i)) {
+ qemu_set_irq(s->cs_lines[i], !(s->regs[R_CSMODE]));
+ }
+ }
+}
+
+static void sifive_spi_update_irq(SiFiveSPIState *s)
+{
+ int level;
+
+ if (fifo8_num_used(&s->tx_fifo) < s->regs[R_TXMARK]) {
+ s->regs[R_IP] |= IP_TXWM;
+ } else {
+ s->regs[R_IP] &= ~IP_TXWM;
+ }
+
+ if (fifo8_num_used(&s->rx_fifo) > s->regs[R_RXMARK]) {
+ s->regs[R_IP] |= IP_RXWM;
+ } else {
+ s->regs[R_IP] &= ~IP_RXWM;
+ }
+
+ level = s->regs[R_IP] & s->regs[R_IE] ? 1 : 0;
+ qemu_set_irq(s->irq, level);
+}
+
+static void sifive_spi_reset(DeviceState *d)
+{
+ SiFiveSPIState *s = SIFIVE_SPI(d);
+
+ memset(s->regs, 0, sizeof(s->regs));
+
+ /* The reset value is high for all implemented CS pins */
+ s->regs[R_CSDEF] = (1 << s->num_cs) - 1;
+
+ /* Populate register with their default value */
+ s->regs[R_SCKDIV] = 0x03;
+ s->regs[R_DELAY0] = 0x1001;
+ s->regs[R_DELAY1] = 0x01;
+
+ sifive_spi_txfifo_reset(s);
+ sifive_spi_rxfifo_reset(s);
+
+ sifive_spi_update_cs(s);
+ sifive_spi_update_irq(s);
+}
+
+static void sifive_spi_flush_txfifo(SiFiveSPIState *s)
+{
+ uint8_t tx;
+ uint8_t rx;
+
+ while (!fifo8_is_empty(&s->tx_fifo)) {
+ tx = fifo8_pop(&s->tx_fifo);
+ rx = ssi_transfer(s->spi, tx);
+
+ if (!fifo8_is_full(&s->rx_fifo)) {
+ if (!(s->regs[R_FMT] & FMT_DIR)) {
+ fifo8_push(&s->rx_fifo, rx);
+ }
+ }
+ }
+}
+
+static bool sifive_spi_is_bad_reg(hwaddr addr, bool allow_reserved)
+{
+ bool bad;
+
+ switch (addr) {
+ /* reserved offsets */
+ case 0x08:
+ case 0x0C:
+ case 0x1C:
+ case 0x20:
+ case 0x24:
+ case 0x30:
+ case 0x34:
+ case 0x38:
+ case 0x3C:
+ case 0x44:
+ case 0x58:
+ case 0x5C:
+ case 0x68:
+ case 0x6C:
+ bad = allow_reserved ? false : true;
+ break;
+ default:
+ bad = false;
+ }
+
+ if (addr >= (SIFIVE_SPI_REG_NUM << 2)) {
+ bad = true;
+ }
+
+ return bad;
+}
+
+static uint64_t sifive_spi_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ SiFiveSPIState *s = opaque;
+ uint32_t r;
+
+ if (sifive_spi_is_bad_reg(addr, true)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read at address 0x%"
+ HWADDR_PRIx "\n", __func__, addr);
+ return 0;
+ }
+
+ addr >>= 2;
+ switch (addr) {
+ case R_TXDATA:
+ if (fifo8_is_full(&s->tx_fifo)) {
+ return TXDATA_FULL;
+ }
+ r = 0;
+ break;
+
+ case R_RXDATA:
+ if (fifo8_is_empty(&s->rx_fifo)) {
+ return RXDATA_EMPTY;
+ }
+ r = fifo8_pop(&s->rx_fifo);
+ break;
+
+ default:
+ r = s->regs[addr];
+ break;
+ }
+
+ sifive_spi_update_irq(s);
+
+ return r;
+}
+
+static void sifive_spi_write(void *opaque, hwaddr addr,
+ uint64_t val64, unsigned int size)
+{
+ SiFiveSPIState *s = opaque;
+ uint32_t value = val64;
+
+ if (sifive_spi_is_bad_reg(addr, false)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write at addr=0x%"
+ HWADDR_PRIx " value=0x%x\n", __func__, addr, value);
+ return;
+ }
+
+ addr >>= 2;
+ switch (addr) {
+ case R_CSID:
+ if (value >= s->num_cs) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csid %d\n",
+ __func__, value);
+ } else {
+ s->regs[R_CSID] = value;
+ sifive_spi_update_cs(s);
+ }
+ break;
+
+ case R_CSDEF:
+ if (value >= (1 << s->num_cs)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csdef %x\n",
+ __func__, value);
+ } else {
+ s->regs[R_CSDEF] = value;
+ }
+ break;
+
+ case R_CSMODE:
+ if (value > 3) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid csmode %x\n",
+ __func__, value);
+ } else {
+ s->regs[R_CSMODE] = value;
+ sifive_spi_update_cs(s);
+ }
+ break;
+
+ case R_TXDATA:
+ if (!fifo8_is_full(&s->tx_fifo)) {
+ fifo8_push(&s->tx_fifo, (uint8_t)value);
+ sifive_spi_flush_txfifo(s);
+ }
+ break;
+
+ case R_RXDATA:
+ case R_IP:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid write to read-only reigster 0x%"
+ HWADDR_PRIx " with 0x%x\n", __func__, addr << 2, value);
+ break;
+
+ case R_TXMARK:
+ case R_RXMARK:
+ if (value >= FIFO_CAPACITY) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid watermark %d\n",
+ __func__, value);
+ } else {
+ s->regs[addr] = value;
+ }
+ break;
+
+ case R_FCTRL:
+ case R_FFMT:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: direct-map flash interface unimplemented\n",
+ __func__);
+ break;
+
+ default:
+ s->regs[addr] = value;
+ break;
+ }
+
+ sifive_spi_update_irq(s);
+}
+
+static const MemoryRegionOps sifive_spi_ops = {
+ .read = sifive_spi_read,
+ .write = sifive_spi_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void sifive_spi_realize(DeviceState *dev, Error **errp)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ SiFiveSPIState *s = SIFIVE_SPI(dev);
+ int i;
+
+ s->spi = ssi_create_bus(dev, "spi");
+ sysbus_init_irq(sbd, &s->irq);
+
+ s->cs_lines = g_new0(qemu_irq, s->num_cs);
+ for (i = 0; i < s->num_cs; i++) {
+ sysbus_init_irq(sbd, &s->cs_lines[i]);
+ }
+
+ memory_region_init_io(&s->mmio, OBJECT(s), &sifive_spi_ops, s,
+ TYPE_SIFIVE_SPI, 0x1000);
+ sysbus_init_mmio(sbd, &s->mmio);
+
+ fifo8_create(&s->tx_fifo, FIFO_CAPACITY);
+ fifo8_create(&s->rx_fifo, FIFO_CAPACITY);
+}
+
+static Property sifive_spi_properties[] = {
+ DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sifive_spi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ device_class_set_props(dc, sifive_spi_properties);
+ dc->reset = sifive_spi_reset;
+ dc->realize = sifive_spi_realize;
+}
+
+static const TypeInfo sifive_spi_info = {
+ .name = TYPE_SIFIVE_SPI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SiFiveSPIState),
+ .class_init = sifive_spi_class_init,
+};
+
+static void sifive_spi_register_types(void)
+{
+ type_register_static(&sifive_spi_info);
+}
+
+type_init(sifive_spi_register_types)