[PATCH v3 1/3] hw/misc/k230_iomux: add Kendryte K230 IOMUX model

Kangjie Huang <[email protected]>
Newsgroups org.nongnu.qemu-riscv,org.nongnu.qemu-devel
Message-ID <[email protected]>
Add a SysBus model for the 64 K230 Function IO configuration
registers documented by the K230 Technical Reference Manual.

Preserve the writable configuration fields across aligned 32-bit
accesses, apply the documented reset values, and ignore writes to
read-only and reserved fields. Offsets outside the documented register
list read as zero and ignore writes.

This provides the register behavior used by the Kendryte SDK U-Boot
pinctrl-single driver and Linux IOMUX configuration code.

Suggested-by: Alistair Francis <[email protected]>
Suggested-by: Chao Liu <[email protected]>
Suggested-by: Junze Cao <[email protected]>
Signed-off-by: Kangjie Huang <[email protected]>
---
 MAINTAINERS                  |   2 +
 hw/misc/Kconfig              |   3 +
 hw/misc/k230_iomux.c         | 125 +++++++++++++++++++++++++++++++++++
 hw/misc/meson.build          |   1 +
 hw/misc/trace-events         |   4 ++
 include/hw/misc/k230_iomux.h |  43 ++++++++++++
 6 files changed, 178 insertions(+)
 create mode 100644 hw/misc/k230_iomux.c
 create mode 100644 include/hw/misc/k230_iomux.h

diff --git a/MAINTAINERS b/MAINTAINERS
index f249be744e..adcff4bbdb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1845,8 +1845,10 @@ S: Maintained
 F: docs/system/riscv/k230.rst
 F: hw/riscv/k230.c
 F: hw/watchdog/k230_wdt.c
+F: hw/misc/k230_iomux.c
 F: include/hw/riscv/k230.h
 F: include/hw/watchdog/k230_wdt.h
+F: include/hw/misc/k230_iomux.h
 F: tests/functional/riscv64/test_k230.py
 F: tests/qtest/k230-wdt-test.c
 
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index b8860dd3e7..5011c10c53 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -260,4 +260,7 @@ config XLNX_ZYNQ_DDRC
 config AXIADO_CLK
     bool
 
+config K230_IOMUX
+    bool
+
 source macio/Kconfig
