All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] *** Add k230 reset management unit support ***
@ 2026-07-09  3:12 jack wang
  2026-07-09  3:12 ` [RFC PATCH 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model jack wang
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: jack wang @ 2026-07-09  3:12 UTC (permalink / raw)
  To: qemu-devel; +Cc: Jack Wang

From: Jack Wang <163wangjack@gmail.com>

This series adds support for reset management unit found on k230 soc and 
successfully  wires it up to k230 machine;

The register design are implemented based on k230 trm and linux reset driver
(drivers/reset/reset-k230.c)

detailed implementations:
1. write-enable mask for control registers(strobe in the upper 16 bits)
2. hardware auto-clearing for reset and flush request bits
3. latching of Done bits and w1c logic
4. qtest coverage for register read/write behaviors and hardware state modelin

There are some checkpatch style warnings (line length, comment
formatting) that will be addressed in v2 based on reviewer feedback.

this is my first time for hardware-unit modeling so apologies for possible mistake.
I am apprecitated for any feedback

Jack Wang (2):
  hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model
  hw/riscv/k230: wire up the RMU device

 docs/system/riscv/k230.rst  |   1 +
 hw/misc/Kconfig             |   3 +
 hw/misc/k230_rmu.c          | 280 ++++++++++++++++++++++++++++++++++++
 hw/misc/meson.build         |   1 +
 hw/misc/trace-events        |   6 +
 hw/riscv/Kconfig            |   1 +
 hw/riscv/k230.c             |  10 +-
 include/hw/misc/k230_rmu.h  |  76 ++++++++++
 include/hw/riscv/k230.h     |   2 +
 tests/qtest/k230-rmu-test.c | 125 ++++++++++++++++
 tests/qtest/meson.build     |   3 +-
 11 files changed, 504 insertions(+), 4 deletions(-)
 create mode 100644 hw/misc/k230_rmu.c
 create mode 100644 include/hw/misc/k230_rmu.h
 create mode 100644 tests/qtest/k230-rmu-test.c

-- 
2.53.0



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [RFC PATCH 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model
  2026-07-09  3:12 [RFC PATCH 0/2] *** Add k230 reset management unit support *** jack wang
@ 2026-07-09  3:12 ` jack wang
  2026-07-16 17:48   ` Daniel Henrique Barboza
  2026-07-17 10:11   ` [RFC PATCH 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model Junze Cao
  2026-07-09  3:12 ` [RFC PATCH 2/2] hw/riscv/k230: wire up the RMU device jack wang
  2026-07-15 11:56 ` [RFC PATCH 0/2] *** Add k230 reset management unit support *** Jack wang
  2 siblings, 2 replies; 13+ messages in thread
From: jack wang @ 2026-07-09  3:12 UTC (permalink / raw)
  To: qemu-devel; +Cc: Jack Wang, Paolo Bonzini, Fabiano Rosas, Laurent Vivier

From: Jack Wang <163wangjack@gmail.com>

Model the K230 Reset Management Unit (RMU / SYSCTL_RST) at 0x91101000.
Its control registers gather the software-controllable reset lines of the
SoC peripherals. The register semantics are modelled after the five reset
types described by the Linux mainline driver drivers/reset/reset-k230.c:

  - CPU0/CPU1: high-half per-bit write-enable strobe plus a done bit (bit12)
  - FLUSH:     the CPU register bit4, auto-cleared by hardware, no done bit
  - HW_DONE:   no write-enable; a done bit is latched when the reset fires
               and acknowledged by software with write-1-to-clear
  - SW_DONE:   plain read/write storage, no write-enable and no done bit

QEMU does not reset the target peripherals, so the model collapses the
hardware reset latency to zero: writing a reset request bit latches the
paired done bit and auto-clears the request bit in the same access, which
lets the kernel's readl_poll_timeout() loops succeed on the first read.

A qtest exercises the reset defaults, write-enable gating, HW_DONE latch
and write-1-to-clear, repeated resets, flush auto-clear and SW_DONE
storage behaviour.

Signed-off-by: Jack Wang <163wangjack@gmail.com>
---
 hw/misc/Kconfig             |   3 +
 hw/misc/k230_rmu.c          | 280 ++++++++++++++++++++++++++++++++++++
 hw/misc/meson.build         |   1 +
 hw/misc/trace-events        |   6 +
 include/hw/misc/k230_rmu.h  |  76 ++++++++++
 tests/qtest/k230-rmu-test.c | 125 ++++++++++++++++
 tests/qtest/meson.build     |   3 +-
 7 files changed, 493 insertions(+), 1 deletion(-)
 create mode 100644 hw/misc/k230_rmu.c
 create mode 100644 include/hw/misc/k230_rmu.h
 create mode 100644 tests/qtest/k230-rmu-test.c

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1543ee6653..a1470b285f 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -251,6 +251,9 @@ config DJMEMC
 config IOSB
     bool
 
+config K230_RMU
+    bool
+
 config XLNX_VERSAL_TRNG
     bool
 
diff --git a/hw/misc/k230_rmu.c b/hw/misc/k230_rmu.c
new file mode 100644
index 0000000000..f9ead7a1a0
--- /dev/null
+++ b/hw/misc/k230_rmu.c
@@ -0,0 +1,280 @@
+/*
+ * K230 Reset Management Unit (RMU / SYSCTL_RST)
+ *
+ * The RMU is a bank of reset-control registers at 0x91101000. Each register
+ * gathers the software-controllable reset lines of a group of peripherals.
+ * Register semantics are modelled after the five reset types described in the
+ * Linux mainline driver drivers/reset/reset-k230.c:
+ *
+ *   - CPU0/CPU1 : have a high-half write-enable strobe and a done bit (bit12).
+ *   - FLUSH     : bit4 of the CPU registers; hardware auto-clears it, no done.
+ *   - HW_DONE   : no write-enable; a done bit is latched when the reset fires
+ *                 and cleared by software (write-1-to-clear).
+ *   - SW_DONE   : plain read/write storage, no write-enable and no done bit.
+ *
+ * QEMU never actually resets the target peripherals, so the model collapses
+ * the hardware reset latency to zero: writing a reset request bit latches the
+ * matching done bit in the same access and auto-clears the request bit, which
+ * lets the kernel's readl_poll_timeout() loops succeed on the first read.
+ *
+ * Copyright (c) 2026 Jack Wang <163wangjack@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "migration/vmstate.h"
+#include "hw/misc/k230_rmu.h"
+#include "trace.h"
+
+/* Sentinel meaning "this reset request bit has no associated done bit". */
+#define K230_RMU_NO_DONE  0xff
+
+/* A single (reset request bit -> done bit) pairing within one register. */
+typedef struct {
+    uint8_t reset_bit;   /* reset request bit index, 0..15               */
+    uint8_t done_bit;    /* matching done bit index, or K230_RMU_NO_DONE  */
+} K230RmuPair;
+
+/* Description of one control register. */
+typedef struct {
+    hwaddr             offset;   /* word-aligned register offset               */
+    bool               has_we;   /* high 16 bits are write-enable (CPU0/CPU1)  */
+    const K230RmuPair *pairs;    /* reset/done pairings, NULL for SW_DONE banks */
+    unsigned           n_pairs;  /* number of pairings (0 for SW_DONE banks)   */
+} K230RmuReg;
+
+/*
+ * Per-register (reset bit -> done bit) tables, copied verbatim from the bit
+ * positions in the Linux driver's k230_resets[] table.
+ */
+static const K230RmuPair pairs_cpu0[]    = { {0, 12}, {4, K230_RMU_NO_DONE} };
+static const K230RmuPair pairs_cpu1[]    = { {0, 12}, {4, K230_RMU_NO_DONE} };
+static const K230RmuPair pairs_ai[]      = { {0, 31} };
+static const K230RmuPair pairs_vpu[]     = { {0, 31} };
+static const K230RmuPair pairs_hisys[]   = { {0, 4}, {1, 5} };  /* done in low bits */
+static const K230RmuPair pairs_sdio[]    = { {0, 28}, {1, 29}, {2, 30} };
+static const K230RmuPair pairs_usb[]     = { {0, 28}, {1, 29}, {0, 30}, {1, 31} };
+static const K230RmuPair pairs_spi[]     = { {0, 28}, {1, 29}, {2, 30} };
+static const K230RmuPair pairs_sec[]     = { {0, 31} };
+static const K230RmuPair pairs_dma[]     = { {0, 28}, {1, 29} };
+static const K230RmuPair pairs_decomp[]  = { {0, 31} };
+static const K230RmuPair pairs_sram[]    = { {0, 28}, {2, 30}, {3, 31} }; /* bit1 SW_DONE */
+static const K230RmuPair pairs_nonai2d[] = { {0, 31} };
+static const K230RmuPair pairs_mctl[]    = { {0, 31} };
+static const K230RmuPair pairs_isp[]     = { {6, 29}, {5, 28} }; /* other low bits SW_DONE */
+static const K230RmuPair pairs_dpu[]     = { {0, 31} };
+static const K230RmuPair pairs_disp[]    = { {0, 31} };
+static const K230RmuPair pairs_gpu[]     = { {0, 31} };
+static const K230RmuPair pairs_audio[]   = { {0, 31} };
+
+#define K230_RMU_REG(off, we, p)  { (off), (we), (p), ARRAY_SIZE(p) }
+#define K230_RMU_SW(off)          { (off), false, NULL, 0 }  /* pure SW_DONE bank */
+
+static const K230RmuReg k230_rmu_regs[] = {
+    K230_RMU_REG(K230_RMU_CPU0_CTRL,    true,  pairs_cpu0),
+    K230_RMU_REG(K230_RMU_CPU1_CTRL,    true,  pairs_cpu1),
+    K230_RMU_REG(K230_RMU_AI_CTRL,      false, pairs_ai),
+    K230_RMU_REG(K230_RMU_VPU_CTRL,     false, pairs_vpu),
+    K230_RMU_SW (K230_RMU_PERI0_CTRL),
+    K230_RMU_SW (K230_RMU_PERI1_CTRL),
+    K230_RMU_REG(K230_RMU_HISYS_CTRL,   false, pairs_hisys),
+    K230_RMU_REG(K230_RMU_SDIO_CTRL,    false, pairs_sdio),
+    K230_RMU_REG(K230_RMU_USB_CTRL,     false, pairs_usb),
+    K230_RMU_REG(K230_RMU_SPI_CTRL,     false, pairs_spi),
+    K230_RMU_REG(K230_RMU_SEC_CTRL,     false, pairs_sec),
+    K230_RMU_REG(K230_RMU_DMA_CTRL,     false, pairs_dma),
+    K230_RMU_REG(K230_RMU_DECOMP_CTRL,  false, pairs_decomp),
+    K230_RMU_REG(K230_RMU_SRAM_CTRL,    false, pairs_sram),
+    K230_RMU_REG(K230_RMU_NONAI2D_CTRL, false, pairs_nonai2d),
+    K230_RMU_REG(K230_RMU_MCTL_CTRL,    false, pairs_mctl),
+    K230_RMU_REG(K230_RMU_ISP_CTRL,     false, pairs_isp),
+    K230_RMU_REG(K230_RMU_DPU_CTRL,     false, pairs_dpu),
+    K230_RMU_REG(K230_RMU_DISP_CTRL,    false, pairs_disp),
+    K230_RMU_REG(K230_RMU_GPU_CTRL,     false, pairs_gpu),
+    K230_RMU_REG(K230_RMU_AUDIO_CTRL,   false, pairs_audio),
+    K230_RMU_SW (K230_RMU_SPI2AXI_CTRL),
+};
+
+/* Look up the register description for a word-aligned offset, or NULL. */
+static const K230RmuReg *k230_rmu_lookup(hwaddr offset)
+{
+    for (size_t i = 0; i < ARRAY_SIZE(k230_rmu_regs); i++) {
+        if (k230_rmu_regs[i].offset == offset) {
+            return &k230_rmu_regs[i];
+        }
+    }
+    return NULL;
+}
+
+static uint64_t k230_rmu_read(void *opaque, hwaddr offset, unsigned size)
+{
+    K230RmuState *s = K230_RMU(opaque);
+    uint32_t value;
+
+    if ((offset & 0x3) || offset >= K230_RMU_MMIO_SIZE) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: bad read offset 0x%" HWADDR_PRIx "\n",
+                      __func__, offset);
+        return 0;
+    }
+
+    /* All hardware side effects happen on write, so a read just returns the
+     * backing store: done bits already latched, request bits already cleared.
+     */
+    value = s->regs[offset / 4];
+    trace_k230_rmu_read(offset, value);
+    return value;
+}
+
+static void k230_rmu_write(void *opaque, hwaddr offset,
+                           uint64_t val64, unsigned size)
+{
+    K230RmuState *s = K230_RMU(opaque);
+    const K230RmuReg *r;
+    uint32_t v = (uint32_t)val64;
+    uint32_t old, new_val, we_gate, done_gate;
+    uint32_t reset_mask = 0, done_mask = 0, sw_mask;
+
+    if ((offset & 0x3) || offset >= K230_RMU_MMIO_SIZE) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: bad write offset 0x%" HWADDR_PRIx "\n",
+                      __func__, offset);
+        return;
+    }
+
+    trace_k230_rmu_write(offset, v);
+    old = s->regs[offset / 4];
+    r = k230_rmu_lookup(offset);
+
+    if (!r) {
+        /* Inside the 4 KiB window but not modelled: accept the write and log
+         * it so unexpected accesses are visible while debugging.
+         */
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: write to unmodelled offset 0x%" HWADDR_PRIx
+                      " = 0x%08x\n", __func__, offset, v);
+        s->regs[offset / 4] = v;
+        return;
+    }
+
+    /* Derive the reset/done masks for this register from its pairing table. */
+    for (unsigned i = 0; i < r->n_pairs; i++) {
+        reset_mask |= BIT(r->pairs[i].reset_bit);
+        if (r->pairs[i].done_bit != K230_RMU_NO_DONE) {
+            done_mask |= BIT(r->pairs[i].done_bit);
+        }
+    }
+
+    /*
+     * we_gate holds the low-half bits this write is allowed to modify. With
+     * write-enable (CPU0/CPU1) a low bit n is writable only when the strobe
+     * bit n+16 is also set; otherwise every low bit is directly writable.
+     */
+    we_gate = r->has_we ? ((v >> K230_RMU_WE_SHIFT) & 0xffffu) : 0xffffu;
+
+    new_val = old;
+
+    /* (A) SW_DONE storage: low-half bits that are neither reset nor done bits
+     *     are plain read/write storage.
+     */
+    sw_mask = 0xffffu & ~reset_mask & ~done_mask & we_gate;
+    new_val = (new_val & ~sw_mask) | (v & sw_mask);
+
+    /* (B) Done bits are write-1-to-clear. CPU-type done bits sit in the low
+     *     half and their clear is gated by write-enable (the strobe is folded
+     *     into we_gate); HW_DONE done bits sit in the high half, clear direct.
+     */
+    done_gate = r->has_we ? (done_mask & we_gate) : done_mask;
+    new_val &= ~(v & done_gate);
+
+    /* (C) A reset request latches its paired done bit immediately (zero
+     *     latency) and the request bit auto-clears so it can fire again.
+     *     FLUSH-type pairs (done_bit == NO_DONE) only auto-clear.
+     */
+    for (unsigned i = 0; i < r->n_pairs; i++) {
+        uint32_t rbit = BIT(r->pairs[i].reset_bit);
+        bool fire = (v & rbit) &&
+                    (!r->has_we || (v & (rbit << K230_RMU_WE_SHIFT)));
+
+        if (fire) {
+            if (r->pairs[i].done_bit != K230_RMU_NO_DONE) {
+                new_val |= BIT(r->pairs[i].done_bit);
+            } else {
+                trace_k230_rmu_flush(offset);
+            }
+            new_val &= ~rbit;
+        }
+    }
+
+    s->regs[offset / 4] = new_val;
+}
+
+static const MemoryRegionOps k230_rmu_ops = {
+    .read       = k230_rmu_read,
+    .write      = k230_rmu_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned       = false,
+    },
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void k230_rmu_reset(DeviceState *dev)
+{
+    K230RmuState *s = K230_RMU(dev);
+
+    trace_k230_rmu_reset_device();
+    memset(s->regs, 0, sizeof(s->regs));
+}
+
+static void k230_rmu_realize(DeviceState *dev, Error **errp)
+{
+    K230RmuState *s = K230_RMU(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->mmio, OBJECT(dev), &k230_rmu_ops, s,
+                          TYPE_K230_RMU, K230_RMU_MMIO_SIZE);
+    sysbus_init_mmio(sbd, &s->mmio);
+}
+
+static const VMStateDescription vmstate_k230_rmu = {
+    .name               = "k230.rmu",
+    .version_id         = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, K230RmuState, K230_RMU_NUM_REGS),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void k230_rmu_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = k230_rmu_realize;
+    device_class_set_legacy_reset(dc, k230_rmu_reset);
+    dc->vmsd = &vmstate_k230_rmu;
+    dc->desc = "K230 Reset Management Unit";
+}
+
+static const TypeInfo k230_rmu_info = {
+    .name          = TYPE_K230_RMU,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(K230RmuState),
+    .class_init    = k230_rmu_class_init,
+};
+
+static void k230_rmu_register_type(void)
+{
+    type_register_static(&k230_rmu_info);
+}
+type_init(k230_rmu_register_type)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 23265f6035..5e1155723f 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -111,6 +111,7 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
 system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files(
   'xlnx-versal-trng.c',
 ))
