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 <[email protected]>
---
 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 <[email protected]>
 L: [email protected]
 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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


Reply via email to