Signed-off-by: Simon Law <[email protected]> --- tests/qtest/k230-cmu-test.c | 312 ++++++++++++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 3 +- 2 files changed, 314 insertions(+), 1 deletion(-)
diff --git a/tests/qtest/k230-cmu-test.c b/tests/qtest/k230-cmu-test.c new file mode 100644 index 0000000000..ce3af909f0 --- /dev/null +++ b/tests/qtest/k230-cmu-test.c @@ -0,0 +1,312 @@ +/* + * QTest testcase for Kendryte K230 Clock Management Unit + * + * Copyright (c) 2026 Simon Law <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18), CMU chapter. + * + * The reset and mask oracle in this file is intentionally independent from + * hw/misc/k230_cmu.c, so a descriptor-table error cannot mask itself. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "libqtest.h" + +#define K230_PLL_BASE 0x91102000 +#define K230_SYSCLK_BASE 0x91100000 +#define K230_PLL_COUNT 4 +#define K230_PLL_STRIDE 0x10 + +#define PLL_CFG0 0x0 +#define PLL_CFG1 0x4 +#define PLL_CTL 0x8 +#define PLL_STAT 0xc + +#define PLL_CFG0_RW_MASK 0x0f3f1fff +#define PLL_CFG1_RW_MASK 0x000f0fff +#define PLL_CTL_RW_MASK 0x00000ff4 + +typedef struct K230SysclkOracle { + uint32_t offset; + uint32_t reset; + uint32_t rw_mask; + uint32_t w1t_mask; +} K230SysclkOracle; + +static const uint32_t pll_reset[K230_PLL_COUNT][4] = { + { 0x0102018f, 0x000200c7, 0x00000034, 0x00000021 }, + { 0x00000062, 0x00020031, 0x00000034, 0x00000021 }, + { 0x00070378, 0x000201bb, 0x00000034, 0x00000021 }, + { 0x0102018f, 0x000200c7, 0x00000074, 0x00000021 }, +}; + +static const K230SysclkOracle sysclk_oracle[] = { + { 0x000, 0x0000e65f, 0x0003ffdf, BIT(31) }, + { 0x004, 0x0009900d, 0x000f803f, BIT(31) }, + { 0x008, 0x0000043d, 0x0000043d, BIT(31) }, + { 0x00c, 0x00000c7f, 0x00007fff, BIT(31) }, + { 0x010, 0x00000001, 0x00000001, 0 }, + { 0x018, 0x7f7fffff, 0x7fffffff, 0 }, + { 0x01c, 0x000b9248, 0x000fffff, BIT(31) }, + { 0x020, 0x00039048, 0x0007fff8, BIT(31) }, + { 0x024, 0xffffffff, 0xffffffff, 0 }, + { 0x028, 0x000000ff, 0x000000ff, 0 }, + { 0x02c, 0x1b6d9249, 0x3fffffff, BIT(31) }, + { 0x030, 0x01dda0c3, 0x7fffffff, BIT(31) }, + { 0x034, 0x01b9f424, 0x7fffffff, BIT(31) }, + { 0x038, 0x006e7d09, 0x07ffffff, BIT(31) }, + { 0x03c, 0x006e7d09, 0x07ffffff, BIT(31) }, + { 0x040, 0x0000f424, 0x0001ffff, 0 }, + { 0x044, 0x000001b9, 0x0000ffff, BIT(31) }, + { 0x050, 0x003ffffe, 0x003ffffe, 0 }, + { 0x054, 0x0003ffff, 0x0003ffff, BIT(31) }, + { 0x058, 0x1efabef9, 0x7ffffff8, BIT(31) }, + { 0x05c, 0x000c7ffd, 0x001c7ffd, BIT(31) }, + { 0x060, 0x000043fd, 0x0003ffff, BIT(31) }, + { 0x064, 0x000fffff, 0x000fffff, 0 }, + { 0x068, 0x00820835, 0x3fffffff, BIT(31) }, + { 0x06c, 0x003a74ef, 0x00ffffff, BIT(31) }, + { 0x070, 0x000001ff, 0x000003ff, BIT(31) }, + { 0x074, 0x00001fff, 0x00001fff, 0 }, + { 0x078, 0x0000a83b, 0x00ffffff, BIT(31) }, + { 0x080, 0x000002f3, 0x00003fff, BIT(31) }, + { 0x100, 0x00000039, 0x0000007f, BIT(31) }, + { 0x104, 0x00000009, 0x0000001f, BIT(31) }, + /* TRM summary stops at 0x104; SDK/Linux ABI requires SPI2AXI at 0x108. */ + { 0x108, 0x00000007, 0x0000000f, BIT(31) }, +}; + +static QTestState *k230_cmu_start(void) +{ + return qtest_init("-machine k230"); +} + +static uint32_t pll_addr(unsigned index, uint32_t offset) +{ + g_assert_cmpuint(index, <, K230_PLL_COUNT); + return K230_PLL_BASE + index * K230_PLL_STRIDE + offset; +} + +static uint32_t merge_rw(uint32_t old_value, uint32_t value, uint32_t mask) +{ + return (old_value & ~mask) | (value & mask); +} + +static void assert_pll_reset(QTestState *qts) +{ + static const uint32_t offsets[] = { PLL_CFG0, PLL_CFG1, PLL_CTL, PLL_STAT }; + + for (unsigned pll = 0; pll < K230_PLL_COUNT; pll++) { + for (unsigned reg = 0; reg < G_N_ELEMENTS(offsets); reg++) { + g_assert_cmphex(qtest_readl(qts, pll_addr(pll, offsets[reg])), ==, + pll_reset[pll][reg]); + } + } +} + +static void assert_sysclk_reset(QTestState *qts) +{ + for (unsigned i = 0; i < G_N_ELEMENTS(sysclk_oracle); i++) { + const K230SysclkOracle *reg = &sysclk_oracle[i]; + + g_assert_cmphex(qtest_readl(qts, K230_SYSCLK_BASE + reg->offset), ==, + reg->reset); + } +} + +static void test_reset_pll(void) +{ + QTestState *qts = k230_cmu_start(); + + assert_pll_reset(qts); + qtest_quit(qts); +} + +static void test_reset_sysclk(void) +{ + QTestState *qts = k230_cmu_start(); + + assert_sysclk_reset(qts); + qtest_quit(qts); +} + +static void test_pll_cfg_masks(void) +{ + static const uint32_t offsets[] = { PLL_CFG0, PLL_CFG1 }; + static const uint32_t masks[] = { PLL_CFG0_RW_MASK, PLL_CFG1_RW_MASK }; + QTestState *qts = k230_cmu_start(); + + for (unsigned pll = 0; pll < K230_PLL_COUNT; pll++) { + for (unsigned reg = 0; reg < G_N_ELEMENTS(offsets); reg++) { + uint32_t addr = pll_addr(pll, offsets[reg]); + uint32_t reset = pll_reset[pll][reg]; + + qtest_writel(qts, addr, 0); + g_assert_cmphex(qtest_readl(qts, addr), ==, + merge_rw(reset, 0, masks[reg])); + qtest_writel(qts, addr, UINT32_MAX); + g_assert_cmphex(qtest_readl(qts, addr), ==, + merge_rw(merge_rw(reset, 0, masks[reg]), + UINT32_MAX, masks[reg])); + + qtest_system_reset(qts); + qtest_writel(qts, addr, BIT(ctz32(masks[reg]))); + g_assert_cmphex(qtest_readl(qts, addr), ==, + merge_rw(reset, BIT(ctz32(masks[reg])), + masks[reg])); + + qtest_system_reset(qts); + qtest_writel(qts, addr, reset | BIT(ctz32(~masks[reg]))); + g_assert_cmphex(qtest_readl(qts, addr), ==, reset); + } + } + + qtest_quit(qts); +} + +static void test_pll_ctl_write_enable(void) +{ + QTestState *qts = k230_cmu_start(); + uint32_t addr = pll_addr(0, PLL_CTL); + + qtest_writel(qts, addr, 0); + g_assert_cmphex(qtest_readl(qts, addr), ==, pll_reset[0][2]); + + qtest_writel(qts, addr, BIT(16 + 2)); + g_assert_cmphex(qtest_readl(qts, addr), ==, + pll_reset[0][2] & ~BIT(2)); + + qtest_writel(qts, addr, BIT(2) | BIT(16 + 2) | BIT(16 + 4)); + g_assert_cmphex(qtest_readl(qts, addr), ==, + merge_rw(pll_reset[0][2] & ~BIT(2), + BIT(2), BIT(2) | BIT(4))); + + qtest_writel(qts, addr, UINT32_MAX); + g_assert_cmphex(qtest_readl(qts, addr), ==, PLL_CTL_RW_MASK); + qtest_quit(qts); +} + +static void test_pll_status(void) +{ + QTestState *qts = k230_cmu_start(); + uint32_t ctl = pll_addr(0, PLL_CTL); + uint32_t stat = pll_addr(0, PLL_STAT); + + qtest_writel(qts, stat, 0); + g_assert_cmphex(qtest_readl(qts, stat), ==, 0x00000021); + + qtest_writel(qts, ctl, BIT(0) | BIT(16)); + g_assert_cmphex(qtest_readl(qts, stat), ==, 0); + + qtest_writel(qts, ctl, BIT(1) | BIT(17)); + g_assert_cmphex(qtest_readl(qts, stat), ==, 0x00000021); + + qtest_writel(qts, ctl, BIT(0) | BIT(1) | BIT(16) | BIT(17)); + g_assert_cmphex(qtest_readl(qts, stat), ==, 0x00000021); + qtest_quit(qts); +} + +static void test_sysclk_masks(void) +{ + QTestState *qts = k230_cmu_start(); + + for (unsigned i = 0; i < G_N_ELEMENTS(sysclk_oracle); i++) { + const K230SysclkOracle *reg = &sysclk_oracle[i]; + uint32_t addr = K230_SYSCLK_BASE + reg->offset; + uint32_t after_zero = merge_rw(reg->reset, 0, reg->rw_mask); + + qtest_writel(qts, addr, 0); + g_assert_cmphex(qtest_readl(qts, addr), ==, after_zero); + qtest_writel(qts, addr, UINT32_MAX); + g_assert_cmphex(qtest_readl(qts, addr), ==, + merge_rw(after_zero, UINT32_MAX, reg->rw_mask)); + } + + qtest_quit(qts); +} + +static void test_sysclk_update(void) +{ + QTestState *qts = k230_cmu_start(); + + for (unsigned i = 0; i < G_N_ELEMENTS(sysclk_oracle); i++) { + const K230SysclkOracle *reg = &sysclk_oracle[i]; + uint32_t addr; + uint32_t flipped; + + if (!reg->w1t_mask) { + continue; + } + + addr = K230_SYSCLK_BASE + reg->offset; + flipped = merge_rw(reg->reset, ~reg->reset, reg->rw_mask); + qtest_writel(qts, addr, flipped | reg->w1t_mask); + g_assert_cmphex(qtest_readl(qts, addr), ==, flipped); + } + + qtest_quit(qts); +} + +static void test_spi2axi(void) +{ + QTestState *qts = k230_cmu_start(); + uint32_t addr = K230_SYSCLK_BASE + 0x108; + + g_assert_cmphex(qtest_readl(qts, addr), ==, 0x00000007); + qtest_writel(qts, addr, 0x00000001); + g_assert_cmphex(qtest_readl(qts, addr), ==, 0x00000001); + qtest_writel(qts, addr, 0x8000000f); + g_assert_cmphex(qtest_readl(qts, addr), ==, 0x0000000f); + qtest_quit(qts); +} + +static void test_unknown_offsets(void) +{ + QTestState *qts = k230_cmu_start(); + /* The 0x40 PLL window has no undefined aligned 32-bit word. */ + uint32_t addr = K230_SYSCLK_BASE + 0x014; + + g_assert_cmphex(qtest_readl(qts, addr), ==, 0); + qtest_writel(qts, addr, UINT32_MAX); + g_assert_cmphex(qtest_readl(qts, addr), ==, 0); + qtest_quit(qts); +} + +static void test_system_reset(void) +{ + QTestState *qts = k230_cmu_start(); + + qtest_writel(qts, pll_addr(0, PLL_CFG0), 0); + qtest_writel(qts, pll_addr(1, PLL_CFG1), 0); + qtest_writel(qts, pll_addr(2, PLL_CTL), BIT(16 + 2)); + qtest_writel(qts, pll_addr(3, PLL_CTL), BIT(0) | BIT(16)); + qtest_writel(qts, K230_SYSCLK_BASE + 0x000, 0); + qtest_writel(qts, K230_SYSCLK_BASE + 0x050, 0); + qtest_writel(qts, K230_SYSCLK_BASE + 0x108, 0); + + qtest_system_reset(qts); + assert_pll_reset(qts); + assert_sysclk_reset(qts); + qtest_quit(qts); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-cmu/reset/pll", test_reset_pll); + qtest_add_func("/k230-cmu/reset/sysclk", test_reset_sysclk); + qtest_add_func("/k230-cmu/pll/cfg-masks", test_pll_cfg_masks); + qtest_add_func("/k230-cmu/pll/ctl-write-enable", test_pll_ctl_write_enable); + qtest_add_func("/k230-cmu/pll/status", test_pll_status); + qtest_add_func("/k230-cmu/sysclk/masks", test_sysclk_masks); + qtest_add_func("/k230-cmu/sysclk/update", test_sysclk_update); + qtest_add_func("/k230-cmu/sysclk/spi2axi", test_spi2axi); + qtest_add_func("/k230-cmu/unknown-offsets", test_unknown_offsets); + qtest_add_func("/k230-cmu/system-reset", test_system_reset); + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 56ff860e21..9f5c46e0bf 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-cmu-test', 'k230-wdt-test'] : []) qtests_hexagon = ['boot-serial-test'] -- 2.43.0