+system_ss.add(when: 'CONFIG_K230_RMU', if_true: files('k230_rmu.c'))
 system_ss.add(when: 'CONFIG_STM32_RCC', if_true: files('stm32_rcc.c'))
 system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
 system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index b88accc437..da0e32582e 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -417,3 +417,9 @@ iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u"
 iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x"
 iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x"
 iommu_testdev_dma_armed(bool armed) "armed=%d"
+
+# k230_rmu.c
+k230_rmu_read(uint64_t offset, uint32_t value) "K230 RMU read:  [0x%" PRIx64 "] -> 0x%08" PRIx32
+k230_rmu_write(uint64_t offset, uint32_t value) "K230 RMU write: [0x%" PRIx64 "] <- 0x%08" PRIx32
+k230_rmu_flush(uint64_t offset) "K230 RMU flush auto-clear at offset 0x%" PRIx64
+k230_rmu_reset_device(void) "K230 RMU device reset"
diff --git a/include/hw/misc/k230_rmu.h b/include/hw/misc/k230_rmu.h
new file mode 100644
index 0000000000..e6f5073c1e
--- /dev/null
+++ b/include/hw/misc/k230_rmu.h
@@ -0,0 +1,76 @@
+/*
+ * K230 Reset Management Unit (RMU / SYSCTL_RST)
+ *
+ * K230 Technical Reference Manual V0.3.1 (2024-11-18):
+ * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
+ *
+ * Register semantics cross-checked against the Linux mainline driver
+ * drivers/reset/reset-k230.c (compatible "canaan,k230-rst").
+ *
+ * Copyright (c) 2026 Jack Wang <163wangjack@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_K230_RMU_H
+#define HW_MISC_K230_RMU_H
+
+#include "qemu/bitops.h"
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_K230_RMU "riscv.k230.rmu"
+OBJECT_DECLARE_SIMPLE_TYPE(K230RmuState, K230_RMU)
+
+/* 1 KiB MMIO window, see K230_DEV_RMU in hw/riscv/k230.c. */
+#define K230_RMU_MMIO_SIZE   0x1000
+#define K230_RMU_NUM_REGS    (K230_RMU_MMIO_SIZE / 4)
+
+/* Control register offsets used by drivers/reset/reset-k230.c. */
+#define K230_RMU_CPU0_CTRL     0x04
+#define K230_RMU_CPU1_CTRL     0x0C
+#define K230_RMU_AI_CTRL       0x14
+#define K230_RMU_VPU_CTRL      0x1C
+#define K230_RMU_PERI0_CTRL    0x20   /* SW_DONE bank: TIMERx, WDTx, MAILBOX   */
+#define K230_RMU_PERI1_CTRL    0x24   /* SW_DONE bank: UARTx, I2Cx, GPIO       */
+#define K230_RMU_HISYS_CTRL    0x2C
+#define K230_RMU_SDIO_CTRL     0x34
+#define K230_RMU_USB_CTRL      0x3C
+#define K230_RMU_SPI_CTRL      0x44
+#define K230_RMU_SEC_CTRL      0x4C
+#define K230_RMU_DMA_CTRL      0x54
+#define K230_RMU_DECOMP_CTRL   0x5C
+#define K230_RMU_SRAM_CTRL     0x64   /* mixed: HW_DONE bits + SW_DONE bit1     */
+#define K230_RMU_NONAI2D_CTRL  0x6C
+#define K230_RMU_MCTL_CTRL     0x74
+#define K230_RMU_ISP_CTRL      0x80   /* mixed: HW_DONE bits + SW_DONE bits     */
+#define K230_RMU_DPU_CTRL      0x88
+#define K230_RMU_DISP_CTRL     0x90
+#define K230_RMU_GPU_CTRL      0x98
+#define K230_RMU_AUDIO_CTRL    0xA4
+#define K230_RMU_SPI2AXI_CTRL  0xA8   /* SW_DONE (active high)                  */
+
+/* Bit layout shared by the CPU0/CPU1 control registers. */
+#define K230_RMU_CPU_RESET     BIT(0)    /* reset request, auto/soft cleared    */
+#define K230_RMU_CPU_FLUSH     BIT(4)    /* L2 flush request, hw auto-clears     */
+#define K230_RMU_CPU_DONE      BIT(12)   /* done bit, write-1-to-clear           */
+
+/* The high 16 bits are per-bit write-enable strobes (CPU0/CPU1 registers). */
+#define K230_RMU_WE_SHIFT      16
+
+struct K230RmuState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    /*
+     * One 32-bit word per MMIO offset. Indexing by word offset keeps the
+     * VMState description trivial. Reset requests auto-clear, so what
+     * actually persists here is done bits and SW_DONE storage bits.
+     */
+    uint32_t regs[K230_RMU_NUM_REGS];
+};
+
+#endif /* HW_MISC_K230_RMU_H */
diff --git a/tests/qtest/k230-rmu-test.c b/tests/qtest/k230-rmu-test.c
new file mode 100644
index 0000000000..c2c862cd1d
--- /dev/null
+++ b/tests/qtest/k230-rmu-test.c
@@ -0,0 +1,125 @@
+/*
+ * 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);
+}
+
+/* Every known register reads back as 0 after reset. */
+static void test_reset_values(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+
+    g_assert_cmphex(rd(qts, K230_RMU_CPU0_CTRL),  ==, 0);
+    g_assert_cmphex(rd(qts, K230_RMU_AI_CTRL),    ==, 0);
+    g_assert_cmphex(rd(qts, K230_RMU_HISYS_CTRL), ==, 0);
+    g_assert_cmphex(rd(qts, K230_RMU_PERI0_CTRL), ==, 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");
+
+    wr(qts, K230_RMU_PERI0_CTRL, BIT(12) | BIT(13));   /* WDT0 / WDT1 reset bits */
+    g_assert_cmphex(rd(qts, K230_RMU_PERI0_CTRL), ==, (BIT(12) | BIT(13)));
+
+    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/cpu_we",         test_cpu_write_enable);
+    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);
+
+    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



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [RFC PATCH 2/2] hw/riscv/k230: wire up the RMU device
  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-09  3:12 ` jack wang
  2026-07-16 17:56   ` Daniel Henrique Barboza
  2026-07-15 11:56 ` [RFC PATCH 0/2] *** Add k230 reset management unit support *** Jack wang
  2 siblings, 1 reply; 13+ messages in thread
From: jack wang @ 2026-07-09  3:12 UTC (permalink / raw)
  To: qemu-devel
  Cc: Jack Wang, Chao Liu, Pierrick Bouvier, Paolo Bonzini,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, open list:K230 Machines

From: Jack Wang <163wangjack@gmail.com>

Replace the "rmu" create_unimplemented_device() stub with a real
K230RmuState instance mapped at the K230_DEV_RMU memmap entry, select
K230_RMU from the K230 Kconfig, and document the device.

Closes: gevico/qemu-camp-2026-k230#11
Signed-off-by: Jack Wang <163wangjack@gmail.com>
---
 docs/system/riscv/k230.rst |  1 +
 hw/riscv/Kconfig           |  1 +
 hw/riscv/k230.c            | 10 +++++++---
 include/hw/riscv/k230.h    |  2 ++
 4 files changed, 11 insertions(+), 3 deletions(-)

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..b55e90c0d2 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,12 @@ 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) */
+    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 +275,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;
 
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [RFC PATCH 0/2] *** Add k230 reset management unit support ***
  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-09  3:12 ` [RFC PATCH 2/2] hw/riscv/k230: wire up the RMU device jack wang
@ 2026-07-15 11:56 ` Jack wang
  2 siblings, 0 replies; 13+ messages in thread
From: Jack wang @ 2026-07-15 11:56 UTC (permalink / raw)
  To: qemu-devel
  Cc: chao.liu.zevorn, pierrick.bouvier, pbonzini, palmer,
	alistair.francis, liwei1518, daniel.barboza, zhiwei_liu, farosas,
	lvivier, qemu-riscv

Hi all,

A polite ping for this RFC series. 
Could anyone please take a look when you have a moment? Any feedback or guidance would be greatly appreciated.

Thanks,
Jack Wang

> 2026年7月9日 11:12,jack wang <163wangjack@gmail.com> 写道:
> 
> From: Jack Wang <163wangjack@gmail.com>
> 
> This series adds support for reset management unit found on k230 soc and 
> successfully  wires it up to k230 machine;
> 
> The register design are implemented based on k230 trm and linux reset driver
> (drivers/reset/reset-k230.c)
> 
> detailed implementations:
> 1. write-enable mask for control registers(strobe in the upper 16 bits)
> 2. hardware auto-clearing for reset and flush request bits
> 3. latching of Done bits and w1c logic
> 4. qtest coverage for register read/write behaviors and hardware state modelin
> 
> There are some checkpatch style warnings (line length, comment
> formatting) that will be addressed in v2 based on reviewer feedback.
> 
> this is my first time for hardware-unit modeling so apologies for possible mistake.
> I am apprecitated for any feedback
> 
> Jack Wang (2):
>  hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model
>  hw/riscv/k230: wire up the RMU device
> 
> docs/system/riscv/k230.rst  |   1 +
> hw/misc/Kconfig             |   3 +
> hw/misc/k230_rmu.c          | 280 ++++++++++++++++++++++++++++++++++++
> hw/misc/meson.build         |   1 +
> hw/misc/trace-events        |   6 +
> hw/riscv/Kconfig            |   1 +
> hw/riscv/k230.c             |  10 +-
> include/hw/misc/k230_rmu.h  |  76 ++++++++++
> include/hw/riscv/k230.h     |   2 +
> tests/qtest/k230-rmu-test.c | 125 ++++++++++++++++
> tests/qtest/meson.build     |   3 +-
> 11 files changed, 504 insertions(+), 4 deletions(-)
> create mode 100644 hw/misc/k230_rmu.c
> create mode 100644 include/hw/misc/k230_rmu.h
> create mode 100644 tests/qtest/k230-rmu-test.c
> 
> -- 
> 2.53.0
> 



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC PATCH 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model
  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-17 10:11   ` [RFC PATCH 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model Junze Cao
  1 sibling, 1 reply; 13+ messages in thread
From: Daniel Henrique Barboza @ 2026-07-16 17:48 UTC (permalink / raw)
  To: jack wang, qemu-devel; +Cc: Paolo Bonzini, Fabiano Rosas, Laurent Vivier

Hi,

On 7/9/2026 12:12 AM, jack wang wrote:
> From: Jack Wang <163wangjack@gmail.com>
> 
> Model the K230 Reset Management Unit (RMU / SYSCTL_RST) at 0x91101000.
> Its control registers gather the software-controllable reset lines of the
> SoC peripherals. The register semantics are modelled after the five reset
> types described by the Linux mainline driver drivers/reset/reset-k230.c:
> 
>    - CPU0/CPU1: high-half per-bit write-enable strobe plus a done bit (bit12)
>    - FLUSH:     the CPU register bit4, auto-cleared by hardware, no done bit
>    - HW_DONE:   no write-enable; a done bit is latched when the reset fires
>                 and acknowledged by software with write-1-to-clear
>    - SW_DONE:   plain read/write storage, no write-enable and no done bit
> 
> QEMU does not reset the target peripherals, so the model collapses the
> hardware reset latency to zero: writing a reset request bit latches the
> paired done bit and auto-clears the request bit in the same access, which
> lets the kernel's readl_poll_timeout() loops succeed on the first read.
> 
> A qtest exercises the reset defaults, write-enable gating, HW_DONE latch
> and write-1-to-clear, repeated resets, flush auto-clear and SW_DONE
> storage behaviour.


The qtest fails with this patch:

  29/352 qtest+qtest-riscv64 - qemu:qtest-riscv64/k230-rmu-test               ERROR            0.33s   killed by signal 6 SIGABRT


Error being thrown:

# starting QEMU: exec ./qemu-system-riscv64 -qtest unix:/tmp/qtest-3816689.sock -qtest-log /dev/null -chardev socket,path=/tmp/qtest-3816689.qmp,id=char0 -object monitor-qmp,id=qmp0,chardev=char0 -display none -audio none -run-with exit-with-parent=on -machine k230  -accel qtest
ok 1 /riscv64/k230/rmu/reset_values
# starting QEMU: exec ./qemu-system-riscv64 -qtest unix:/tmp/qtest-3816689.sock -qtest-log /dev/null -chardev socket,path=/tmp/qtest-3816689.qmp,id=char0 -object monitor-qmp,id=qmp0,chardev=char0 -display none -audio none -run-with exit-with-parent=on -machine k230  -accel qtest
**
ERROR:../tests/qtest/k230-rmu-test.c:50:test_cpu_write_enable: assertion failed (rd(qts, K230_RMU_CPU0_CTRL) == K230_RMU_CPU_DONE): (0x00000000 == 0x00001000)
not ok /riscv64/k230/rmu/cpu_we - ERROR:../tests/qtest/k230-rmu-test.c:50:test_cpu_write_enable: assertion failed (rd(qts, K230_RMU_CPU0_CTRL) == K230_RMU_CPU_DONE): (0x00000000 == 0x00001000)
Bail out!


If we apply patch 2/2 the qtest succeeds.  Which is good, but we aim for all
tests being green for all individual patches to ease our lives when bisecting
code.

You can either squash patch 2/2 into this one or move the new qtest to patch 2/2
where it'll pass.


The code LGTM aside from this bit:

> 
> Signed-off-by: Jack Wang <163wangjack@gmail.com>
> ---
>   hw/misc/Kconfig             |   3 +
>   hw/misc/k230_rmu.c          | 280 ++++++++++++++++++++++++++++++++++++
>   hw/misc/meson.build         |   1 +
>   hw/misc/trace-events        |   6 +
>   include/hw/misc/k230_rmu.h  |  76 ++++++++++
>   tests/qtest/k230-rmu-test.c | 125 ++++++++++++++++
>   tests/qtest/meson.build     |   3 +-
>   7 files changed, 493 insertions(+), 1 deletion(-)
>   create mode 100644 hw/misc/k230_rmu.c
>   create mode 100644 include/hw/misc/k230_rmu.h
>   create mode 100644 tests/qtest/k230-rmu-test.c
> 

[...]

> +static void k230_rmu_class_init(ObjectClass *klass, const void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->realize = k230_rmu_realize;
> +    device_class_set_legacy_reset(dc, k230_rmu_reset);


This is a deprecated API.  If you look at include/hw/core/qdev.h:


---
* device_class_set_legacy_reset(): set the DeviceClass::reset method
  * @dc: The device class
  * @dev_reset: the reset function
  *
  * This function sets the DeviceClass::reset method. This is widely
  * used in existing code, but new code should prefer to use the
  * Resettable API as documented in docs/devel/reset.rst.
---

I suggest reading docs/devel/reset.rst to understand which reset phases might
be applicable to this device.  Some devices implement just the phases.hold cb,
others implement enter and/or exit.  Seeing k230_rmu_reset() I think you can
get away with implementing it with just phases.hold but don't just take my word
for it.  Check out the docs and see what makes sense for this RMU.


Thanks,
Daniel


> +    dc->vmsd = &vmstate_k230_rmu;
> +    dc->desc = "K230 Reset Management Unit";
> +}
> +
> +static const TypeInfo k230_rmu_info = {
> +    .name          = TYPE_K230_RMU,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(K230RmuState),
> +    .class_init    = k230_rmu_class_init,
> +};
> +
> +static void k230_rmu_register_type(void)
> +{
> +    type_register_static(&k230_rmu_info);
> +}
> +type_init(k230_rmu_register_type)
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 23265f6035..5e1155723f 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -111,6 +111,7 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
>   system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files(
>     'xlnx-versal-trng.c',
>   ))
> +system_ss.add(when: 'CONFIG_K230_RMU', if_true: files('k230_rmu.c'))
>   system_ss.add(when: 'CONFIG_STM32_RCC', if_true: files('stm32_rcc.c'))
>   system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
>   system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
> diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> index b88accc437..da0e32582e 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -417,3 +417,9 @@ iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u"
>   iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x"
>   iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x"
>   iommu_testdev_dma_armed(bool armed) "armed=%d"
> +
> +# k230_rmu.c
> +k230_rmu_read(uint64_t offset, uint32_t value) "K230 RMU read:  [0x%" PRIx64 "] -> 0x%08" PRIx32
> +k230_rmu_write(uint64_t offset, uint32_t value) "K230 RMU write: [0x%" PRIx64 "] <- 0x%08" PRIx32
> +k230_rmu_flush(uint64_t offset) "K230 RMU flush auto-clear at offset 0x%" PRIx64
> +k230_rmu_reset_device(void) "K230 RMU device reset"
> diff --git a/include/hw/misc/k230_rmu.h b/include/hw/misc/k230_rmu.h
> new file mode 100644
> index 0000000000..e6f5073c1e
> --- /dev/null
> +++ b/include/hw/misc/k230_rmu.h
> @@ -0,0 +1,76 @@
> +/*
> + * K230 Reset Management Unit (RMU / SYSCTL_RST)
> + *
> + * K230 Technical Reference Manual V0.3.1 (2024-11-18):
> + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
> + *
> + * Register semantics cross-checked against the Linux mainline driver
> + * drivers/reset/reset-k230.c (compatible "canaan,k230-rst").
> + *
> + * Copyright (c) 2026 Jack Wang <163wangjack@gmail.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#ifndef HW_MISC_K230_RMU_H
> +#define HW_MISC_K230_RMU_H
> +
> +#include "qemu/bitops.h"
> +#include "hw/core/sysbus.h"
> +#include "qom/object.h"
> +
> +#define TYPE_K230_RMU "riscv.k230.rmu"
> +OBJECT_DECLARE_SIMPLE_TYPE(K230RmuState, K230_RMU)
> +
> +/* 1 KiB MMIO window, see K230_DEV_RMU in hw/riscv/k230.c. */
> +#define K230_RMU_MMIO_SIZE   0x1000
> +#define K230_RMU_NUM_REGS    (K230_RMU_MMIO_SIZE / 4)
> +
> +/* Control register offsets used by drivers/reset/reset-k230.c. */
> +#define K230_RMU_CPU0_CTRL     0x04
> +#define K230_RMU_CPU1_CTRL     0x0C
> +#define K230_RMU_AI_CTRL       0x14
> +#define K230_RMU_VPU_CTRL      0x1C
> +#define K230_RMU_PERI0_CTRL    0x20   /* SW_DONE bank: TIMERx, WDTx, MAILBOX   */
> +#define K230_RMU_PERI1_CTRL    0x24   /* SW_DONE bank: UARTx, I2Cx, GPIO       */
> +#define K230_RMU_HISYS_CTRL    0x2C
> +#define K230_RMU_SDIO_CTRL     0x34
> +#define K230_RMU_USB_CTRL      0x3C
> +#define K230_RMU_SPI_CTRL      0x44
> +#define K230_RMU_SEC_CTRL      0x4C
> +#define K230_RMU_DMA_CTRL      0x54
> +#define K230_RMU_DECOMP_CTRL   0x5C
> +#define K230_RMU_SRAM_CTRL     0x64   /* mixed: HW_DONE bits + SW_DONE bit1     */
> +#define K230_RMU_NONAI2D_CTRL  0x6C
> +#define K230_RMU_MCTL_CTRL     0x74
> +#define K230_RMU_ISP_CTRL      0x80   /* mixed: HW_DONE bits + SW_DONE bits     */
> +#define K230_RMU_DPU_CTRL      0x88
> +#define K230_RMU_DISP_CTRL     0x90
> +#define K230_RMU_GPU_CTRL      0x98
> +#define K230_RMU_AUDIO_CTRL    0xA4
> +#define K230_RMU_SPI2AXI_CTRL  0xA8   /* SW_DONE (active high)                  */
> +
> +/* Bit layout shared by the CPU0/CPU1 control registers. */
> +#define K230_RMU_CPU_RESET     BIT(0)    /* reset request, auto/soft cleared    */
> +#define K230_RMU_CPU_FLUSH     BIT(4)    /* L2 flush request, hw auto-clears     */
> +#define K230_RMU_CPU_DONE      BIT(12)   /* done bit, write-1-to-clear           */
> +
> +/* The high 16 bits are per-bit write-enable strobes (CPU0/CPU1 registers). */
> +#define K230_RMU_WE_SHIFT      16
> +
> +struct K230RmuState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    MemoryRegion mmio;
> +
> +    /*
> +     * One 32-bit word per MMIO offset. Indexing by word offset keeps the
> +     * VMState description trivial. Reset requests auto-clear, so what
> +     * actually persists here is done bits and SW_DONE storage bits.
> +     */
> +    uint32_t regs[K230_RMU_NUM_REGS];
> +};
> +
> +#endif /* HW_MISC_K230_RMU_H */
> diff --git a/tests/qtest/k230-rmu-test.c b/tests/qtest/k230-rmu-test.c
> new file mode 100644
> index 0000000000..c2c862cd1d
> --- /dev/null
> +++ b/tests/qtest/k230-rmu-test.c
> @@ -0,0 +1,125 @@
> +/*
> + * 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);
> +}
> +
> +/* Every known register reads back as 0 after reset. */
> +static void test_reset_values(void)
> +{
> +    QTestState *qts = qtest_init("-machine k230");
> +
> +    g_assert_cmphex(rd(qts, K230_RMU_CPU0_CTRL),  ==, 0);
> +    g_assert_cmphex(rd(qts, K230_RMU_AI_CTRL),    ==, 0);
> +    g_assert_cmphex(rd(qts, K230_RMU_HISYS_CTRL), ==, 0);
> +    g_assert_cmphex(rd(qts, K230_RMU_PERI0_CTRL), ==, 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");
> +
> +    wr(qts, K230_RMU_PERI0_CTRL, BIT(12) | BIT(13));   /* WDT0 / WDT1 reset bits */
> +    g_assert_cmphex(rd(qts, K230_RMU_PERI0_CTRL), ==, (BIT(12) | BIT(13)));
> +
> +    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/cpu_we",         test_cpu_write_enable);
> +    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);
> +
> +    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']
>   



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC PATCH 2/2] hw/riscv/k230: wire up the RMU device
  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
  0 siblings, 1 reply; 13+ messages in thread
