Skip to content

Commit

Permalink
[ot] hw/riscv: ot_earlgrey: rework flash bus management.
Browse files Browse the repository at this point in the history
- enables multiple SPI data flash configuration
- fixes bus assignment (collisions between SPI buses and internal flash)
- flash device type should now be defined as board properties
- only explicitly defined flash devices are instantiated, as the OT FPGA
  HW enables swapping/selecting flash types

Signed-off-by: Emmanuel Blot <[email protected]>
  • Loading branch information
rivos-eblot committed Jan 28, 2025
1 parent 8a9047d commit 6a84cf0
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 45 deletions.
28 changes: 16 additions & 12 deletions docs/opentitan/earlgrey.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ See the section "Useful execution options" for documentation about the `no_epmp_
qemu-system-riscv32 -M ot-earlgrey -display none -serial mon:stdio \
-object ot-rom_img,id=rom,file=rom_with_fake_keys_fpga_cw310.elf \
-drive if=pflash,file=otp-rma.raw,format=raw \
-drive if=mtd,bus=1,file=flash.raw,format=raw
-drive if=mtd,bus=2,file=flash.raw,format=raw
````

where `otp-rma.raw` contains the RMA OTP image and `flash.raw` contains the signed binary file of the
Expand Down Expand Up @@ -145,14 +145,14 @@ See [`tools.md`](tools.md)
* `-display none` can be used to prevent QEMU to open a semi-graphical windows as the default
console, and use the current shell instead.

### Flash
### Embedded Flash

* `-drive if=mtd,bus=1,file=<filename>,format=raw` should be used to specify a path to a QEMU RAW
image file used as the OpenTitan internal flash controller image. This _RAW_ file should have
been generated with the [`flashgen.py`](flashgen.md) tool.
* `-drive if=mtd,id=eflash,bus=2,file=<filename>,format=raw` should be used to specify a path to a
QEMU RAW image file used as the OpenTitan internal flash controller image. This _RAW_ file should
have been generated with the [`flashgen.py`](flashgen.md) tool.

Note: for now, bus 1 is assigned to the internal controller with the embedded flash storage. See
also SPI Host section.
Note: MTD bus 2 is assigned to the internal controller with the embedded flash storage. See also
the SPI Host section.

### OTBN

Expand All @@ -168,17 +168,21 @@ See [`tools.md`](tools.md)

### SPI Host

* `-global ot-earlgrey-board.spiflash<bus>=<flash_type>` should be used to instanciate a SPI
dataflash device of the specified type to the first device (/CS0) of the specified bus.
Any SPI dataflash device supported by QEMU can be used. To list the supported devices, use
`grep -F 'INFO("' hw/block/m25p80.c | cut -d'"' -f2`

* `-drive if=mtd,bus=0,file=<filename>,format=raw` should be used to specify a path to a QEMU RAW
image file used as the ISSP IS25WP128 SPI data flash backend file. This _RAW_ file should have
been created with the qemu-img tool. There is no dedicated tool to populate this image file for
now.
image file used as the SPI data flash backend file. This _RAW_ file should have been created with
the qemu-img tool. There is no dedicated tool to populate this image file for now.

````sh
qemu-img create -f raw spi.raw 16M
````

For now, bus 0 is assigned to the SPI Host controller with an external flash storage. See also
Flash controller section.
MTD bus 0 is assigned to the SPI0 Host controller and MTD bus 1 is assigned to the SPI1 Host
controller. See also Embedded Flash controller section.

### UART

Expand Down
140 changes: 110 additions & 30 deletions hw/riscv/ot_earlgrey.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qmp/qlist.h"
#include "qom/object.h"
#include "exec/address-spaces.h"
#include "hw/block/flash.h"
#include "hw/boards.h"
#include "hw/intc/sifive_plic.h"
#include "hw/jtag/tap_ctrl.h"
Expand Down Expand Up @@ -155,6 +157,26 @@ enum OtEgResetRequest {
OT_EG_RESET_COUNT
};

/* Data flash buses */
enum OtEgMtdBus {
OT_EG_MTD_SPI0,
OT_EG_MTD_SPI1,
OT_EG_MTD_SPI_COUNT,
OT_EG_MTD_EFLASH = OT_EG_MTD_SPI_COUNT,
};

/* "Parallel" flash buses */
enum OtEgPflashBus {
OT_EG_PFLASH_OTP,
};

enum OtEGBoardDevice {
OT_EG_BOARD_DEV_SOC,
OT_EG_BOARD_DEV_FLASH0,
OT_EG_BOARD_DEV_FLASH1,
OT_EG_BOARD_DEV_COUNT,
};

/* EarlGrey/CW310 Peripheral clock is 6 MHz */
#define OT_EG_PERIPHERAL_CLK_HZ 6000000u

Expand Down Expand Up @@ -669,6 +691,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = {
OT_EG_SOC_GPIO_ALERT(0, 19)
),
.prop = IBEXDEVICEPROPDEFS(
IBEX_DEV_STRING_PROP("ot_id", "spi0"),
IBEX_DEV_UINT_PROP("bus-num", 0)
),
},
Expand All @@ -683,6 +706,7 @@ static const IbexDeviceDef ot_eg_soc_devices[] = {
OT_EG_SOC_GPIO_ALERT(0, 20)
),
.prop = IBEXDEVICEPROPDEFS(
IBEX_DEV_STRING_PROP("ot_id", "spi1"),
IBEX_DEV_UINT_PROP("bus-num", 1)
),
},
Expand Down Expand Up @@ -1122,12 +1146,6 @@ static const IbexDeviceDef ot_eg_soc_devices[] = {
/* clang-format on */
};

enum OtEGBoardDevice {
OT_EG_BOARD_DEV_SOC,
OT_EG_BOARD_DEV_FLASH,
OT_EG_BOARD_DEV_COUNT,
};

/* ------------------------------------------------------------------------ */
/* Type definitions */
/* ------------------------------------------------------------------------ */
Expand All @@ -1147,7 +1165,8 @@ struct OtEGSoCState {
struct OtEGBoardState {
DeviceState parent_obj;

DeviceState **devices;
/* optional SPI data flash (type of device) */
char *spiflash[OT_EG_MTD_SPI_COUNT];
};

struct OtEGMachineState {
Expand Down Expand Up @@ -1182,7 +1201,7 @@ static void ot_eg_soc_dm_configure(DeviceState *dev, const IbexDeviceDef *def,
static void ot_eg_soc_flash_ctrl_configure(
DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent)
{
DriveInfo *dinfo = drive_get(IF_MTD, 1, 0);
DriveInfo *dinfo = drive_get(IF_MTD, OT_EG_MTD_EFLASH, 0);
(void)def;
(void)parent;

Expand Down Expand Up @@ -1223,7 +1242,7 @@ static void ot_eg_soc_hart_configure(DeviceState *dev, const IbexDeviceDef *def,
static void ot_eg_soc_otp_ctrl_configure(
DeviceState *dev, const IbexDeviceDef *def, DeviceState *parent)
{
DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0);
DriveInfo *dinfo = drive_get(IF_PFLASH, OT_EG_PFLASH_OTP, 0);
(void)def;
(void)parent;

Expand Down Expand Up @@ -1401,39 +1420,100 @@ type_init(ot_eg_soc_register_types);
/* Board */
/* ------------------------------------------------------------------------ */

static void ot_eg_board_set_spiflash0(Object *obj, const char *value,
Error **errp)
{
OtEGBoardState *board = RISCV_OT_EG_BOARD(obj);
(void)errp;

g_free(board->spiflash[OT_EG_MTD_SPI0]);
board->spiflash[OT_EG_MTD_SPI0] = g_strdup(value);
}

static void ot_eg_board_set_spiflash1(Object *obj, const char *value,
Error **errp)
{
OtEGBoardState *board = RISCV_OT_EG_BOARD(obj);
(void)errp;

g_free(board->spiflash[OT_EG_MTD_SPI1]);
board->spiflash[OT_EG_MTD_SPI1] = g_strdup(value);
}

static void ot_eg_board_realize(DeviceState *dev, Error **errp)
{
OtEGBoardState *board = RISCV_OT_EG_BOARD(dev);

DeviceState *soc = board->devices[OT_EG_BOARD_DEV_SOC];
DeviceState *soc = qdev_new(TYPE_RISCV_OT_EG_SOC);

object_property_add_child(OBJECT(board), "soc", OBJECT(soc));
sysbus_realize_and_unref(SYS_BUS_DEVICE(soc), &error_fatal);

DeviceState *spihost =
RISCV_OT_EG_SOC(soc)->devices[OT_EG_SOC_DEV_SPI_HOST0];
DeviceState *flash = board->devices[OT_EG_BOARD_DEV_FLASH];
BusState *spibus = qdev_get_child_bus(spihost, "spi0");
g_assert(spibus);

DriveInfo *dinfo = drive_get(IF_MTD, 0, 0);
if (dinfo) {
qdev_prop_set_drive_err(DEVICE(flash), "drive",
blk_by_legacy_dinfo(dinfo), &error_fatal);
for (unsigned fix = 0; fix < OT_EG_MTD_SPI_COUNT; fix++) {
const char *flash_type = board->spiflash[OT_EG_MTD_SPI0 + fix];
/*
* skip this flash slot if no device type has been defined on the QEMU
* command line
*/
if (!flash_type) {
continue;
}

/* qdev_new aborts if the specified device is not supported */
DeviceState *flash = qdev_new(flash_type);

if (!object_dynamic_cast(OBJECT(flash), TYPE_M25P80)) {
error_setg(errp, "%s is not a SPI dataflash device", flash_type);
}

/*
* retrieve the SPI host controller bus. Although each SPI host only
* has one SPI bus, each bus name in QEMU needs to be unique. The SPI
* host controller uses its bus-num property as a suffix for naming its
* bus
*/
DeviceState *spihost =
RISCV_OT_EG_SOC(soc)->devices[OT_EG_SOC_DEV_SPI_HOST0 + fix];
char *busname = g_strdup_printf("spi%u", fix);
BusState *spibus = qdev_get_child_bus(spihost, busname);
g_assert(spibus);

/*
* if a "drive" property for this bus/unit pair is defined on the QEMU
* command line, assigned it to the flash device
*/
DriveInfo *dinfo = drive_get(IF_MTD, (int)fix, 0);
if (dinfo) {
qdev_prop_set_drive_err(DEVICE(flash), "drive",
blk_by_legacy_dinfo(dinfo), &error_fatal);
}

/* the flash device is a child of the board */
char *flashname = g_strdup_printf("dataflash%u", fix);
object_property_add_child(OBJECT(board), flashname, OBJECT(flash));
/* connect it as a peripheral of the SPI host controller bus */
ssi_realize_and_unref(flash, SSI_BUS(spibus), errp);

/*
* finally, connect the first CS line of the SPI controller to control
* to select this SPI flash device
*/
qemu_irq cs = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0);
qdev_connect_gpio_out_named(spihost, SSI_GPIO_CS, 0, cs);

g_free(flashname);
g_free(busname);
}
object_property_add_child(OBJECT(board), "dataflash", OBJECT(flash));
ssi_realize_and_unref(flash, SSI_BUS(spibus), errp);

qemu_irq cs = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0);
qdev_connect_gpio_out_named(spihost, SSI_GPIO_CS, 0, cs);
}

static void ot_eg_board_init(Object *obj)
{
OtEGBoardState *s = RISCV_OT_EG_BOARD(obj);

s->devices = g_new0(DeviceState *, OT_EG_BOARD_DEV_COUNT);
s->devices[OT_EG_BOARD_DEV_SOC] = qdev_new(TYPE_RISCV_OT_EG_SOC);
s->devices[OT_EG_BOARD_DEV_FLASH] = qdev_new("is25wp128");
object_property_add_str(obj, "spiflash0", NULL, &ot_eg_board_set_spiflash0);
object_property_set_description(obj, "spiflash0",
"SPI dataflash on SPI0 bus");
object_property_add_str(obj, "spiflash1", NULL, &ot_eg_board_set_spiflash1);
object_property_set_description(obj, "spiflash1",
"SPI dataflash on SPI1 bus");
}

static void ot_eg_board_class_init(ObjectClass *oc, void *data)
Expand Down
8 changes: 5 additions & 3 deletions include/hw/riscv/ot_earlgrey.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@

#include "qom/object.h"

#define TYPE_RISCV_OT_EG_MACHINE MACHINE_TYPE_NAME("ot-earlgrey")
#define OT_EARLGREY "ot-earlgrey"

#define TYPE_RISCV_OT_EG_MACHINE MACHINE_TYPE_NAME(OT_EARLGREY)
OBJECT_DECLARE_SIMPLE_TYPE(OtEGMachineState, RISCV_OT_EG_MACHINE)

#define TYPE_RISCV_OT_EG_BOARD "riscv.ot_earlgrey.board"
#define TYPE_RISCV_OT_EG_BOARD OT_EARLGREY "-board"
OBJECT_DECLARE_SIMPLE_TYPE(OtEGBoardState, RISCV_OT_EG_BOARD)

#define TYPE_RISCV_OT_EG_SOC "riscv.ot_earlgrey.soc"
#define TYPE_RISCV_OT_EG_SOC OT_EARLGREY "-soc"
OBJECT_DECLARE_TYPE(OtEGSoCState, OtEGSoCClass, RISCV_OT_EG_SOC)

#endif /* HW_RISCV_OT_EARLGREY_H */

0 comments on commit 6a84cf0

Please sign in to comment.