[PATCH 02/11] hw/riscv/k230: Instantiate K230 SSI controllers
Kangjie Huang <[email protected]> Sun, 26 Jul 2026 20:28:20 +0800
| Newsgroups | org.nongnu.qemu-riscv,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <41750ee3b9b96b8f6caf234b0855f4cae79b761c.1785064313.git.flamboyant.h.01@gmail.com> |
Instantiate the three SSI controller profiles used by K230 and map their MMIO regions at the documented addresses. Add the K230 SSI qtest target. Cover the three instance profiles, reset state, chip-select masks, representative register masks, and system reset behaviour. Signed-off-by: Kangjie Huang <[email protected]> --- docs/system/riscv/k230.rst | 1 + hw/riscv/Kconfig | 1 + hw/riscv/k230.c | 35 +++++-- include/hw/riscv/k230.h | 2 + tests/qtest/k230-dw-ssi-test.c | 178 +++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 4 +- 6 files changed, 211 insertions(+), 10 deletions(-) create mode 100644 tests/qtest/k230-dw-ssi-test.c diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst index cea8202e55..0358709d2c 100644 --- a/docs/system/riscv/k230.rst +++ b/docs/system/riscv/k230.rst @@ -20,6 +20,7 @@ The ``k230`` machine supports the following devices: * Platform-Level Interrupt Controller (PLIC) * 2 K230 Watchdog Timer * 5 UART +* 3 K230 SSI controllers for SPI and QSPI Boot options ------------ diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index de37c08cae..69059ae044 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -162,3 +162,4 @@ config K230 select SERIAL_MM select UNIMP select K230_WDT + select K230_DW_SSI diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 656f28190c..acd5445b31 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -110,6 +110,12 @@ static void k230_soc_init(Object *obj) object_initialize_child(obj, "c908-cpu", cpu0, TYPE_RISCV_HART_ARRAY); object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT); object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT); + object_initialize_child(obj, "k230-qspi0", &s->dw_ssi[0], + TYPE_K230_DW_SSI); + object_initialize_child(obj, "k230-qspi1", &s->dw_ssi[1], + TYPE_K230_DW_SSI); + object_initialize_child(obj, "k230-spi-opi", &s->dw_ssi[2], + TYPE_K230_DW_SSI); qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908); @@ -198,6 +204,19 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) } } + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[0]), "num-cs", 5); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[1]), "num-cs", 5); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[2]), "num-cs", 1); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[0]), "max-lines", 4); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[1]), "max-lines", 4); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[2]), "max-lines", 8); + + for (int i = 0; i < ARRAY_SIZE(s->dw_ssi); i++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->dw_ssi[i]), errp)) { + return; + } + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[0]), 0, memmap[K230_DEV_WDT0].base); sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[0]), 0, qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT0_IRQ)); @@ -206,6 +225,13 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[1]), 0, qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT1_IRQ)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dw_ssi[0]), 0, + memmap[K230_DEV_QSPI0].base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dw_ssi[1]), 0, + memmap[K230_DEV_QSPI1].base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dw_ssi[2]), 0, + memmap[K230_DEV_SPI].base); + /* unimplemented devices */ create_unimplemented_device("kpu.l2-cache", memmap[K230_DEV_KPU_L2_CACHE].base, @@ -349,15 +375,6 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) create_unimplemented_device("sd1", memmap[K230_DEV_SD1].base, memmap[K230_DEV_SD1].size); - create_unimplemented_device("qspi0", memmap[K230_DEV_QSPI0].base, - memmap[K230_DEV_QSPI0].size); - - create_unimplemented_device("qspi1", memmap[K230_DEV_QSPI1].base, - memmap[K230_DEV_QSPI1].size); - - create_unimplemented_device("spi", memmap[K230_DEV_SPI].base, - memmap[K230_DEV_SPI].size); - create_unimplemented_device("hi_sys_cfg", memmap[K230_DEV_HI_SYS_CFG].base, memmap[K230_DEV_HI_SYS_CFG].size); diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index 592e1c26bf..4a79b7ed45 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -17,6 +17,7 @@ #include "hw/core/boards.h" #include "hw/riscv/riscv_hart.h" +#include "hw/ssi/k230_dw_ssi.h" #include "hw/watchdog/k230_wdt.h" #define C908_CPU_HARTID (0) @@ -33,6 +34,7 @@ typedef struct K230SoCState { RISCVHartArrayState c908_cpu; /* Small core */ K230WdtState wdt[2]; + K230DwSsiState dw_ssi[3]; MemoryRegion sram; MemoryRegion bootrom; diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c new file mode 100644 index 0000000000..dcd6cb1d6e --- /dev/null +++ b/tests/qtest/k230-dw-ssi-test.c @@ -0,0 +1,178 @@ +/* + * QTest for the Kendryte K230 DesignWare SSI + * + * Copyright (c) 2026 Kangjie Huang <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define K230_SPI0_BASE 0x91584000ULL +#define K230_SPI1_BASE 0x91582000ULL +#define K230_SPI2_BASE 0x91583000ULL +#define K230_SSI_CTRLR0 0x000 +#define K230_SSI_CTRLR1 0x004 +#define K230_SSI_SSIENR 0x008 +#define K230_SSI_SER 0x010 +#define K230_SSI_BAUDR 0x014 +#define K230_SSI_TXFTLR 0x018 +#define K230_SSI_TXFLR 0x020 +#define K230_SSI_RXFLR 0x024 +#define K230_SSI_SR 0x028 +#define K230_SSI_IMR 0x02c +#define K230_SSI_ISR 0x030 +#define K230_SSI_RISR 0x034 +#define K230_SSI_DMACR 0x04c +#define K230_SSI_IDR 0x058 +#define K230_SSI_VERSION_ID 0x05c +#define K230_SSI_SPI_CTRLR0 0x0f4 +#define K230_SSI_XIP_MODE_BITS 0x0fc +#define K230_SSI_XIP_INCR_INST 0x100 +#define K230_SSI_SPIDR 0x120 +#define K230_SSI_SPIAR 0x124 +#define K230_SSI_AXIAR0 0x128 +#define K230_SSI_AXIAR1 0x12c +#define K230_SSI_AXIECR 0x130 +#define K230_SSI_DONECR 0x134 + +#define K230_SSI_CTRLR0_RESET 0x00004007U +#define K230_SSI_SPI_CTRLR0_SPI_RESET 0x04000200U +#define K230_SSI_SPI_CTRLR0_FMC_RESET 0x28000200U +#define K230_SSI_IMR_RESET 0x0000003fU +#define K230_SSI_IDR_RESET 0xa1b2c3d5U +#define K230_SSI_VERSION_RESET 0x3130332aU + +#define K230_SSI_CTRLR0_WRITABLE_MASK 0x01cf7f1fU +#define K230_SSI_BAUDR_WRITABLE_MASK 0x0000fffeU + +#define K230_SSI_CTRLR0_DFS_MASK 0x1fU +#define K230_SSI_CTRLR0_TMOD_SHIFT 10 + +#define K230_SSI_TMOD_TR 0 +#define K230_SSI_TMOD_EEPROM_READ 3 + +#define K230_SSI_SR_BUSY BIT(0) +#define K230_SSI_SR_TFNF BIT(1) +#define K230_SSI_SR_TFE BIT(2) +#define K230_SSI_SR_RFNE BIT(3) + +#define K230_SSI_INT_AXIE BIT(8) +#define K230_SSI_INT_DONE BIT(11) + +typedef struct K230SsiInstance { + uint64_t base; + uint32_t num_cs; + uint32_t spi_ctrlr0_reset; +} K230SsiInstance; + +static const K230SsiInstance k230_ssi_instances[3] = { + { + .base = K230_SPI0_BASE, + .num_cs = 1, + .spi_ctrlr0_reset = K230_SSI_SPI_CTRLR0_FMC_RESET, + }, { + .base = K230_SPI1_BASE, + .num_cs = 5, + .spi_ctrlr0_reset = K230_SSI_SPI_CTRLR0_SPI_RESET, + }, { + .base = K230_SPI2_BASE, + .num_cs = 5, + .spi_ctrlr0_reset = K230_SSI_SPI_CTRLR0_SPI_RESET, + }, +}; + +static QTestState *k230_ssi_start(void) +{ + return qtest_init("-machine k230"); +} + +static uint32_t k230_ssi_readl(QTestState *qts, uint64_t base, + uint32_t offset) +{ + return qtest_readl(qts, base + offset); +} + +static void k230_ssi_writel(QTestState *qts, uint64_t base, + uint32_t offset, uint32_t value) +{ + qtest_writel(qts, base + offset, value); +} + +static void k230_ssi_disable(QTestState *qts, uint64_t base) +{ + k230_ssi_writel(qts, base, K230_SSI_SSIENR, 0); +} + +static void k230_ssi_configure(QTestState *qts, uint64_t base, + uint32_t tmod, uint32_t dfs_bits, + uint32_t ndf) +{ + uint32_t ctrlr0; + + g_assert_cmpuint(dfs_bits, >=, 4); + g_assert_cmpuint(dfs_bits, <=, 32); + g_assert_cmpuint(tmod, <=, K230_SSI_TMOD_EEPROM_READ); + + k230_ssi_disable(qts, base); + ctrlr0 = (dfs_bits - 1) & K230_SSI_CTRLR0_DFS_MASK; + ctrlr0 |= tmod << K230_SSI_CTRLR0_TMOD_SHIFT; + k230_ssi_writel(qts, base, K230_SSI_CTRLR0, ctrlr0); + k230_ssi_writel(qts, base, K230_SSI_CTRLR1, ndf); + k230_ssi_writel(qts, base, K230_SSI_BAUDR, 2); +} + +static void k230_ssi_enable_cs(QTestState *qts, uint64_t base, uint32_t ser) +{ + k230_ssi_writel(qts, base, K230_SSI_SER, ser); + k230_ssi_writel(qts, base, K230_SSI_SSIENR, 1); +} + +static void test_register_contract(void) +{ + QTestState *qts = k230_ssi_start(); + + for (int i = 0; i < ARRAY_SIZE(k230_ssi_instances); i++) { + const K230SsiInstance *inst = &k230_ssi_instances[i]; + + g_assert_cmphex(k230_ssi_readl(qts, inst->base, K230_SSI_CTRLR0), + ==, K230_SSI_CTRLR0_RESET); + g_assert_cmphex(k230_ssi_readl(qts, inst->base, K230_SSI_SSIENR), + ==, 0); + g_assert_cmphex(k230_ssi_readl(qts, inst->base, + K230_SSI_SPI_CTRLR0), + ==, inst->spi_ctrlr0_reset); + k230_ssi_writel(qts, inst->base, K230_SSI_SER, UINT32_MAX); + g_assert_cmphex(k230_ssi_readl(qts, inst->base, K230_SSI_SER), + ==, MAKE_64BIT_MASK(0, inst->num_cs)); + } + + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_IDR), + ==, K230_SSI_IDR_RESET); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, + K230_SSI_VERSION_ID), + ==, K230_SSI_VERSION_RESET); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_CTRLR0, UINT32_MAX); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_CTRLR0), + ==, K230_SSI_CTRLR0_WRITABLE_MASK); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_BAUDR, UINT32_MAX); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_BAUDR), + ==, K230_SSI_BAUDR_WRITABLE_MASK); + + qtest_system_reset(qts); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_CTRLR0), + ==, K230_SSI_CTRLR0_RESET); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_IMR), + ==, K230_SSI_IMR_RESET); + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-dw-ssi/register-contract", test_register_contract); + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 56ff860e21..f5bcc7bece 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -297,7 +297,8 @@ qtests_riscv64 = ['riscv-csr-test'] + \ (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and config_all_devices.has_key('CONFIG_RISCV_IOMMU') ? ['iommu-riscv-test'] : []) + \ - (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) + (config_all_devices.has_key('CONFIG_K230') ? + ['k230-wdt-test', 'k230-dw-ssi-test'] : []) qtests_hexagon = ['boot-serial-test'] @@ -400,6 +401,7 @@ qtests = { [gio], 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], + 'k230-dw-ssi-test': files('k230-dw-ssi-test.c'), 'migration-test': test_migration_files + migration_tls_files + migration_colo_files, 'pxe-test': files('boot-sector.c'), 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c', -- 2.43.0