From: Daniel Henrique Barboza @ 2026-07-16 17:56 UTC (permalink / raw)
  To: jack wang, qemu-devel
  Cc: Chao Liu, Pierrick Bouvier, Paolo Bonzini, Palmer Dabbelt,
	Alistair Francis, Weiwei Li, Liu Zhiwei, open list:K230 Machines

Hi,

On 7/9/2026 12:12 AM, jack wang wrote:
> From: Jack Wang <163wangjack@gmail.com>
> 
> Replace the "rmu" create_unimplemented_device() stub with a real
> K230RmuState instance mapped at the K230_DEV_RMU memmap entry, select
> K230_RMU from the K230 Kconfig, and document the device.

So, as I said in patch 1, applying this patch will fix the qtest that you
introduced there.  I believe we want to either squash this patch in patch
1 or move the qtest creation to this patch.  Both are fine to me.

> 
> Closes: gevico/qemu-camp-2026-k230#11

Not sure what gevico is (seems like a bootcamp, according to Google at least)  but
the "Closes" tag usually contains a gitlab bug or any other link that is publicly
available.  It's better to remove it.


Thanks,
Daniel


> Signed-off-by: Jack Wang <163wangjack@gmail.com>
> ---
>   docs/system/riscv/k230.rst |  1 +
>   hw/riscv/Kconfig           |  1 +
>   hw/riscv/k230.c            | 10 +++++++---
>   include/hw/riscv/k230.h    |  2 ++
>   4 files changed, 11 insertions(+), 3 deletions(-)
> 
> 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..b55e90c0d2 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,12 @@ 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) */
> +    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 +275,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;
>   



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC PATCH 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model
  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-17 10:11   ` Junze Cao
  1 sibling, 0 replies; 13+ messages in thread
From: Junze Cao @ 2026-07-17 10:11 UTC (permalink / raw)
  To: Jack Wang
  Cc: Junze Cao, qemu-devel, qemu-riscv, Chao Liu, Paolo Bonzini,
	Fabiano Rosas, Laurent Vivier

Hi Wang,

Thanks for working on the K230 RMU model.

I reviewed this series against the K230 TRM and the upstream Linux
reset-k230 driver. The series builds and the supplied qtest passes, but
I found several issues that should be addressed in v2.

1. The RMU currently acknowledges reset requests without actually
resetting the target devices. Please connect at least the existing K230
watchdog devices and add a qtest which verifies that their state is
reset through the RMU.

2. The register reset state does not match the TRM. Please initialize
each modelled register with its documented reset value and update the
reset-value test accordingly.

3. Some register access permissions and reset types are incorrect. In
particular, some upper SW_DONE reset lines cannot be changed, reserved
bits are writable, and CPU1 is treated as self-clearing even though it
requires separate assert and deassert operations.

4. Patch 1 adds k230-rmu-test while the K230 machine still uses the
unimplemented RMU stub. K230_RMU is enabled and wired only by patch 2,
so patch 1 cannot pass its own test when checked out independently.
Please reorder the changes, move the test to patch 2, or squash the two
patches.

5. The new source, header and qtest files should be added to the K230
section in MAINTAINERS. Please address the checkpatch errors and
warnings before sending v2 as well.

6. Please consider modelling the reset timing registers. At minimum,
their reset values and writable fields should be represented. Real-time
delays may remain simplified if the zero-latency behavior is documented
and preserves guest-visible ordering.

7. Please add coverage for the corrected reset values, CPU1
transitions, upper SW_DONE fields, reserved bits, and reset propagation
to the watchdogs.

Regards,
Junze Cao


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC PATCH 2/2] hw/riscv/k230: wire up the RMU device
  2026-07-16 17:56   ` Daniel Henrique Barboza
