From: Brian Cain <brian.cain@oss.qualcomm.com>
To: qemu-devel@nongnu.org
Cc: philmd@oss.qualcomm.com,
Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>,
Fabiano Rosas <farosas@suse.de>,
Laurent Vivier <lvivier@redhat.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Brian Cain <brian.cain@oss.qualcomm.com>,
matheus.bernardino@oss.qualcomm.com
Subject: [PATCH v4 13/18] tests/qtest: add L2VIC qtest
Date: Wed, 29 Jul 2026 18:28:18 -0700 [thread overview]
Message-ID: <20260730012824.1135905-14-brian.cain@oss.qualcomm.com> (raw)
In-Reply-To: <20260730012824.1135905-1-brian.cain@oss.qualcomm.com>
Add a qtest exercising L2VIC register access and interrupt
enable/disable.
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
MAINTAINERS | 1 +
tests/qtest/l2vic-test.c | 249 +++++++++++++++++++++++++++++++++++++++
tests/qtest/meson.build | 2 +-
3 files changed, 251 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..adb4dc7281e
--- /dev/null
+++ b/tests/qtest/l2vic-test.c
@@ -0,0 +1,249 @@
+/*
+ * 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 */
+
+/*
+ * VID group readback: records which irq last fired through each VID
+ * group. Outputs themselves are momentary pulses (see l2vic_update()),
+ * so these registers -- not the qtest IRQ level snapshot -- are how the
+ * test observes VID steering.
+ */
+#define L2VIC_VID_GRP_0 0x0
+#define L2VIC_VID_GRP_1 0x4
+#define L2VIC_VID_GRP_2 0x8
+#define L2VIC_VID_GRP_3 0xC
+
+typedef struct {
+ const char *machine;
+ const struct hexagon_machine_config *cfg;
+} L2VICMachineCfg;
+
+static const L2VICMachineCfg l2vic_machines[] = {
+ { "virt", &v68n_1024 },
+ { "V66G_1024", &v66g_1024 },
+};
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0);
+ l2vic_write32(base, L2VIC_INT_ENABLE_CLEARn, 0);
+}
+
+/*
+ * IRQs 0-7 pack their group-enable/VID-select nibbles into
+ * L2VIC_INT_GRPn_0 (int_group_n[0]), 4 bits per irq: bit 3 enables
+ * VID steering, bits 0-2 select the VID group (0-3), which pulses
+ * output line vid+2.
+ */
+static void l2vic_set_vid_group(uint64_t base, int irq, int vid)
+{
+ uint32_t val = l2vic_read32(base, L2VIC_INT_GRPn_0);
+ uint32_t nibble = 0x8 | (vid & 0x7);
+
+ val &= ~(0xFu << (irq * 4));
+ val |= nibble << (irq * 4);
+ l2vic_write32(base, L2VIC_INT_GRPn_0, val);
+}
+
+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);
+ l2vic_write32(base, L2VIC_INT_GRPn_0, 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);
+ /* Default VID group (0) records the delivering irq, output line 2 */
+ g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_0), ==, 0);
+
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0x1);
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0x1, ==, 0x0);
+
+ /* IRQ1 steered to VID group 1 -> output line 3 */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0x2);
+ l2vic_set_vid_group(base, 1, 1);
+ 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);
+ g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_1), ==, 1);
+
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0x2);
+
+ /* IRQ4 steered to VID group 2 -> output line 4 */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0x10);
+ l2vic_set_vid_group(base, 4, 2);
+ l2vic_write32(base, L2VIC_INT_ENABLE_SETn, 0x10);
+ l2vic_write32(base, L2VIC_SOFT_INTn, 0x10);
+
+ val = l2vic_read32(base, L2VIC_INT_STATUSn);
+ g_assert_cmpuint(val & 0x10, ==, 0x10);
+ g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_2), ==, 4);
+
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0x10);
+
+ /* IRQ5 steered to VID group 3 -> output line 5 */
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0x20);
+ l2vic_set_vid_group(base, 5, 3);
+ 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, ==, 0x20);
+ g_assert_cmpuint(l2vic_read32(base, L2VIC_VID_GRP_3), ==, 5);
+
+ l2vic_write32(base, L2VIC_INT_CLEARn, 0x20);
+
+ /* Restore defaults; the block below reuses IRQ 3-5 without VID steering */
+ l2vic_write32(base, L2VIC_INT_GRPn_0, 0);
+ l2vic_write32(base, L2VIC_INT_TYPEn, 0);
+
+ /* 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);
+
+ /*
+ * Only one irq becomes active per delivery; each clear unblocks the
+ * next pending one, so drain until all four have been delivered.
+ */
+ while ((val = l2vic_read32(base, L2VIC_INT_STATUSn)) & 0xF) {
+ l2vic_write32(base, L2VIC_INT_CLEARn, val & 0xF);
+ }
+
+ /* 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);
+
+ 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);
+
+ 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
next prev parent reply other threads:[~2026-07-30 1:29 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-30 1:28 [PATCH v4 00/18] Add hexagon l2vic, qtimer devices Brian Cain
2026-07-30 1:28 ` [PATCH v4 01/18] hw/hexagon: add hex-subsys Brian Cain
2026-07-31 17:28 ` Pierrick Bouvier
2026-07-30 1:28 ` [PATCH v4 02/18] hw/hexagon: move VTCM to the common machine state Brian Cain
2026-07-31 17:29 ` Pierrick Bouvier
2026-07-30 1:28 ` [PATCH v4 03/18] hw/hexagon: move global registers to hex-subsys Brian Cain
2026-07-31 17:29 ` Pierrick Bouvier
2026-07-30 1:28 ` [PATCH v4 04/18] hw/hexagon: move the TLB " Brian Cain
2026-07-31 17:30 ` Pierrick Bouvier
2026-07-30 1:28 ` [PATCH v4 05/18] hw/hexagon: group the CPUs in a cluster Brian Cain
2026-07-31 17:34 ` Pierrick Bouvier
2026-07-30 1:28 ` [PATCH v4 06/18] bitops.h: Add find_first_bit32() Brian Cain
2026-07-30 1:28 ` [PATCH v4 07/18] hw/intc: Add l2vic interrupt controller Brian Cain
2026-07-30 1:28 ` [PATCH v4 08/18] hw/hexagon: extract get_reg_value/set_reg_value stubs in globalreg Brian Cain
2026-07-30 1:28 ` [PATCH v4 09/18] hw/hexagon: connect l2vic device Brian Cain
2026-07-30 1:28 ` [PATCH v4 10/18] hw/hexagon/virt: instantiate virtio-mmio transports Brian Cain
2026-07-30 1:28 ` [PATCH v4 11/18] hw/hexagon/virt: add l2vic interrupt-controller and virtio-mmio FDT nodes Brian Cain
2026-07-31 17:35 ` Pierrick Bouvier
2026-07-30 1:28 ` [PATCH v4 12/18] hw/hexagon/virt: connect pl011 UART interrupt Brian Cain
2026-07-30 1:28 ` Brian Cain [this message]
2026-07-30 1:28 ` [PATCH v4 14/18] tests/functional/hexagon: add arch_tests functional test Brian Cain
2026-07-30 1:28 ` [PATCH v4 15/18] hw/timer: Add QCT QTimer device model Brian Cain
2026-07-31 17:59 ` Pierrick Bouvier
2026-08-05 21:10 ` Brian Cain
2026-07-30 1:28 ` [PATCH v4 16/18] hw/hexagon: connect qtimer device Brian Cain
2026-07-31 17:36 ` Pierrick Bouvier
2026-07-30 1:28 ` [PATCH v4 17/18] tests/qtest: add qct-qtimer qtest Brian Cain
2026-07-31 17:38 ` Pierrick Bouvier
2026-07-30 1:28 ` [PATCH v4 18/18] tests/functional/hexagon: enable more arch_tests cases Brian Cain
2026-07-31 17:39 ` Pierrick Bouvier
2026-07-31 17:42 ` Pierrick Bouvier
2026-08-03 15:43 ` Brian Cain
2026-08-04 21:35 ` Pierrick Bouvier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260730012824.1135905-14-brian.cain@oss.qualcomm.com \
--to=brian.cain@oss.qualcomm.com \
--cc=farosas@suse.de \
--cc=lvivier@redhat.com \
--cc=matheus.bernardino@oss.qualcomm.com \
--cc=pbonzini@redhat.com \
--cc=philmd@oss.qualcomm.com \
--cc=pierrick.bouvier@oss.qualcomm.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.