From nobody Sat Jul 25 12:29:10 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1784484234; cv=none; d=zohomail.com; s=zohoarc; b=aJRUWj3KEa46OYsjA+qr+gz+2s8YrXhpXx+JMH/ij0jNDH7y6OZ3YsXn28HZWRPKb8S7y9oWiIyuuBjggVPhd4EUWtSbP6/0a7wZ/IDLUQ+KoZg4IWm+qhF8GCjI3Ob9nc12Gp+hfCf0tAAKHcjsYkYLFMHJeQDx5m1J29shBvI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1784484234; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=sDBzCBBeKzYhlZ4EBSkgFwMKpaTq84txzgP/MSeZwi0=; b=exmZNmW5DODtRbiIj3EEqJSVj9PsRT/8zJFO26mnZi7ZpVUr/q393i21Fw1Y7uFW/SwNIsd3ID9SbmKLBfeGmbyXw/zaJ1QDmAHRlTyN8dwVra0sSWHsL3cVss2YyMXcf6oxSFEVUSA3nHjIAgwZVbLiZsm7mRpKU5gRPixhmdE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from=<163wangjack@gmail.com> (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1784484234507544.8831817781797; Sun, 19 Jul 2026 11:03:54 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wlVrI-0006wL-7O; Sun, 19 Jul 2026 14:03:24 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wlVrF-0006va-TW for qemu-devel@nongnu.org; Sun, 19 Jul 2026 14:03:21 -0400 Received: from mail-pg1-x52c.google.com ([2607:f8b0:4864:20::52c]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wlVrC-0004hS-HG for qemu-devel@nongnu.org; Sun, 19 Jul 2026 14:03:21 -0400 Received: by mail-pg1-x52c.google.com with SMTP id 41be03b00d2f7-c96b08cdd1cso6149800a12.0 for ; Sun, 19 Jul 2026 11:03:18 -0700 (PDT) Received: from localhost.localdomain ([82.40.42.115]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-cb519de3437sm3312907a12.28.2026.07.19.11.03.12 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 19 Jul 2026 11:03:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784484197; x=1785088997; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=sDBzCBBeKzYhlZ4EBSkgFwMKpaTq84txzgP/MSeZwi0=; b=e0RzWY8fxWsB1fWDRH8OQ7WydvqILNiN4LPmx11tk1GNdOnahbC33WlpHuQ3/dxR76 gJQilYf4NLdgAFVIj/ZVMNQmJ33i+tktFZImd2I1cJ9SMIhUSfZV5M1gGgOdRd4LL1g8 p1q9XjR9FBoOOOi1LT4jL10BB1Drjxxv2kZ4nVB/2N7OCOzyaVdKUq4n0d7W+Qz0SqIS Fdjdetcwa1Gv/GZBGS6gUFLDLQdfY7EE1Lb7HGy6gHoaXXHNlM0g+hXXae2nJuDLYDCa uMYSgKnmiFMAfLKnQix8IQO32Y/AhCMo2aXfbQRpRlLhg/wFGDjrOv8I1ikGD2XM1MO9 //Hg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784484197; x=1785088997; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=sDBzCBBeKzYhlZ4EBSkgFwMKpaTq84txzgP/MSeZwi0=; b=HZSn2oB0gmoP+iJgDV07O1d5mPU6SveVRW0Bmh8jMsu5M3et5MO6QF0VS7vs6qNVxl Cg/9CbQ8WP0icu6yDQXWHkejiIyAK8Whg1SwA5FjQ1FU9FUmdPO71C0Ebij/7RgWFFOi HxOB37u9xz1xkTZRmgTO6RZO2+2hB8DymH5cfYwZQmxli2QJo5VVORujB9p9r+RHuBYW bw+B/jy4yXc4ljk2ae4QrlrceMdnOH+iZYSIrh7w2xb9fYzznGM+yVdLQdJ/HEk7mm9g 7U+GfiWqCwwA0gPDyeDiy1QaCBD8Ea4V2pzhuwYS33nJmyNY79vgp3CAjdga3ZgajNRt nOUg== X-Gm-Message-State: AOJu0YxR4btaQUMlyF9hXlSP8MOt/ve5c98wIOaLDJho8C6E4nJV71Lb wdyZtCn6zt+YdvQYqaEJvFE02SZ91GbCvpTougWrxPawQZUhR68OkhJLRqmldA== X-Gm-Gg: AfdE7cm1tBIeluOhaqyJYvEX9WK/hN7zssoESb2reYHrD9uBCnEzu41ov9uBiWxXpHB BjKZBaN4BShooS6xF251CHSoBMJcJu/cfQuerNCQsxRuNqGiGhQYMalVLvGE5eY2GRbueyCILZW d3OEwDqwGZGd4Uz4ApctndS8h3mpDTO3XRDoTVnVDdSGgs+P/FiigFytdoKUnooXS/Z7PWPaUCE /VsCUjYnilG3p7XAfDghXPGyfFx++WHl96xI/5e4CB04vsaH/ubmRodsbfeKJimFMUjbthMiO9a XNVGHQR1XBtK7XXwPrTPHOaFL2/84M0/dC5Tor3KDpOtveKybyh84gHfKxIXkGOtRMZA6dsa6jE L6TrDaFFioUfEncA8njP/WnYOW0m/pA7yO88i2G0B4Yz+eH2Nu4ai6vuQ8qBNM0c9zX/mjSFXS2 ycgeAPk4t9G5L3JsBsgeVMKdjt X-Received: by 2002:a05:6a21:9210:b0:3c0:9c19:65ad with SMTP id adf61e73a8af0-3c3ada3981dmr13492055637.69.1784484196454; Sun, 19 Jul 2026 11:03:16 -0700 (PDT) From: Jack Wang <163wangjack@gmail.com> To: qemu-devel@nongnu.org Cc: Chao Liu , Daniel Henrique Barboza , Alistair Francis , Jack Wang <163wangjack@gmail.com>, Paolo Bonzini , qemu-riscv@nongnu.org (open list:K230 Machines) Subject: [PATCH v2 1/2] hw/misc/k230_rmu: add Kendryte K230 Reset Management Unit model Date: Mon, 20 Jul 2026 02:02:46 +0800 Message-ID: <20260719180247.8660-2-163wangjack@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260719180247.8660-1-163wangjack@gmail.com> References: <20260719180247.8660-1-163wangjack@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::52c; envelope-from=163wangjack@gmail.com; helo=mail-pg1-x52c.google.com X-Spam_score_int: -13 X-Spam_score: -1.4 X-Spam_bar: - X-Spam_report: (-1.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, FROM_STARTS_WITH_NUMS=0.738, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1784484236850158500 Content-Type: text/plain; charset="utf-8" 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 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 =20 +config K230_RMU + bool + config XLNX_VERSAL_TRNG bool =20 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 regist= er + * 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 f= ires + * and cleared by software (write-1-to-clear). + * - SW_DONE : plain read/write storage, no write-enable and no done b= it. + * + * For most groups QEMU has no real peripheral to reset, so the model coll= apses + * 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() lo= ops + * succeed on the first read. For the watchdogs, which ARE modelled, asser= ting + * their PERI0 reset bits additionally performs a real device_cold_reset()= on + * the linked WDT devices (still zero latency, guest-visible ordering inta= ct). + * + * 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_DON= E */ + 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 b= it + * positions in the Linux driver's k230_resets[] table. The last field mar= ks + * whether the reset request auto-clears (see K230RmuPair::self_clear). + */ +static const K230RmuPair pairs_cpu0[] =3D { {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[] =3D { {0, 12, false}, + {4, K230_RMU_NO_DONE, true} }; +static const K230RmuPair pairs_ai[] =3D { {0, 31, true} }; +static const K230RmuPair pairs_vpu[] =3D { {0, 31, true} }; +/* done in low bits */ +static const K230RmuPair pairs_hisys[] =3D { {0, 4, true}, {1, 5, true} = }; +static const K230RmuPair pairs_sdio[] =3D { {0, 28, true}, {1, 29, true= }, + {2, 30, true} }; +static const K230RmuPair pairs_usb[] =3D { {0, 28, true}, {1, 29, true= }, + {0, 30, true}, {1, 31, true} = }; +static const K230RmuPair pairs_spi[] =3D { {0, 28, true}, {1, 29, true= }, + {2, 30, true} }; +static const K230RmuPair pairs_sec[] =3D { {0, 31, true} }; +static const K230RmuPair pairs_dma[] =3D { {0, 28, true}, {1, 29, true= } }; +static const K230RmuPair pairs_decomp[] =3D { {0, 31, true} }; +/* bit1 SW_DONE */ +static const K230RmuPair pairs_sram[] =3D { {0, 28, true}, {2, 30, true= }, + {3, 31, true} }; +static const K230RmuPair pairs_nonai2d[] =3D { {0, 31, true} }; +static const K230RmuPair pairs_mctl[] =3D { {0, 31, true} }; +/* other low bits SW_DONE */ +static const K230RmuPair pairs_isp[] =3D { {6, 29, true}, {5, 28, true= } }; +static const K230RmuPair pairs_dpu[] =3D { {0, 31, true} }; +static const K230RmuPair pairs_disp[] =3D { {0, 31, true} }; +static const K230RmuPair pairs_gpu[] =3D { {0, 31, true} }; +static const K230RmuPair pairs_audio[] =3D { {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 summ= arised + * "Total Reset Value"). writable_mask lists the bits the TRM documents as + * accessible; undocumented (reserved) bits read back as 0 and ignore writ= es. + * + * 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[] =3D { + /* Control registers (offset, write-enable, pairs, reset_val, writable= ). */ + K230_RMU_REG(K230_RMU_CPU0_CTRL, true, pairs_cpu0, 0x00000000, 0x00007= 01f), + K230_RMU_REG(K230_RMU_CPU1_CTRL, true, pairs_cpu1, 0x00000001, 0x00003= 011), + K230_RMU_REG(K230_RMU_AI_CTRL, false, pairs_ai, 0x00000000, 0x80000001= ), + K230_RMU_REG(K230_RMU_VPU_CTRL, false, pairs_vpu, 0x00000000, 0x800000= 01), + 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, 0x7000= 0007), + K230_RMU_REG(K230_RMU_USB_CTRL, false, pairs_usb, 0x00000000, 0xf00000= 03), + K230_RMU_REG(K230_RMU_SPI_CTRL, false, pairs_spi, 0x00000000, 0x700000= 07), + K230_RMU_REG(K230_RMU_SEC_CTRL, false, pairs_sec, 0x00000000, 0x800000= 01), + K230_RMU_REG(K230_RMU_DMA_CTRL, false, pairs_dma, 0x00000000, 0x300000= 03), + K230_RMU_REG(K230_RMU_DECOMP_CTRL, false, pairs_decomp, 0x0, 0x8000000= 1), + K230_RMU_REG(K230_RMU_SRAM_CTRL, false, pairs_sram, 0x00000002, 0xd000= 000f), + K230_RMU_REG(K230_RMU_NONAI2D_CTRL, false, pairs_nonai2d, 0x0, 0x80000= 001), + K230_RMU_REG(K230_RMU_MCTL_CTRL, false, pairs_mctl, 0x00000000, 0x8000= 0001), + K230_RMU_REG(K230_RMU_ISP_CTRL, false, pairs_isp, 0x0000039f, 0x300003= ff), + K230_RMU_REG(K230_RMU_DPU_CTRL, false, pairs_dpu, 0x00000000, 0x800000= 01), + K230_RMU_REG(K230_RMU_DISP_CTRL, false, pairs_disp, 0x00000000, 0x8000= 0001), + K230_RMU_REG(K230_RMU_GPU_CTRL, false, pairs_gpu, 0x00000000, 0x800000= 01), + 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 =3D 0; i < ARRAY_SIZE(k230_rmu_regs); i++) { + if (k230_rmu_regs[i].offset =3D=3D offset) { + return &k230_rmu_regs[i]; + } + } + return NULL; +} + +/* + * Propagate a software reset request to the real peripherals the RMU cont= rols. + * 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 !=3D 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 =3D K230_RMU(opaque); + uint32_t value; + + if ((offset & 0x3) || offset >=3D 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 t= he + * backing store: done bits already latched, request bits already clea= red. + */ + value =3D 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 =3D K230_RMU(opaque); + const K230RmuReg *r; + uint32_t v =3D (uint32_t)val64; + uint32_t old, new_val, we_gate, done_gate; + uint32_t reset_mask =3D 0, done_mask =3D 0, persist_mask =3D 0, sw_mas= k; + + if ((offset & 0x3) || offset >=3D 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 =3D s->regs[offset / 4]; + r =3D 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 + " =3D 0x%08x\n", __func__, offset, v); + s->regs[offset / 4] =3D v; + return; + } + + /* + * Derive the reset/done/persist masks for this register from its pair= ing + * table. persist_mask collects reset bits that do NOT self-clear (CPU= 1): + * those behave as write-enable-gated storage so software can deassert= them. + */ + for (unsigned i =3D 0; i < r->n_pairs; i++) { + reset_mask |=3D BIT(r->pairs[i].reset_bit); + if (r->pairs[i].done_bit !=3D K230_RMU_NO_DONE) { + done_mask |=3D BIT(r->pairs[i].done_bit); + } + if (!r->pairs[i].self_clear) { + persist_mask |=3D BIT(r->pairs[i].reset_bit); + } + } + + /* + * we_gate holds the low-half bits this write is allowed to modify. Wi= th + * write-enable (CPU0/CPU1) a low bit n is writable only when the stro= be + * 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 i= s also + * set, so only the low half can ever change; without write-enable eve= ry bit + * is directly writable (reserved bits are still dropped by writable_m= ask). + */ + we_gate =3D r->has_we ? ((v >> K230_RMU_WE_SHIFT) & 0xffffu) : 0xfffff= fffu; + + new_val =3D 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 t= he + * CPU1 request can be asserted and later deasserted). Reserved bi= ts + * are dropped via writable_mask; for write-enable registers only = the + * low half is ever reachable (we_gate is confined to 16 bits abov= e). + */ + sw_mask =3D ((~reset_mask & ~done_mask) | persist_mask) + & we_gate & r->writable_mask; + new_val =3D (new_val & ~sw_mask) | (v & sw_mask); + + /* + * (B) Done bits are write-1-to-clear. CPU-type done bits sit in the l= ow + * half and their clear is gated by write-enable (the strobe is fo= lded + * into we_gate); HW_DONE done bits sit in the high half, clear di= rect. + */ + done_gate =3D r->has_we ? (done_mask & we_gate) : done_mask; + new_val &=3D ~(v & done_gate); + + /* + * (C) A reset request latches its paired done bit immediately (zero + * latency). Self-clearing requests then auto-clear so they can fi= re + * again; non-self-clearing ones (CPU1) stay asserted until deasse= rted. + * FLUSH-type pairs (done_bit =3D=3D NO_DONE) only auto-clear. + */ + for (unsigned i =3D 0; i < r->n_pairs; i++) { + uint32_t rbit =3D BIT(r->pairs[i].reset_bit); + bool fire =3D (v & rbit) && + (!r->has_we || (v & (rbit << K230_RMU_WE_SHIFT))); + + if (fire) { + if (r->pairs[i].done_bit !=3D K230_RMU_NO_DONE) { + new_val |=3D BIT(r->pairs[i].done_bit); + } else { + trace_k230_rmu_flush(offset); + } + if (r->pairs[i].self_clear) { + new_val &=3D ~rbit; + } + } + } + + s->regs[offset / 4] =3D new_val; + + /* (D) Drive a real cold reset of any linked peripheral (e.g. watchdog= s). */ + k230_rmu_propagate(s, offset, v); +} + +static const MemoryRegionOps k230_rmu_ops =3D { + .read =3D k230_rmu_read, + .write =3D k230_rmu_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + .unaligned =3D false, + }, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, +}; + +static void k230_rmu_reset_hold(Object *obj, ResetType type) +{ + K230RmuState *s =3D 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 =3D 0; i < ARRAY_SIZE(k230_rmu_regs); i++) { + s->regs[k230_rmu_regs[i].offset / 4] =3D k230_rmu_regs[i].reset_va= l; + } +} + +static void k230_rmu_realize(DeviceState *dev, Error **errp) +{ + K230RmuState *s =3D K230_RMU(dev); + SysBusDevice *sbd =3D 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 =3D 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 =3D { + .name =3D "k230.rmu", + .version_id =3D 1, + .minimum_version_id =3D 1, + .fields =3D (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 =3D DEVICE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->realize =3D k230_rmu_realize; + /* Resettable API: a register-clearing device needs only phases.hold. = */ + rc->phases.hold =3D k230_rmu_reset_hold; + dc->vmsd =3D &vmstate_k230_rmu; + dc->desc =3D "K230 Reset Management Unit"; +} + +static const TypeInfo k230_rmu_info =3D { + .name =3D TYPE_K230_RMU, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(K230RmuState), + .instance_init =3D k230_rmu_init, + .class_init =3D 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: file= s( 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_s= yscfg.c')) system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_s= yscfg.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) "gv= a=3D0x%" PRIx64 " len=3D%u" iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=3D0= x%x actual=3D0x%x" iommu_testdev_dma_result(uint32_t result) "DMA completed result=3D0x%x" iommu_testdev_dma_armed(bool armed) "armed=3D%d" + +# k230_rmu.c +k230_rmu_read(uint64_t offset, uint32_t value) "K230 RMU read: [0x%" PRIx= 64 "] -> 0x%08" PRIx32 +k230_rmu_write(uint64_t offset, uint32_t value) "K230 RMU write: [0x%" PRI= x64 "] <- 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-res= et 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_Tec= hnical_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 co= mment. + * 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 t= he 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 c= ontrol + * registers above and are plain read/write timing storage with no side ef= fects, + * 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 clear= ed */ +#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. Populate= d 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 */ --=20 2.53.0 From nobody Sat Jul 25 12:29:10 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1784484243; cv=none; d=zohomail.com; s=zohoarc; b=YqLLwptIZ71K66kxQk+l5+3tDYFsXDNLz5EfR4d2qlKWMn2fnbO7T2S2rMw2OMMuagY2tqi0E6JPLjy0mmPt50iCZCPic6aBXylG1TExxEIEl9r2vS9CLVYvvnxVKLnTKUbkLl5Ew/inaBuuxZcGNAkdcXpbV70CmNh3ZbB3wXQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1784484243; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=TwQYHCt7Kk8nxw2rwfRfbN3mzgvXTaoOo6RqYm4euOs=; b=K4LHpRyVDrDWbURgCeI5CE0amHaChG7183dvloRlzoguzC/pbN7DLYQjFco4li1O1x3XFhBtobHwS4EaGQY7zaCrPE0w8ZgT+Sv3FxHE+Px5qaiquskqLFs+/JIYmNOtCysf3TgdXCL1KRcfMv/o47PfD8D1dKTx0hIB22X3uW8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from=<163wangjack@gmail.com> (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1784484243191738.8540129979749; Sun, 19 Jul 2026 11:04:03 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wlVrR-0006wz-Ol; Sun, 19 Jul 2026 14:03:33 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wlVrQ-0006we-Hd for qemu-devel@nongnu.org; Sun, 19 Jul 2026 14:03:32 -0400 Received: from mail-pg1-x52c.google.com ([2607:f8b0:4864:20::52c]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <163wangjack@gmail.com>) id 1wlVrI-0004hq-Mg for qemu-devel@nongnu.org; Sun, 19 Jul 2026 14:03:31 -0400 Received: by mail-pg1-x52c.google.com with SMTP id 41be03b00d2f7-ca7bea5e5b3so6688713a12.1 for ; Sun, 19 Jul 2026 11:03:24 -0700 (PDT) Received: from localhost.localdomain ([82.40.42.115]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-cb519de3437sm3312907a12.28.2026.07.19.11.03.16 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sun, 19 Jul 2026 11:03:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784484203; x=1785089003; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=TwQYHCt7Kk8nxw2rwfRfbN3mzgvXTaoOo6RqYm4euOs=; b=IJd8u8w4t6i45iuB1xmNiKQKQ8lEwUanJxcDVeb8C/g87BYv+wPnaOKcpVNDAg7PJ9 CpQRHmNTqgkmXNh85l6RsvQv3adIg6351lqOqJMwCw3lPyEtTCqNaUqCVeVK9Ar6Eumc f2z1JjTFZ3oE/qw1o2o/QQQRDeVN8UeAskN1tUx6vL0PAZTFppvCf95WcsZnJLtig99K fAmla++QGiQs0vwqtLu5yz6MMiwqYWiIUmlaybek1gzI7AkJJpzl7Aa39N9DZi8RtEtd 2RJTuMNDFbLXkfS5kAB/AB50fQR2fF3IAgrtYwEquR69aN68ultka9fYff23GxDAou+F Z4rg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784484203; x=1785089003; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=TwQYHCt7Kk8nxw2rwfRfbN3mzgvXTaoOo6RqYm4euOs=; b=ibWSXygWRAmEtLymCdvpeieZfqRIbvTOCbjaUvfpAcX/RVfPmJ7NWZKut+NlPUf8nb XmrqVcPW8bAk878t8zGRpnD+eEf1NSutHyDgnK2gR7u5xHU7+QzC1ABTdio5qc9cI7un PRFBjuOtfvrXUfOc51Ll62dlYSt6ERdCHy/gtXrPmbq5o9aWdXBR5pz3inD7P2x0pGmK AegsVv50uwC3zqPqwrJwRt8rTM3ssfmA7SiRd3zfYh3m5tLuLQVMaEW7oup9BhzN0KWP JTIw8vXUibqOSCoJtMNKtkt6Hjhb/gUJyThzMmQGkIjU/GiAdLIP9MoKxHqCkMbfQFPE r8gg== X-Gm-Message-State: AOJu0YzB2UPUKPyFyxuNaoFXP6EfCuyXapMJ/jFWFul18FzNxaN6r5Pl 5qZSy1hHchq+Wid+SkeMAZjC0lx01W/CHrv9JxHx32pZShRBsXh0jjSGX+wlsg== X-Gm-Gg: AfdE7ck1t/+V+5T7bU2nzmctc4YhR/njiurGCU9oit2tavgowBdUOqev70hB+QdFoYB JrKkvjmnlRbcat8S96Q57Aop5dX6AeyMAOeiHt6R/mbwkfgxysvPsh3nzQgze6Kn/kkwr9UrZp4 sbmMBXGSEUi49h2ExmSxTa72DScii346ruEIwOHDyXZYhKWFpV0YXZPb8/Io8VHGLVVCEwdyn6b oQYGVr1HKpgEtP4PUr8OLbGQILnB2kcfZ3Pc5oYqdU8S/+jf5rm4oATcJX9euSc0jYSr3erb+sz 65TldSZHuaBgVyDv3rCOx1zyck3YxrNcD1y1BcPMJhPoH1SrBIGzR+A3mmVFpcXgCA/msQUOGDT a/0pfqAMzPtuzfPj/DPsBNJDbfnhKoFFD9z335ppFFtCRVFgjgMfe1S+aeo8HSIFyBAN6anvhf/ AyZlgKWxBQdtu9KQ== X-Received: by 2002:a05:6a21:3983:b0:3c3:68b0:e273 with SMTP id adf61e73a8af0-3c3ad6042c6mr12130265637.15.1784484202793; Sun, 19 Jul 2026 11:03:22 -0700 (PDT) From: Jack Wang <163wangjack@gmail.com> To: qemu-devel@nongnu.org Cc: Chao Liu , Daniel Henrique Barboza , Alistair Francis , Jack Wang <163wangjack@gmail.com>, Pierrick Bouvier , Paolo Bonzini , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Fabiano Rosas , Laurent Vivier , qemu-riscv@nongnu.org (open list:K230 Machines) Subject: [PATCH v2 2/2] hw/riscv/k230: wire up the RMU device Date: Mon, 20 Jul 2026 02:02:47 +0800 Message-ID: <20260719180247.8660-3-163wangjack@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260719180247.8660-1-163wangjack@gmail.com> References: <20260719180247.8660-1-163wangjack@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::52c; envelope-from=163wangjack@gmail.com; helo=mail-pg1-x52c.google.com X-Spam_score_int: -13 X-Spam_score: -1.4 X-Spam_bar: - X-Spam_report: (-1.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, FROM_STARTS_WITH_NUMS=0.738, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1784484244944158500 Content-Type: text/plain; charset="utf-8" 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=3Dy 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 =20 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 =20 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); =20 qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C9= 08); @@ -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_IR= Q)); =20 + /* 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); =20 - 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); =20 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" =20 #define C908_CPU_HARTID (0) =20 @@ -33,6 +34,7 @@ typedef struct K230SoCState { RISCVHartArrayState c908_cpu; /* Small core */ =20 K230WdtState wdt[2]; + K230RmuState rmu; MemoryRegion sram; MemoryRegion bootrom; =20 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 regist= er's + * summarised "Total Reset Value". + */ +static void test_reset_values(void) +{ + QTestState *qts =3D qtest_init("-machine k230"); + + /* Control registers. */ + g_assert_cmphex(rd(qts, K230_RMU_CPU0_CTRL), =3D=3D, 0x00000000); + g_assert_cmphex(rd(qts, K230_RMU_CPU1_CTRL), =3D=3D, 0x00000001); + g_assert_cmphex(rd(qts, K230_RMU_AI_CTRL), =3D=3D, 0x00000000); + g_assert_cmphex(rd(qts, K230_RMU_PERI0_CTRL), =3D=3D, 0x000ff0ff); + g_assert_cmphex(rd(qts, K230_RMU_PERI1_CTRL), =3D=3D, 0x007e7fff); + g_assert_cmphex(rd(qts, K230_RMU_HISYS_CTRL), =3D=3D, 0x00000000); + g_assert_cmphex(rd(qts, K230_RMU_SRAM_CTRL), =3D=3D, 0x00000002); + g_assert_cmphex(rd(qts, K230_RMU_ISP_CTRL), =3D=3D, 0x0000039f); + + /* A representative reset-time-control register. */ + g_assert_cmphex(rd(qts, K230_RMU_CPU0_TIM), =3D=3D, 0x00fff880); + g_assert_cmphex(rd(qts, K230_RMU_USB_TIM), =3D=3D, 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 =3D qtest_init("-machine k230"); + + g_assert_cmphex(rd(qts, K230_RMU_MCTL_TIM), =3D=3D, 0x00000304); + wr(qts, K230_RMU_MCTL_TIM, 0x12345678); + g_assert_cmphex(rd(qts, K230_RMU_MCTL_TIM), =3D=3D, 0x12345678); + + qtest_quit(qts); +} + +/* + * Reserved bits are read-only zero: a write to an undocumented bit positi= on is + * dropped. ISP_CTL documents bits 0-9 and 28-29; bit 15 is reserved. + */ +static void test_reserved_bits(void) +{ + QTestState *qts =3D qtest_init("-machine k230"); + uint32_t before =3D 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), =3D=3D, 0); + + qtest_quit(qts); +} + +/* + * CPU0: a low-half write without its strobe is ignored; with the strobe t= he + * reset request fires, latches done (bit12), and the request bit auto-cle= ars. + */ +static void test_cpu_write_enable(void) +{ + QTestState *qts =3D 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), =3D=3D, 0); + + /* With strobe (bit16): request bit0 takes effect, done (bit12) latche= s. */ + 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), =3D=3D, 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 =3D qtest_init("-machine k230"); + uint32_t v; + + wr(qts, K230_RMU_HISYS_CTRL, BIT(0)); /* no strobe */ + v =3D rd(qts, K230_RMU_HISYS_CTRL); + g_assert_cmphex(v & BIT(4), =3D=3D, BIT(4)); /* done latched */ + g_assert_cmphex(v & BIT(0), =3D=3D, 0); /* request bit auto-cle= ared */ + + wr(qts, K230_RMU_HISYS_CTRL, BIT(4)); /* write-1-to-clear */ + g_assert_cmphex(rd(qts, K230_RMU_HISYS_CTRL) & BIT(4), =3D=3D, 0); + + qtest_quit(qts); +} + +/* Repeated resets: one HW_DONE line can be triggered again and again. */ +static void test_repeat_reset(void) +{ + QTestState *qts =3D qtest_init("-machine k230"); + + for (int i =3D 0; i < 3; i++) { + wr(qts, K230_RMU_AI_CTRL, BIT(0)); /* request */ + g_assert_cmphex(rd(qts, K230_RMU_AI_CTRL) & BIT(31), =3D=3D, BIT(3= 1)); + wr(qts, K230_RMU_AI_CTRL, BIT(31)); /* W1C done */ + g_assert_cmphex(rd(qts, K230_RMU_AI_CTRL) & BIT(31), =3D=3D, 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 =3D 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, =3D= =3D, 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 =3D 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), =3D=3D, (BIT(12) | BIT(1= 3))); + + qtest_quit(qts); +} + +/* + * CPU1 needs separate assert and deassert operations: unlike CPU0 its res= et + * request bit does NOT self-clear. Assert (bit0 + strobe) latches done (b= it12) + * and keeps bit0 set; deassert (strobe only, bit0 low) clears bit0. + */ +static void test_cpu1_two_step(void) +{ + QTestState *qts =3D 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), =3D=3D, + 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), =3D=3D, 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), =3D=3D, 0); + + qtest_quit(qts); +} + +/* + * Reset propagation: asserting a watchdog reset line in PERI0_CTRL perfor= ms a + * real cold reset of the linked WDT device. Put WDT0 into a non-default s= tate + * (TORR !=3D 0), pulse the RMU, and confirm WDT0 came back to its reset v= alue. + */ +#define K230_WDT0_BASE 0x91106000ULL +#define K230_WDT_TORR 0x04 + +static void test_wdt_reset_propagation(void) +{ + QTestState *qts =3D 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), =3D= =3D, 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), =3D= =3D, 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 =3D ['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'] : []) =20 qtests_hexagon =3D ['boot-serial-test'] =20 --=20 2.53.0