@ 2026-07-17 13:41     ` Chao Liu
  2026-07-17 13:56       ` Daniel Henrique Barboza
  0 siblings, 1 reply; 13+ messages in thread
From: Chao Liu @ 2026-07-17 13:41 UTC (permalink / raw)
  To: Daniel Henrique Barboza
  Cc: jack wang, qemu-devel, Pierrick Bouvier, Paolo Bonzini,
	Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei,
	open list:K230 Machines

On Thu, Jul 16, 2026 at 02:56:30PM +0800, Daniel Henrique Barboza wrote:

Hi Daniel,
> Hi,
> 
> On 7/9/2026 12:12 AM, jack wang wrote:
> > From: Jack Wang <163wangjack@gmail.com>
> > 
> > Replace the "rmu" create_unimplemented_device() stub with a real
> > K230RmuState instance mapped at the K230_DEV_RMU memmap entry, select
> > K230_RMU from the K230 Kconfig, and document the device.
> 
> So, as I said in patch 1, applying this patch will fix the qtest that you
> introduced there.  I believe we want to either squash this patch in patch
> 1 or move the qtest creation to this patch.  Both are fine to me.
> 
> > 
> > Closes: gevico/qemu-camp-2026-k230#11
> 
> Not sure what gevico is (seems like a bootcamp, according to Google at least)  but
> the "Closes" tag usually contains a gitlab bug or any other link that is publicly
> available.  It's better to remove it.
> 
I believe this link refers to:
https://github.com/gevico/qemu-camp-2026-k230/issues/11

However, according to the QEMU development collaboration workflow,
we don't need to add it.

