On Thu, Jul 16, 2026 at 09:24:23PM +0800, Junze Cao wrote: > Add qtests for DDRC and PHY reset values, register access policy, the > software-update handshake, and PHY register ownership. > > Exercise PHY training and mailbox acknowledgement through the DFI > initialization sequence. Include negative coverage to ensure DFI cannot > complete before PHY training or enter Normal mode before completion is > enabled. > > Signed-off-by: Junze Cao <[email protected]> Suggested-by: Chao Liu <[email protected]>
Thanks, Chao > --- > MAINTAINERS | 1 + > tests/qtest/k230-ddr-test.c | 226 ++++++++++++++++++++++++++++++++++++ > tests/qtest/meson.build | 3 +- > 3 files changed, 229 insertions(+), 1 deletion(-) > create mode 100644 tests/qtest/k230-ddr-test.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 1cb3ef6c02..ace7def55e 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1833,6 +1833,7 @@ F: include/hw/misc/k230_ddr.h > F: include/hw/riscv/k230.h > F: include/hw/watchdog/k230_wdt.h > F: tests/functional/riscv64/test_k230.py > +F: tests/qtest/k230-ddr-test.c > F: tests/qtest/k230-wdt-test.c > > RX Machines > diff --git a/tests/qtest/k230-ddr-test.c b/tests/qtest/k230-ddr-test.c > new file mode 100644 > index 0000000000..72362929bc > --- /dev/null > +++ b/tests/qtest/k230-ddr-test.c > @@ -0,0 +1,226 @@ > +/* > + * QTest testcase for the K230 DDR controller and PHY > + * > + * Exercises register access, reset, PHY ownership, training mailbox, and DFI > + * initialization. > + * > + * Copyright (c) 2026 Junze Cao <[email protected]> > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + */ > + > +#include "qemu/osdep.h" > +#include "libqtest.h" > + > +#define K230_DDRC_BASE UINT64_C(0x98000000) > +#define K230_DDR_PHY_BASE UINT64_C(0x9a000000) > + > +#define K230_DDRC_MSTR 0x000 > +#define K230_DDRC_STAT 0x004 > +#define K230_DDRC_DRAMTMG4 0x110 > +#define K230_DDRC_DFIMISC 0x1b0 > +#define K230_DDRC_DFISTAT 0x1bc > +#define K230_DDRC_SWCTL 0x320 > +#define K230_DDRC_SWSTAT 0x324 > + > +#define K230_DDRC_STAT_OPERATING_MODE_MASK 0x7 > +#define K230_DDRC_DFISTAT_DFI_INIT_COMPLETE_MASK 0x1 > + > +#define K230_DDR_PHY_CSR(index) \ > + (K230_DDR_PHY_BASE + (uint64_t)(index) * sizeof(uint32_t)) > + > +#define K230_DDR_PHY_ATX_IMPEDANCE 0x00043 > +#define K230_DDR_PHY_TX_IMPEDANCE_CTRL1 0x10049 > +#define K230_DDR_PHY_VREF_IN_GLOBAL 0x200b2 > +#define K230_DDR_PHY_MICRO_CONT_MUX_SEL 0xd0000 > +#define K230_DDR_PHY_TRAINING_STATUS 0xd0004 > +#define K230_DDR_PHY_TRAINING_ACK 0xd0031 > +#define K230_DDR_PHY_TRAINING_MESSAGE 0xd0032 > +#define K230_DDR_PHY_TRAINING_TRIGGER 0xd0099 > + > +static void complete_phy_training(QTestState *qts) > +{ > + qtest_writel(qts, > + K230_DDR_PHY_CSR(K230_DDR_PHY_MICRO_CONT_MUX_SEL), 1); > + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_TRIGGER), 9); > + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_TRIGGER), 1); > + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_TRIGGER), 0); > +} > + > +static void test_reset_and_register_access(void) > +{ > + QTestState *qts = qtest_init("-machine k230"); > + > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_MSTR), > + ==, 0x01040000); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT), > + ==, 0x00000000); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DRAMTMG4), > + ==, 0x05040405); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC), > + ==, 0x00000001); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWCTL), > + ==, 0x00000001); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), > + ==, 0x00000001); > + > + g_assert_cmphex(qtest_readl(qts, > + K230_DDR_PHY_CSR(K230_DDR_PHY_ATX_IMPEDANCE)), > + ==, 0x03ff); > + g_assert_cmphex(qtest_readl(qts, > + K230_DDR_PHY_CSR(K230_DDR_PHY_TX_IMPEDANCE_CTRL1)), > + ==, 0x0fff); > + g_assert_cmphex(qtest_readl(qts, > + K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL)), > + ==, 0x0200); > + > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_MSTR, 0x01040008); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_MSTR), > + ==, 0x01040008); > + > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_STAT, 0xffffffff); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT), > + ==, 0x00000000); > + > + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL), > + 0xffffffff); > + g_assert_cmphex(qtest_readl(qts, > + K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL)), > + ==, 0x7fff); > + > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DRAMTMG4, 0); > + > + qtest_system_reset(qts); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_MSTR), > + ==, 0x01040000); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DRAMTMG4), > + ==, 0x05040405); > + g_assert_cmphex(qtest_readl(qts, > + K230_DDR_PHY_CSR(K230_DDR_PHY_VREF_IN_GLOBAL)), > + ==, 0x0200); > + > + qtest_quit(qts); > +} > + > +static void test_software_update_handshake(void) > +{ > + QTestState *qts = qtest_init("-machine k230"); > + > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_SWCTL, 0); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), > + ==, 0); > + > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_SWCTL, 1); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), > + ==, 1); > + > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT, 0); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_SWSTAT), > + ==, 1); > + > + qtest_quit(qts); > +} > + > +static void test_phy_ownership(void) > +{ > + QTestState *qts = qtest_init("-machine k230"); > + uint64_t atx = K230_DDR_PHY_CSR(K230_DDR_PHY_ATX_IMPEDANCE); > + uint64_t mux = K230_DDR_PHY_CSR(K230_DDR_PHY_MICRO_CONT_MUX_SEL); > + > + qtest_writel(qts, atx, 0x155); > + g_assert_cmphex(qtest_readl(qts, atx), ==, 0x155); > + > + qtest_writel(qts, mux, 1); > + qtest_writel(qts, atx, 0x2aa); > + g_assert_cmphex(qtest_readl(qts, atx), ==, 0x155); > + > + qtest_writel(qts, mux, 0); > + qtest_writel(qts, atx, 0x2aa); > + g_assert_cmphex(qtest_readl(qts, atx), ==, 0x2aa); > + > + qtest_quit(qts); > +} > + > +static void test_dfi_prerequisites(void) > +{ > + QTestState *qts = qtest_init("-machine k230"); > + > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0x20); > + > + g_assert_cmphex(qtest_readl(qts, > + K230_DDRC_BASE + K230_DDRC_DFISTAT) & > + K230_DDRC_DFISTAT_DFI_INIT_COMPLETE_MASK, > + ==, 0); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT) & > + K230_DDRC_STAT_OPERATING_MODE_MASK, > + ==, 0); > + > + qtest_quit(qts); > +} > + > +static void test_dfi_complete_enable(void) > +{ > + QTestState *qts = qtest_init("-machine k230"); > + > + complete_phy_training(qts); > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0x20); > + > + g_assert_cmphex(qtest_readl(qts, > + K230_DDRC_BASE + K230_DDRC_DFISTAT) & > + K230_DDRC_DFISTAT_DFI_INIT_COMPLETE_MASK, > + ==, 1); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT) & > + K230_DDRC_STAT_OPERATING_MODE_MASK, > + ==, 0); > + > + qtest_quit(qts); > +} > + > +static void test_training_and_dfi_handshake(void) > +{ > + QTestState *qts = qtest_init("-machine k230"); > + > + complete_phy_training(qts); > + > + g_assert_cmphex(qtest_readl(qts, > + K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_STATUS)), > + ==, 0); > + g_assert_cmphex(qtest_readl(qts, > + K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_MESSAGE)), > + ==, 0x07); > + > + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_ACK), 0); > + g_assert_cmphex(qtest_readl(qts, > + K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_STATUS)), > + ==, 1); > + qtest_writel(qts, K230_DDR_PHY_CSR(K230_DDR_PHY_TRAINING_ACK), 1); > + > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0x20); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_DFISTAT), > + ==, 1); > + > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 0); > + qtest_writel(qts, K230_DDRC_BASE + K230_DDRC_DFIMISC, 1); > + g_assert_cmphex(qtest_readl(qts, K230_DDRC_BASE + K230_DDRC_STAT) & 0x7, > + ==, 1); > + > + qtest_quit(qts); > +} > + > +int main(int argc, char **argv) > +{ > + g_test_init(&argc, &argv, NULL); > + > + qtest_add_func("/k230-ddr/reset-and-register-access", > + test_reset_and_register_access); > + qtest_add_func("/k230-ddr/software-update-handshake", > + test_software_update_handshake); > + qtest_add_func("/k230-ddr/phy-ownership", test_phy_ownership); > + qtest_add_func("/k230-ddr/dfi-prerequisites", test_dfi_prerequisites); > + qtest_add_func("/k230-ddr/dfi-complete-enable", > + test_dfi_complete_enable); > + qtest_add_func("/k230-ddr/training-and-dfi-handshake", > + test_training_and_dfi_handshake); > + > + return g_test_run(); > +} > diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build > index 56ff860e21..dc110f72c8 100644 > --- a/tests/qtest/meson.build > +++ b/tests/qtest/meson.build > @@ -297,7 +297,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-ddr-test', 'k230-wdt-test'] : []) > > qtests_hexagon = ['boot-serial-test'] > > -- > 2.53.0 >
