* [PATCH v2 1/6] target/riscv: Add stubs for T-Head PMU CSRs
2026-05-14 1:15 [PATCH v2 0/6] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
@ 2026-05-14 1:15 ` Kuan-Wei Chiu
2026-05-14 2:48 ` Chao Liu
2026-05-14 1:15 ` [PATCH v2 2/6] hw/char: Add dw8250 UART Kuan-Wei Chiu
` (4 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Kuan-Wei Chiu @ 2026-05-14 1:15 UTC (permalink / raw)
To: pbonzini, marcandre.lureau, palmer, alistair.francis,
christoph.muellner, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu.zevorn, jserv,
eleanor15x, marscheng, qemu-devel, qemu-riscv, Kuan-Wei Chiu
T-Head CPUs use custom CSRs for performance monitoring, specifically
mcounterinten (0x7ca) and mcounterof (0x7cb). [1]
Since we don't implement these custom PMU registers yet, the system
crashes with an illegal instruction trap when OpenSBI like this:
system_opcode_insn: Failed to access CSR 0x7ca from M-mode
sbi_trap_error: hart0: trap1: illegal instruction handler failed (error -1)
Add simple read/write stubs for these two CSRs. By silently ignoring
writes and returning 0 on reads, we prevent the fatal exceptions and
allow to continue normally.
Link: https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf [1]
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
target/riscv/th_csr.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c
index 49eb7bbab5..b095364c31 100644
--- a/target/riscv/th_csr.c
+++ b/target/riscv/th_csr.c
@@ -21,12 +21,19 @@
#include "cpu_vendorid.h"
#define CSR_TH_SXSTATUS 0x5c0
+#define CSR_TH_MCOUNTERINTEN 0x7ca
+#define CSR_TH_MCOUNTEROF 0x7cb
/* TH_SXSTATUS bits */
#define TH_SXSTATUS_UCME BIT(16)
#define TH_SXSTATUS_MAEE BIT(21)
#define TH_SXSTATUS_THEADISAEE BIT(22)
+static RISCVException mmode(CPURISCVState *env, int csrno)
+{
+ return RISCV_EXCP_NONE;
+}
+
static RISCVException smode(CPURISCVState *env, int csrno)
{
if (riscv_has_ext(env, RVS)) {
@@ -49,11 +56,34 @@ static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno,
return RISCV_EXCP_NONE;
}
+static RISCVException read_th_pmu(CPURISCVState *env, int csrno,
+ target_ulong *val)
+{
+ *val = 0;
+ return RISCV_EXCP_NONE;
+}
+
+static RISCVException write_th_pmu(CPURISCVState *env, int csrno,
+ target_ulong val, uintptr_t retaddr)
+{
+ return RISCV_EXCP_NONE;
+}
+
const RISCVCSR th_csr_list[] = {
{
.csrno = CSR_TH_SXSTATUS,
.insertion_test = test_thead_mvendorid,
.csr_ops = { "th.sxstatus", smode, read_th_sxstatus }
},
+ {
+ .csrno = CSR_TH_MCOUNTERINTEN,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcounterinten", mmode, read_th_pmu, write_th_pmu }
+ },
+ {
+ .csrno = CSR_TH_MCOUNTEROF,
+ .insertion_test = test_thead_mvendorid,
+ .csr_ops = { "th.mcounterof", mmode, read_th_pmu, write_th_pmu }
+ },
{ }
};
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v2 1/6] target/riscv: Add stubs for T-Head PMU CSRs
2026-05-14 1:15 ` [PATCH v2 1/6] target/riscv: Add stubs for T-Head PMU CSRs Kuan-Wei Chiu
@ 2026-05-14 2:48 ` Chao Liu
2026-05-14 18:07 ` Kuan-Wei Chiu
0 siblings, 1 reply; 10+ messages in thread
From: Chao Liu @ 2026-05-14 2:48 UTC (permalink / raw)
To: Kuan-Wei Chiu
Cc: pbonzini, marcandre.lureau, palmer, alistair.francis,
christoph.muellner, farosas, lvivier, liwei1518, daniel.barboza,
zhiwei_liu, jserv, eleanor15x, marscheng, qemu-devel, qemu-riscv
On Thu, May 14, 2026 at 01:15:23AM +0000, Kuan-Wei Chiu wrote:
> T-Head CPUs use custom CSRs for performance monitoring, specifically
> mcounterinten (0x7ca) and mcounterof (0x7cb). [1]
>
> Since we don't implement these custom PMU registers yet, the system
> crashes with an illegal instruction trap when OpenSBI like this:
>
> system_opcode_insn: Failed to access CSR 0x7ca from M-mode
> sbi_trap_error: hart0: trap1: illegal instruction handler failed (error -1)
>
> Add simple read/write stubs for these two CSRs. By silently ignoring
> writes and returning 0 on reads, we prevent the fatal exceptions and
> allow to continue normally.
>
> Link: https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf [1]
> Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
> ---
> target/riscv/th_csr.c | 30 ++++++++++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
>
> diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c
> index 49eb7bbab5..b095364c31 100644
> --- a/target/riscv/th_csr.c
> +++ b/target/riscv/th_csr.c
> @@ -21,12 +21,19 @@
> #include "cpu_vendorid.h"
>
> #define CSR_TH_SXSTATUS 0x5c0
> +#define CSR_TH_MCOUNTERINTEN 0x7ca
> +#define CSR_TH_MCOUNTEROF 0x7cb
>
> /* TH_SXSTATUS bits */
> #define TH_SXSTATUS_UCME BIT(16)
> #define TH_SXSTATUS_MAEE BIT(21)
> #define TH_SXSTATUS_THEADISAEE BIT(22)
>
> +static RISCVException mmode(CPURISCVState *env, int csrno)
> +{
> + return RISCV_EXCP_NONE;
> +}
We can add an RVM extension check, similar in form to smode():
static RISCVException mmode(CPURISCVState *env, int csrno)
{
if (riscv_has_ext(env, RVM)) {
return RISCV_EXCP_NONE;
}
return RISCV_EXCP_ILLEGAL_INST;
}
For your reference, I have added more T-Head CSR support in the
K230 patchset.
https://lore.kernel.org/qemu-devel/1c7319cd00a50bffeb41f9cc13339a8cd0c07350.1778516731.git.chao.liu.zevorn@gmail.com/
As for this patch, I think the other changes look mostly fine.
Thanks,
Chao
> +
> static RISCVException smode(CPURISCVState *env, int csrno)
> {
> if (riscv_has_ext(env, RVS)) {
> @@ -49,11 +56,34 @@ static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno,
> return RISCV_EXCP_NONE;
> }
>
> +static RISCVException read_th_pmu(CPURISCVState *env, int csrno,
> + target_ulong *val)
> +{
> + *val = 0;
> + return RISCV_EXCP_NONE;
> +}
> +
> +static RISCVException write_th_pmu(CPURISCVState *env, int csrno,
> + target_ulong val, uintptr_t retaddr)
> +{
> + return RISCV_EXCP_NONE;
> +}
> +
> const RISCVCSR th_csr_list[] = {
> {
> .csrno = CSR_TH_SXSTATUS,
> .insertion_test = test_thead_mvendorid,
> .csr_ops = { "th.sxstatus", smode, read_th_sxstatus }
> },
> + {
> + .csrno = CSR_TH_MCOUNTERINTEN,
> + .insertion_test = test_thead_mvendorid,
> + .csr_ops = { "th.mcounterinten", mmode, read_th_pmu, write_th_pmu }
> + },
> + {
> + .csrno = CSR_TH_MCOUNTEROF,
> + .insertion_test = test_thead_mvendorid,
> + .csr_ops = { "th.mcounterof", mmode, read_th_pmu, write_th_pmu }
> + },
> { }
> };
> --
> 2.54.0.563.g4f69b47b94-goog
>
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v2 1/6] target/riscv: Add stubs for T-Head PMU CSRs
2026-05-14 2:48 ` Chao Liu
@ 2026-05-14 18:07 ` Kuan-Wei Chiu
2026-05-14 22:32 ` Chao Liu
0 siblings, 1 reply; 10+ messages in thread
From: Kuan-Wei Chiu @ 2026-05-14 18:07 UTC (permalink / raw)
To: Chao Liu
Cc: pbonzini, marcandre.lureau, palmer, alistair.francis,
christoph.muellner, farosas, lvivier, liwei1518, daniel.barboza,
zhiwei_liu, jserv, eleanor15x, marscheng, qemu-devel, qemu-riscv
Hi Chao,
On Thu, May 14, 2026 at 10:48:07AM +0800, Chao Liu wrote:
> On Thu, May 14, 2026 at 01:15:23AM +0000, Kuan-Wei Chiu wrote:
> > T-Head CPUs use custom CSRs for performance monitoring, specifically
> > mcounterinten (0x7ca) and mcounterof (0x7cb). [1]
> >
> > Since we don't implement these custom PMU registers yet, the system
> > crashes with an illegal instruction trap when OpenSBI like this:
> >
> > system_opcode_insn: Failed to access CSR 0x7ca from M-mode
> > sbi_trap_error: hart0: trap1: illegal instruction handler failed (error -1)
> >
> > Add simple read/write stubs for these two CSRs. By silently ignoring
> > writes and returning 0 on reads, we prevent the fatal exceptions and
> > allow to continue normally.
> >
> > Link: https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf [1]
> > Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
> > ---
> > target/riscv/th_csr.c | 30 ++++++++++++++++++++++++++++++
> > 1 file changed, 30 insertions(+)
> >
> > diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c
> > index 49eb7bbab5..b095364c31 100644
> > --- a/target/riscv/th_csr.c
> > +++ b/target/riscv/th_csr.c
> > @@ -21,12 +21,19 @@
> > #include "cpu_vendorid.h"
> >
> > #define CSR_TH_SXSTATUS 0x5c0
> > +#define CSR_TH_MCOUNTERINTEN 0x7ca
> > +#define CSR_TH_MCOUNTEROF 0x7cb
> >
> > /* TH_SXSTATUS bits */
> > #define TH_SXSTATUS_UCME BIT(16)
> > #define TH_SXSTATUS_MAEE BIT(21)
> > #define TH_SXSTATUS_THEADISAEE BIT(22)
> >
> > +static RISCVException mmode(CPURISCVState *env, int csrno)
> > +{
> > + return RISCV_EXCP_NONE;
> > +}
> We can add an RVM extension check, similar in form to smode():
>
> static RISCVException mmode(CPURISCVState *env, int csrno)
> {
> if (riscv_has_ext(env, RVM)) {
Thanks for your review.
However, I think this might not be correct.
IIUC, RVM represents the standard M extension for integer
multiplication and division instructions, rather than indicating
whether M mode is implemented. Is it even possible for a riscv machine
implementation to not have M mode?
Looking at the misa csr description in the spec, the 'S' and 'U' bits
specifically denote the implementation of supervisor and user modes,
respectively, but there is no corresponding bit for M mode. I assume
this implies M mode is unconditionally required?
> return RISCV_EXCP_NONE;
> }
>
> return RISCV_EXCP_ILLEGAL_INST;
> }
>
> For your reference, I have added more T-Head CSR support in the
> K230 patchset.
>
> https://lore.kernel.org/qemu-devel/1c7319cd00a50bffeb41f9cc13339a8cd0c07350.1778516731.git.chao.liu.zevorn@gmail.com/
It looks like your patch covers much more ground than mine and has
already received quite a few reviews. I assume once your series is
merged, I can simply drop my patch #1.
So I guess the most sensible workflow here is for me to wait until your
patchset is merged into the subsystem maintainer's tree (assuming it
will route through Alistair's tree [1] I guess?, please correct me if
I'm wrong!), and then I'll rebase my series on top of it and send out
v3?
[1]: https://github.com/alistair23/qemu.git riscv-to-apply.next
Regards,
Kuan-Wei
>
> As for this patch, I think the other changes look mostly fine.
>
> Thanks,
> Chao
> > +
> > static RISCVException smode(CPURISCVState *env, int csrno)
> > {
> > if (riscv_has_ext(env, RVS)) {
> > @@ -49,11 +56,34 @@ static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno,
> > return RISCV_EXCP_NONE;
> > }
> >
> > +static RISCVException read_th_pmu(CPURISCVState *env, int csrno,
> > + target_ulong *val)
> > +{
> > + *val = 0;
> > + return RISCV_EXCP_NONE;
> > +}
> > +
> > +static RISCVException write_th_pmu(CPURISCVState *env, int csrno,
> > + target_ulong val, uintptr_t retaddr)
> > +{
> > + return RISCV_EXCP_NONE;
> > +}
> > +
> > const RISCVCSR th_csr_list[] = {
> > {
> > .csrno = CSR_TH_SXSTATUS,
> > .insertion_test = test_thead_mvendorid,
> > .csr_ops = { "th.sxstatus", smode, read_th_sxstatus }
> > },
> > + {
> > + .csrno = CSR_TH_MCOUNTERINTEN,
> > + .insertion_test = test_thead_mvendorid,
> > + .csr_ops = { "th.mcounterinten", mmode, read_th_pmu, write_th_pmu }
> > + },
> > + {
> > + .csrno = CSR_TH_MCOUNTEROF,
> > + .insertion_test = test_thead_mvendorid,
> > + .csr_ops = { "th.mcounterof", mmode, read_th_pmu, write_th_pmu }
> > + },
> > { }
> > };
> > --
> > 2.54.0.563.g4f69b47b94-goog
> >
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v2 1/6] target/riscv: Add stubs for T-Head PMU CSRs
2026-05-14 18:07 ` Kuan-Wei Chiu
@ 2026-05-14 22:32 ` Chao Liu
0 siblings, 0 replies; 10+ messages in thread
From: Chao Liu @ 2026-05-14 22:32 UTC (permalink / raw)
To: Kuan-Wei Chiu
Cc: pbonzini, marcandre.lureau, palmer, alistair.francis,
christoph.muellner, farosas, lvivier, liwei1518, daniel.barboza,
zhiwei_liu, jserv, eleanor15x, marscheng, qemu-devel, qemu-riscv
On Fri, May 15, 2026 at 02:07:18AM +0800, Kuan-Wei Chiu wrote:
> Hi Chao,
>
> On Thu, May 14, 2026 at 10:48:07AM +0800, Chao Liu wrote:
> > On Thu, May 14, 2026 at 01:15:23AM +0000, Kuan-Wei Chiu wrote:
> > > T-Head CPUs use custom CSRs for performance monitoring, specifically
> > > mcounterinten (0x7ca) and mcounterof (0x7cb). [1]
> > >
> > > Since we don't implement these custom PMU registers yet, the system
> > > crashes with an illegal instruction trap when OpenSBI like this:
> > >
> > > system_opcode_insn: Failed to access CSR 0x7ca from M-mode
> > > sbi_trap_error: hart0: trap1: illegal instruction handler failed (error -1)
> > >
> > > Add simple read/write stubs for these two CSRs. By silently ignoring
> > > writes and returning 0 on reads, we prevent the fatal exceptions and
> > > allow to continue normally.
> > >
> > > Link: https://occ-intl-prod.oss-ap-southeast-1.aliyuncs.com/resource/XuanTie-OpenC906-UserManual.pdf [1]
> > > Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
> > > ---
> > > target/riscv/th_csr.c | 30 ++++++++++++++++++++++++++++++
> > > 1 file changed, 30 insertions(+)
> > >
> > > diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c
> > > index 49eb7bbab5..b095364c31 100644
> > > --- a/target/riscv/th_csr.c
> > > +++ b/target/riscv/th_csr.c
> > > @@ -21,12 +21,19 @@
> > > #include "cpu_vendorid.h"
> > >
> > > #define CSR_TH_SXSTATUS 0x5c0
> > > +#define CSR_TH_MCOUNTERINTEN 0x7ca
> > > +#define CSR_TH_MCOUNTEROF 0x7cb
> > >
> > > /* TH_SXSTATUS bits */
> > > #define TH_SXSTATUS_UCME BIT(16)
> > > #define TH_SXSTATUS_MAEE BIT(21)
> > > #define TH_SXSTATUS_THEADISAEE BIT(22)
> > >
> > > +static RISCVException mmode(CPURISCVState *env, int csrno)
> > > +{
> > > + return RISCV_EXCP_NONE;
> > > +}
> > We can add an RVM extension check, similar in form to smode():
> >
> > static RISCVException mmode(CPURISCVState *env, int csrno)
> > {
> > if (riscv_has_ext(env, RVM)) {
>
> Thanks for your review.
> However, I think this might not be correct.
>
> IIUC, RVM represents the standard M extension for integer
> multiplication and division instructions, rather than indicating
> whether M mode is implemented. Is it even possible for a riscv machine
> implementation to not have M mode?
>
> Looking at the misa csr description in the spec, the 'S' and 'U' bits
> specifically denote the implementation of supervisor and user modes,
> respectively, but there is no corresponding bit for M mode. I assume
> this implies M mode is unconditionally required?
>
Good catch, your analysis is correct. I will fix this in the next version.
> > return RISCV_EXCP_NONE;
> > }
> >
> > return RISCV_EXCP_ILLEGAL_INST;
> > }
> >
> > For your reference, I have added more T-Head CSR support in the
> > K230 patchset.
> >
> > https://lore.kernel.org/qemu-devel/1c7319cd00a50bffeb41f9cc13339a8cd0c07350.1778516731.git.chao.liu.zevorn@gmail.com/
>
> It looks like your patch covers much more ground than mine and has
> already received quite a few reviews. I assume once your series is
> merged, I can simply drop my patch #1.
>
> So I guess the most sensible workflow here is for me to wait until your
> patchset is merged into the subsystem maintainer's tree (assuming it
> will route through Alistair's tree [1] I guess?, please correct me if
> I'm wrong!), and then I'll rebase my series on top of it and send out
> v3?
>
I think both are fine, we need to see what Alistair suggests.
Thanks,
Chao
> [1]: https://github.com/alistair23/qemu.git riscv-to-apply.next
>
> Regards,
> Kuan-Wei
>
> >
> > As for this patch, I think the other changes look mostly fine.
> >
> > Thanks,
> > Chao
> > > +
> > > static RISCVException smode(CPURISCVState *env, int csrno)
> > > {
> > > if (riscv_has_ext(env, RVS)) {
> > > @@ -49,11 +56,34 @@ static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno,
> > > return RISCV_EXCP_NONE;
> > > }
> > >
> > > +static RISCVException read_th_pmu(CPURISCVState *env, int csrno,
> > > + target_ulong *val)
> > > +{
> > > + *val = 0;
> > > + return RISCV_EXCP_NONE;
> > > +}
> > > +
> > > +static RISCVException write_th_pmu(CPURISCVState *env, int csrno,
> > > + target_ulong val, uintptr_t retaddr)
> > > +{
> > > + return RISCV_EXCP_NONE;
> > > +}
> > > +
> > > const RISCVCSR th_csr_list[] = {
> > > {
> > > .csrno = CSR_TH_SXSTATUS,
> > > .insertion_test = test_thead_mvendorid,
> > > .csr_ops = { "th.sxstatus", smode, read_th_sxstatus }
> > > },
> > > + {
> > > + .csrno = CSR_TH_MCOUNTERINTEN,
> > > + .insertion_test = test_thead_mvendorid,
> > > + .csr_ops = { "th.mcounterinten", mmode, read_th_pmu, write_th_pmu }
> > > + },
> > > + {
> > > + .csrno = CSR_TH_MCOUNTEROF,
> > > + .insertion_test = test_thead_mvendorid,
> > > + .csr_ops = { "th.mcounterof", mmode, read_th_pmu, write_th_pmu }
> > > + },
> > > { }
> > > };
> > > --
> > > 2.54.0.563.g4f69b47b94-goog
> > >
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 2/6] hw/char: Add dw8250 UART
2026-05-14 1:15 [PATCH v2 0/6] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
2026-05-14 1:15 ` [PATCH v2 1/6] target/riscv: Add stubs for T-Head PMU CSRs Kuan-Wei Chiu
@ 2026-05-14 1:15 ` Kuan-Wei Chiu
2026-05-14 1:15 ` [PATCH v2 3/6] hw/misc: Add Sophgo CV1800B clock controller Kuan-Wei Chiu
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Kuan-Wei Chiu @ 2026-05-14 1:15 UTC (permalink / raw)
To: pbonzini, marcandre.lureau, palmer, alistair.francis,
christoph.muellner, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu.zevorn, jserv,
eleanor15x, marscheng, qemu-devel, qemu-riscv, Kuan-Wei Chiu
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:
[ 0.293566] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[ 0.306929] Oops - store (or AMO) access fault [#1]
[ 0.307020] Modules linked in:
[ 0.307192] CPU: 0 UID: 0 PID: 1 Comm: swapper/0 Not tainted 7.0.0 #1 PREEMPTLAZY
[ 0.307250] Hardware name: Milk-V Duo (DT)
[ 0.307294] epc : dw8250_setup_port+0x22/0x520
[ 0.307504] ra : dw8250_probe+0x57e/0x5b8
[ 0.307518] epc : ffffffff80708dd6 ra : ffffffff8070a49e sp : ffffffc60000b820
[ 0.307525] gp : ffffffff81a32ba8 tp : ffffffd602180cc0 t0 : 0000000000000073
[ 0.307533] t1 : 000000000000006c t2 : 0000000000000000 s0 : ffffffc60000b830
[ 0.307539] s1 : ffffffd6028c8640 a0 : ffffffc60000b848 a1 : ffffffff813162c1
[ 0.307546] a2 : ffffffff813162c0 a3 : ffffffd6028c8640 a4 : ffffffc60002d0b4
[ 0.307552] a5 : 0000000000000001 a6 : 0000000000000094 a7 : 0000000000000060
[ 0.307558] s2 : ffffffd60225d410 s3 : ffffffd60225d400 s4 : 0000000000000000
[ 0.307573] s5 : ffffffff80e31a48 s6 : 0000000000000008 s7 : 0000000000000000
[ 0.307584] s8 : 0000000000000149 s9 : 0000000000000000 s10: 0000000000000000
[ 0.307590] s11: 0000000000000000 t3 : ffffffd602007c00 t4 : ffffffff81601540
[ 0.307604] t5 : 0000000000000003 t6 : ffffffd602a42f82 ssp : 0000000000000000
[ 0.307611] status: 0000000200000120 badaddr: ffffffc60002d0b4 cause: 0000000000000007
[ 0.307652] [<ffffffff80708dd6>] dw8250_setup_port+0x22/0x520
[ 0.307695] [<ffffffff8070a49e>] dw8250_probe+0x57e/0x5b8
[ 0.307702] [<ffffffff80731f0e>] platform_probe+0x46/0x80
[ 0.307708] [<ffffffff8072f67c>] really_probe+0x84/0x22c
[ 0.307715] [<ffffffff8072f880>] __driver_probe_device+0x5c/0xd4
[ 0.307721] [<ffffffff8072f9be>] driver_probe_device+0x2e/0xf4
[ 0.307727] [<ffffffff8072fbe6>] __driver_attach+0x6e/0x14c
[ 0.307734] [<ffffffff8072d5f0>] bus_for_each_dev+0x60/0xb0
[ 0.307740] [<ffffffff8072f1b2>] driver_attach+0x1a/0x24
[ 0.307746] [<ffffffff8072e8da>] bus_add_driver+0xca/0x1d8
[ 0.307752] [<ffffffff80730aaa>] driver_register+0x3e/0xdc
[ 0.307757] [<ffffffff80731c54>] __platform_driver_register+0x1c/0x24
[ 0.307779] [<ffffffff80c334f6>] dw8250_platform_driver_init+0x1a/0x24
[ 0.307793] [<ffffffff80011992>] do_one_initcall+0x4e/0x2a4
[ 0.307800] [<ffffffff80c01362>] kernel_init_freeable+0x226/0x2b0
[ 0.307807] [<ffffffff80bc3798>] kernel_init+0x1c/0x144
[ 0.307813] [<ffffffff8001361c>] ret_from_fork_kernel+0x18/0x164
[ 0.307820] [<ffffffff80bcefb6>] ret_from_fork_kernel_asm+0x16/0x18
[ 0.307914] Code: 3683 2085 0b63 32f7 000f 0140 6918 4785 0713 0b47 (c31c) 2583
[ 0.308041] ---[ end trace 0000000000000000 ]---
[ 0.308180] Kernel panic - not syncing: Fatal exception in interrupt
[ 0.315760] ---[ end Kernel panic - not syncing: Fatal exception in interrupt ]---
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
hw/char/Kconfig | 4 ++
hw/char/dw8250.c | 118 +++++++++++++++++++++++++++++++++++++++
hw/char/meson.build | 1 +
include/hw/char/dw8250.h | 27 +++++++++
4 files changed, 150 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..4ec829ceaf
--- /dev/null
+++ b/hw/char/dw8250.c
@@ -0,0 +1,118 @@
+/* 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 "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 /* Receiver Output Enable Register */
+#define DW_UART_DLF 0xC0 /* Divisor Latch Fraction Register */
+#define DW_UART_CPR 0xF4 /* Component Parameter Register */
+#define DW_UART_UCV 0xF8 /* UART Component Version */
+#define DW_UART_CTR 0xFC /* 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)
+{
+ switch (addr) {
+ 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)
+{
+}
+
+static const MemoryRegionOps dw8250_ext_ops = {
+ .read = dw8250_ext_read,
+ .write = dw8250_ext_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.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);
+ sysbus_realize(serial_sbd, errp);
+
+ 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.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 3/6] hw/misc: Add Sophgo CV1800B clock controller
2026-05-14 1:15 [PATCH v2 0/6] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
2026-05-14 1:15 ` [PATCH v2 1/6] target/riscv: Add stubs for T-Head PMU CSRs Kuan-Wei Chiu
2026-05-14 1:15 ` [PATCH v2 2/6] hw/char: Add dw8250 UART Kuan-Wei Chiu
@ 2026-05-14 1:15 ` Kuan-Wei Chiu
2026-05-14 1:15 ` [PATCH v2 4/6] hw/riscv: Add Sophgo CV1800B SoC support Kuan-Wei Chiu
` (2 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Kuan-Wei Chiu @ 2026-05-14 1:15 UTC (permalink / raw)
To: pbonzini, marcandre.lureau, palmer, alistair.francis,
christoph.muellner, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu.zevorn, jserv,
eleanor15x, marscheng, 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:
[ 0.888739] Waiting for root device /dev/mmcblk0...
[ 10.727739] mmc0: Timeout waiting for hardware cmd interrupt.
[ 10.728042] mmc0: sdhci: ============ SDHCI REGISTER DUMP ===========
[ 10.728356] mmc0: sdhci: Sys addr: 0x00000002 | Version: 0x00002402
[ 10.728618] mmc0: sdhci: Blk size: 0x00000000 | Blk cnt: 0x00000000
[ 10.728919] mmc0: sdhci: Argument: 0x00000000 | Trn mode: 0x00000000
[ 10.729271] mmc0: sdhci: Present: 0x01ff0000 | Host ctl: 0x00000001
[ 10.729591] mmc0: sdhci: Power: 0x0000000f | Blk gap: 0x00000000
[ 10.729903] mmc0: sdhci: Wake-up: 0x00000000 | Clock: 0x00000000
[ 10.730223] mmc0: sdhci: Timeout: 0x00000000 | Int stat: 0x00000000
[ 10.730537] mmc0: sdhci: Int enab: 0x00ff0083 | Sig enab: 0x00ff0083
[ 10.730795] mmc0: sdhci: ACmd stat: 0x00000000 | Slot int: 0x00000000
[ 10.731005] mmc0: sdhci: Caps: 0x056900b9 | Caps_1: 0x00000000
[ 10.731211] mmc0: sdhci: Cmd: 0x00000000 | Max curr: 0x00000000
[ 10.731415] mmc0: sdhci: Resp[0]: 0x00000000 | Resp[1]: 0x00000000
[ 10.731636] mmc0: sdhci: Resp[2]: 0x00000000 | Resp[3]: 0x00000000
[ 10.731851] mmc0: sdhci: Host ctl2: 0x00000000
[ 10.732018] mmc0: sdhci: ADMA Err: 0x00000000 | ADMA Ptr: 0x00000000
[ 10.732229] mmc0: sdhci: ============================================
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
---
hw/misc/Kconfig | 3 ++
hw/misc/cv1800b_clk.c | 89 +++++++++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
include/hw/misc/cv1800b_clk.h | 24 ++++++++++
4 files changed, 117 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 99bdf09219..42cdf771a4 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -254,4 +254,7 @@ config IOSB
config XLNX_VERSAL_TRNG
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..738f0910db
--- /dev/null
+++ b/hw/misc/cv1800b_clk.c
@@ -0,0 +1,89 @@
+/* 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_CLK_BYP_0 (0x030 / 4)
+#define REG_CLK_BYP_1 (0x034 / 4)
+
+static uint64_t cv1800b_clk_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ CV1800BClkState *s = opaque;
+ uint32_t val = 0;
+
+ if ((addr / 4) < ARRAY_SIZE(s->regs)) {
+ val = s->regs[addr / 4];
+ }
+
+ return val;
+}
+
+static void cv1800b_clk_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+ CV1800BClkState *s = opaque;
+
+ if ((addr / 4) < ARRAY_SIZE(s->regs)) {
+ s->regs[addr / 4] = 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, 0x1000);
+ 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 245ab9b98c..a4c5f90cd8 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -35,6 +35,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..05c0d1ca1b
--- /dev/null
+++ b/include/hw/misc/cv1800b_clk.h
@@ -0,0 +1,24 @@
+/* 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)
+
+struct CV1800BClkState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ uint32_t regs[0x1000 / 4];
+};
+
+#endif
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 4/6] hw/riscv: Add Sophgo CV1800B SoC support
2026-05-14 1:15 [PATCH v2 0/6] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
` (2 preceding siblings ...)
2026-05-14 1:15 ` [PATCH v2 3/6] hw/misc: Add Sophgo CV1800B clock controller Kuan-Wei Chiu
@ 2026-05-14 1:15 ` Kuan-Wei Chiu
2026-05-14 1:15 ` [PATCH v2 5/6] hw/riscv: Add Milk-V Duo board support Kuan-Wei Chiu
2026-05-14 1:15 ` [PATCH v2 6/6] tests/qtest: Add qtest for Milk-V Duo board Kuan-Wei Chiu
5 siblings, 0 replies; 10+ messages in thread
From: Kuan-Wei Chiu @ 2026-05-14 1:15 UTC (permalink / raw)
To: pbonzini, marcandre.lureau, palmer, alistair.francis,
christoph.muellner, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu.zevorn, jserv,
eleanor15x, marscheng, 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 | 168 +++++++++++++++++++++++++++++++++++++
hw/riscv/meson.build | 2 +
include/hw/riscv/cv1800b.h | 52 ++++++++++++
4 files changed, 230 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 2518b04175..5b68991edb 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -138,3 +138,11 @@ config MIPS_BOSTON_AIA
select RISCV_MIPS_CMGCR
select RISCV_MIPS_CPC
select RISCV_MIPS_CPS
+
+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..c6749e1202
--- /dev/null
+++ b/hw/riscv/cv1800b.c
@@ -0,0 +1,168 @@
+/* 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/char/serial.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);
+ object_initialize_child(obj, "clk", &s->clk, TYPE_CV1800B_CLK);
+}
+
+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;
+ DeviceState *uart, *sdhci;
+
+ qdev_prop_set_uint32(DEVICE(&s->cpus), "num-harts", num_harts);
+ 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);
+
+ sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal);
+
+ 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);
+
+ uart = qdev_new("dw8250");
+ qdev_prop_set_uint8(uart, "regshift", 2);
+ qdev_prop_set_chr(uart, "chardev", serial_hd(0));
+ sysbus_realize(SYS_BUS_DEVICE(uart), errp);
+ sysbus_mmio_map(SYS_BUS_DEVICE(uart), 0, cv1800b_memmap[CV1800B_DEV_UART0].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(uart), 0,
+ qdev_get_gpio_in(DEVICE(s->plic), CV1800B_UART0_IRQ));
+
+ sdhci = qdev_new(TYPE_SYSBUS_SDHCI);
+ qdev_prop_set_uint8(sdhci, "sd-spec-version", 3);
+ qdev_prop_set_uint64(sdhci, "capareg", 0x056900b9);
+ sysbus_realize(SYS_BUS_DEVICE(sdhci), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(sdhci), 0, cv1800b_memmap[CV1800B_DEV_SD0].base);
+ sysbus_connect_irq(SYS_BUS_DEVICE(sdhci), 0,
+ qdev_get_gpio_in(DEVICE(s->plic), CV1800B_SD0_IRQ));
+
+ sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_fatal);
+ 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 533472e22a..04e25eeece 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -18,4 +18,6 @@ riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c
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_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..a214f7a9f6
--- /dev/null
+++ b/include/hw/riscv/cv1800b.h
@@ -0,0 +1,52 @@
+/* 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"
+
+#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;
+};
+
+#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.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 5/6] hw/riscv: Add Milk-V Duo board support
2026-05-14 1:15 [PATCH v2 0/6] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
` (3 preceding siblings ...)
2026-05-14 1:15 ` [PATCH v2 4/6] hw/riscv: Add Sophgo CV1800B SoC support Kuan-Wei Chiu
@ 2026-05-14 1:15 ` Kuan-Wei Chiu
2026-05-14 1:15 ` [PATCH v2 6/6] tests/qtest: Add qtest for Milk-V Duo board Kuan-Wei Chiu
5 siblings, 0 replies; 10+ messages in thread
From: Kuan-Wei Chiu @ 2026-05-14 1:15 UTC (permalink / raw)
To: pbonzini, marcandre.lureau, palmer, alistair.francis,
christoph.muellner, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu.zevorn, jserv,
eleanor15x, marscheng, qemu-devel, qemu-riscv, Kuan-Wei Chiu
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>
---
MAINTAINERS | 11 ++
configs/devices/riscv64-softmmu/default.mak | 1 +
hw/riscv/Kconfig | 6 +
hw/riscv/meson.build | 1 +
hw/riscv/milkv_duo.c | 122 ++++++++++++++++++++
5 files changed, 141 insertions(+)
create mode 100644 hw/riscv/milkv_duo.c
diff --git a/MAINTAINERS b/MAINTAINERS
index afa178c5cc..a5649fd31f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1780,6 +1780,17 @@ F: docs/system/riscv/xiangshan-kunminghu.rst
F: hw/riscv/xiangshan_kmh.c
F: include/hw/riscv/xiangshan_kmh.h
+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 5b68991edb..8615b1cc3e 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -146,3 +146,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 04e25eeece..19a0bf8e5b 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -19,5 +19,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_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..5e8d5ea009
--- /dev/null
+++ b/hw/riscv/milkv_duo.c
@@ -0,0 +1,122 @@
+/* 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>
+
+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;
+ 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_load_addr = cv1800b_memmap[CV1800B_DEV_DRAM].base;
+ firmware_end_addr = firmware_load_addr;
+ if (machine->firmware) {
+ firmware_end_addr = riscv_find_and_load_firmware(machine, machine->firmware,
+ &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);
+ rom_add_blob_fixed_as("fdt", machine->fdt, fdt_size, fdt_load_addr,
+ &address_space_memory);
+ }
+
+ 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_info = {
+ .name = TYPE_MILK_V_DUO,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(MilkVDuoState),
+ .class_init = milkv_duo_machine_class_init,
+};
+
+static void milkv_duo_machine_register_types(void)
+{
+ type_register_static(&milkv_duo_machine_type_info);
+}
+
+type_init(milkv_duo_machine_register_types)
--
2.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 6/6] tests/qtest: Add qtest for Milk-V Duo board
2026-05-14 1:15 [PATCH v2 0/6] hw/riscv: Add support for Milk-V Duo board Kuan-Wei Chiu
` (4 preceding siblings ...)
2026-05-14 1:15 ` [PATCH v2 5/6] hw/riscv: Add Milk-V Duo board support Kuan-Wei Chiu
@ 2026-05-14 1:15 ` Kuan-Wei Chiu
5 siblings, 0 replies; 10+ messages in thread
From: Kuan-Wei Chiu @ 2026-05-14 1:15 UTC (permalink / raw)
To: pbonzini, marcandre.lureau, palmer, alistair.francis,
christoph.muellner, farosas, lvivier
Cc: liwei1518, daniel.barboza, zhiwei_liu, chao.liu.zevorn, jserv,
eleanor15x, marscheng, 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 a5649fd31f..8e62809728 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1790,6 +1790,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 43f83ffd3a..524340276e 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -290,7 +290,8 @@ qtests_riscv64 = ['riscv-csr-test'] + \
(unpack_edk2_blobs ? ['bios-tables-test'] : []) + \
(config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and
config_all_devices.has_key('CONFIG_RISCV_IOMMU') ?
- ['iommu-riscv-test'] : [])
+ ['iommu-riscv-test'] : []) + \
+ ['milkv-duo-test']
qos_test_ss = ss.source_set()
qos_test_ss.add(
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.54.0.563.g4f69b47b94-goog
^ permalink raw reply related [flat|nested] 10+ messages in thread