Gevico, here is the English transliteration for the Chinese "格维". This
is a major open-source community in China focused on technical software,
which I currently organize and own.

Within our community, we run a QEMU technical training camp primarily
centered on the RISC-V architecture and exploratory work regarding
GPGPU microarchitecture.

One of our key projects involves hardware modeling for the K230, where
we are implementing a digital satellite-borne computer. The recent
contributions seen upstream were primarily patches sent by participants
of this project. We are currently in the process of contributing our
results from this stage to the QEMU upstream.

If you are interested in this, you can visit the link below.
https://qemu.gevico.online/tutorial/2026/ch3/qemu-k230/

Thanks,
Chao
> 
> Thanks,
> Daniel
> 
> 
> > Signed-off-by: Jack Wang <163wangjack@gmail.com>
> > ---
> >   docs/system/riscv/k230.rst |  1 +
> >   hw/riscv/Kconfig           |  1 +
> >   hw/riscv/k230.c            | 10 +++++++---
> >   include/hw/riscv/k230.h    |  2 ++
> >   4 files changed, 11 insertions(+), 3 deletions(-)
> > 
> > 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..b55e90c0d2 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,12 @@ 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) */
> > +    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 +275,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;
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC PATCH 2/2] hw/riscv/k230: wire up the RMU device
  2026-07-17 13:41     ` Chao Liu
@ 2026-07-17 13:56       ` Daniel Henrique Barboza
  2026-07-17 14:04         ` Chao Liu
  0 siblings, 1 reply; 13+ messages in thread
From: Daniel Henrique Barboza @ 2026-07-17 13:56 UTC (permalink / raw)
  To: Chao Liu
  Cc: jack wang, qemu-devel, Pierrick Bouvier, Paolo Bonzini,
	Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei,
	open list:K230 Machines



On 7/17/2026 10:41 AM, Chao Liu wrote:
> On Thu, Jul 16, 2026 at 02:56:30PM +0800, Daniel Henrique Barboza wrote:
> 
> Hi Daniel,
>> Hi,
>>
>> On 7/9/2026 12:12 AM, jack wang wrote:
>>> From: Jack Wang <163wangjack@gmail.com>
>>>
>>> Replace the "rmu" create_unimplemented_device() stub with a real
>>> K230RmuState instance mapped at the K230_DEV_RMU memmap entry, select
>>> K230_RMU from the K230 Kconfig, and document the device.
>>
>> So, as I said in patch 1, applying this patch will fix the qtest that you
>> introduced there.  I believe we want to either squash this patch in patch
>> 1 or move the qtest creation to this patch.  Both are fine to me.
>>
>>>
>>> Closes: gevico/qemu-camp-2026-k230#11
>>
>> Not sure what gevico is (seems like a bootcamp, according to Google at least)  but
>> the "Closes" tag usually contains a gitlab bug or any other link that is publicly
>> available.  It's better to remove it.
>>
> I believe this link refers to:
> https://github.com/gevico/qemu-camp-2026-k230/issues/11
> 
> However, according to the QEMU development collaboration workflow,
> we don't need to add it.
> 
> Gevico, here is the English transliteration for the Chinese "格维". This
> is a major open-source community in China focused on technical software,
> which I currently organize and own.
> 
> Within our community, we run a QEMU technical training camp primarily
> centered on the RISC-V architecture and exploratory work regarding
> GPGPU microarchitecture.
> 
> One of our key projects involves hardware modeling for the K230, where
> we are implementing a digital satellite-borne computer. The recent
> contributions seen upstream were primarily patches sent by participants
> of this project. We are currently in the process of contributing our
> results from this stage to the QEMU upstream.

Thanks for the info.  This seems very interesting indeed, and please keep
the QEMU contributions flowing :D


Cheers,
Daniel

> 
> If you are interested in this, you can visit the link below.
> https://qemu.gevico.online/tutorial/2026/ch3/qemu-k230/
> 
> Thanks,
> Chao
>>
>> Thanks,
>> Daniel
>>
>>
>>> Signed-off-by: Jack Wang <163wangjack@gmail.com>
>>> ---
>>>    docs/system/riscv/k230.rst |  1 +
>>>    hw/riscv/Kconfig           |  1 +
>>>    hw/riscv/k230.c            | 10 +++++++---
>>>    include/hw/riscv/k230.h    |  2 ++
>>>    4 files changed, 11 insertions(+), 3 deletions(-)
>>>
>>> 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..b55e90c0d2 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,12 @@ 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) */
>>> +    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 +275,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;
>>



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [RFC PATCH 2/2] hw/riscv/k230: wire up the RMU device
  2026-07-17 13:56       ` Daniel Henrique Barboza
@ 2026-07-17 14:04         ` Chao Liu
  0 siblings, 0 replies; 13+ messages in thread
From: Chao Liu @ 2026-07-17 14:04 UTC (permalink / raw)
  To: Daniel Henrique Barboza
  Cc: jack wang, qemu-devel, Pierrick Bouvier, Paolo Bonzini,
	Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei,
	open list:K230 Machines

On Fri, Jul 17, 2026 at 10:56:17AM +0800, Daniel Henrique Barboza wrote:
> 
> 
> On 7/17/2026 10:41 AM, Chao Liu wrote:
> > On Thu, Jul 16, 2026 at 02:56:30PM +0800, Daniel Henrique Barboza wrote:
> > 
> > Hi Daniel,
> > > Hi,
> > > 
> > > On 7/9/2026 12:12 AM, jack wang wrote:
> > > > From: Jack Wang <163wangjack@gmail.com>
> > > > 
> > > > Replace the "rmu" create_unimplemented_device() stub with a real
> > > > K230RmuState instance mapped at the K230_DEV_RMU memmap entry, select
> > > > K230_RMU from the K230 Kconfig, and document the device.
> > > 
> > > So, as I said in patch 1, applying this patch will fix the qtest that you
> > > introduced there.  I believe we want to either squash this patch in patch
> > > 1 or move the qtest creation to this patch.  Both are fine to me.
> > > 
> > > > 
> > > > Closes: gevico/qemu-camp-2026-k230#11
> > > 
> > > Not sure what gevico is (seems like a bootcamp, according to Google at least)  but
> > > the "Closes" tag usually contains a gitlab bug or any other link that is publicly
> > > available.  It's better to remove it.
> > > 
> > I believe this link refers to:
> > https://github.com/gevico/qemu-camp-2026-k230/issues/11
> > 
> > However, according to the QEMU development collaboration workflow,
> > we don't need to add it.
> > 
> > Gevico, here is the English transliteration for the Chinese "格维". This
> > is a major open-source community in China focused on technical software,
> > which I currently organize and own.
> > 
> > Within our community, we run a QEMU technical training camp primarily
> > centered on the RISC-V architecture and exploratory work regarding
> > GPGPU microarchitecture.
> > 
> > One of our key projects involves hardware modeling for the K230, where
> > we are implementing a digital satellite-borne computer. The recent
> > contributions seen upstream were primarily patches sent by participants
> > of this project. We are currently in the process of contributing our
> > results from this stage to the QEMU upstream.
> 
> Thanks for the info.  This seems very interesting indeed, and please keep
> the QEMU contributions flowing :D
> 
Yes, sir. :)

Thanks,
Chao

> Cheers,
> Daniel
> 
> > 
> > If you are interested in this, you can visit the link below.
> > https://qemu.gevico.online/tutorial/2026/ch3/qemu-k230/
> > 
> > Thanks,
> > Chao
> > > 
> > > Thanks,
> > > Daniel
> > > 
> > > 
> > > > Signed-off-by: Jack Wang <163wangjack@gmail.com>
> > > > ---
> > > >    docs/system/riscv/k230.rst |  1 +
> > > >    hw/riscv/Kconfig           |  1 +
> > > >    hw/riscv/k230.c            | 10 +++++++---
> > > >    include/hw/riscv/k230.h    |  2 ++
> > > >    4 files changed, 11 insertions(+), 3 deletions(-)
> > > > 
> > > > 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..b55e90c0d2 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,12 @@ 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) */
> > > > +    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 +275,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;
> > > 
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 0/2] hw/riscv/k230: add the K230 Reset Management Unit
  2026-07-16 17:48   ` Daniel Henrique Barboza
@ 2026-07-19 18:02     ` 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       ` [PATCH v2 2/2] hw/riscv/k230: wire up the RMU device Jack Wang
  0 siblings, 2 replies; 13+ messages in thread
From: Jack Wang @ 2026-07-19 18:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Chao Liu, Daniel Henrique Barboza, Alistair Francis, Jack Wang

References: <20260709031237.21284-1-163wangjack@gmail.com>

This v2 patch reply on review of 
This series models the Kendryte K230 Reset Management Unit (RMU), a bank of
reset-control registers at 0x91101000, and wires it into the K230 SoC. It
replaces the create_unimplemented_device("rmu") stub so guest reset drivers
(drivers/reset/reset-k230.c) work against a real model.

Patch 1 adds the standalone device model; patch 2 wires it into the SoC and
adds the qtest. The qtest is deliberately placed in patch 2 accorinding to 
suggestion from Daniel: it drives "-machine k230", which only
exists once the device is wired up.

Register semantics, reset values and reserved-bit masks are taken from the
K230 TRM chapter 2.1 "Reset" (V0.3.1) and cross-checked against the mainline
Linux driver.

Changes since v1:
 - Switched to the Resettable API (phases.hold instead of the legacy
   device_class_set_legacy_reset()), per Daniel's review.
 - Model real reset propagation: the RMU now cold-resets the two linked
   watchdogs via "wdt0"/"wdt1" QOM links when their PERI0 reset bits are
   written (Cao review).
 - CPU1 reset request is now a two-step assert/deassert (not self-clearing),
   matching the hardware (Cao review ).
 - Filled in the documented per-register reset values and reserved-bit
   (writable) masks from the TRM; the *_rst_done status bits reset to 0, so
   they follow the per-bit "Reset" column rather than the summarised total.
 - Added the reset-time-control (*_RST_TIM) registers as plain storage with
   their documented reset values.
 - Fixed the storage mask so the upper half of non-write-enable registers is
   writable (surfaced by the new TIM registers).
 - New qtests: reset values, TIM storage, reserved-bit read-only, CPU1
   two-step, and real watchdog reset propagation.
 - Moved the qtest from patch 1 to patch 2 for bisectability; added the
   MAINTAINERS entries.

Jack Wang (2):
  hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model
  hw/riscv/k230: wire up the RMU device

 MAINTAINERS                 |   3 +
 docs/system/riscv/k230.rst  |   1 +
 hw/misc/Kconfig             |   3 +
 hw/misc/k230_rmu.c          | 425 ++++++++++++++++++++++++++++++++++++
 hw/misc/meson.build         |   1 +
 hw/misc/trace-events        |   7 +
 hw/riscv/Kconfig            |   1 +
 hw/riscv/k230.c             |  15 +-
 include/hw/misc/k230_rmu.h  | 126 +++++++++++
 include/hw/riscv/k230.h     |   2 +
 tests/qtest/k230-rmu-test.c | 225 +++++++++++++++++++
 tests/qtest/meson.build     |   3 +-
 12 files changed, 808 insertions(+), 4 deletions(-)
 create mode 100644 hw/misc/k230_rmu.c
 create mode 100644 include/hw/misc/k230_rmu.h
 create mode 100644 tests/qtest/k230-rmu-test.c

