Add a qtest exercising L2VIC register access and interrupt
enable/disable.
Reviewed-by: Pierrick Bouvier <[email protected]>
Signed-off-by: Brian Cain <[email protected]>
---
MAINTAINERS | 1 +
tests/qtest/l2vic-test.c | 221 +++++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 2 +-
3 files changed, 223 insertions(+), 1 deletion(-)
create mode 100644 tests/qtest/l2vic-test.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a76366d6da5..114bbec45ad 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -252,6 +252,7 @@ S: Supported
F: target/hexagon/
F: hw/intc/hex-l2vic.c
F: include/hw/intc/hex-l2vic.h
+F: tests/qtest/l2vic-test.c
X: target/hexagon/idef-parser/
X: target/hexagon/gen_idef_parser_funcs.py
F: linux-user/hexagon/
diff --git a/tests/qtest/l2vic-test.c b/tests/qtest/l2vic-test.c
new file mode 100644
index 00000000000..e9ffb529275
--- /dev/null
+++ b/tests/qtest/l2vic-test.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * QTest testcase for the L2VIC Interrupt Controller
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest-single.h"
+#include "hw/hexagon/hexagon.h"
+
+#include "hw/hexagon/machine_cfg_v66g_1024.h.inc"
+#include "hw/hexagon/machine_cfg_v68n_1024.h.inc"
+
+/* L2VIC register offsets exercised by this test */
+#define L2VIC_INT_ENABLEn 0x100 /* Read/Write */
+#define L2VIC_INT_ENABLE_CLEARn 0x180 /* Write */
+#define L2VIC_INT_ENABLE_SETn 0x200 /* Write */
+#define L2VIC_INT_TYPEn 0x280 /* Read/Write */
+#define L2VIC_INT_STATUSn 0x380 /* Read */
+#define L2VIC_INT_CLEARn 0x400 /* Write */
+#define L2VIC_SOFT_INTn 0x480 /* Write */
+#define L2VIC_INT_PENDINGn 0x500 /* Read */
+#define L2VIC_INT_GRPn_0 0x600 /* Read/Write */
+#define L2VIC_INT_GRPn_1 0x680 /* Read/Write */
+#define L2VIC_INT_GRPn_2 0x700 /* Read/Write */
+#define L2VIC_INT_GRPn_3 0x780 /* Read/Write */
+
+typedef struct {
+ const char *machine;
+ const struct hexagon_machine_config *cfg;
+} L2VICMachineCfg;
+
+static const L2VICMachineCfg l2vic_machines[] = {
+ { "virt", &v68n_1024 },
+ { "V66G_1024", &v66g_1024 },
+};
+
+/* Helper functions */
+static uint32_t l2vic_read32(uint64_t base, uint32_t offset)
+{
+ return readl(base + offset);
+}
+
+static void l2vic_write32(uint64_t base, uint32_t offset, uint32_t value)
+{
+ writel(base + offset, value);
+}
+
+/* Test basic register access */
+static void test_l2vic_register_access(uint64_t base)
+{
+ uint32_t val;
+
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x1);
+ val = l2vic_read32(base, L2VIC_INT_ENABLEn);
+ g_assert_cmpuint(val & 0x1, ==, 0x1);
+
+ l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0x1);
+ val = l2vic_read32(base, L2VIC_INT_ENABLEn);
+ g_assert_cmpuint(val & 0x1, ==, 0x0);
+}
+
+/* Test interrupt enable/disable */
+static void test_l2vic_interrupt_enable(uint64_t base)
+{
+ uint32_t val;
+
+ val = l2vic_read32(base, L2VIC_INT_ENABLEn);
+ g_assert_cmpuint(val, ==, 0);
+
+ /* Enable IRQ 0 and 2 */
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x5);
+ val = l2vic_read32(base, L2VIC_INT_ENABLEn);
+ g_assert_cmpuint(val & 0x5, ==, 0x5);
+
+ /* Disable IRQ 0, leaving IRQ 2 enabled */
+ l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0x1);
+ val = l2vic_read32(base, L2VIC_INT_ENABLEn);
+ g_assert_cmpuint(val & 0x1, ==, 0x0);
+ g_assert_cmpuint(val & 0x4, ==, 0x4);
+}
+
+/* Test register read/write without triggering interrupts */
+static void test_l2vic_basic_functionality(uint64_t base)
+{
+ l2vic_read32(base, L2VIC_INT_ENABLEn);
+ l2vic_read32(base, L2VIC_INT_PENDINGn);
+ l2vic_read32(base, L2VIC_INT_STATUSn);
+ l2vic_read32(base, L2VIC_INT_TYPEn);
+
+ /* Exercise write-only registers */
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0);
+ l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0);
+}
+
+static void test_l2vic_irq_outputs(uint64_t base)
+{
+ uint32_t val;
+
+ l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0xFFFFFFFF);
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0xFFFFFFFF);
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0);
+
+ /* Group 0 / IRQ2: soft interrupts require edge-triggered config */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0x1);
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x1);
+ l2vic_write32(base, L2VIC_SOFT_INTn, 0x1);
+
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0x1, ==, 0x1);
+
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0x1);
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0x1, ==, 0x0);
+
+ /* Group 1 / IRQ3 */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0x2);
+ l2vic_write32(base, L2VIC_INT_GRPn_1, 0x2);
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x2);
+ l2vic_write32(base, L2VIC_SOFT_INTn, 0x2);
+
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0x2, ==, 0x2);
+ val = l2vic_read32(base, L2VIC_INT_GRPn_1);
+ g_assert_cmpuint(val & 0x2, ==, 0x2);
+
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0x2);
+
+ /* Group 2 / IRQ4 */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0x4);
+ l2vic_write32(base, L2VIC_INT_GRPn_2, 0x4);
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x4);
+ l2vic_write32(base, L2VIC_SOFT_INTn, 0x4);
+
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0x4, ==, 0x4);
+ val = l2vic_read32(base, L2VIC_INT_GRPn_2);
+ g_assert_cmpuint(val & 0x4, ==, 0x4);
+
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0x4);
+
+ /* Group 3 / IRQ5 */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0x8);
+ l2vic_write32(base, L2VIC_INT_GRPn_3, 0x8);
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x8);
+ l2vic_write32(base, L2VIC_SOFT_INTn, 0x8);
+
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0x8, ==, 0x8);
+ val = l2vic_read32(base, L2VIC_INT_GRPn_3);
+ g_assert_cmpuint(val & 0x8, ==, 0x8);
+
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0x8);
+
+ /* Multiple pending: at most one active at a time */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0xF);
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0xF);
+ l2vic_write32(base, L2VIC_SOFT_INTn, 0xF);
+
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0xF, !=, 0x0);
+
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ l2vic_write32(base, L2VIC_INT_CLEARn, val & 0xF);
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+
+ /* Level-triggered sources ignore soft interrupts */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0x0);
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x20);
+ l2vic_write32(base, L2VIC_SOFT_INTn, 0x20);
+
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0x20, ==, 0x0);
+
+ /* Same source, now edge-triggered, does fire */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0x20);
+ l2vic_write32(base, L2VIC_SOFT_INTn, 0x20);
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0x20, ==, 0x20);
+
+ /* Clean up */
+ l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0xFFFFFFFF);
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0xFFFFFFFF);
+ l2vic_write32(base, L2VIC_INT_GRPn_0, 0);
+ l2vic_write32(base, L2VIC_INT_GRPn_1, 0);
+ l2vic_write32(base, L2VIC_INT_GRPn_2, 0);
+ l2vic_write32(base, L2VIC_INT_GRPn_3, 0);
+}
+
+static void test_l2vic_on_machine(gconstpointer data)
+{
+ const L2VICMachineCfg *mc = data;
+ g_autofree char *args = g_strdup_printf("-machine %s", mc->machine);
+ uint64_t base = mc->cfg->l2vic_base;
+
+ qtest_start(args);
+
+ /* Run all the L2VIC tests */
+ test_l2vic_register_access(base);
+ test_l2vic_interrupt_enable(base);
+ test_l2vic_basic_functionality(base);
+ test_l2vic_irq_outputs(base);
+
+ qtest_end();
+}
+
+int main(int argc, char **argv)
+{
+ size_t i;
+
+ g_test_init(&argc, &argv, NULL);
+
+ for (i = 0; i < ARRAY_SIZE(l2vic_machines); i++) {
+ g_autofree char *path = g_strdup_printf("/l2vic/%s/all-tests",
+ l2vic_machines[i].machine);
+ qtest_add_data_func(path, &l2vic_machines[i], test_l2vic_on_machine);
+ }
+
+ return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 56ff860e216..a76863c7dd6 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -299,7 +299,7 @@ qtests_riscv64 = ['riscv-csr-test'] + \
['iommu-riscv-test'] : []) + \
(config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : [])
-qtests_hexagon = ['boot-serial-test']
+qtests_hexagon = ['boot-serial-test', 'l2vic-test']
qos_test_ss = ss.source_set()
qos_test_ss.add(
--
2.34.1