* [PATCH v4 1/7] hw/char: Add dw8250 UART
2026-08-22 19:43 [PATCH v4 0/7] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
@ 2026-08-22 19:43 ` Kuan-Wei Chiu
2026-08-22 19:43 ` [PATCH v4 2/7] hw/misc: Add Sophgo CV1800B clock controller Kuan-Wei Chiu
` (5 subsequent siblings)
6 siblings, 0 replies; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-22 19:43 UTC (permalink / raw)
To: pierrick.bouvier, almer, alistair.francis, pbonzini,
marcandre.lureau, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu, wxchen0913,
jserv, marscheng, eleanor15x, qemu-devel, qemu-riscv,
Kuan-Wei Chiu, Chao Liu
Add the dw8250 uart support. This hardware is a widely used 16550A
derivative that includes additional registers.
Without this specific device support, the Linux 8250_dw driver fails to
probe the extended registers (UCV, CPR, etc.), which are essential for
correct feature detection.
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
---
hw/char/Kconfig | 4 ++
hw/char/dw8250.c | 131 +++++++++++++++++++++++++++++++++++++++
hw/char/meson.build | 1 +
include/hw/char/dw8250.h | 27 ++++++++
4 files changed, 163 insertions(+)
create mode 100644 hw/char/dw8250.c
create mode 100644 include/hw/char/dw8250.h
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 020c0a84bb..418d99b757 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -95,3 +95,7 @@ config IP_OCTAL_232
bool
default y
depends on IPACK
+
+config DW8250
+ bool
+ select SERIAL
diff --git a/hw/char/dw8250.c b/hw/char/dw8250.c
new file mode 100644
index 0000000000..ccf05dd9ad
--- /dev/null
+++ b/hw/char/dw8250.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Synopsys DesignWare APB UART (DW 8250)
+ *
+ * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "hw/char/dw8250.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/core/qdev-properties-system.h"
+
+#define DW_UART_REGION_SIZE 0x100
+
+#define DW_UART_RE_EN (0xB4 >> 2) /* Receiver Output Enable Register */
+#define DW_UART_DLF (0xC0 >> 2) /* Divisor Latch Fraction Register */
+#define DW_UART_CPR (0xF4 >> 2) /* Component Parameter Register */
+#define DW_UART_UCV (0xF8 >> 2) /* UART Component Version */
+#define DW_UART_CTR (0xFC >> 2) /* Component Type Register */
+
+#define DW_UART_UCV_VALUE 0x3332332A /* "323*" -> v3.23a */
+#define DW_UART_CTR_VALUE 0x44570110 /* "DW" */
+
+static uint64_t dw8250_ext_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ DW8250State *s = opaque;
+
+ switch (addr >> s->regshift) {
+ case DW_UART_UCV:
+ return DW_UART_UCV_VALUE;
+ case DW_UART_CPR:
+ return 0x00000000; /* No advanced features (DMA, extra FIFOs) */
+ case DW_UART_CTR:
+ return DW_UART_CTR_VALUE;
+
+ case DW_UART_RE_EN:
+ case DW_UART_DLF:
+ /*
+ * Return 0 to indicate these optional features
+ * (RS485 and Fractional Divisor) are not implemented.
+ */
+ return 0x00000000;
+
+ default:
+ return 0;
+ }
+}
+
+static void dw8250_ext_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+ qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write to address 0x%" HWADDR_PRIx "\n",
+ __func__, addr);
+}
+
+static const MemoryRegionOps dw8250_ext_ops = {
+ .read = dw8250_ext_read,
+ .write = dw8250_ext_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+};
+
+static void dw8250_instance_init(Object *obj)
+{
+ DW8250State *s = DW8250(obj);
+
+ s->serial_mm = qdev_new("serial-mm");
+ object_property_add_child(obj, "serial-mm", OBJECT(s->serial_mm));
+ object_property_add_alias(obj, "chardev", OBJECT(s->serial_mm), "chardev");
+}
+
+static void dw8250_realize(DeviceState *dev, Error **errp)
+{
+ DW8250State *s = DW8250(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+ SysBusDevice *serial_sbd = SYS_BUS_DEVICE(s->serial_mm);
+
+ memory_region_init(&s->container, OBJECT(dev), "dw8250-container",
+ DW_UART_REGION_SIZE);
+ sysbus_init_mmio(sbd, &s->container);
+
+ qdev_prop_set_uint8(s->serial_mm, "regshift", s->regshift);
+ qdev_prop_set_uint8(s->serial_mm, "endianness", DEVICE_LITTLE_ENDIAN);
+ if (!sysbus_realize(serial_sbd, errp)) {
+ return;
+ }
+
+ memory_region_init_io(&s->ext_iomem, OBJECT(dev), &dw8250_ext_ops, s,
+ "dw8250-ext", DW_UART_REGION_SIZE);
+ memory_region_add_subregion(&s->container, 0, &s->ext_iomem);
+
+ memory_region_add_subregion_overlap(&s->container, 0,
+ sysbus_mmio_get_region(serial_sbd, 0), 1);
+
+ sysbus_pass_irq(sbd, serial_sbd);
+}
+
+static const Property dw8250_properties[] = {
+ DEFINE_PROP_UINT8("regshift", DW8250State, regshift, 2),
+};
+
+static void dw8250_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = dw8250_realize;
+ device_class_set_props(dc, dw8250_properties);
+}
+
+static const TypeInfo dw8250_info = {
+ .name = TYPE_DW8250,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(DW8250State),
+ .instance_init = dw8250_instance_init,
+ .class_init = dw8250_class_init,
+};
+
+static void dw8250_register_types(void)
+{
+ type_register_static(&dw8250_info);
+}
+
+type_init(dw8250_register_types)
diff --git a/hw/char/meson.build b/hw/char/meson.build
index fc3d7ee506..b2250ee6ae 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -38,6 +38,7 @@ system_ss.add(when: 'CONFIG_STM32L4X5_USART', if_true: files('stm32l4x5_usart.c'
system_ss.add(when: 'CONFIG_MCHP_PFSOC_MMUART', if_true: files('mchp_pfsoc_mmuart.c'))
system_ss.add(when: 'CONFIG_HTIF', if_true: files('riscv_htif.c'))
system_ss.add(when: 'CONFIG_GOLDFISH_TTY', if_true: files('goldfish_tty.c'))
+system_ss.add(when: 'CONFIG_DW8250', if_true: files('dw8250.c'))
specific_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('terminal3270.c'))
specific_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr_vty.c'))
diff --git a/include/hw/char/dw8250.h b/include/hw/char/dw8250.h
new file mode 100644
index 0000000000..59396ad202
--- /dev/null
+++ b/include/hw/char/dw8250.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Synopsys DesignWare APB UART (DW 8250)
+ *
+ * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#ifndef HW_CHAR_DW8250_H
+#define HW_CHAR_DW8250_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_DW8250 "dw8250"
+OBJECT_DECLARE_SIMPLE_TYPE(DW8250State, DW8250)
+
+struct DW8250State {
+ SysBusDevice parent_obj;
+
+ MemoryRegion container;
+ MemoryRegion ext_iomem;
+ DeviceState *serial_mm;
+
+ uint8_t regshift;
+};
+
+#endif
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 2/7] hw/misc: Add Sophgo CV1800B clock controller
2026-08-22 19:43 [PATCH v4 0/7] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
2026-08-22 19:43 ` [PATCH v4 1/7] hw/char: Add dw8250 UART Kuan-Wei Chiu
@ 2026-08-22 19:43 ` Kuan-Wei Chiu
2026-09-25 3:58 ` Alistair Francis
2026-08-22 19:43 ` [PATCH v4 3/7] hw/riscv: Add Sophgo CV1800B SoC support Kuan-Wei Chiu
` (4 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-22 19:43 UTC (permalink / raw)
To: pierrick.bouvier, almer, alistair.francis, pbonzini,
marcandre.lureau, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu, wxchen0913,
jserv, marscheng, eleanor15x, qemu-devel, qemu-riscv,
Kuan-Wei Chiu
Add a stub for the CV1800B clock controller. This is specifically
required for the SDHCI controller to function correctly under Linux.
The Linux 'sophgo,cv1800-clk' driver probes this device to determine
the clock tree configuration. This implementation sets the bypass
registers (CLK_BYP_0 and CLK_BYP_1) to 0xFFFFFFFF during reset,
matching the POR default state. This bypasses the PLLs and allows the
SDHCI and other peripherals to operate using the 25MHz reference clock.
Without this device, the SD card driver fails to initialize, preventing
the system from mounting the root filesystem from the SD card.
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
hw/misc/Kconfig | 3 ++
hw/misc/cv1800b_clk.c | 90 +++++++++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
include/hw/misc/cv1800b_clk.h | 26 ++++++++++
4 files changed, 120 insertions(+)
create mode 100644 hw/misc/cv1800b_clk.c
create mode 100644 include/hw/misc/cv1800b_clk.h
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index b8860dd3e7..2325ddd348 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -260,4 +260,7 @@ config XLNX_ZYNQ_DDRC
config AXIADO_CLK
bool
+config SOPHGO_CV1800B_CLK
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/cv1800b_clk.c b/hw/misc/cv1800b_clk.c
new file mode 100644
index 0000000000..b92f0889a3
--- /dev/null
+++ b/hw/misc/cv1800b_clk.c
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Sophgo CV1800B Clock Controller
+ *
+ * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/misc/cv1800b_clk.h"
+
+#define REG_BYTE_WIDTH (4)
+#define REG_CLK_BYP_0 (0x030 / REG_BYTE_WIDTH)
+#define REG_CLK_BYP_1 (0x034 / REG_BYTE_WIDTH)
+
+static uint64_t cv1800b_clk_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ CV1800BClkState *s = opaque;
+ uint32_t val = 0;
+
+ if ((addr / REG_BYTE_WIDTH) < ARRAY_SIZE(s->regs)) {
+ val = s->regs[addr / REG_BYTE_WIDTH];
+ }
+
+ return val;
+}
+
+static void cv1800b_clk_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+ CV1800BClkState *s = opaque;
+
+ if ((addr / REG_BYTE_WIDTH) < ARRAY_SIZE(s->regs)) {
+ s->regs[addr / REG_BYTE_WIDTH] = val;
+ }
+}
+
+static const MemoryRegionOps cv1800b_clk_ops = {
+ .read = cv1800b_clk_read,
+ .write = cv1800b_clk_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void cv1800b_clk_reset_hold(Object *obj, ResetType type)
+{
+ CV1800BClkState *s = CV1800B_CLK(obj);
+
+ memset(s->regs, 0, sizeof(s->regs));
+
+ /*
+ * TODO: Implement proper PLL state machines.
+ * For now, use POR default to bypass PLLs and boot via 25MHz XTAL.
+ */
+ s->regs[REG_CLK_BYP_0] = 0xFFFFFFFF;
+ s->regs[REG_CLK_BYP_1] = 0xFFFFFFFF;
+}
+
+static void cv1800b_clk_init(Object *obj)
+{
+ CV1800BClkState *s = CV1800B_CLK(obj);
+
+ memory_region_init_io(&s->iomem, obj, &cv1800b_clk_ops, s,
+ TYPE_CV1800B_CLK, CV1800B_CLK_IOSIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+}
+
+static void cv1800b_clk_class_init(ObjectClass *klass, const void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->phases.hold = cv1800b_clk_reset_hold;
+}
+
+static const TypeInfo cv1800b_clk_info = {
+ .name = TYPE_CV1800B_CLK,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(CV1800BClkState),
+ .instance_init = cv1800b_clk_init,
+ .class_init = cv1800b_clk_class_init,
+};
+
+static void cv1800b_clk_register_types(void)
+{
+ type_register_static(&cv1800b_clk_info);
+}
+
+type_init(cv1800b_clk_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 858ca845a2..4c6ae551ba 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -37,6 +37,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c'))
system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c'))
system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c'))
system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c'))
+system_ss.add(when: 'CONFIG_SOPHGO_CV1800B_CLK', if_true: files('cv1800b_clk.c'))
subdir('macio')
diff --git a/include/hw/misc/cv1800b_clk.h b/include/hw/misc/cv1800b_clk.h
new file mode 100644
index 0000000000..4d03d887c6
--- /dev/null
+++ b/include/hw/misc/cv1800b_clk.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Sophgo CV1800B Clock Controller
+ *
+ * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#ifndef HW_MISC_CV1800B_CLK_H
+#define HW_MISC_CV1800B_CLK_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_CV1800B_CLK "cv1800b-clk"
+OBJECT_DECLARE_SIMPLE_TYPE(CV1800BClkState, CV1800B_CLK)
+
+#define CV1800B_CLK_IOSIZE 0x1000
+
+struct CV1800BClkState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ uint32_t regs[CV1800B_CLK_IOSIZE / sizeof(uint32_t)];
+};
+
+#endif
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v4 2/7] hw/misc: Add Sophgo CV1800B clock controller
2026-08-22 19:43 ` [PATCH v4 2/7] hw/misc: Add Sophgo CV1800B clock controller Kuan-Wei Chiu
@ 2026-09-25 3:58 ` Alistair Francis
0 siblings, 0 replies; 14+ messages in thread
From: Alistair Francis @ 2026-09-25 3:58 UTC (permalink / raw)
To: marcandre.lureau@redhat.com, pbonzini@redhat.com, farosas@suse.de,
pierrick.bouvier@oss.qualcomm.com, almer@dabbelt.com,
visitorckw@gmail.com, lvivier@redhat.com
Cc: wxchen0913@gmail.com, marscheng@google.com, qemu-devel@nongnu.org,
daniel.barboza@oss.qualcomm.com, jserv@ccns.ncku.edu.tw,
qemu-riscv@nongnu.org, chao.liu@processmission.com,
zhiwei_liu@linux.alibaba.com, eleanor15x@gmail.com,
liwei1518@gmail.com
On Sat, 2026-08-22 at 19:43 +0000, Kuan-Wei Chiu wrote:
> Add a stub for the CV1800B clock controller. This is specifically
> required for the SDHCI controller to function correctly under Linux.
>
> The Linux 'sophgo,cv1800-clk' driver probes this device to determine
> the clock tree configuration. This implementation sets the bypass
> registers (CLK_BYP_0 and CLK_BYP_1) to 0xFFFFFFFF during reset,
> matching the POR default state. This bypasses the PLLs and allows the
> SDHCI and other peripherals to operate using the 25MHz reference
> clock.
>
> Without this device, the SD card driver fails to initialize,
> preventing
> the system from mounting the root filesystem from the SD card.
>
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
> ---
> hw/misc/Kconfig | 3 ++
> hw/misc/cv1800b_clk.c | 90
> +++++++++++++++++++++++++++++++++++
> hw/misc/meson.build | 1 +
> include/hw/misc/cv1800b_clk.h | 26 ++++++++++
> 4 files changed, 120 insertions(+)
> create mode 100644 hw/misc/cv1800b_clk.c
> create mode 100644 include/hw/misc/cv1800b_clk.h
>
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index b8860dd3e7..2325ddd348 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -260,4 +260,7 @@ config XLNX_ZYNQ_DDRC
> config AXIADO_CLK
> bool
>
> +config SOPHGO_CV1800B_CLK
> + bool
> +
> source macio/Kconfig
> diff --git a/hw/misc/cv1800b_clk.c b/hw/misc/cv1800b_clk.c
> new file mode 100644
> index 0000000000..b92f0889a3
> --- /dev/null
> +++ b/hw/misc/cv1800b_clk.c
> @@ -0,0 +1,90 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Sophgo CV1800B Clock Controller
> + *
> + * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "hw/misc/cv1800b_clk.h"
> +
> +#define REG_BYTE_WIDTH (4)
You can use sizeof(uint32_t) to match `regs`
> +#define REG_CLK_BYP_0 (0x030 / REG_BYTE_WIDTH)
> +#define REG_CLK_BYP_1 (0x034 / REG_BYTE_WIDTH)
> +
> +static uint64_t cv1800b_clk_read(void *opaque, hwaddr addr, unsigned
> int size)
> +{
> + CV1800BClkState *s = opaque;
> + uint32_t val = 0;
> +
> + if ((addr / REG_BYTE_WIDTH) < ARRAY_SIZE(s->regs)) {
The check should be `if (addr < CV1800B_CLK_IOSIZE)` as that's what
regs actually is allocated as
> + val = s->regs[addr / REG_BYTE_WIDTH];
> + }
Also should log a guest error if it's outside the range.
> +
> + return val;
> +}
> +
> +static void cv1800b_clk_write(void *opaque, hwaddr addr, uint64_t
> val, unsigned int size)
> +{
> + CV1800BClkState *s = opaque;
> +
> + if ((addr / REG_BYTE_WIDTH) < ARRAY_SIZE(s->regs)) {
Same here
Alistair
> + s->regs[addr / REG_BYTE_WIDTH] = val;
> + }
> +}
> +
> +static const MemoryRegionOps cv1800b_clk_ops = {
> + .read = cv1800b_clk_read,
> + .write = cv1800b_clk_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4,
> + },
> +};
> +
> +static void cv1800b_clk_reset_hold(Object *obj, ResetType type)
> +{
> + CV1800BClkState *s = CV1800B_CLK(obj);
> +
> + memset(s->regs, 0, sizeof(s->regs));
> +
> + /*
> + * TODO: Implement proper PLL state machines.
> + * For now, use POR default to bypass PLLs and boot via 25MHz
> XTAL.
> + */
> + s->regs[REG_CLK_BYP_0] = 0xFFFFFFFF;
> + s->regs[REG_CLK_BYP_1] = 0xFFFFFFFF;
> +}
> +
> +static void cv1800b_clk_init(Object *obj)
> +{
> + CV1800BClkState *s = CV1800B_CLK(obj);
> +
> + memory_region_init_io(&s->iomem, obj, &cv1800b_clk_ops, s,
> + TYPE_CV1800B_CLK, CV1800B_CLK_IOSIZE);
> + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
> +}
> +
> +static void cv1800b_clk_class_init(ObjectClass *klass, const void
> *data)
> +{
> + ResettableClass *rc = RESETTABLE_CLASS(klass);
> +
> + rc->phases.hold = cv1800b_clk_reset_hold;
> +}
> +
> +static const TypeInfo cv1800b_clk_info = {
> + .name = TYPE_CV1800B_CLK,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(CV1800BClkState),
> + .instance_init = cv1800b_clk_init,
> + .class_init = cv1800b_clk_class_init,
> +};
> +
> +static void cv1800b_clk_register_types(void)
> +{
> + type_register_static(&cv1800b_clk_info);
> +}
> +
> +type_init(cv1800b_clk_register_types)
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 858ca845a2..4c6ae551ba 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -37,6 +37,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI',
> if_true: files('sifive_e_prci.c'))
> system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true:
> files('sifive_e_aon.c'))
> system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true:
> files('sifive_u_otp.c'))
> system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true:
> files('sifive_u_prci.c'))
> +system_ss.add(when: 'CONFIG_SOPHGO_CV1800B_CLK', if_true:
> files('cv1800b_clk.c'))
>
> subdir('macio')
>
> diff --git a/include/hw/misc/cv1800b_clk.h
> b/include/hw/misc/cv1800b_clk.h
> new file mode 100644
> index 0000000000..4d03d887c6
> --- /dev/null
> +++ b/include/hw/misc/cv1800b_clk.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Sophgo CV1800B Clock Controller
> + *
> + * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
> + */
> +
> +#ifndef HW_MISC_CV1800B_CLK_H
> +#define HW_MISC_CV1800B_CLK_H
> +
> +#include "hw/core/sysbus.h"
> +#include "qom/object.h"
> +
> +#define TYPE_CV1800B_CLK "cv1800b-clk"
> +OBJECT_DECLARE_SIMPLE_TYPE(CV1800BClkState, CV1800B_CLK)
> +
> +#define CV1800B_CLK_IOSIZE 0x1000
> +
> +struct CV1800BClkState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion iomem;
> + uint32_t regs[CV1800B_CLK_IOSIZE / sizeof(uint32_t)];
> +};
> +
> +#endif
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v4 3/7] hw/riscv: Add Sophgo CV1800B SoC support
2026-08-22 19:43 [PATCH v4 0/7] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
2026-08-22 19:43 ` [PATCH v4 1/7] hw/char: Add dw8250 UART Kuan-Wei Chiu
2026-08-22 19:43 ` [PATCH v4 2/7] hw/misc: Add Sophgo CV1800B clock controller Kuan-Wei Chiu
@ 2026-08-22 19:43 ` Kuan-Wei Chiu
2026-09-25 4:00 ` Alistair Francis
2026-08-22 19:43 ` [PATCH v4 4/7] hw/riscv: Add Milk-V Duo board support Kuan-Wei Chiu
` (3 subsequent siblings)
6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-22 19:43 UTC (permalink / raw)
To: pierrick.bouvier, almer, alistair.francis, pbonzini,
marcandre.lureau, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu, wxchen0913,
jserv, marscheng, eleanor15x, qemu-devel, qemu-riscv,
Kuan-Wei Chiu
Add the Sophgo CV1800B SoC, which is the heart of the Milk-V Duo board.
The SoC features a T-Head C906 CPU along with integrated PLIC, CLINT,
and dw8250 UART. The memory map and interrupts are configured according
to the CV1800B datasheet. [1]
Several peripheral blocks are included as unimplemented devices to
ensure that drivers can probe successfully without causing errors
during boot.
Link: https://github.com/milkv-duo/duo-files/tree/main/duo/datasheet [1]
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
hw/riscv/Kconfig | 8 ++
hw/riscv/cv1800b.c | 174 +++++++++++++++++++++++++++++++++++++
hw/riscv/meson.build | 2 +
include/hw/riscv/cv1800b.h | 56 ++++++++++++
4 files changed, 240 insertions(+)
create mode 100644 hw/riscv/cv1800b.c
create mode 100644 include/hw/riscv/cv1800b.h
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index de37c08cae..5ab38f228f 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -162,3 +162,11 @@ config K230
select SERIAL_MM
select UNIMP
select K230_WDT
+
+config SOPHGO_CV1800B
+ bool
+ depends on RISCV64
+ select RISCV_ACLINT
+ select SIFIVE_PLIC
+ select SOPHGO_CV1800B_CLK
+ select DW8250
diff --git a/hw/riscv/cv1800b.c b/hw/riscv/cv1800b.c
new file mode 100644
index 0000000000..32e46a4639
--- /dev/null
+++ b/hw/riscv/cv1800b.c
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Sophgo CV1800B SoC
+ *
+ * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/riscv/cv1800b.h"
+#include "hw/core/qdev-properties.h"
+#include "target/riscv/cpu-qom.h"
+#include "system/system.h"
+#include "hw/intc/riscv_aclint.h"
+#include "system/address-spaces.h"
+#include "hw/intc/sifive_plic.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/boot.h"
+#include "hw/sd/sdhci.h"
+#include "hw/misc/unimp.h"
+
+const MemMapEntry cv1800b_memmap[] = {
+ [CV1800B_DEV_TOP_MISC] = { 0x03000000, 0x1000 },
+ [CV1800B_DEV_PINMUX] = { 0x03001000, 0x1000 },
+ [CV1800B_DEV_CLK] = { 0x03002000, 0x1000 },
+ [CV1800B_DEV_RST] = { 0x03003000, 0x1000 },
+ [CV1800B_DEV_WDT] = { 0x03010000, 0x1000 },
+ [CV1800B_DEV_GPIO] = { 0x03020000, 0x4000 },
+ [CV1800B_DEV_UART0] = { 0x04140000, 0x10000 },
+ [CV1800B_DEV_SD0] = { 0x04310000, 0x10000 },
+ [CV1800B_DEV_ROM] = { 0x04400000, 0x10000 },
+ [CV1800B_DEV_RTC_GPIO] = { 0x05021000, 0x1000 },
+ [CV1800B_DEV_RTC_IO] = { 0x05027000, 0x1000 },
+ [CV1800B_DEV_PLIC] = { 0x70000000, 0x4000000 },
+ [CV1800B_DEV_CLINT] = { 0x74000000, 0x10000 },
+ [CV1800B_DEV_DRAM] = { 0x80000000, 0x0 },
+};
+
+static void cv1800b_soc_instance_init(Object *obj)
+{
+ CV1800BSoCState *s = CV1800B_SOC(obj);
+
+ object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY);
+ qdev_prop_set_uint32(DEVICE(&s->cpus), "hartid-base", 0);
+ qdev_prop_set_string(DEVICE(&s->cpus), "cpu-type", TYPE_RISCV_CPU_THEAD_C906);
+ qdev_prop_set_uint64(DEVICE(&s->cpus), "resetvec",
+ cv1800b_memmap[CV1800B_DEV_ROM].base);
+
+ object_initialize_child(obj, "clk", &s->clk, TYPE_CV1800B_CLK);
+ object_initialize_child(obj, "uart", &s->uart, TYPE_DW8250);
+ object_initialize_child(obj, "sdhci", &s->sdhci, TYPE_SYSBUS_SDHCI);
+}
+
+static void cv1800b_soc_realize(DeviceState *dev, Error **errp)
+{
+ CV1800BSoCState *s = CV1800B_SOC(dev);
+ MachineState *ms = MACHINE(qdev_get_machine());
+ uint32_t num_harts = ms->smp.cpus;
+ MemoryRegion *system_memory = get_system_memory();
+ char *plic_hart_config;
+
+ qdev_prop_set_uint32(DEVICE(&s->cpus), "num-harts", num_harts);
+
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpus), errp)) {
+ return;
+ }
+
+ memory_region_init_rom(&s->rom, OBJECT(dev), "cv1800b.rom",
+ cv1800b_memmap[CV1800B_DEV_ROM].size, &error_fatal);
+ memory_region_add_subregion(system_memory,
+ cv1800b_memmap[CV1800B_DEV_ROM].base, &s->rom);
+
+ riscv_aclint_swi_create(cv1800b_memmap[CV1800B_DEV_CLINT].base,
+ 0, num_harts, false);
+ riscv_aclint_mtimer_create(cv1800b_memmap[CV1800B_DEV_CLINT].base +
+ RISCV_ACLINT_SWI_SIZE,
+ RISCV_ACLINT_DEFAULT_MTIMER_SIZE,
+ 0, num_harts, RISCV_ACLINT_DEFAULT_MTIMECMP,
+ RISCV_ACLINT_DEFAULT_MTIME,
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true);
+
+ plic_hart_config = riscv_plic_hart_config_string(num_harts);
+ s->plic = sifive_plic_create(
+ cv1800b_memmap[CV1800B_DEV_PLIC].base,
+ plic_hart_config,
+ num_harts,
+ 0,
+ CV1800B_PLIC_NUM_SOURCES,
+ CV1800B_PLIC_NUM_PRIORITIES,
+ 0x0,
+ 0x1000,
+ 0x2000,
+ 0x80,
+ 0x200000,
+ 0x1000,
+ cv1800b_memmap[CV1800B_DEV_PLIC].size);
+
+ g_free(plic_hart_config);
+
+ qdev_prop_set_uint8(DEVICE(&s->uart), "regshift", 2);
+ qdev_prop_set_chr(DEVICE(&s->uart), "chardev", serial_hd(0));
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart), errp)) {
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart), 0, cv1800b_memmap[CV1800B_DEV_UART0].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), 0,
+ qdev_get_gpio_in(DEVICE(s->plic), CV1800B_UART0_IRQ));
+
+ qdev_prop_set_uint8(DEVICE(&s->sdhci), "sd-spec-version", 3);
+ qdev_prop_set_uint64(DEVICE(&s->sdhci), "capareg", 0x056900b9);
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) {
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0, cv1800b_memmap[CV1800B_DEV_SD0].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
+ qdev_get_gpio_in(DEVICE(s->plic), CV1800B_SD0_IRQ));
+
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->clk), errp)) {
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0,
+ cv1800b_memmap[CV1800B_DEV_CLK].base);
+
+ create_unimplemented_device("cv1800b.top_misc",
+ cv1800b_memmap[CV1800B_DEV_TOP_MISC].base,
+ cv1800b_memmap[CV1800B_DEV_TOP_MISC].size);
+
+ create_unimplemented_device("cv1800b.pinmux",
+ cv1800b_memmap[CV1800B_DEV_PINMUX].base,
+ cv1800b_memmap[CV1800B_DEV_PINMUX].size);
+
+ create_unimplemented_device("cv1800b.rst",
+ cv1800b_memmap[CV1800B_DEV_RST].base,
+ cv1800b_memmap[CV1800B_DEV_RST].size);
+
+ create_unimplemented_device("cv1800b.wdt",
+ cv1800b_memmap[CV1800B_DEV_WDT].base,
+ cv1800b_memmap[CV1800B_DEV_WDT].size);
+
+ create_unimplemented_device("cv1800b.gpio0_3",
+ cv1800b_memmap[CV1800B_DEV_GPIO].base,
+ cv1800b_memmap[CV1800B_DEV_GPIO].size);
+
+ create_unimplemented_device("cv1800b.rtc_gpio",
+ cv1800b_memmap[CV1800B_DEV_RTC_GPIO].base,
+ cv1800b_memmap[CV1800B_DEV_RTC_GPIO].size);
+
+ create_unimplemented_device("cv1800b.rtc_io",
+ cv1800b_memmap[CV1800B_DEV_RTC_IO].base,
+ cv1800b_memmap[CV1800B_DEV_RTC_IO].size);
+}
+
+static void cv1800b_soc_class_init(ObjectClass *oc, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = cv1800b_soc_realize;
+ dc->user_creatable = false;
+}
+
+static const TypeInfo cv1800b_soc_type_info = {
+ .name = TYPE_CV1800B_SOC,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(CV1800BSoCState),
+ .instance_init = cv1800b_soc_instance_init,
+ .class_init = cv1800b_soc_class_init,
+};
+
+static void cv1800b_soc_register_types(void)
+{
+ type_register_static(&cv1800b_soc_type_info);
+}
+
+type_init(cv1800b_soc_register_types)
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 0d82ceacc4..b278b443b6 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -21,4 +21,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_MIPS_CPS', if_true: files('cps.c'))
riscv_ss.add(when: 'CONFIG_MIPS_BOSTON_AIA', if_true: files('boston-aia.c'))
riscv_ss.add(when: 'CONFIG_K230', if_true: files('k230.c'))
+riscv_ss.add(when: 'CONFIG_SOPHGO_CV1800B', if_true: files('cv1800b.c'))
+
hw_arch += {'riscv': riscv_ss}
diff --git a/include/hw/riscv/cv1800b.h b/include/hw/riscv/cv1800b.h
new file mode 100644
index 0000000000..bde5eff96b
--- /dev/null
+++ b/include/hw/riscv/cv1800b.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Sophgo CV1800B SoC
+ *
+ * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#ifndef HW_RISCV_CV1800B_H
+#define HW_RISCV_CV1800B_H
+
+#include "hw/core/boards.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/misc/cv1800b_clk.h"
+#include "hw/char/dw8250.h"
+#include "hw/sd/sdhci.h"
+
+#define TYPE_CV1800B_SOC "cv1800b-soc"
+OBJECT_DECLARE_SIMPLE_TYPE(CV1800BSoCState, CV1800B_SOC)
+
+struct CV1800BSoCState {
+ DeviceState parent_obj;
+
+ RISCVHartArrayState cpus;
+ MemoryRegion rom;
+ DeviceState *plic;
+ CV1800BClkState clk;
+ DW8250State uart;
+ SDHCIState sdhci;
+};
+
+#define CV1800B_PLIC_NUM_SOURCES 136
+#define CV1800B_PLIC_NUM_PRIORITIES 31
+
+#define CV1800B_UART0_IRQ 44
+#define CV1800B_SD0_IRQ 36
+
+enum {
+ CV1800B_DEV_TOP_MISC,
+ CV1800B_DEV_PINMUX,
+ CV1800B_DEV_CLK,
+ CV1800B_DEV_RST,
+ CV1800B_DEV_WDT,
+ CV1800B_DEV_GPIO,
+ CV1800B_DEV_UART0,
+ CV1800B_DEV_SD0,
+ CV1800B_DEV_ROM,
+ CV1800B_DEV_RTC_GPIO,
+ CV1800B_DEV_RTC_IO,
+ CV1800B_DEV_PLIC,
+ CV1800B_DEV_CLINT,
+ CV1800B_DEV_DRAM,
+};
+
+extern const MemMapEntry cv1800b_memmap[];
+
+#endif
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v4 3/7] hw/riscv: Add Sophgo CV1800B SoC support
2026-08-22 19:43 ` [PATCH v4 3/7] hw/riscv: Add Sophgo CV1800B SoC support Kuan-Wei Chiu
@ 2026-09-25 4:00 ` Alistair Francis
0 siblings, 0 replies; 14+ messages in thread
From: Alistair Francis @ 2026-09-25 4:00 UTC (permalink / raw)
To: marcandre.lureau@redhat.com, pbonzini@redhat.com, farosas@suse.de,
pierrick.bouvier@oss.qualcomm.com, almer@dabbelt.com,
visitorckw@gmail.com, lvivier@redhat.com
Cc: wxchen0913@gmail.com, marscheng@google.com, qemu-devel@nongnu.org,
daniel.barboza@oss.qualcomm.com, jserv@ccns.ncku.edu.tw,
qemu-riscv@nongnu.org, chao.liu@processmission.com,
zhiwei_liu@linux.alibaba.com, eleanor15x@gmail.com,
liwei1518@gmail.com
On Sat, 2026-08-22 at 19:43 +0000, Kuan-Wei Chiu wrote:
> Add the Sophgo CV1800B SoC, which is the heart of the Milk-V Duo
> board.
>
> The SoC features a T-Head C906 CPU along with integrated PLIC, CLINT,
> and dw8250 UART. The memory map and interrupts are configured
> according
> to the CV1800B datasheet. [1]
>
> Several peripheral blocks are included as unimplemented devices to
> ensure that drivers can probe successfully without causing errors
> during boot.
>
> Link:
> https://github.com/milkv-duo/duo-files/tree/main/duo/datasheet [1]
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> hw/riscv/Kconfig | 8 ++
> hw/riscv/cv1800b.c | 174
> +++++++++++++++++++++++++++++++++++++
> hw/riscv/meson.build | 2 +
> include/hw/riscv/cv1800b.h | 56 ++++++++++++
> 4 files changed, 240 insertions(+)
> create mode 100644 hw/riscv/cv1800b.c
> create mode 100644 include/hw/riscv/cv1800b.h
>
> diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> index de37c08cae..5ab38f228f 100644
> --- a/hw/riscv/Kconfig
> +++ b/hw/riscv/Kconfig
> @@ -162,3 +162,11 @@ config K230
> select SERIAL_MM
> select UNIMP
> select K230_WDT
> +
> +config SOPHGO_CV1800B
> + bool
> + depends on RISCV64
> + select RISCV_ACLINT
> + select SIFIVE_PLIC
> + select SOPHGO_CV1800B_CLK
> + select DW8250
> diff --git a/hw/riscv/cv1800b.c b/hw/riscv/cv1800b.c
> new file mode 100644
> index 0000000000..32e46a4639
> --- /dev/null
> +++ b/hw/riscv/cv1800b.c
> @@ -0,0 +1,174 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Sophgo CV1800B SoC
> + *
> + * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "hw/riscv/cv1800b.h"
> +#include "hw/core/qdev-properties.h"
> +#include "target/riscv/cpu-qom.h"
> +#include "system/system.h"
> +#include "hw/intc/riscv_aclint.h"
> +#include "system/address-spaces.h"
> +#include "hw/intc/sifive_plic.h"
> +#include "target/riscv/cpu.h"
> +#include "hw/riscv/boot.h"
> +#include "hw/sd/sdhci.h"
> +#include "hw/misc/unimp.h"
> +
> +const MemMapEntry cv1800b_memmap[] = {
> + [CV1800B_DEV_TOP_MISC] = { 0x03000000, 0x1000 },
> + [CV1800B_DEV_PINMUX] = { 0x03001000, 0x1000 },
> + [CV1800B_DEV_CLK] = { 0x03002000, 0x1000 },
> + [CV1800B_DEV_RST] = { 0x03003000, 0x1000 },
> + [CV1800B_DEV_WDT] = { 0x03010000, 0x1000 },
> + [CV1800B_DEV_GPIO] = { 0x03020000, 0x4000 },
> + [CV1800B_DEV_UART0] = { 0x04140000, 0x10000 },
> + [CV1800B_DEV_SD0] = { 0x04310000, 0x10000 },
> + [CV1800B_DEV_ROM] = { 0x04400000, 0x10000 },
> + [CV1800B_DEV_RTC_GPIO] = { 0x05021000, 0x1000 },
> + [CV1800B_DEV_RTC_IO] = { 0x05027000, 0x1000 },
> + [CV1800B_DEV_PLIC] = { 0x70000000, 0x4000000 },
> + [CV1800B_DEV_CLINT] = { 0x74000000, 0x10000 },
> + [CV1800B_DEV_DRAM] = { 0x80000000, 0x0 },
> +};
> +
> +static void cv1800b_soc_instance_init(Object *obj)
> +{
> + CV1800BSoCState *s = CV1800B_SOC(obj);
> +
> + object_initialize_child(obj, "cpus", &s->cpus,
> TYPE_RISCV_HART_ARRAY);
> + qdev_prop_set_uint32(DEVICE(&s->cpus), "hartid-base", 0);
> + qdev_prop_set_string(DEVICE(&s->cpus), "cpu-type",
> TYPE_RISCV_CPU_THEAD_C906);
> + qdev_prop_set_uint64(DEVICE(&s->cpus), "resetvec",
> + cv1800b_memmap[CV1800B_DEV_ROM].base);
> +
> + object_initialize_child(obj, "clk", &s->clk, TYPE_CV1800B_CLK);
> + object_initialize_child(obj, "uart", &s->uart, TYPE_DW8250);
> + object_initialize_child(obj, "sdhci", &s->sdhci,
> TYPE_SYSBUS_SDHCI);
> +}
> +
> +static void cv1800b_soc_realize(DeviceState *dev, Error **errp)
> +{
> + CV1800BSoCState *s = CV1800B_SOC(dev);
> + MachineState *ms = MACHINE(qdev_get_machine());
> + uint32_t num_harts = ms->smp.cpus;
> + MemoryRegion *system_memory = get_system_memory();
> + char *plic_hart_config;
> +
> + qdev_prop_set_uint32(DEVICE(&s->cpus), "num-harts", num_harts);
> +
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->cpus), errp)) {
> + return;
> + }
> +
> + memory_region_init_rom(&s->rom, OBJECT(dev), "cv1800b.rom",
> + cv1800b_memmap[CV1800B_DEV_ROM].size,
> &error_fatal);
> + memory_region_add_subregion(system_memory,
> +
> cv1800b_memmap[CV1800B_DEV_ROM].base, &s->rom);
> +
> + riscv_aclint_swi_create(cv1800b_memmap[CV1800B_DEV_CLINT].base,
> + 0, num_harts, false);
> +
> riscv_aclint_mtimer_create(cv1800b_memmap[CV1800B_DEV_CLINT].base +
> + RISCV_ACLINT_SWI_SIZE,
> + RISCV_ACLINT_DEFAULT_MTIMER_SIZE,
> + 0, num_harts,
> RISCV_ACLINT_DEFAULT_MTIMECMP,
> + RISCV_ACLINT_DEFAULT_MTIME,
> + RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ,
> true);
> +
> + plic_hart_config = riscv_plic_hart_config_string(num_harts);
> + s->plic = sifive_plic_create(
> + cv1800b_memmap[CV1800B_DEV_PLIC].base,
> + plic_hart_config,
> + num_harts,
> + 0,
> + CV1800B_PLIC_NUM_SOURCES,
> + CV1800B_PLIC_NUM_PRIORITIES,
> + 0x0,
> + 0x1000,
> + 0x2000,
> + 0x80,
> + 0x200000,
> + 0x1000,
> + cv1800b_memmap[CV1800B_DEV_PLIC].size);
> +
> + g_free(plic_hart_config);
> +
> + qdev_prop_set_uint8(DEVICE(&s->uart), "regshift", 2);
> + qdev_prop_set_chr(DEVICE(&s->uart), "chardev", serial_hd(0));
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart), errp)) {
> + return;
> + }
> + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart), 0,
> cv1800b_memmap[CV1800B_DEV_UART0].base);
> + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), 0,
> + qdev_get_gpio_in(DEVICE(s->plic),
> CV1800B_UART0_IRQ));
> +
> + qdev_prop_set_uint8(DEVICE(&s->sdhci), "sd-spec-version", 3);
> + qdev_prop_set_uint64(DEVICE(&s->sdhci), "capareg", 0x056900b9);
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->sdhci), errp)) {
> + return;
> + }
> + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sdhci), 0,
> cv1800b_memmap[CV1800B_DEV_SD0].base);
> + sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
> + qdev_get_gpio_in(DEVICE(s->plic),
> CV1800B_SD0_IRQ));
> +
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->clk), errp)) {
> + return;
> + }
> + sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0,
> + cv1800b_memmap[CV1800B_DEV_CLK].base);
> +
> + create_unimplemented_device("cv1800b.top_misc",
> +
> cv1800b_memmap[CV1800B_DEV_TOP_MISC].base,
> +
> cv1800b_memmap[CV1800B_DEV_TOP_MISC].size);
> +
> + create_unimplemented_device("cv1800b.pinmux",
> +
> cv1800b_memmap[CV1800B_DEV_PINMUX].base,
> +
> cv1800b_memmap[CV1800B_DEV_PINMUX].size);
> +
> + create_unimplemented_device("cv1800b.rst",
> +
> cv1800b_memmap[CV1800B_DEV_RST].base,
> +
> cv1800b_memmap[CV1800B_DEV_RST].size);
> +
> + create_unimplemented_device("cv1800b.wdt",
> +
> cv1800b_memmap[CV1800B_DEV_WDT].base,
> +
> cv1800b_memmap[CV1800B_DEV_WDT].size);
> +
> + create_unimplemented_device("cv1800b.gpio0_3",
> +
> cv1800b_memmap[CV1800B_DEV_GPIO].base,
> +
> cv1800b_memmap[CV1800B_DEV_GPIO].size);
> +
> + create_unimplemented_device("cv1800b.rtc_gpio",
> +
> cv1800b_memmap[CV1800B_DEV_RTC_GPIO].base,
> +
> cv1800b_memmap[CV1800B_DEV_RTC_GPIO].size);
> +
> + create_unimplemented_device("cv1800b.rtc_io",
> +
> cv1800b_memmap[CV1800B_DEV_RTC_IO].base,
> +
> cv1800b_memmap[CV1800B_DEV_RTC_IO].size);
> +}
> +
> +static void cv1800b_soc_class_init(ObjectClass *oc, const void
> *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(oc);
> +
> + dc->realize = cv1800b_soc_realize;
> + dc->user_creatable = false;
> +}
> +
> +static const TypeInfo cv1800b_soc_type_info = {
> + .name = TYPE_CV1800B_SOC,
> + .parent = TYPE_DEVICE,
> + .instance_size = sizeof(CV1800BSoCState),
> + .instance_init = cv1800b_soc_instance_init,
> + .class_init = cv1800b_soc_class_init,
> +};
> +
> +static void cv1800b_soc_register_types(void)
> +{
> + type_register_static(&cv1800b_soc_type_info);
> +}
> +
> +type_init(cv1800b_soc_register_types)
> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> index 0d82ceacc4..b278b443b6 100644
> --- a/hw/riscv/meson.build
> +++ b/hw/riscv/meson.build
> @@ -21,4 +21,6 @@ riscv_ss.add(when: 'CONFIG_RISCV_MIPS_CPS',
> if_true: files('cps.c'))
> riscv_ss.add(when: 'CONFIG_MIPS_BOSTON_AIA', if_true: files('boston-
> aia.c'))
> riscv_ss.add(when: 'CONFIG_K230', if_true: files('k230.c'))
>
> +riscv_ss.add(when: 'CONFIG_SOPHGO_CV1800B', if_true:
> files('cv1800b.c'))
> +
> hw_arch += {'riscv': riscv_ss}
> diff --git a/include/hw/riscv/cv1800b.h b/include/hw/riscv/cv1800b.h
> new file mode 100644
> index 0000000000..bde5eff96b
> --- /dev/null
> +++ b/include/hw/riscv/cv1800b.h
> @@ -0,0 +1,56 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Sophgo CV1800B SoC
> + *
> + * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
> + */
> +
> +#ifndef HW_RISCV_CV1800B_H
> +#define HW_RISCV_CV1800B_H
> +
> +#include "hw/core/boards.h"
> +#include "hw/riscv/riscv_hart.h"
> +#include "hw/misc/cv1800b_clk.h"
> +#include "hw/char/dw8250.h"
> +#include "hw/sd/sdhci.h"
> +
> +#define TYPE_CV1800B_SOC "cv1800b-soc"
> +OBJECT_DECLARE_SIMPLE_TYPE(CV1800BSoCState, CV1800B_SOC)
> +
> +struct CV1800BSoCState {
> + DeviceState parent_obj;
> +
> + RISCVHartArrayState cpus;
> + MemoryRegion rom;
> + DeviceState *plic;
> + CV1800BClkState clk;
> + DW8250State uart;
> + SDHCIState sdhci;
> +};
> +
> +#define CV1800B_PLIC_NUM_SOURCES 136
> +#define CV1800B_PLIC_NUM_PRIORITIES 31
> +
> +#define CV1800B_UART0_IRQ 44
> +#define CV1800B_SD0_IRQ 36
> +
> +enum {
> + CV1800B_DEV_TOP_MISC,
> + CV1800B_DEV_PINMUX,
> + CV1800B_DEV_CLK,
> + CV1800B_DEV_RST,
> + CV1800B_DEV_WDT,
> + CV1800B_DEV_GPIO,
> + CV1800B_DEV_UART0,
> + CV1800B_DEV_SD0,
> + CV1800B_DEV_ROM,
> + CV1800B_DEV_RTC_GPIO,
> + CV1800B_DEV_RTC_IO,
> + CV1800B_DEV_PLIC,
> + CV1800B_DEV_CLINT,
> + CV1800B_DEV_DRAM,
> +};
> +
> +extern const MemMapEntry cv1800b_memmap[];
> +
> +#endif
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v4 4/7] hw/riscv: Add Milk-V Duo board support
2026-08-22 19:43 [PATCH v4 0/7] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
` (2 preceding siblings ...)
2026-08-22 19:43 ` [PATCH v4 3/7] hw/riscv: Add Sophgo CV1800B SoC support Kuan-Wei Chiu
@ 2026-08-22 19:43 ` Kuan-Wei Chiu
2026-08-22 19:43 ` [PATCH v4 5/7] tests/qtest: Add qtest for Milk-V Duo board Kuan-Wei Chiu
` (2 subsequent siblings)
6 siblings, 0 replies; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-22 19:43 UTC (permalink / raw)
To: pierrick.bouvier, almer, alistair.francis, pbonzini,
marcandre.lureau, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu, wxchen0913,
jserv, marscheng, eleanor15x, qemu-devel, qemu-riscv,
Kuan-Wei Chiu, Chao Liu
Add support for the Milk-V Duo development board, which is powered by
the Sophgo CV1800B SoC.
The implementation includes:
- Board-level machine initialization with 64mb of default ram.
- Integration of the CV1800B SoC.
- Support for loading external FDT, kernel, and initrd images.
- Proper setup of the reset vector to match the CV1800B's boot flow.
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
---
MAINTAINERS | 11 ++
configs/devices/riscv64-softmmu/default.mak | 1 +
hw/riscv/Kconfig | 6 +
hw/riscv/meson.build | 1 +
hw/riscv/milkv_duo.c | 120 ++++++++++++++++++++
5 files changed, 139 insertions(+)
create mode 100644 hw/riscv/milkv_duo.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 08017f4786..dde85ef420 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1851,6 +1851,17 @@ F: include/hw/watchdog/k230_wdt.h
F: tests/functional/riscv64/test_k230.py
F: tests/qtest/k230-wdt-test.c
+Milk-V Duo
+M: Kuan-Wei Chiu <visitorckw@gmail.com>
+S: Maintained
+F: hw/char/dw8250.c
+F: hw/misc/cv1800b_clk.c
+F: hw/riscv/cv1800b.c
+F: hw/riscv/milkv_duo.c
+F: include/hw/char/dw8250.h
+F: include/hw/misc/cv1800b_clk.h
+F: include/hw/riscv/cv1800b.h
+
RX Machines
-----------
rx-gdbsim
diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak
index a8e4d0ab33..2ba91b14d4 100644
--- a/configs/devices/riscv64-softmmu/default.mak
+++ b/configs/devices/riscv64-softmmu/default.mak
@@ -13,3 +13,4 @@
# CONFIG_SHAKTI_C=n
# CONFIG_XIANGSHAN_KUNMINGHU=n
# CONFIG_MIPS_BOSTON_AIA=n
+# CONFIG_MILKV_DUO=n
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 5ab38f228f..6360de4260 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -170,3 +170,9 @@ config SOPHGO_CV1800B
select SIFIVE_PLIC
select SOPHGO_CV1800B_CLK
select DW8250
+
+config MILKV_DUO
+ bool
+ depends on RISCV64
+ default y
+ select SOPHGO_CV1800B
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index b278b443b6..0ab1baec88 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -22,5 +22,6 @@ riscv_ss.add(when: 'CONFIG_MIPS_BOSTON_AIA', if_true: files('boston-aia.c'))
riscv_ss.add(when: 'CONFIG_K230', if_true: files('k230.c'))
riscv_ss.add(when: 'CONFIG_SOPHGO_CV1800B', if_true: files('cv1800b.c'))
+riscv_ss.add(when: 'CONFIG_MILKV_DUO', if_true: files('milkv_duo.c'))
hw_arch += {'riscv': riscv_ss}
diff --git a/hw/riscv/milkv_duo.c b/hw/riscv/milkv_duo.c
new file mode 100644
index 0000000000..ec09243bef
--- /dev/null
+++ b/hw/riscv/milkv_duo.c
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Milk-V Duo board
+ *
+ * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "hw/core/boards.h"
+#include "hw/riscv/cv1800b.h"
+#include "hw/riscv/boot.h"
+#include "target/riscv/cpu-qom.h"
+#include "system/system.h"
+#include "system/device_tree.h"
+#include "qemu/error-report.h"
+#include "hw/core/loader.h"
+#include <libfdt.h>
+#include "hw/riscv/machines-qom.h"
+
+struct MilkVDuoState {
+ MachineState parent_obj;
+ CV1800BSoCState soc;
+};
+
+#define TYPE_MILK_V_DUO MACHINE_TYPE_NAME("milkv-duo")
+OBJECT_DECLARE_SIMPLE_TYPE(MilkVDuoState, MILK_V_DUO)
+
+static void milkv_duo_init(MachineState *machine)
+{
+ MilkVDuoState *s = MILK_V_DUO(machine);
+ MemoryRegion *system_memory = get_system_memory();
+ RISCVBootInfo boot_info;
+ const char *firmware_name;
+ hwaddr firmware_load_addr, firmware_end_addr;
+ hwaddr fdt_load_addr = 0;
+ int fdt_size = 0;
+ uint64_t kernel_entry = 0;
+
+ object_initialize_child(OBJECT(machine), "soc", &s->soc, TYPE_CV1800B_SOC);
+ qdev_realize(DEVICE(&s->soc), NULL, &error_fatal);
+
+ memory_region_add_subregion(system_memory,
+ cv1800b_memmap[CV1800B_DEV_DRAM].base,
+ machine->ram);
+
+ riscv_boot_info_init(&boot_info, &s->soc.cpus);
+
+ firmware_name = riscv_default_firmware_name(&s->soc.cpus);
+ firmware_load_addr = cv1800b_memmap[CV1800B_DEV_DRAM].base;
+ firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
+ firmware_name,
+ &firmware_load_addr, NULL);
+
+ if (machine->dtb) {
+ machine->fdt = load_device_tree(machine->dtb, &fdt_size);
+ if (!machine->fdt) {
+ error_report("Failed to load device tree");
+ exit(1);
+ }
+
+ if (machine->kernel_cmdline && *machine->kernel_cmdline) {
+ if (fdt_path_offset(machine->fdt, "/chosen") < 0) {
+ qemu_fdt_add_subnode(machine->fdt, "/chosen");
+ }
+ qemu_fdt_setprop_string(machine->fdt, "/chosen", "bootargs",
+ machine->kernel_cmdline);
+ }
+ }
+
+ if (machine->kernel_filename) {
+ hwaddr kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
+ firmware_end_addr);
+ riscv_load_kernel(machine, &boot_info, kernel_start_addr, true, NULL);
+ kernel_entry = boot_info.image_low_addr;
+ }
+
+ if (machine->dtb) {
+ fdt_load_addr = riscv_compute_fdt_addr(cv1800b_memmap[CV1800B_DEV_DRAM].base,
+ machine->ram_size, machine, &boot_info);
+ riscv_load_fdt(fdt_load_addr, machine->fdt);
+ }
+
+ riscv_setup_rom_reset_vec(machine, &s->soc.cpus,
+ firmware_load_addr,
+ cv1800b_memmap[CV1800B_DEV_ROM].base,
+ cv1800b_memmap[CV1800B_DEV_ROM].size,
+ kernel_entry,
+ fdt_load_addr);
+}
+
+static void milkv_duo_machine_class_init(ObjectClass *oc, const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ static const char *const valid_cpu_types[] = {
+ TYPE_RISCV_CPU_THEAD_C906,
+ NULL
+ };
+
+ mc->desc = "Milk-V Duo Board (CV1800B)";
+ mc->init = milkv_duo_init;
+ mc->max_cpus = 2;
+ mc->default_cpu_type = TYPE_RISCV_CPU_THEAD_C906;
+ mc->valid_cpu_types = valid_cpu_types;
+ mc->default_ram_size = 64 * MiB;
+ mc->default_ram_id = "riscv.milkv_duo.ram";
+}
+
+static const TypeInfo milkv_duo_machine_type_infos[] = {
+ {
+ .name = TYPE_MILK_V_DUO,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(MilkVDuoState),
+ .class_init = milkv_duo_machine_class_init,
+ .interfaces = riscv64_machine_interfaces,
+ },
+};
+
+DEFINE_TYPES(milkv_duo_machine_type_infos)
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 5/7] tests/qtest: Add qtest for Milk-V Duo board
2026-08-22 19:43 [PATCH v4 0/7] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
` (3 preceding siblings ...)
2026-08-22 19:43 ` [PATCH v4 4/7] hw/riscv: Add Milk-V Duo board support Kuan-Wei Chiu
@ 2026-08-22 19:43 ` Kuan-Wei Chiu
2026-09-25 4:01 ` Alistair Francis
2026-08-22 19:43 ` [PATCH v4 6/7] tests/functional/riscv64: Add boot test " Kuan-Wei Chiu
2026-08-22 19:43 ` [PATCH v4 7/7] docs/system/riscv: Add Milk-V Duo board documentation Kuan-Wei Chiu
6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-22 19:43 UTC (permalink / raw)
To: pierrick.bouvier, almer, alistair.francis, pbonzini,
marcandre.lureau, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu, wxchen0913,
jserv, marscheng, eleanor15x, qemu-devel, qemu-riscv,
Kuan-Wei Chiu
Add minimal qtest coverage for the Milk-V Duo machine to validate basic
MMIO mapping and register access.
Currently tested:
- Verify DW8250 UART component version and type signatures.
- Test read/write access to CV1800B clock bypass register.
Tested with:
$ meson test -C build -v qemu:qtest-riscv64/milkv-duo-test
[...]
Ok: 1
Fail: 0
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
MAINTAINERS | 1 +
tests/qtest/meson.build | 3 +-
tests/qtest/milkv-duo-test.c | 70 ++++++++++++++++++++++++++++++++++++
3 files changed, 73 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/milkv-duo-test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index dde85ef420..245eb7bb58 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1861,6 +1861,7 @@ F: hw/riscv/milkv_duo.c
F: include/hw/char/dw8250.h
F: include/hw/misc/cv1800b_clk.h
F: include/hw/riscv/cv1800b.h
+F: tests/qtest/milkv-duo-test.c
RX Machines
-----------
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 12ed368997..d02dbf7433 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -299,7 +299,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'] : []) + \
+ ['milkv-duo-test']
qtests_hexagon = ['boot-serial-test', 'l2vic-test', 'qct-qtimer-test']
diff --git a/tests/qtest/milkv-duo-test.c b/tests/qtest/milkv-duo-test.c
new file mode 100644
index 0000000000..06064b8733
--- /dev/null
+++ b/tests/qtest/milkv-duo-test.c
@@ -0,0 +1,70 @@
+/*
+ * QTest for Milk-V Duo Board
+ *
+ * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+
+#define CV1800B_CLK_BASE 0x03002000
+#define CV1800B_CLK_BYPASS 0x030
+#define CV1800B_CLK_BYPASS_RESET 0xFFFFFFFF
+#define TEST_PATTERN_5A 0x5A5A5A5A
+#define TEST_PATTERN_A5 0xA5A5A5A5
+
+#define CV1800B_UART0_BASE 0x04140000
+#define DW_UART_UCV 0xF8
+#define DW_UART_CTR 0xFC
+#define DW_UART_VERSION_3_23A 0x3332332A
+#define DW_UART_TYPE_SIGNATURE 0x44570110
+
+static void test_milkv_duo_uart(void)
+{
+ QTestState *qts;
+ uint32_t component_version;
+ uint32_t component_type;
+
+ qts = qtest_init("-M milkv-duo");
+
+ component_version = qtest_readl(qts, CV1800B_UART0_BASE + DW_UART_UCV);
+ g_assert_cmphex(component_version, ==, DW_UART_VERSION_3_23A);
+
+ component_type = qtest_readl(qts, CV1800B_UART0_BASE + DW_UART_CTR);
+ g_assert_cmphex(component_type, ==, DW_UART_TYPE_SIGNATURE);
+
+ qtest_quit(qts);
+}
+
+static void test_milkv_duo_clk(void)
+{
+ QTestState *qts;
+ uint32_t clk_bypass_val;
+
+ qts = qtest_init("-M milkv-duo");
+
+ clk_bypass_val = qtest_readl(qts, CV1800B_CLK_BASE + CV1800B_CLK_BYPASS);
+ g_assert_cmphex(clk_bypass_val, ==, CV1800B_CLK_BYPASS_RESET);
+
+ qtest_writel(qts, CV1800B_CLK_BASE + CV1800B_CLK_BYPASS, TEST_PATTERN_5A);
+ clk_bypass_val = qtest_readl(qts, CV1800B_CLK_BASE + CV1800B_CLK_BYPASS);
+ g_assert_cmphex(clk_bypass_val, ==, TEST_PATTERN_5A);
+
+ qtest_writel(qts, CV1800B_CLK_BASE + CV1800B_CLK_BYPASS, TEST_PATTERN_A5);
+ clk_bypass_val = qtest_readl(qts, CV1800B_CLK_BASE + CV1800B_CLK_BYPASS);
+ g_assert_cmphex(clk_bypass_val, ==, TEST_PATTERN_A5);
+
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("/riscv/milkv-duo/uart", test_milkv_duo_uart);
+ qtest_add_func("/riscv/milkv-duo/clk", test_milkv_duo_clk);
+
+ return g_test_run();
+}
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v4 5/7] tests/qtest: Add qtest for Milk-V Duo board
2026-08-22 19:43 ` [PATCH v4 5/7] tests/qtest: Add qtest for Milk-V Duo board Kuan-Wei Chiu
@ 2026-09-25 4:01 ` Alistair Francis
0 siblings, 0 replies; 14+ messages in thread
From: Alistair Francis @ 2026-09-25 4:01 UTC (permalink / raw)
To: marcandre.lureau@redhat.com, pbonzini@redhat.com, farosas@suse.de,
pierrick.bouvier@oss.qualcomm.com, almer@dabbelt.com,
visitorckw@gmail.com, lvivier@redhat.com
Cc: wxchen0913@gmail.com, marscheng@google.com, qemu-devel@nongnu.org,
daniel.barboza@oss.qualcomm.com, jserv@ccns.ncku.edu.tw,
qemu-riscv@nongnu.org, chao.liu@processmission.com,
zhiwei_liu@linux.alibaba.com, eleanor15x@gmail.com,
liwei1518@gmail.com
On Sat, 2026-08-22 at 19:43 +0000, Kuan-Wei Chiu wrote:
> Add minimal qtest coverage for the Milk-V Duo machine to validate
> basic
> MMIO mapping and register access.
>
> Currently tested:
> - Verify DW8250 UART component version and type signatures.
> - Test read/write access to CV1800B clock bypass register.
>
> Tested with:
> $ meson test -C build -v qemu:qtest-riscv64/milkv-duo-test
> [...]
> Ok: 1
> Fail: 0
>
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> MAINTAINERS | 1 +
> tests/qtest/meson.build | 3 +-
> tests/qtest/milkv-duo-test.c | 70
> ++++++++++++++++++++++++++++++++++++
> 3 files changed, 73 insertions(+), 1 deletion(-)
> create mode 100644 tests/qtest/milkv-duo-test.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index dde85ef420..245eb7bb58 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1861,6 +1861,7 @@ F: hw/riscv/milkv_duo.c
> F: include/hw/char/dw8250.h
> F: include/hw/misc/cv1800b_clk.h
> F: include/hw/riscv/cv1800b.h
> +F: tests/qtest/milkv-duo-test.c
>
> RX Machines
> -----------
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index 12ed368997..d02dbf7433 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -299,7 +299,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'] :
> []) + \
> + ['milkv-duo-test']
>
> qtests_hexagon = ['boot-serial-test', 'l2vic-test', 'qct-qtimer-
> test']
>
> diff --git a/tests/qtest/milkv-duo-test.c b/tests/qtest/milkv-duo-
> test.c
> new file mode 100644
> index 0000000000..06064b8733
> --- /dev/null
> +++ b/tests/qtest/milkv-duo-test.c
> @@ -0,0 +1,70 @@
> +/*
> + * QTest for Milk-V Duo Board
> + *
> + * Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "libqtest.h"
> +
> +#define CV1800B_CLK_BASE 0x03002000
> +#define CV1800B_CLK_BYPASS 0x030
> +#define CV1800B_CLK_BYPASS_RESET 0xFFFFFFFF
> +#define TEST_PATTERN_5A 0x5A5A5A5A
> +#define TEST_PATTERN_A5 0xA5A5A5A5
> +
> +#define CV1800B_UART0_BASE 0x04140000
> +#define DW_UART_UCV 0xF8
> +#define DW_UART_CTR 0xFC
> +#define DW_UART_VERSION_3_23A 0x3332332A
> +#define DW_UART_TYPE_SIGNATURE 0x44570110
> +
> +static void test_milkv_duo_uart(void)
> +{
> + QTestState *qts;
> + uint32_t component_version;
> + uint32_t component_type;
> +
> + qts = qtest_init("-M milkv-duo");
> +
> + component_version = qtest_readl(qts, CV1800B_UART0_BASE +
> DW_UART_UCV);
> + g_assert_cmphex(component_version, ==, DW_UART_VERSION_3_23A);
> +
> + component_type = qtest_readl(qts, CV1800B_UART0_BASE +
> DW_UART_CTR);
> + g_assert_cmphex(component_type, ==, DW_UART_TYPE_SIGNATURE);
> +
> + qtest_quit(qts);
> +}
> +
> +static void test_milkv_duo_clk(void)
> +{
> + QTestState *qts;
> + uint32_t clk_bypass_val;
> +
> + qts = qtest_init("-M milkv-duo");
> +
> + clk_bypass_val = qtest_readl(qts, CV1800B_CLK_BASE +
> CV1800B_CLK_BYPASS);
> + g_assert_cmphex(clk_bypass_val, ==, CV1800B_CLK_BYPASS_RESET);
> +
> + qtest_writel(qts, CV1800B_CLK_BASE + CV1800B_CLK_BYPASS,
> TEST_PATTERN_5A);
> + clk_bypass_val = qtest_readl(qts, CV1800B_CLK_BASE +
> CV1800B_CLK_BYPASS);
> + g_assert_cmphex(clk_bypass_val, ==, TEST_PATTERN_5A);
> +
> + qtest_writel(qts, CV1800B_CLK_BASE + CV1800B_CLK_BYPASS,
> TEST_PATTERN_A5);
> + clk_bypass_val = qtest_readl(qts, CV1800B_CLK_BASE +
> CV1800B_CLK_BYPASS);
> + g_assert_cmphex(clk_bypass_val, ==, TEST_PATTERN_A5);
> +
> + qtest_quit(qts);
> +}
> +
> +int main(int argc, char **argv)
> +{
> + g_test_init(&argc, &argv, NULL);
> +
> + qtest_add_func("/riscv/milkv-duo/uart", test_milkv_duo_uart);
> + qtest_add_func("/riscv/milkv-duo/clk", test_milkv_duo_clk);
> +
> + return g_test_run();
> +}
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v4 6/7] tests/functional/riscv64: Add boot test for Milk-V Duo board
2026-08-22 19:43 [PATCH v4 0/7] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
` (4 preceding siblings ...)
2026-08-22 19:43 ` [PATCH v4 5/7] tests/qtest: Add qtest for Milk-V Duo board Kuan-Wei Chiu
@ 2026-08-22 19:43 ` Kuan-Wei Chiu
2026-09-25 4:02 ` Alistair Francis
2026-08-22 19:43 ` [PATCH v4 7/7] docs/system/riscv: Add Milk-V Duo board documentation Kuan-Wei Chiu
6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-22 19:43 UTC (permalink / raw)
To: pierrick.bouvier, almer, alistair.francis, pbonzini,
marcandre.lureau, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu, wxchen0913,
jserv, marscheng, eleanor15x, qemu-devel, qemu-riscv,
Kuan-Wei Chiu
Add a functional boot test for the Milk-V Duo board to validate the
end-to-end boot flow.
$ ./build/run ./tests/functional/riscv64/test_milkv_duo.py
TAP version 13
ok 1 test_milkv_duo.MilkvDuoMachine.test_riscv64_milkv_duo
1..1
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
MAINTAINERS | 1 +
tests/functional/riscv64/meson.build | 1 +
tests/functional/riscv64/test_milkv_duo.py | 50 ++++++++++++++++++++++
3 files changed, 52 insertions(+)
create mode 100755 tests/functional/riscv64/test_milkv_duo.py
diff --git a/MAINTAINERS b/MAINTAINERS
index 245eb7bb58..3f79334c59 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1861,6 +1861,7 @@ F: hw/riscv/milkv_duo.c
F: include/hw/char/dw8250.h
F: include/hw/misc/cv1800b_clk.h
F: include/hw/riscv/cv1800b.h
+F: tests/functional/riscv64/test_milkv_duo.py
F: tests/qtest/milkv-duo-test.c
RX Machines
diff --git a/tests/functional/riscv64/meson.build b/tests/functional/riscv64/meson.build
index d1a3e6c2bf..8a4f1d00f9 100644
--- a/tests/functional/riscv64/meson.build
+++ b/tests/functional/riscv64/meson.build
@@ -18,4 +18,5 @@ tests_riscv64_system_thorough = [
'sifive_u',
'tt_atlantis',
'tuxrun',
+ 'milkv_duo',
]
diff --git a/tests/functional/riscv64/test_milkv_duo.py b/tests/functional/riscv64/test_milkv_duo.py
new file mode 100755
index 0000000000..aff247e61f
--- /dev/null
+++ b/tests/functional/riscv64/test_milkv_duo.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+#
+# Functional test that boots a Linux kernel on a Milk-V Duo machine
+#
+# Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from qemu_test import LinuxKernelTest, Asset
+from qemu_test import wait_for_console_pattern
+
+class MilkvDuoMachine(LinuxKernelTest):
+
+ timeout = 120
+
+ ASSET_KERNEL = Asset(
+ 'https://github.com/visitorckw/milkv-duo-qemu-assets/releases/download/v1.0/Image',
+ '3f157ecfcb86d28bfcff9fd3c1a43f60a8513b1919739fdc9a4d946aba35b2af'
+ )
+ ASSET_DTB = Asset(
+ 'https://github.com/visitorckw/milkv-duo-qemu-assets/releases/download/v1.0/cv1800b-milkv-duo.dtb',
+ '2a5d165f64c4b2d55bba282b2974941ab223f6f5220c2e7721e35ab81311c098'
+ )
+ ASSET_ROOTFS = Asset(
+ 'https://github.com/visitorckw/milkv-duo-qemu-assets/releases/download/v1.0/rootfs.ext4',
+ '9e7de9222ab4a79d1291866453aa47cd67a543216df6159bf9b42d055240a676'
+ )
+
+ def test_riscv64_milkv_duo(self):
+ self.set_machine('milkv-duo')
+
+ kernel_path = self.ASSET_KERNEL.fetch()
+ dtb_path = self.ASSET_DTB.fetch()
+ rootfs_path = self.ASSET_ROOTFS.fetch()
+
+ self.vm.set_console()
+ self.vm.add_args('-bios', 'default',
+ '-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-drive', f'file={rootfs_path},format=raw,id=sd-card,snapshot=on',
+ '-device', 'sd-card,drive=sd-card',
+ '-append', 'console=ttyS0,115200 root=/dev/mmcblk0 rootwait earlycon')
+
+ self.vm.launch()
+
+ wait_for_console_pattern(self, 'Booting Linux on hartid')
+ wait_for_console_pattern(self, 'Run /sbin/init as init process')
+
+if __name__ == '__main__':
+ LinuxKernelTest.main()
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v4 6/7] tests/functional/riscv64: Add boot test for Milk-V Duo board
2026-08-22 19:43 ` [PATCH v4 6/7] tests/functional/riscv64: Add boot test " Kuan-Wei Chiu
@ 2026-09-25 4:02 ` Alistair Francis
2026-09-27 18:32 ` Kuan-Wei Chiu
0 siblings, 1 reply; 14+ messages in thread
From: Alistair Francis @ 2026-09-25 4:02 UTC (permalink / raw)
To: marcandre.lureau@redhat.com, pbonzini@redhat.com, farosas@suse.de,
pierrick.bouvier@oss.qualcomm.com, almer@dabbelt.com,
visitorckw@gmail.com, lvivier@redhat.com
Cc: wxchen0913@gmail.com, marscheng@google.com, qemu-devel@nongnu.org,
daniel.barboza@oss.qualcomm.com, jserv@ccns.ncku.edu.tw,
qemu-riscv@nongnu.org, chao.liu@processmission.com,
zhiwei_liu@linux.alibaba.com, eleanor15x@gmail.com,
liwei1518@gmail.com
On Sat, 2026-08-22 at 19:43 +0000, Kuan-Wei Chiu wrote:
> Add a functional boot test for the Milk-V Duo board to validate the
> end-to-end boot flow.
>
> $ ./build/run ./tests/functional/riscv64/test_milkv_duo.py
> TAP version 13
> ok 1 test_milkv_duo.MilkvDuoMachine.test_riscv64_milkv_duo
> 1..1
>
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
> ---
> MAINTAINERS | 1 +
> tests/functional/riscv64/meson.build | 1 +
> tests/functional/riscv64/test_milkv_duo.py | 50
> ++++++++++++++++++++++
> 3 files changed, 52 insertions(+)
> create mode 100755 tests/functional/riscv64/test_milkv_duo.py
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 245eb7bb58..3f79334c59 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1861,6 +1861,7 @@ F: hw/riscv/milkv_duo.c
> F: include/hw/char/dw8250.h
> F: include/hw/misc/cv1800b_clk.h
> F: include/hw/riscv/cv1800b.h
> +F: tests/functional/riscv64/test_milkv_duo.py
> F: tests/qtest/milkv-duo-test.c
>
> RX Machines
> diff --git a/tests/functional/riscv64/meson.build
> b/tests/functional/riscv64/meson.build
> index d1a3e6c2bf..8a4f1d00f9 100644
> --- a/tests/functional/riscv64/meson.build
> +++ b/tests/functional/riscv64/meson.build
> @@ -18,4 +18,5 @@ tests_riscv64_system_thorough = [
> 'sifive_u',
> 'tt_atlantis',
> 'tuxrun',
> + 'milkv_duo',
> ]
> diff --git a/tests/functional/riscv64/test_milkv_duo.py
> b/tests/functional/riscv64/test_milkv_duo.py
> new file mode 100755
> index 0000000000..aff247e61f
> --- /dev/null
> +++ b/tests/functional/riscv64/test_milkv_duo.py
> @@ -0,0 +1,50 @@
> +#!/usr/bin/env python3
> +#
> +# Functional test that boots a Linux kernel on a Milk-V Duo machine
> +#
> +# Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +from qemu_test import LinuxKernelTest, Asset
> +from qemu_test import wait_for_console_pattern
> +
> +class MilkvDuoMachine(LinuxKernelTest):
> +
> + timeout = 120
> +
> + ASSET_KERNEL = Asset(
> +
> 'https://github.com/visitorckw/milkv-duo-qemu-assets/releases/downloa
> d/v1.0/Image',
> +
> '3f157ecfcb86d28bfcff9fd3c1a43f60a8513b1919739fdc9a4d946aba35b2af'
> + )
> + ASSET_DTB = Asset(
> +
> 'https://github.com/visitorckw/milkv-duo-qemu-assets/releases/downloa
> d/v1.0/cv1800b-milkv-duo.dtb',
> +
> '2a5d165f64c4b2d55bba282b2974941ab223f6f5220c2e7721e35ab81311c098'
> + )
> + ASSET_ROOTFS = Asset(
> +
> 'https://github.com/visitorckw/milkv-duo-qemu-assets/releases/downloa
> d/v1.0/rootfs.ext4',
> +
> '9e7de9222ab4a79d1291866453aa47cd67a543216df6159bf9b42d055240a676'
> + )
Is there a more official source we could use?
Alistair
> +
> + def test_riscv64_milkv_duo(self):
> + self.set_machine('milkv-duo')
> +
> + kernel_path = self.ASSET_KERNEL.fetch()
> + dtb_path = self.ASSET_DTB.fetch()
> + rootfs_path = self.ASSET_ROOTFS.fetch()
> +
> + self.vm.set_console()
> + self.vm.add_args('-bios', 'default',
> + '-kernel', kernel_path,
> + '-dtb', dtb_path,
> + '-drive',
> f'file={rootfs_path},format=raw,id=sd-card,snapshot=on',
> + '-device', 'sd-card,drive=sd-card',
> + '-append', 'console=ttyS0,115200
> root=/dev/mmcblk0 rootwait earlycon')
> +
> + self.vm.launch()
> +
> + wait_for_console_pattern(self, 'Booting Linux on hartid')
> + wait_for_console_pattern(self, 'Run /sbin/init as init
> process')
> +
> +if __name__ == '__main__':
> + LinuxKernelTest.main()
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH v4 6/7] tests/functional/riscv64: Add boot test for Milk-V Duo board
2026-09-25 4:02 ` Alistair Francis
@ 2026-09-27 18:32 ` Kuan-Wei Chiu
0 siblings, 0 replies; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-09-27 18:32 UTC (permalink / raw)
To: Alistair Francis
Cc: marcandre.lureau@redhat.com, pbonzini@redhat.com, farosas@suse.de,
pierrick.bouvier@oss.qualcomm.com, almer@dabbelt.com,
lvivier@redhat.com, wxchen0913@gmail.com, marscheng@google.com,
qemu-devel@nongnu.org, daniel.barboza@oss.qualcomm.com,
jserv@ccns.ncku.edu.tw, qemu-riscv@nongnu.org,
chao.liu@processmission.com, zhiwei_liu@linux.alibaba.com,
eleanor15x@gmail.com, liwei1518@gmail.com
On Fri, Sep 25, 2026 at 04:02:45AM +0000, Alistair Francis wrote:
> On Sat, 2026-08-22 at 19:43 +0000, Kuan-Wei Chiu wrote:
> > Add a functional boot test for the Milk-V Duo board to validate the
> > end-to-end boot flow.
> >
> > $ ./build/run ./tests/functional/riscv64/test_milkv_duo.py
> > TAP version 13
> > ok 1 test_milkv_duo.MilkvDuoMachine.test_riscv64_milkv_duo
> > 1..1
> >
> > Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
> > ---
> > MAINTAINERS | 1 +
> > tests/functional/riscv64/meson.build | 1 +
> > tests/functional/riscv64/test_milkv_duo.py | 50
> > ++++++++++++++++++++++
> > 3 files changed, 52 insertions(+)
> > create mode 100755 tests/functional/riscv64/test_milkv_duo.py
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 245eb7bb58..3f79334c59 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -1861,6 +1861,7 @@ F: hw/riscv/milkv_duo.c
> > F: include/hw/char/dw8250.h
> > F: include/hw/misc/cv1800b_clk.h
> > F: include/hw/riscv/cv1800b.h
> > +F: tests/functional/riscv64/test_milkv_duo.py
> > F: tests/qtest/milkv-duo-test.c
> >
> > RX Machines
> > diff --git a/tests/functional/riscv64/meson.build
> > b/tests/functional/riscv64/meson.build
> > index d1a3e6c2bf..8a4f1d00f9 100644
> > --- a/tests/functional/riscv64/meson.build
> > +++ b/tests/functional/riscv64/meson.build
> > @@ -18,4 +18,5 @@ tests_riscv64_system_thorough = [
> > 'sifive_u',
> > 'tt_atlantis',
> > 'tuxrun',
> > + 'milkv_duo',
> > ]
> > diff --git a/tests/functional/riscv64/test_milkv_duo.py
> > b/tests/functional/riscv64/test_milkv_duo.py
> > new file mode 100755
> > index 0000000000..aff247e61f
> > --- /dev/null
> > +++ b/tests/functional/riscv64/test_milkv_duo.py
> > @@ -0,0 +1,50 @@
> > +#!/usr/bin/env python3
> > +#
> > +# Functional test that boots a Linux kernel on a Milk-V Duo machine
> > +#
> > +# Copyright (c) 2026 Kuan-Wei Chiu <visitorckw@gmail.com>
> > +#
> > +# SPDX-License-Identifier: GPL-2.0-or-later
> > +
> > +from qemu_test import LinuxKernelTest, Asset
> > +from qemu_test import wait_for_console_pattern
> > +
> > +class MilkvDuoMachine(LinuxKernelTest):
> > +
> > + timeout = 120
> > +
> > + ASSET_KERNEL = Asset(
> > +
> > 'https://github.com/visitorckw/milkv-duo-qemu-assets/releases/downloa
> > d/v1.0/Image',
> > +
> > '3f157ecfcb86d28bfcff9fd3c1a43f60a8513b1919739fdc9a4d946aba35b2af'
> > + )
> > + ASSET_DTB = Asset(
> > +
> > 'https://github.com/visitorckw/milkv-duo-qemu-assets/releases/downloa
> > d/v1.0/cv1800b-milkv-duo.dtb',
> > +
> > '2a5d165f64c4b2d55bba282b2974941ab223f6f5220c2e7721e35ab81311c098'
> > + )
> > + ASSET_ROOTFS = Asset(
> > +
> > 'https://github.com/visitorckw/milkv-duo-qemu-assets/releases/downloa
> > d/v1.0/rootfs.ext4',
> > +
> > '9e7de9222ab4a79d1291866453aa47cd67a543216df6159bf9b42d055240a676'
> > + )
>
> Is there a more official source we could use?
>
I initially tried booting the board using milk-v duo's buildroot SDK
release. It failed to boot probably because their downstream device
tree and kernel rely on many custom out oftree bindings that differ
from mainline Linux. This is why I originally hosted my own mainline
compiled images on my personal github.
However, I just experimented with switching to tuxboot assets similar
to how sifive_u is tested, and I can confirm that booting with the
tuxboot kernel, dtb, and rootfs works perfectly all the way to the
shell.
I'll update the test to fetch these assets from tuxboot.
Regards,
Kuan-Wei
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v4 7/7] docs/system/riscv: Add Milk-V Duo board documentation
2026-08-22 19:43 [PATCH v4 0/7] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
` (5 preceding siblings ...)
2026-08-22 19:43 ` [PATCH v4 6/7] tests/functional/riscv64: Add boot test " Kuan-Wei Chiu
@ 2026-08-22 19:43 ` Kuan-Wei Chiu
2026-09-25 4:04 ` Alistair Francis
6 siblings, 1 reply; 14+ messages in thread
From: Kuan-Wei Chiu @ 2026-08-22 19:43 UTC (permalink / raw)
To: pierrick.bouvier, almer, alistair.francis, pbonzini,
marcandre.lureau, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu, wxchen0913,
jserv, marscheng, eleanor15x, qemu-devel, qemu-riscv,
Kuan-Wei Chiu
Add documentation for the Milk-V Duo board. This includes a brief
overview of the hardware, a list of supported devices, and practical
instructions on how to boot the machine using OpenSBI and a Linux
kernel with an emulated SD card.
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
MAINTAINERS | 1 +
docs/system/riscv/milkv-duo.rst | 49 +++++++++++++++++++++++++++++++++
docs/system/target-riscv.rst | 1 +
3 files changed, 51 insertions(+)
create mode 100644 docs/system/riscv/milkv-duo.rst
diff --git a/MAINTAINERS b/MAINTAINERS
index 3f79334c59..9750a39ca3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1854,6 +1854,7 @@ F: tests/qtest/k230-wdt-test.c
Milk-V Duo
M: Kuan-Wei Chiu <visitorckw@gmail.com>
S: Maintained
+F: docs/system/riscv/milkv-duo.rst
F: hw/char/dw8250.c
F: hw/misc/cv1800b_clk.c
F: hw/riscv/cv1800b.c
diff --git a/docs/system/riscv/milkv-duo.rst b/docs/system/riscv/milkv-duo.rst
new file mode 100644
index 0000000000..1758db2c1e
--- /dev/null
+++ b/docs/system/riscv/milkv-duo.rst
@@ -0,0 +1,49 @@
+Milk-V Duo board (``milkv-duo``)
+================================
+
+The ``milkv-duo`` machine is compatible with the Milk-V Duo development board.
+
+The Milk-V Duo is an ultra-compact embedded development platform based on the
+Sophgo CV1800B SoC. The CV1800B features T-Head C906 RISC-V processing cores
+along with integrated peripherals.
+
+For more information, see <https://milkv.io/duo>
+
+Supported devices
+-----------------
+The ``milkv-duo`` machine supports the following devices:
+
+* T-Head C906 cores
+* Core Local Interruptor (CLINT)
+* Platform-Level Interrupt Controller (PLIC)
+* DesignWare APB UART (dw8250)
+* SDHCI controller
+* CV1800B clock controller
+
+Boot options
+------------
+The ``milkv-duo`` machine supports direct boot using a generic RISC-V OpenSBI
+firmware and a Linux kernel. The root filesystem can be provided via the
+emulated SD card.
+
+Running
+-------
+To boot the machine, you need a Linux kernel (built with CV1800B support), a
+device tree blob (DTB), and a root filesystem image (e.g., built via BusyBox
+or Buildroot).
+
+Use the following command to boot the system directly to the Linux shell:
+
+.. code-block:: bash
+
+ $ qemu-system-riscv64 -M milkv-duo -nographic \
+ -bios pc-bios/opensbi-riscv64-generic-fw_dynamic.bin \
+ -dtb /path/to/linux/arch/riscv/boot/dts/sophgo/cv1800b-milkv-duo.dtb \
+ -kernel /path/to/linux/arch/riscv/boot/Image \
+ -drive file=/path/to/rootfs.ext4,format=raw,id=sd-card \
+ -device sd-card,drive=sd-card \
+ -append "console=ttyS0,115200 root=/dev/mmcblk0 rootwait earlycon"
+
+This command boots the generic OpenSBI firmware, loads the Linux Image and the
+specified DTB, and mounts the ext4 root filesystem from the emulated SD card.
+The console output is routed to the ``ttyS0`` serial port.
diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst
index 2639866a3e..1e49055e29 100644
--- a/docs/system/target-riscv.rst
+++ b/docs/system/target-riscv.rst
@@ -69,6 +69,7 @@ undocumented; you can get a complete list by running
riscv/k230
riscv/microblaze-v-generic
riscv/microchip-icicle-kit
+ riscv/milkv-duo
riscv/mips
riscv/shakti-c
riscv/sifive_u
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH v4 7/7] docs/system/riscv: Add Milk-V Duo board documentation
2026-08-22 19:43 ` [PATCH v4 7/7] docs/system/riscv: Add Milk-V Duo board documentation Kuan-Wei Chiu
@ 2026-09-25 4:04 ` Alistair Francis
0 siblings, 0 replies; 14+ messages in thread
From: Alistair Francis @ 2026-09-25 4:04 UTC (permalink / raw)
To: marcandre.lureau@redhat.com, pbonzini@redhat.com, farosas@suse.de,
pierrick.bouvier@oss.qualcomm.com, almer@dabbelt.com,
visitorckw@gmail.com, lvivier@redhat.com
Cc: wxchen0913@gmail.com, marscheng@google.com, qemu-devel@nongnu.org,
daniel.barboza@oss.qualcomm.com, jserv@ccns.ncku.edu.tw,
qemu-riscv@nongnu.org, chao.liu@processmission.com,
zhiwei_liu@linux.alibaba.com, eleanor15x@gmail.com,
liwei1518@gmail.com
On Sat, 2026-08-22 at 19:43 +0000, Kuan-Wei Chiu wrote:
> Add documentation for the Milk-V Duo board. This includes a brief
> overview of the hardware, a list of supported devices, and practical
> instructions on how to boot the machine using OpenSBI and a Linux
> kernel with an emulated SD card.
>
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> MAINTAINERS | 1 +
> docs/system/riscv/milkv-duo.rst | 49
> +++++++++++++++++++++++++++++++++
> docs/system/target-riscv.rst | 1 +
> 3 files changed, 51 insertions(+)
> create mode 100644 docs/system/riscv/milkv-duo.rst
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3f79334c59..9750a39ca3 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1854,6 +1854,7 @@ F: tests/qtest/k230-wdt-test.c
> Milk-V Duo
> M: Kuan-Wei Chiu <visitorckw@gmail.com>
> S: Maintained
> +F: docs/system/riscv/milkv-duo.rst
> F: hw/char/dw8250.c
> F: hw/misc/cv1800b_clk.c
> F: hw/riscv/cv1800b.c
> diff --git a/docs/system/riscv/milkv-duo.rst
> b/docs/system/riscv/milkv-duo.rst
> new file mode 100644
> index 0000000000..1758db2c1e
> --- /dev/null
> +++ b/docs/system/riscv/milkv-duo.rst
> @@ -0,0 +1,49 @@
> +Milk-V Duo board (``milkv-duo``)
> +================================
> +
> +The ``milkv-duo`` machine is compatible with the Milk-V Duo
> development board.
> +
> +The Milk-V Duo is an ultra-compact embedded development platform
> based on the
> +Sophgo CV1800B SoC. The CV1800B features T-Head C906 RISC-V
> processing cores
> +along with integrated peripherals.
> +
> +For more information, see <https://milkv.io/duo>
> +
> +Supported devices
> +-----------------
> +The ``milkv-duo`` machine supports the following devices:
> +
> +* T-Head C906 cores
> +* Core Local Interruptor (CLINT)
> +* Platform-Level Interrupt Controller (PLIC)
> +* DesignWare APB UART (dw8250)
> +* SDHCI controller
> +* CV1800B clock controller
> +
> +Boot options
> +------------
> +The ``milkv-duo`` machine supports direct boot using a generic RISC-
> V OpenSBI
> +firmware and a Linux kernel. The root filesystem can be provided via
> the
> +emulated SD card.
> +
> +Running
> +-------
> +To boot the machine, you need a Linux kernel (built with CV1800B
> support), a
> +device tree blob (DTB), and a root filesystem image (e.g., built via
> BusyBox
> +or Buildroot).
> +
> +Use the following command to boot the system directly to the Linux
> shell:
> +
> +.. code-block:: bash
> +
> + $ qemu-system-riscv64 -M milkv-duo -nographic \
> + -bios pc-bios/opensbi-riscv64-generic-fw_dynamic.bin \
> + -dtb /path/to/linux/arch/riscv/boot/dts/sophgo/cv1800b-milkv-
> duo.dtb \
> + -kernel /path/to/linux/arch/riscv/boot/Image \
> + -drive file=/path/to/rootfs.ext4,format=raw,id=sd-card \
> + -device sd-card,drive=sd-card \
> + -append "console=ttyS0,115200 root=/dev/mmcblk0 rootwait
> earlycon"
> +
> +This command boots the generic OpenSBI firmware, loads the Linux
> Image and the
> +specified DTB, and mounts the ext4 root filesystem from the emulated
> SD card.
> +The console output is routed to the ``ttyS0`` serial port.
> diff --git a/docs/system/target-riscv.rst b/docs/system/target-
> riscv.rst
> index 2639866a3e..1e49055e29 100644
> --- a/docs/system/target-riscv.rst
> +++ b/docs/system/target-riscv.rst
> @@ -69,6 +69,7 @@ undocumented; you can get a complete list by
> running
> riscv/k230
> riscv/microblaze-v-generic
> riscv/microchip-icicle-kit
> + riscv/milkv-duo
> riscv/mips
> riscv/shakti-c
> riscv/sifive_u
^ permalink raw reply [flat|nested] 14+ messages in thread