-- 
2.53.0



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model
  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       ` Jack Wang
  2026-07-19 18:02       ` [PATCH v2 2/2] hw/riscv/k230: wire up the RMU device Jack Wang
  1 sibling, 0 replies; 13+ messages in thread
From: Jack Wang @ 2026-07-19 18:02 UTC (permalink / raw)
  To: qemu-devel
  Cc: Chao Liu, Daniel Henrique Barboza, Alistair Francis, Jack Wang,
	Paolo Bonzini, open list:K230 Machines

The K230 Reset Management Unit (RMU) is a bank of reset-control registers
at 0x91101000. Each register gathers the software-controllable reset lines
of a group of peripherals. Register semantics are modelled after the reset
types described in the Linux mainline driver drivers/reset/reset-k230.c and
cross-checked against the K230 TRM chapter 2.1 "Reset":

  - CPU0      : high-half write-enable strobe and a done bit (bit12); the
                reset request is self-clearing.
  - CPU1      : same layout as CPU0 but the reset request is NOT self-
                clearing: software asserts it and later deasserts it as two
                separate operations.
  - FLUSH     : bit4 of the CPU registers; hardware auto-clears it, no done.
  - HW_DONE   : no write-enable; a done bit is latched when the reset fires
                and cleared by software (write-1-to-clear).
  - SW_DONE   : plain read/write storage, no write-enable and no done bit.
  - *_RST_TIM : reset-time-control registers; plain read/write storage that
                comes up with its documented reset value.

For most groups QEMU has no real peripheral to reset, so the model collapses
the hardware reset latency to zero: writing a reset request bit latches the
matching done bit in the same access and (for self-clearing lines) auto-
clears the request bit, which lets the kernel's readl_poll_timeout() loops
succeed on the first read. The reset values and reserved-bit masks of every
modelled register are taken from the K230 TRM chapter 2.1.5.

An optional pair of "wdt0"/"wdt1" QOM links lets the machine connect the two
watchdogs; asserting their PERI0 reset bits then performs a real
device_cold_reset() on the linked WDT devices.

This commit adds:
- Device model in hw/misc/k230_rmu.c with per-register pairing tables
- Header with register offsets and bit definitions
- Kconfig and meson.build integration
- Trace events for read/write/reset operations

Signed-off-by: Jack Wang <163wangjack@gmail.com>
---
 MAINTAINERS                |   2 +
 hw/misc/Kconfig            |   3 +
 hw/misc/k230_rmu.c         | 425 +++++++++++++++++++++++++++++++++++++
 hw/misc/meson.build        |   1 +
 hw/misc/trace-events       |   7 +
 include/hw/misc/k230_rmu.h | 126 +++++++++++
 6 files changed, 564 insertions(+)
 create mode 100644 hw/misc/k230_rmu.c
 create mode 100644 include/hw/misc/k230_rmu.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 2ecfd7159d..80061ce66d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1808,8 +1808,10 @@ M: Chao Liu <chao.liu.zevorn@gmail.com>
 L: qemu-riscv@nongnu.org
 S: Maintained
 F: docs/system/riscv/k230.rst
+F: hw/misc/k230_rmu.c
 F: hw/riscv/k230.c
 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-wdt-test.c
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1543ee6653..a1470b285f 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -251,6 +251,9 @@ config DJMEMC
 config IOSB
     bool
 
+config K230_RMU
+    bool
+
 config XLNX_VERSAL_TRNG
     bool
 
