From: Jack Wang <163wangjack@gmail.com>
To: qemu-devel@nongnu.org
Cc: Chao Liu <chao.liu.zevorn@gmail.com>,
Daniel Henrique Barboza <dbarboza@ventanamicro.com>,
Alistair Francis <alistair.francis@wdc.com>,
Jack Wang <163wangjack@gmail.com>,
Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Palmer Dabbelt <palmer@dabbelt.com>,
Weiwei Li <liwei1518@gmail.com>,
Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>,
Liu Zhiwei <zhiwei_liu@linux.alibaba.com>,
Fabiano Rosas <farosas@suse.de>,
Laurent Vivier <lvivier@redhat.com>,
qemu-riscv@nongnu.org (open list:K230 Machines)
Subject: [PATCH v2 2/2] hw/riscv/k230: wire up the RMU device
Date: Mon, 20 Jul 2026 02:02:47 +0800 [thread overview]
Message-ID: <20260719180247.8660-3-163wangjack@gmail.com> (raw)
In-Reply-To: <20260719180247.8660-1-163wangjack@gmail.com>
The previous commit added the K230 RMU device model but left it
unconnected. This commit integrates it into the K230 SoC:
- Instantiate K230RmuState in K230SoCState structure
- Realize and memory-map the device at 0x91101000 in k230_soc_realize()
- Connect the two watchdogs via the RMU "wdt0"/"wdt1" QOM links so that a
software reset request in PERI0 cold-resets the real WDT devices
- Remove the create_unimplemented_device("rmu") stub
- Add CONFIG_K230_RMU=y selection in hw/riscv/Kconfig
- Update documentation to list RMU in supported devices
- Add the qtest and its MAINTAINERS entry
The qtest lives here rather than with the device model so the series stays
bisectable: it drives "-machine k230", which only exists once the device is
wired up. It covers all reset types, the documented reset values, the
reserved-bit masks, the CPU1 two-step assert/deassert sequence and the real
watchdog reset propagation.
Signed-off-by: Jack Wang <163wangjack@gmail.com>
---
MAINTAINERS | 1 +
docs/system/riscv/k230.rst | 1 +
hw/riscv/Kconfig | 1 +
hw/riscv/k230.c | 15 ++-
include/hw/riscv/k230.h | 2 +
tests/qtest/k230-rmu-test.c | 225 ++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 3 +-
7 files changed, 244 insertions(+), 4 deletions(-)
create mode 100644 tests/qtest/k230-rmu-test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 80061ce66d..91305b86fe 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1814,6 +1814,7 @@ F: hw/watchdog/k230_wdt.c
F: include/hw/misc/k230_rmu.h
F: include/hw/riscv/k230.h
F: include/hw/watchdog/k230_wdt.h
+F: tests/qtest/k230-rmu-test.c
F: tests/qtest/k230-wdt-test.c
RX Machines
diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst
index cea8202e55..3f2313a127 100644
--- a/docs/system/riscv/k230.rst
+++ b/docs/system/riscv/k230.rst
@@ -19,6 +19,7 @@ The ``k230`` machine supports the following devices:
* Core Local Interruptor (CLINT)
* Platform-Level Interrupt Controller (PLIC)
* 2 K230 Watchdog Timer
+* K230 Reset Management Unit (RMU)
* 5 UART
Boot options
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 54e41a6afc..bffa4e69c8 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -149,3 +149,4 @@ config K230
select SERIAL_MM
select UNIMP
select K230_WDT
+ select K230_RMU
diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c
index 502281c52c..dc008eb6fa 100644
--- a/hw/riscv/k230.c
+++ b/hw/riscv/k230.c
@@ -110,6 +110,7 @@ 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-rmu", &s->rmu, TYPE_K230_RMU);
qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0);
qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908);
@@ -206,6 +207,17 @@ 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));
+ /* RMU (reset management unit) */
+ /* Link the watchdogs so the RMU can reset them (before realize). */
+ object_property_set_link(OBJECT(&s->rmu), "wdt0",
+ OBJECT(&s->wdt[0]), &error_abort);
+ object_property_set_link(OBJECT(&s->rmu), "wdt1",
+ OBJECT(&s->wdt[1]), &error_abort);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->rmu), errp)) {
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->rmu), 0, memmap[K230_DEV_RMU].base);
+
/* unimplemented devices */
create_unimplemented_device("kpu.l2-cache",
memmap[K230_DEV_KPU_L2_CACHE].base,
@@ -268,9 +280,6 @@ static void k230_soc_realize(DeviceState *dev, Error **errp)
create_unimplemented_device("cmu", memmap[K230_DEV_CMU].base,
memmap[K230_DEV_CMU].size);
- create_unimplemented_device("rmu", memmap[K230_DEV_RMU].base,
- memmap[K230_DEV_RMU].size);
-
create_unimplemented_device("boot", memmap[K230_DEV_BOOT].base,
memmap[K230_DEV_BOOT].size);
diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h
index 592e1c26bf..13f2a1ab0b 100644
--- a/include/hw/riscv/k230.h
+++ b/include/hw/riscv/k230.h
@@ -18,6 +18,7 @@
#include "hw/core/boards.h"
#include "hw/riscv/riscv_hart.h"
#include "hw/watchdog/k230_wdt.h"
+#include "hw/misc/k230_rmu.h"
#define C908_CPU_HARTID (0)
@@ -33,6 +34,7 @@ typedef struct K230SoCState {
RISCVHartArrayState c908_cpu; /* Small core */
K230WdtState wdt[2];
+ K230RmuState rmu;
MemoryRegion sram;
MemoryRegion bootrom;
diff --git a/tests/qtest/k230-rmu-test.c b/tests/qtest/k230-rmu-test.c
new file mode 100644
index 0000000000..a16c837382
--- /dev/null
+++ b/tests/qtest/k230-rmu-test.c
@@ -0,0 +1,225 @@
+/*
+ * QTest for the K230 Reset Management Unit.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "libqtest.h"
+#include "hw/misc/k230_rmu.h"
+
+#define K230_RMU_BASE 0x91101000ULL
+
+static inline uint32_t rd(QTestState *qts, uint64_t off)
+{
+ return qtest_readl(qts, K230_RMU_BASE + off);
+}
+static inline void wr(QTestState *qts, uint64_t off, uint32_t val)
+{
+ qtest_writel(qts, K230_RMU_BASE + off, val);
+}
+
+/*
+ * Documented reset values (K230 TRM chapter 2.1.5, per-bit "Reset" column).
+ * The *_rst_done status bits reset to 0, so these differ from each register's
+ * summarised "Total Reset Value".
+ */
+static void test_reset_values(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* Control registers. */
+ g_assert_cmphex(rd(qts, K230_RMU_CPU0_CTRL), ==, 0x00000000);
+ g_assert_cmphex(rd(qts, K230_RMU_CPU1_CTRL), ==, 0x00000001);
+ g_assert_cmphex(rd(qts, K230_RMU_AI_CTRL), ==, 0x00000000);
+ g_assert_cmphex(rd(qts, K230_RMU_PERI0_CTRL), ==, 0x000ff0ff);
+ g_assert_cmphex(rd(qts, K230_RMU_PERI1_CTRL), ==, 0x007e7fff);
+ g_assert_cmphex(rd(qts, K230_RMU_HISYS_CTRL), ==, 0x00000000);
+ g_assert_cmphex(rd(qts, K230_RMU_SRAM_CTRL), ==, 0x00000002);
+ g_assert_cmphex(rd(qts, K230_RMU_ISP_CTRL), ==, 0x0000039f);
+
+ /* A representative reset-time-control register. */
+ g_assert_cmphex(rd(qts, K230_RMU_CPU0_TIM), ==, 0x00fff880);
+ g_assert_cmphex(rd(qts, K230_RMU_USB_TIM), ==, 0x0008d288);
+
+ qtest_quit(qts);
+}
+
+/*
+ * Reset-time-control registers are plain read/write storage: they come up with
+ * their documented value and accept arbitrary writes with no side effects.
+ */
+static void test_tim_storage(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ g_assert_cmphex(rd(qts, K230_RMU_MCTL_TIM), ==, 0x00000304);
+ wr(qts, K230_RMU_MCTL_TIM, 0x12345678);
+ g_assert_cmphex(rd(qts, K230_RMU_MCTL_TIM), ==, 0x12345678);
+
+ qtest_quit(qts);
+}
+
+/*
+ * Reserved bits are read-only zero: a write to an undocumented bit position is
+ * dropped. ISP_CTL documents bits 0-9 and 28-29; bit 15 is reserved.
+ */
+static void test_reserved_bits(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+ uint32_t before = rd(qts, K230_RMU_ISP_CTRL);
+
+ wr(qts, K230_RMU_ISP_CTRL, before | BIT(15));
+ g_assert_cmphex(rd(qts, K230_RMU_ISP_CTRL) & BIT(15), ==, 0);
+
+ qtest_quit(qts);
+}
+
+/*
+ * CPU0: a low-half write without its strobe is ignored; with the strobe the
+ * reset request fires, latches done (bit12), and the request bit auto-clears.
+ */
+static void test_cpu_write_enable(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* No strobe: the low-half write is dropped, reads back as 0. */
+ wr(qts, K230_RMU_CPU0_CTRL, K230_RMU_CPU_RESET);
+ g_assert_cmphex(rd(qts, K230_RMU_CPU0_CTRL), ==, 0);
+
+ /* With strobe (bit16): request bit0 takes effect, done (bit12) latches. */
+ wr(qts, K230_RMU_CPU0_CTRL,
+ K230_RMU_CPU_RESET | (K230_RMU_CPU_RESET << K230_RMU_WE_SHIFT));
+ g_assert_cmphex(rd(qts, K230_RMU_CPU0_CTRL), ==, K230_RMU_CPU_DONE);
+
+ qtest_quit(qts);
+}
+
+/*
+ * HW_DONE: no write-enable needed. Writing HISYS request bit0 latches its
+ * done bit (bit4); writing 1 to the done bit clears it.
+ */
+static void test_hw_done_and_w1c(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+ uint32_t v;
+
+ wr(qts, K230_RMU_HISYS_CTRL, BIT(0)); /* no strobe */
+ v = rd(qts, K230_RMU_HISYS_CTRL);
+ g_assert_cmphex(v & BIT(4), ==, BIT(4)); /* done latched */
+ g_assert_cmphex(v & BIT(0), ==, 0); /* request bit auto-cleared */
+
+ wr(qts, K230_RMU_HISYS_CTRL, BIT(4)); /* write-1-to-clear */
+ g_assert_cmphex(rd(qts, K230_RMU_HISYS_CTRL) & BIT(4), ==, 0);
+
+ qtest_quit(qts);
+}
+
+/* Repeated resets: one HW_DONE line can be triggered again and again. */
+static void test_repeat_reset(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ for (int i = 0; i < 3; i++) {
+ wr(qts, K230_RMU_AI_CTRL, BIT(0)); /* request */
+ g_assert_cmphex(rd(qts, K230_RMU_AI_CTRL) & BIT(31), ==, BIT(31));
+ wr(qts, K230_RMU_AI_CTRL, BIT(31)); /* W1C done */
+ g_assert_cmphex(rd(qts, K230_RMU_AI_CTRL) & BIT(31), ==, 0);
+ }
+
+ qtest_quit(qts);
+}
+
+/* FLUSH: the CPU0 bit4 flush request is auto-cleared, reads back as 0. */
+static void test_flush_auto_clear(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ wr(qts, K230_RMU_CPU0_CTRL,
+ K230_RMU_CPU_FLUSH | (K230_RMU_CPU_FLUSH << K230_RMU_WE_SHIFT));
+ g_assert_cmphex(rd(qts, K230_RMU_CPU0_CTRL) & K230_RMU_CPU_FLUSH, ==, 0);
+
+ qtest_quit(qts);
+}
+
+/* SW_DONE: the PERI0 low half is plain storage, no strobe and no done bit. */
+static void test_sw_done_storage(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* WDT0 / WDT1 reset bits */
+ wr(qts, K230_RMU_PERI0_CTRL, BIT(12) | BIT(13));
+ g_assert_cmphex(rd(qts, K230_RMU_PERI0_CTRL), ==, (BIT(12) | BIT(13)));
+
+ qtest_quit(qts);
+}
+
+/*
+ * CPU1 needs separate assert and deassert operations: unlike CPU0 its reset
+ * request bit does NOT self-clear. Assert (bit0 + strobe) latches done (bit12)
+ * and keeps bit0 set; deassert (strobe only, bit0 low) clears bit0.
+ */
+static void test_cpu1_two_step(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* Assert: request bit0 stays set AND done bit12 latches. */
+ wr(qts, K230_RMU_CPU1_CTRL,
+ K230_RMU_CPU_RESET | (K230_RMU_CPU_RESET << K230_RMU_WE_SHIFT));
+ g_assert_cmphex(rd(qts, K230_RMU_CPU1_CTRL), ==,
+ K230_RMU_CPU_RESET | K230_RMU_CPU_DONE);
+
+ /* Deassert: drive bit0 low with its strobe; bit0 clears, done stays. */
+ wr(qts, K230_RMU_CPU1_CTRL, K230_RMU_CPU_RESET << K230_RMU_WE_SHIFT);
+ g_assert_cmphex(rd(qts, K230_RMU_CPU1_CTRL), ==, K230_RMU_CPU_DONE);
+
+ /* Done is write-1-to-clear (gated by its strobe). */
+ wr(qts, K230_RMU_CPU1_CTRL,
+ K230_RMU_CPU_DONE | (K230_RMU_CPU_DONE << K230_RMU_WE_SHIFT));
+ g_assert_cmphex(rd(qts, K230_RMU_CPU1_CTRL), ==, 0);
+
+ qtest_quit(qts);
+}
+
+/*
+ * Reset propagation: asserting a watchdog reset line in PERI0_CTRL performs a
+ * real cold reset of the linked WDT device. Put WDT0 into a non-default state
+ * (TORR != 0), pulse the RMU, and confirm WDT0 came back to its reset value.
+ */
+#define K230_WDT0_BASE 0x91106000ULL
+#define K230_WDT_TORR 0x04
+
+static void test_wdt_reset_propagation(void)
+{
+ QTestState *qts = qtest_init("-machine k230");
+
+ /* Dirty WDT0: TORR reset value is 0, write a non-zero timeout range. */
+ qtest_writel(qts, K230_WDT0_BASE + K230_WDT_TORR, 0xf);
+ g_assert_cmphex(qtest_readl(qts, K230_WDT0_BASE + K230_WDT_TORR), ==, 0xf);
+
+ /* Assert WDT0's software reset line through the RMU (PERI0 bit12). */
+ wr(qts, K230_RMU_PERI0_CTRL, K230_RMU_PERI0_WDT0_RST);
+
+ /* WDT0 must be back to its reset value. */
+ g_assert_cmphex(qtest_readl(qts, K230_WDT0_BASE + K230_WDT_TORR), ==, 0);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/k230/rmu/reset_values", test_reset_values);
+ qtest_add_func("/k230/rmu/tim_storage", test_tim_storage);
+ qtest_add_func("/k230/rmu/reserved_bits", test_reserved_bits);
+ qtest_add_func("/k230/rmu/cpu_we", test_cpu_write_enable);
+ qtest_add_func("/k230/rmu/cpu1_two_step", test_cpu1_two_step);
+ qtest_add_func("/k230/rmu/hw_done_w1c", test_hw_done_and_w1c);
+ qtest_add_func("/k230/rmu/repeat_reset", test_repeat_reset);
+ qtest_add_func("/k230/rmu/flush", test_flush_auto_clear);
+ qtest_add_func("/k230/rmu/sw_done", test_sw_done_storage);
+ qtest_add_func("/k230/rmu/wdt_reset_prop", test_wdt_reset_propagation);
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 822e0bd286..70c9a53454 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -294,7 +294,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-rmu-test'] : [])
qtests_hexagon = ['boot-serial-test']
--
2.53.0
next prev parent reply other threads:[~2026-07-19 18:04 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-09 3:12 [RFC PATCH 0/2] *** Add k230 reset management unit support *** jack wang
2026-07-09 3:12 ` [RFC PATCH 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model jack wang
2026-07-16 17:48 ` Daniel Henrique Barboza
2026-07-19 18:02 ` [PATCH v2 0/2] hw/riscv/k230: add the K230 Reset Management Unit Jack Wang
2026-07-19 18:02 ` [PATCH v2 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model Jack Wang
2026-07-19 18:02 ` Jack Wang [this message]
2026-07-17 10:11 ` [RFC PATCH " Junze Cao
2026-07-09 3:12 ` [RFC PATCH 2/2] hw/riscv/k230: wire up the RMU device jack wang
2026-07-16 17:56 ` Daniel Henrique Barboza
2026-07-17 13:41 ` Chao Liu
2026-07-17 13:56 ` Daniel Henrique Barboza
2026-07-17 14:04 ` Chao Liu
2026-07-15 11:56 ` [RFC PATCH 0/2] *** Add k230 reset management unit support *** Jack wang
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260719180247.8660-3-163wangjack@gmail.com \
--to=163wangjack@gmail.com \
--cc=alistair.francis@wdc.com \
--cc=chao.liu.zevorn@gmail.com \
--cc=daniel.barboza@oss.qualcomm.com \
--cc=dbarboza@ventanamicro.com \
--cc=farosas@suse.de \
--cc=liwei1518@gmail.com \
--cc=lvivier@redhat.com \
--cc=palmer@dabbelt.com \
--cc=pbonzini@redhat.com \
--cc=pierrick.bouvier@oss.qualcomm.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-riscv@nongnu.org \
--cc=zhiwei_liu@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.