diff --git a/hw/misc/k230_iomux.c b/hw/misc/k230_iomux.c
new file mode 100644
index 0000000000..11fcc22a09
--- /dev/null
+++ b/hw/misc/k230_iomux.c
@@ -0,0 +1,125 @@
+/*
+ * Kendryte K230 IOMUX
+ *
+ * Copyright (c) 2026 Kangjie Huang <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Provides the Function IO configuration registers documented by the K230
+ * Technical Reference Manual.
+ *
+ * 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
+ *
+ * For more information, see <https://www.kendryte.com/en/proDetail/230>
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/k230_iomux.h"
+#include "migration/vmstate.h"
+#include "qemu/module.h"
+#include "trace.h"
+
+/* TRM V0.3.1 section 12.9.2.1 Function IO register list. */
+static const uint32_t k230_iomux_reset_values[K230_IOMUX_NUM_REGS] = {
+    0x944, 0x944, 0x929, 0x908, 0x888, 0x908, 0x948, 0x890,
+    0x890, 0x890, 0x910, 0x890, 0x890, 0x8a9, 0xa9e, 0xabf,
+    0xb9e, 0xb9e, 0xb9e, 0xb9e, 0xb9e, 0xb9e, 0xb9e, 0xb9e,
+    0xb1e, 0xa90, 0xabf, 0xb9e, 0xb9e, 0xb9e, 0xb9e, 0xb9e,
+    0xbd0, 0xbd0, 0xbd0, 0xbd0, 0xbd0, 0xbd0, 0x890, 0x910,
+    0x890, 0x910, 0x890, 0x910, 0x890, 0x910, 0x890, 0x910,
+    0x890, 0x910, 0x890, 0x910, 0x890, 0x910, 0x89e, 0x89f,
+    0x99e, 0x99e, 0x99e, 0x99e, 0x890, 0x890, 0x8a9, 0x8a9,
+};
+
+static uint64_t k230_iomux_read(void *opaque, hwaddr offset, unsigned size)
+{
+    K230IomuxState *s = K230_IOMUX(opaque);
+    uint32_t value = 0;
+
+    if (offset < K230_IOMUX_REGS_SIZE) {
+        value = s->regs[offset >> 2];
+    }
+
+    trace_k230_iomux_read(offset, value);
+    return value;
+}
+
+static void k230_iomux_write(void *opaque, hwaddr offset,
+                             uint64_t value, unsigned size)
+{
+    K230IomuxState *s = K230_IOMUX(opaque);
+
+    trace_k230_iomux_write(offset, value);
+    if (offset < K230_IOMUX_REGS_SIZE) {
+        s->regs[offset >> 2] = value & K230_IOMUX_WRITABLE_MASK;
+    }
+}
+
+static const MemoryRegionOps k230_iomux_ops = {
+    .read = k230_iomux_read,
+    .write = k230_iomux_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+};
+
+static void k230_iomux_enter_reset(Object *obj, ResetType type)
+{
+    K230IomuxState *s = K230_IOMUX(obj);
+
+    memcpy(s->regs, k230_iomux_reset_values,
+           sizeof(k230_iomux_reset_values));
+}
+
+static void k230_iomux_init(Object *obj)
+{
+    K230IomuxState *s = K230_IOMUX(obj);
+
+    memory_region_init_io(&s->mmio, obj, &k230_iomux_ops, s,
+                          TYPE_K230_IOMUX, K230_IOMUX_MMIO_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static const VMStateDescription vmstate_k230_iomux = {
+    .name = "k230.iomux",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, K230IomuxState, K230_IOMUX_NUM_REGS),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void k230_iomux_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    dc->desc = "Kendryte K230 IOMUX";
+    dc->vmsd = &vmstate_k230_iomux;
+    rc->phases.enter = k230_iomux_enter_reset;
+}
+
+static const TypeInfo k230_iomux_info = {
+    .name          = TYPE_K230_IOMUX,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(K230IomuxState),
+    .instance_init = k230_iomux_init,
+    .class_init    = k230_iomux_class_init,
+};
+
+static void k230_iomux_register_types(void)
+{
+    type_register_static(&k230_iomux_info);
+}
+
+type_init(k230_iomux_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index e86d9ad6b3..fab6336c91 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -171,3 +171,4 @@ system_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa_ec.c'))
 system_ss.add(when: 'CONFIG_LASI', if_true: files('lasi.c'))
 
 system_ss.add(when: 'CONFIG_AXIADO_CLK', if_true: files('axiado_clk.c'))
+system_ss.add(when: 'CONFIG_K230_IOMUX', if_true: files('k230_iomux.c'))
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index c9a868b3ef..388967c9e1 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -405,6 +405,10 @@ djmemc_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRI
 iosb_read(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u"
 iosb_write(int reg, uint64_t value, unsigned int size) "reg=0x%x value=0x%"PRIx64" size=%u"
 
+# k230_iomux.c
+k230_iomux_read(uint64_t offset, uint32_t value) "offset=0x%" PRIx64 " value=0x%" PRIx32
+k230_iomux_write(uint64_t offset, uint64_t value) "offset=0x%" PRIx64 " value=0x%" PRIx64
+
 # aspeed_sli.c
 aspeed_sli_write(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
 aspeed_sli_read(uint64_t offset, unsigned int size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
diff --git a/include/hw/misc/k230_iomux.h b/include/hw/misc/k230_iomux.h
new file mode 100644
index 0000000000..0a637c6292
--- /dev/null
+++ b/include/hw/misc/k230_iomux.h
@@ -0,0 +1,43 @@
+/*
+ * Kendryte K230 IOMUX
+ *
+ * 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
+ *
+ * Copyright (c) 2026 Kangjie Huang <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_K230_IOMUX_H
+#define HW_MISC_K230_IOMUX_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_K230_IOMUX "riscv.k230.iomux"
+OBJECT_DECLARE_SIMPLE_TYPE(K230IomuxState, K230_IOMUX)
+
+#define K230_IOMUX_MMIO_SIZE 0x800
+#define K230_IOMUX_NUM_REGS 64
+#define K230_IOMUX_REGS_SIZE \
+    (K230_IOMUX_NUM_REGS * sizeof(uint32_t))
+
+/*
+ * Register layout (TRM V0.3.1, section 12.9.2.2):
+ *   bit 31     DI: RO, input data from outside the chip to the PAD,
+ *               reads as zero; the model does not simulate external
+ *               pin input
+ *   bits 30-14 RSV: RO, reserved
+ *   bits 13-0  RW configuration fields
+ */
+#define K230_IOMUX_WRITABLE_MASK 0x00003fff
+
+struct K230IomuxState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+    uint32_t regs[K230_IOMUX_NUM_REGS];
+};
+
+#endif /* HW_MISC_K230_IOMUX_H */
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.