diff --git a/hw/misc/k230_rmu.c b/hw/misc/k230_rmu.c
new file mode 100644
index 0000000000..af9767b3f4
--- /dev/null
+++ b/hw/misc/k230_rmu.c
@@ -0,0 +1,425 @@
+/*
+ * K230 Reset Management Unit (RMU / SYSCTL_RST)
+ *
+ * The RMU is a bank of reset-control registers at 0x91101000. Each register
+ * gathers the software-controllable reset lines of a group of peripherals.
+ * Register semantics are modelled after the five reset types described in the
+ * Linux mainline driver drivers/reset/reset-k230.c:
+ *
+ *   - CPU0      : high-half write-enable strobe, a done bit (bit12); the reset
+ *                 request is self-clearing.
+ *   - CPU1      : same layout as CPU0 but the reset request is NOT self-
+ *                 clearing: software must assert it and later deassert it as
+ *                 two separate operations.
+ *   - FLUSH     : bit4 of the CPU registers; hardware auto-clears it, no done.
+ *   - HW_DONE   : no write-enable; a done bit is latched when the reset fires
+ *                 and cleared by software (write-1-to-clear).
+ *   - SW_DONE   : plain read/write storage, no write-enable and no done bit.
+ *
+ * For most groups QEMU has no real peripheral to reset, so the model collapses
+ * the hardware reset latency to zero: writing a reset request bit latches the
+ * matching done bit in the same access and (for self-clearing lines) auto-
+ * clears the request bit, which lets the kernel's readl_poll_timeout() loops
+ * succeed on the first read. For the watchdogs, which ARE modelled, asserting
+ * their PERI0 reset bits additionally performs a real device_cold_reset() on
+ * the linked WDT devices (still zero latency, guest-visible ordering intact).
+ *
+ * Copyright (c) 2026 Jack Wang <163wangjack@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "migration/vmstate.h"
+#include "hw/core/qdev.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/misc/k230_rmu.h"
+#include "trace.h"
+
+/* Sentinel meaning "this reset request bit has no associated done bit". */
+#define K230_RMU_NO_DONE  0xff
+
+/* A single (reset request bit -> done bit) pairing within one register. */
+typedef struct {
+    uint8_t reset_bit;   /* reset request bit index, 0..15               */
+    uint8_t done_bit;    /* matching done bit index, or K230_RMU_NO_DONE  */
+    /*
+     * true:  the reset request auto-clears once the reset fires.
+     * false: it stays asserted until software deasserts it (CPU1).
+     */
+    bool    self_clear;
+} K230RmuPair;
+
+/* Description of one control register. */
+typedef struct {
+    hwaddr             offset;        /* word-aligned register offset */
+    bool               has_we;        /* high 16 bits are write-enable */
+    const K230RmuPair *pairs;         /* reset/done pairs, NULL for SW_DONE */
+    unsigned           n_pairs;       /* number of pairings (0 for SW_DONE) */
+    uint32_t           reset_val;     /* TRM reset value (per-bit column) */
+    uint32_t           writable_mask; /* writable bits; reserved bits are 0 */
+} K230RmuReg;
+
+/*
+ * Per-register (reset bit -> done bit) tables, copied verbatim from the bit
+ * positions in the Linux driver's k230_resets[] table. The last field marks
+ * whether the reset request auto-clears (see K230RmuPair::self_clear).
+ */
+static const K230RmuPair pairs_cpu0[]    = { {0, 12, true},
+                                             {4, K230_RMU_NO_DONE, true} };
+/* CPU1 reset bit0 is NOT self-clearing: assert and deassert are two steps. */
+static const K230RmuPair pairs_cpu1[]    = { {0, 12, false},
+                                             {4, K230_RMU_NO_DONE, true} };
+static const K230RmuPair pairs_ai[]      = { {0, 31, true} };
+static const K230RmuPair pairs_vpu[]     = { {0, 31, true} };
+/* done in low bits */
+static const K230RmuPair pairs_hisys[]   = { {0, 4, true}, {1, 5, true} };
+static const K230RmuPair pairs_sdio[]    = { {0, 28, true}, {1, 29, true},
+                                             {2, 30, true} };
+static const K230RmuPair pairs_usb[]     = { {0, 28, true}, {1, 29, true},
+                                             {0, 30, true}, {1, 31, true} };
+static const K230RmuPair pairs_spi[]     = { {0, 28, true}, {1, 29, true},
+                                             {2, 30, true} };
+static const K230RmuPair pairs_sec[]     = { {0, 31, true} };
+static const K230RmuPair pairs_dma[]     = { {0, 28, true}, {1, 29, true} };
+static const K230RmuPair pairs_decomp[]  = { {0, 31, true} };
+/* bit1 SW_DONE */
+static const K230RmuPair pairs_sram[]    = { {0, 28, true}, {2, 30, true},
+                                             {3, 31, true} };
+static const K230RmuPair pairs_nonai2d[] = { {0, 31, true} };
+static const K230RmuPair pairs_mctl[]    = { {0, 31, true} };
+/* other low bits SW_DONE */
+static const K230RmuPair pairs_isp[]     = { {6, 29, true}, {5, 28, true} };
+static const K230RmuPair pairs_dpu[]     = { {0, 31, true} };
+static const K230RmuPair pairs_disp[]    = { {0, 31, true} };
+static const K230RmuPair pairs_gpu[]     = { {0, 31, true} };
+static const K230RmuPair pairs_audio[]   = { {0, 31, true} };
+
+/*
+ * Reset values and reserved-bit masks are taken from the K230 TRM (chapter
+ * 2.1.5).
+ *
+ * reset_val uses the per-bit "Reset" column of each register (the status
+ * "*_rst_done" bits reset to 0, so it can differ from the register's summarised
+ * "Total Reset Value"). writable_mask lists the bits the TRM documents as
+ * accessible; undocumented (reserved) bits read back as 0 and ignore writes.
+ *
+ * K230_RMU_REG():  a control register with a reset/done pairing table.
+ * K230_RMU_SW():   a plain read/write storage bank (no reset/done pairs).
+ * K230_RMU_TIM():  a reset-time-control register - plain storage, fully
+ *                  writable, backed by its documented reset value.
+ */
+#define K230_RMU_REG(off, we, p, rst, wmask) \
+    { (off), (we), (p), ARRAY_SIZE(p), (rst), (wmask) }
+#define K230_RMU_SW(off, rst, wmask) \
+    { (off), false, NULL, 0, (rst), (wmask) }
+#define K230_RMU_TIM(off, rst) \
+    { (off), false, NULL, 0, (rst), 0xffffffffu }
+
+static const K230RmuReg k230_rmu_regs[] = {
+    /* Control registers (offset, write-enable, pairs, reset_val, writable). */
+    K230_RMU_REG(K230_RMU_CPU0_CTRL, true, pairs_cpu0, 0x00000000, 0x0000701f),
+    K230_RMU_REG(K230_RMU_CPU1_CTRL, true, pairs_cpu1, 0x00000001, 0x00003011),
+    K230_RMU_REG(K230_RMU_AI_CTRL, false, pairs_ai, 0x00000000, 0x80000001),
+    K230_RMU_REG(K230_RMU_VPU_CTRL, false, pairs_vpu, 0x00000000, 0x80000001),
+    K230_RMU_SW(K230_RMU_PERI0_CTRL, 0x000ff0ff, 0x000ff0ff),
+    K230_RMU_SW(K230_RMU_PERI1_CTRL, 0x007e7fff, 0x007e7fff),
+    K230_RMU_REG(K230_RMU_HISYS_CTRL, false, pairs_hisys, 0x00000000, 0x33),
+    K230_RMU_REG(K230_RMU_SDIO_CTRL, false, pairs_sdio, 0x00000000, 0x70000007),
+    K230_RMU_REG(K230_RMU_USB_CTRL, false, pairs_usb, 0x00000000, 0xf0000003),
+    K230_RMU_REG(K230_RMU_SPI_CTRL, false, pairs_spi, 0x00000000, 0x70000007),
+    K230_RMU_REG(K230_RMU_SEC_CTRL, false, pairs_sec, 0x00000000, 0x80000001),
+    K230_RMU_REG(K230_RMU_DMA_CTRL, false, pairs_dma, 0x00000000, 0x30000003),
+    K230_RMU_REG(K230_RMU_DECOMP_CTRL, false, pairs_decomp, 0x0, 0x80000001),
+    K230_RMU_REG(K230_RMU_SRAM_CTRL, false, pairs_sram, 0x00000002, 0xd000000f),
+    K230_RMU_REG(K230_RMU_NONAI2D_CTRL, false, pairs_nonai2d, 0x0, 0x80000001),
+    K230_RMU_REG(K230_RMU_MCTL_CTRL, false, pairs_mctl, 0x00000000, 0x80000001),
+    K230_RMU_REG(K230_RMU_ISP_CTRL, false, pairs_isp, 0x0000039f, 0x300003ff),
+    K230_RMU_REG(K230_RMU_DPU_CTRL, false, pairs_dpu, 0x00000000, 0x80000001),
+    K230_RMU_REG(K230_RMU_DISP_CTRL, false, pairs_disp, 0x00000000, 0x80000001),
+    K230_RMU_REG(K230_RMU_GPU_CTRL, false, pairs_gpu, 0x00000000, 0x80000001),
+    K230_RMU_REG(K230_RMU_AUDIO_CTRL, false, pairs_audio, 0x0, 0x80000001),
+    K230_RMU_SW(K230_RMU_SPI2AXI_CTRL, 0x00000000, 0xffffffff),
+
+    /* Reset-time-control registers (offset, reset_val). */
+    K230_RMU_TIM(K230_RMU_CPU0_TIM, 0x00fff880),
+    K230_RMU_TIM(K230_RMU_CPU1_TIM, 0x00066660),
+    K230_RMU_TIM(K230_RMU_AI_TIM, 0x00000880),
+    K230_RMU_TIM(K230_RMU_VPU_TIM, 0x00000880),
+    K230_RMU_TIM(K230_RMU_HISYS_TIM, 0x00080800),
+    K230_RMU_TIM(K230_RMU_SDCTL_TIM, 0x00080800),
+    K230_RMU_TIM(K230_RMU_USB_TIM, 0x0008d288),
+    K230_RMU_TIM(K230_RMU_SPI_TIM, 0x00080c00),
+    K230_RMU_TIM(K230_RMU_SEC_TIM, 0x00080800),
+    K230_RMU_TIM(K230_RMU_DMAC_TIM, 0x00040400),
+    K230_RMU_TIM(K230_RMU_DECOMP_TIM, 0x00040400),
+    K230_RMU_TIM(K230_RMU_SRAM_TIM, 0x00020200),
+    K230_RMU_TIM(K230_RMU_NONAI2D_TIM, 0x00020200),
+    K230_RMU_TIM(K230_RMU_MCTL_TIM, 0x00000304),
+    K230_RMU_TIM(K230_RMU_ISP_TIM, 0x00030202),
+    K230_RMU_TIM(K230_RMU_ISP_DW_TIM, 0x00020202),
+    K230_RMU_TIM(K230_RMU_DPU_TIM, 0x00020200),
+    K230_RMU_TIM(K230_RMU_DISP_TIM, 0x00040404),
+    K230_RMU_TIM(K230_RMU_V2P5D_TIM, 0x00040404),
+    K230_RMU_TIM(K230_RMU_AUDIO_TIM, 0x00000880),
+};
+
+/* Look up the register description for a word-aligned offset, or NULL. */
+static const K230RmuReg *k230_rmu_lookup(hwaddr offset)
+{
+    for (size_t i = 0; i < ARRAY_SIZE(k230_rmu_regs); i++) {
+        if (k230_rmu_regs[i].offset == offset) {
+            return &k230_rmu_regs[i];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Propagate a software reset request to the real peripherals the RMU controls.
+ * Only the two watchdogs are modelled today; asserting their PERI0 reset bits
+ * cold-resets the linked WDT device (a no-op if no device is linked).
+ */
+static void k230_rmu_propagate(K230RmuState *s, hwaddr offset, uint32_t v)
+{
+    if (offset != K230_RMU_PERI0_CTRL) {
+        return;
+    }
+    if ((v & K230_RMU_PERI0_WDT0_RST) && s->reset_targets[0]) {
+        trace_k230_rmu_target_reset(offset, 0);
+        device_cold_reset(s->reset_targets[0]);
+    }
+    if ((v & K230_RMU_PERI0_WDT1_RST) && s->reset_targets[1]) {
+        trace_k230_rmu_target_reset(offset, 1);
+        device_cold_reset(s->reset_targets[1]);
+    }
+}
+
+static uint64_t k230_rmu_read(void *opaque, hwaddr offset, unsigned size)
+{
+    K230RmuState *s = K230_RMU(opaque);
+    uint32_t value;
+
+    if ((offset & 0x3) || offset >= K230_RMU_MMIO_SIZE) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: bad read offset 0x%" HWADDR_PRIx "\n",
+                      __func__, offset);
+        return 0;
+    }
+
+    /*
+     * All hardware side effects happen on write, so a read just returns the
+     * backing store: done bits already latched, request bits already cleared.
+     */
+    value = s->regs[offset / 4];
+    trace_k230_rmu_read(offset, value);
+    return value;
+}
+
+static void k230_rmu_write(void *opaque, hwaddr offset,
+                           uint64_t val64, unsigned size)
+{
+    K230RmuState *s = K230_RMU(opaque);
+    const K230RmuReg *r;
+    uint32_t v = (uint32_t)val64;
+    uint32_t old, new_val, we_gate, done_gate;
+    uint32_t reset_mask = 0, done_mask = 0, persist_mask = 0, sw_mask;
+
+    if ((offset & 0x3) || offset >= K230_RMU_MMIO_SIZE) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: bad write offset 0x%" HWADDR_PRIx "\n",
+                      __func__, offset);
+        return;
+    }
+
+    trace_k230_rmu_write(offset, v);
+    old = s->regs[offset / 4];
+    r = k230_rmu_lookup(offset);
+
+    if (!r) {
+        /*
+         * Inside the 4 KiB window but not modelled: accept the write and log
+         * it so unexpected accesses are visible while debugging.
+         */
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: write to unmodelled offset 0x%" HWADDR_PRIx
+                      " = 0x%08x\n", __func__, offset, v);
+        s->regs[offset / 4] = v;
+        return;
+    }
+
+    /*
+     * Derive the reset/done/persist masks for this register from its pairing
+     * table. persist_mask collects reset bits that do NOT self-clear (CPU1):
+     * those behave as write-enable-gated storage so software can deassert them.
+     */
+    for (unsigned i = 0; i < r->n_pairs; i++) {
+        reset_mask |= BIT(r->pairs[i].reset_bit);
+        if (r->pairs[i].done_bit != K230_RMU_NO_DONE) {
+            done_mask |= BIT(r->pairs[i].done_bit);
+        }
+        if (!r->pairs[i].self_clear) {
+            persist_mask |= BIT(r->pairs[i].reset_bit);
+        }
+    }
+
+    /*
+     * we_gate holds the low-half bits this write is allowed to modify. With
+     * write-enable (CPU0/CPU1) a low bit n is writable only when the strobe
+     * bit n+16 is also set; otherwise every low bit is directly writable.
+     */
+    /*
+     * we_gate holds the bits this write is allowed to modify. With write-enable
+     * (CPU0/CPU1) a low bit n is writable only when the strobe bit n+16 is also
+     * set, so only the low half can ever change; without write-enable every bit
+     * is directly writable (reserved bits are still dropped by writable_mask).
+     */
+    we_gate = r->has_we ? ((v >> K230_RMU_WE_SHIFT) & 0xffffu) : 0xffffffffu;
+
+    new_val = old;
+
+    /*
+     * (A) Writable storage: bits that are neither reset nor done bits are plain
+     *     read/write storage, plus any non-self-clearing reset bits (so the
+     *     CPU1 request can be asserted and later deasserted). Reserved bits
+     *     are dropped via writable_mask; for write-enable registers only the
+     *     low half is ever reachable (we_gate is confined to 16 bits above).
+     */
+    sw_mask = ((~reset_mask & ~done_mask) | persist_mask)
+              & we_gate & r->writable_mask;
+    new_val = (new_val & ~sw_mask) | (v & sw_mask);
+
+    /*
+     * (B) Done bits are write-1-to-clear. CPU-type done bits sit in the low
+     *     half and their clear is gated by write-enable (the strobe is folded
+     *     into we_gate); HW_DONE done bits sit in the high half, clear direct.
+     */
+    done_gate = r->has_we ? (done_mask & we_gate) : done_mask;
+    new_val &= ~(v & done_gate);
+
+    /*
+     * (C) A reset request latches its paired done bit immediately (zero
+     *     latency). Self-clearing requests then auto-clear so they can fire
+     *     again; non-self-clearing ones (CPU1) stay asserted until deasserted.
+     *     FLUSH-type pairs (done_bit == NO_DONE) only auto-clear.
+     */
+    for (unsigned i = 0; i < r->n_pairs; i++) {
+        uint32_t rbit = BIT(r->pairs[i].reset_bit);
+        bool fire = (v & rbit) &&
+                    (!r->has_we || (v & (rbit << K230_RMU_WE_SHIFT)));
+
+        if (fire) {
+            if (r->pairs[i].done_bit != K230_RMU_NO_DONE) {
+                new_val |= BIT(r->pairs[i].done_bit);
+            } else {
+                trace_k230_rmu_flush(offset);
+            }
+            if (r->pairs[i].self_clear) {
+                new_val &= ~rbit;
+            }
+        }
+    }
+
+    s->regs[offset / 4] = new_val;
+
+    /* (D) Drive a real cold reset of any linked peripheral (e.g. watchdogs). */
+    k230_rmu_propagate(s, offset, v);
+}
+
+static const MemoryRegionOps k230_rmu_ops = {
+    .read       = k230_rmu_read,
+    .write      = k230_rmu_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned       = false,
+    },
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void k230_rmu_reset_hold(Object *obj, ResetType type)
+{
+    K230RmuState *s = K230_RMU(obj);
+
+    trace_k230_rmu_reset_device();
+
+    /*
+     * Start from all-zero, then apply each modelled register's documented reset
+     * value (K230 TRM chapter 2.1.5). Unmodelled offsets stay 0.
+     */
+    memset(s->regs, 0, sizeof(s->regs));
+    for (size_t i = 0; i < ARRAY_SIZE(k230_rmu_regs); i++) {
+        s->regs[k230_rmu_regs[i].offset / 4] = k230_rmu_regs[i].reset_val;
+    }
+}
+
+static void k230_rmu_realize(DeviceState *dev, Error **errp)
+{
+    K230RmuState *s = K230_RMU(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->mmio, OBJECT(dev), &k230_rmu_ops, s,
+                          TYPE_K230_RMU, K230_RMU_MMIO_SIZE);
+    sysbus_init_mmio(sbd, &s->mmio);
+}
+
+static void k230_rmu_init(Object *obj)
+{
+    K230RmuState *s = K230_RMU(obj);
+
+    /*
+     * Optional links to the peripherals the RMU resets. The machine sets these
+     * before realize; leaving one unset simply disables that reset path.
+     */
+    object_property_add_link(obj, "wdt0", TYPE_DEVICE,
+                             (Object **)&s->reset_targets[0],
+                             qdev_prop_allow_set_link_before_realize,
+                             OBJ_PROP_LINK_STRONG);
+    object_property_add_link(obj, "wdt1", TYPE_DEVICE,
+                             (Object **)&s->reset_targets[1],
+                             qdev_prop_allow_set_link_before_realize,
+                             OBJ_PROP_LINK_STRONG);
+}
+
+static const VMStateDescription vmstate_k230_rmu = {
+    .name               = "k230.rmu",
+    .version_id         = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, K230RmuState, K230_RMU_NUM_REGS),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void k230_rmu_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    dc->realize = k230_rmu_realize;
+    /* Resettable API: a register-clearing device needs only phases.hold. */
+    rc->phases.hold = k230_rmu_reset_hold;
+    dc->vmsd = &vmstate_k230_rmu;
+    dc->desc = "K230 Reset Management Unit";
+}
+
+static const TypeInfo k230_rmu_info = {
+    .name          = TYPE_K230_RMU,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(K230RmuState),
+    .instance_init = k230_rmu_init,
+    .class_init    = k230_rmu_class_init,
+};
+
+static void k230_rmu_register_type(void)
+{
+    type_register_static(&k230_rmu_info);
+}
+type_init(k230_rmu_register_type)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 23265f6035..5e1155723f 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -111,6 +111,7 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
 system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files(
   'xlnx-versal-trng.c',
 ))
+system_ss.add(when: 'CONFIG_K230_RMU', if_true: files('k230_rmu.c'))
 system_ss.add(when: 'CONFIG_STM32_RCC', if_true: files('stm32_rcc.c'))
 system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
 system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index b88accc437..ee65409713 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -417,3 +417,10 @@ iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u"
 iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x"
 iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x"
 iommu_testdev_dma_armed(bool armed) "armed=%d"
+
+# k230_rmu.c
+k230_rmu_read(uint64_t offset, uint32_t value) "K230 RMU read:  [0x%" PRIx64 "] -> 0x%08" PRIx32
+k230_rmu_write(uint64_t offset, uint32_t value) "K230 RMU write: [0x%" PRIx64 "] <- 0x%08" PRIx32
+k230_rmu_flush(uint64_t offset) "K230 RMU flush auto-clear at offset 0x%" PRIx64
+k230_rmu_reset_device(void) "K230 RMU device reset"
+k230_rmu_target_reset(uint64_t offset, unsigned target) "K230 RMU cold-reset target at offset 0x%" PRIx64 " target %u"
diff --git a/include/hw/misc/k230_rmu.h b/include/hw/misc/k230_rmu.h
new file mode 100644
index 0000000000..40ad70a14c
--- /dev/null
+++ b/include/hw/misc/k230_rmu.h
@@ -0,0 +1,126 @@
+/*
+ * K230 Reset Management Unit (RMU / SYSCTL_RST)
+ *
+ * K230 Technical Reference Manual V0.3.1 (2024-11-18):
+ * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf
+ *
+ * Register semantics cross-checked against the Linux mainline driver
+ * drivers/reset/reset-k230.c (compatible "canaan,k230-rst").
+ *
+ * Copyright (c) 2026 Jack Wang <163wangjack@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_K230_RMU_H
+#define HW_MISC_K230_RMU_H
+
+#include "qemu/bitops.h"
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_K230_RMU "riscv.k230.rmu"
+OBJECT_DECLARE_SIMPLE_TYPE(K230RmuState, K230_RMU)
+
+/* 1 KiB MMIO window, see K230_DEV_RMU in hw/riscv/k230.c. */
+#define K230_RMU_MMIO_SIZE   0x1000
+#define K230_RMU_NUM_REGS    (K230_RMU_MMIO_SIZE / 4)
+
+/*
+ * Control-register offsets used by drivers/reset/reset-k230.c. The driver names
+ * differ from the K230 TRM (chapter 2.1 "Reset"), but the offsets and bit
+ * layouts match the TRM's "*_RST_CTL" registers, named in the trailing comment.
+ * The documented reset values live in k230_rmu_regs[] in k230_rmu.c.
+ */
+#define K230_RMU_CPU0_CTRL     0x04   /* CPU0_RST_CTL */
+#define K230_RMU_CPU1_CTRL     0x0C   /* CPU1_RST_CTL */
+#define K230_RMU_AI_CTRL       0x14   /* AI_RST_CTL */
+#define K230_RMU_VPU_CTRL      0x1C   /* VPU_RST_CTL */
+#define K230_RMU_PERI0_CTRL    0x20   /* SOC_CTL_RST_CTL */
+/*
+ * PERI0 (SOC_CTL_RST_CTL) software-reset bits for the two watchdogs. In the TRM
+ * these are wdt_0_reset (bit12) / wdt_1_reset (bit13), whose reset value is 1
+ * ("reset disassert"). The model treats a write of these bits as "trigger a
+ * reset", driving a real cold reset of the linked WDT devices (see the
+ * "wdt0"/"wdt1" QOM links).
+ */
+#define K230_RMU_PERI0_WDT0_RST  BIT(12)
+#define K230_RMU_PERI0_WDT1_RST  BIT(13)
+#define K230_RMU_PERI1_CTRL    0x24   /* LOSYS_RST_CTL */
+#define K230_RMU_HISYS_CTRL    0x2C   /* HISYS_RST_CTL */
+#define K230_RMU_SDIO_CTRL     0x34   /* SDC_RST_CTL */
+#define K230_RMU_USB_CTRL      0x3C   /* USB_RST_CTL */
+#define K230_RMU_SPI_CTRL      0x44   /* SPI_RST_CTL */
+#define K230_RMU_SEC_CTRL      0x4C   /* SEC_RST_CTL */
+#define K230_RMU_DMA_CTRL      0x54   /* DMA_RST_CTL */
+#define K230_RMU_DECOMP_CTRL   0x5C   /* DECOMPRESS_RST_CTL */
+#define K230_RMU_SRAM_CTRL     0x64   /* SRAM_RST_CTL */
+#define K230_RMU_NONAI2D_CTRL  0x6C   /* NONAI2D_RST_CTL */
+#define K230_RMU_MCTL_CTRL     0x74   /* MCTL_RST_CTL */
+#define K230_RMU_ISP_CTRL      0x80   /* ISP_RST_CTL */
+#define K230_RMU_DPU_CTRL      0x88   /* DPU_RST_CTL */
+#define K230_RMU_DISP_CTRL     0x90   /* DISP_RST_CTL */
+#define K230_RMU_GPU_CTRL      0x98   /* V2P5D_RST_CTL */
+#define K230_RMU_AUDIO_CTRL    0xA4   /* AUDIO_RST_CTL */
+#define K230_RMU_SPI2AXI_CTRL  0xA8   /* SW_DONE (not in TRM) */
+
+/*
+ * Reset-time-control ("*_RST_TIM") registers. These interleave with the control
+ * registers above and are plain read/write timing storage with no side effects,
+ * so the model backs them with their documented reset value (see k230_rmu.c).
+ */
+#define K230_RMU_CPU0_TIM      0x00   /* CPU0_RST_TIM */
+#define K230_RMU_CPU1_TIM      0x08   /* CPU1_RST_TIM */
+#define K230_RMU_AI_TIM        0x10   /* AI_RST_TIM */
+#define K230_RMU_VPU_TIM       0x18   /* VPU_RST_TIM */
+#define K230_RMU_HISYS_TIM     0x28   /* HISYS_HCLK_TIM */
+#define K230_RMU_SDCTL_TIM     0x30   /* SDCTL_RST_TIM */
+#define K230_RMU_USB_TIM       0x38   /* USB_RST_TIM */
+#define K230_RMU_SPI_TIM       0x40   /* SPI_RST_TIM */
+#define K230_RMU_SEC_TIM       0x48   /* SEC_SYS_RST_TIM */
+#define K230_RMU_DMAC_TIM      0x50   /* DMAC_RST_TIM */
+#define K230_RMU_DECOMP_TIM    0x58   /* DECOMPRESS_RST_TIM */
+#define K230_RMU_SRAM_TIM      0x60   /* SRAM_RST_TIM */
+#define K230_RMU_NONAI2D_TIM   0x68   /* NONAI2D_RST_TIM */
+#define K230_RMU_MCTL_TIM      0x70   /* MCTL_RST_TIM */
+#define K230_RMU_ISP_TIM       0x78   /* ISP_RST_TIM */
+#define K230_RMU_ISP_DW_TIM    0x7C   /* ISP_DW_RST_TIM */
+#define K230_RMU_DPU_TIM       0x84   /* DPU_RST_TIM */
+#define K230_RMU_DISP_TIM      0x8C   /* DISP_SYS_RST_TIM */
+#define K230_RMU_V2P5D_TIM     0x94   /* V2P5D_SYS_RST_TIM */
+#define K230_RMU_AUDIO_TIM     0xA0   /* AUDIO_RST_TIM */
+
+/* Bit layout shared by the CPU0/CPU1 control registers. */
+#define K230_RMU_CPU_RESET     BIT(0)    /* reset request, auto/soft cleared */
+#define K230_RMU_CPU_FLUSH     BIT(4)    /* L2 flush, hw auto-clears */
+#define K230_RMU_CPU_DONE      BIT(12)   /* done bit, write-1-to-clear */
+
+/* The high 16 bits are per-bit write-enable strobes (CPU0/CPU1 registers). */
+#define K230_RMU_WE_SHIFT      16
+
+/* Devices the RMU can cold-reset: WDT0, WDT1 (via the "wdt0"/"wdt1" links). */
+#define K230_RMU_NUM_TARGETS   2
+
+struct K230RmuState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+
+    /*
+     * One 32-bit word per MMIO offset. Indexing by word offset keeps the
+     * VMState description trivial. Reset requests auto-clear, so what
+     * actually persists here is done bits and SW_DONE storage bits.
+     */
+    uint32_t regs[K230_RMU_NUM_REGS];
+
+    /*
+     * Optional links to the peripherals the RMU actually resets. Populated by
+     * the machine via the "wdt0"/"wdt1" QOM link properties; a NULL entry just
+     * means "no device connected" and the reset request is a no-op.
+     */
+    DeviceState *reset_targets[K230_RMU_NUM_TARGETS];
+};
+
+#endif /* HW_MISC_K230_RMU_H */
-- 
2.53.0



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v2 2/2] hw/riscv/k230: wire up the RMU device
  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
  1 sibling, 0 replies; 13+ messages in thread
From: Jack Wang @ 2026-07-19 18:02 UTC (permalink / raw)
  To: qemu-devel
  Cc: Chao Liu, Daniel Henrique Barboza, Alistair Francis, Jack Wang,
	Pierrick Bouvier, Paolo Bonzini, Palmer Dabbelt, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Fabiano Rosas,
	Laurent Vivier, open list:K230 Machines

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



^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-07-19 18:04 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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       ` [PATCH v2 2/2] hw/riscv/k230: wire up the RMU device Jack Wang
2026-07-17 10:11   ` [RFC PATCH 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model 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

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.