[PATCH v3 2/2] tests/qtest: add K230 GPIO controller test

"guochun wang" <[email protected]>
Newsgroups org.nongnu.qemu-devel
Message-ID <[email protected]>
Add qtest cases verifying the K230 GPIO model against TRM v0.3.1
behaviour and Linux gpio-k230 driver requirements.

Tests cover:
  - Reset values: all registers cleared after machine init
  - Edge detection: rising / falling / both-edge, with EOI clearing
  - Level-sensitive: active-high / active-low, cleared by source removal
  - EOI: clears edge interrupts only, raw status verified
  - INTEN gating and INTMASK masking (raw status still recorded)
  - No new interrupts while DDR=output or CTL=hardware mode
  - PLIC integration: per-pin IRQ, claim/complete flow
  - Multiple simultaneous interrupts on independent lines
  - GPIO1 controller (second instance at 0x9140C000)
  - DDR/CTL mode switching: pending interrupts preserved; on switch
    back to input/software mode, level interrupts re-evaluated against
    current ext level, edge interrupts left untouched (TRM v0.3.1)

Update MAINTAINERS with the new test file.

Signed-off-by: guochun wang <[email protected]>
Reviewed-by: Daniel Henrique Barboza <[email protected]>
---
 MAINTAINERS                  |   1 +
 tests/qtest/k230-gpio-test.c | 521 +++++++++++++++++++++++++++++++++++
 tests/qtest/meson.build      |   2 +-
 3 files changed, 523 insertions(+), 1 deletion(-)
 create mode 100644 tests/qtest/k230-gpio-test.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 4301d29f7e..bb4f585426 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1834,6 +1834,7 @@ F: include/hw/gpio/k230_gpio.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-gpio-test.c
 F: tests/qtest/k230-wdt-test.c
 
 RX Machines
diff --git a/tests/qtest/k230-gpio-test.c b/tests/qtest/k230-gpio-test.c
new file mode 100644
index 0000000000..9c930cbd1c
--- /dev/null
+++ b/tests/qtest/k230-gpio-test.c
@@ -0,0 +1,521 @@
+/*
+ * QTest testcase for K230 GPIO
+ *
+ * Copyright (c) 2025 Wang Guochun <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * K230 Technical Reference Manual V0.3.1 (2024-11-18)
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "libqtest.h"
+#include "hw/gpio/k230_gpio.h"
+
+#define K230_GPIO0_BASE  0x9140B000
+#define K230_GPIO1_BASE  0x9140C000
+
+#define GPIO_BASE K230_GPIO0_BASE
+
+#define PLIC_BASE           0xF00000000ULL
+#define PLIC_PRIORITY       (PLIC_BASE + 0x000000)
+#define PLIC_PENDING        (PLIC_BASE + 0x001000)
+#define PLIC_ENABLE         (PLIC_BASE + 0x002000)
+#define PLIC_CONTEXT        (PLIC_BASE + 0x200000)
+#define PLIC_THRESHOLD      (PLIC_CONTEXT + 0x0)
+#define PLIC_CLAIM          (PLIC_CONTEXT + 0x4)
+
+#define GPIO0_IRQ_BASE  32
+#define GPIO1_IRQ_BASE  64
+
+static inline bool plic_irq_pending(QTestState *qts, int irq)
+{
+    uint32_t word = qtest_readl(qts, PLIC_PENDING + (irq / 32) * 4);
+    return extract32(word, irq % 32, 1);
+}
+
+static void gpio0_set_input(QTestState *qts, int line, int level)
+{
+    qtest_set_irq_in(qts, "/machine/soc/k230-gpio0",
+                     "unnamed-gpio-in", line, level);
+}
+
+static void gpio1_set_input(QTestState *qts, int line, int level)
+{
+    qtest_set_irq_in(qts, "/machine/soc/k230-gpio1",
+                     "unnamed-gpio-in", line, level);
+}
+
+static void plic_enable_irq(QTestState *qts, int irq)
+{
+    qtest_writel(qts, PLIC_PRIORITY + irq * 4, 1);
+    qtest_writel(qts, PLIC_THRESHOLD, 0);
+    qtest_writel(qts, PLIC_ENABLE + (irq / 32) * 4, BIT(irq % 32));
+}
+
+static void plic_claim_complete(QTestState *qts, int irq)
+{
+    uint32_t claimed = qtest_readl(qts, PLIC_CLAIM);
+    g_assert_cmpuint(claimed, ==, irq);
+    qtest_writel(qts, PLIC_CLAIM, irq);
+}
+
+static uint32_t gpio0_read(QTestState *qts, hwaddr offset)
+{
+    return qtest_readl(qts, GPIO_BASE + offset);
+}
+
+static void gpio0_write(QTestState *qts, hwaddr offset, uint32_t value)
+{
+    qtest_writel(qts, GPIO_BASE + offset, value);
+}
+
+static uint32_t gpio0_raw_intstatus(QTestState *qts)
+{
+    return gpio0_read(qts, K230_GPIO_RAW_INTSTATUS);
+}
+
+static void test_reset_values(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_SWPORTA_DR), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_SWPORTA_DDR), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_SWPORTA_CTL), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_INTEN), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_INTMASK), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_INTTYPE_LEVEL), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_INT_POLARITY), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_INTSTATUS), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_RAW_INTSTATUS), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_CONFIG_REG2), ==, 0x0);
+    g_assert_cmphex(gpio0_read(qts, K230_GPIO_CONFIG_REG1), ==, 0x0);
+
+    qtest_quit(qts);
+}
+
+static void test_edge_rising(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 5;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(5));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(5));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(5));
+
+    gpio0_set_input(qts, 5, 0);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    gpio0_set_input(qts, 5, 1);
+    g_assert_true(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_edge_falling(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 3;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(3));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, 0);
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(3));
+
+    gpio0_set_input(qts, 3, 1);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    gpio0_set_input(qts, 3, 0);
+    g_assert_true(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_both_edge(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 7;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(7));
+    gpio0_write(qts, K230_GPIO_INT_BOTHEDGE, BIT(7));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(7));
+
+    gpio0_set_input(qts, 7, 0);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    gpio0_set_input(qts, 7, 1);
+    g_assert_true(plic_irq_pending(qts, gpio_irq));
+
+    gpio0_write(qts, K230_GPIO_PORTA_EOI, BIT(7));
+    g_assert_cmphex(gpio0_raw_intstatus(qts) & BIT(7), ==, 0);
+    plic_claim_complete(qts, gpio_irq);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    gpio0_set_input(qts, 7, 0);
+    g_assert_true(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_level_high(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 2;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, 0);
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(2));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(2));
+
+    gpio0_set_input(qts, 2, 0);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    gpio0_set_input(qts, 2, 1);
+    g_assert_true(plic_irq_pending(qts, gpio_irq));
+
+    gpio0_set_input(qts, 2, 0);
+    g_assert_cmphex(gpio0_raw_intstatus(qts) & BIT(2), ==, 0);
+    plic_claim_complete(qts, gpio_irq);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_level_low(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 4;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, 0);
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, 0);
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(4));
+
+    gpio0_set_input(qts, 4, 1);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    gpio0_set_input(qts, 4, 0);
+    g_assert_true(plic_irq_pending(qts, gpio_irq));
+
+    /*
+     * Level goes away: GPIO deasserts, but the PLIC pending bit is
+     * sticky. Verify the GPIO-side status is cleared, then
+     * claim/complete to clear the PLIC pending bit.
+     */
+    gpio0_set_input(qts, 4, 1);
+    g_assert_cmphex(gpio0_raw_intstatus(qts) & BIT(4), ==, 0);
+    plic_claim_complete(qts, gpio_irq);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_eoi_clear(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 10;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(10));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(10));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(10));
+
+    gpio0_set_input(qts, 10, 0);
+    gpio0_set_input(qts, 10, 1);
+    g_assert_true(plic_irq_pending(qts, gpio_irq));
+
+    /*
+     * EOI clears the GPIO-side raw status, then claim/complete
+     * clears the sticky PLIC pending bit.
+     */
+    gpio0_write(qts, K230_GPIO_PORTA_EOI, BIT(10));
+    g_assert_cmphex(gpio0_raw_intstatus(qts) & BIT(10), ==, 0);
+    plic_claim_complete(qts, gpio_irq);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_inten_disabled(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 15;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(15));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(15));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, 0);
+
+    gpio0_set_input(qts, 15, 0);
+    gpio0_set_input(qts, 15, 1);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_intmask(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 20;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(20));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(20));
+    gpio0_write(qts, K230_GPIO_INTMASK, BIT(20));
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(20));
+
+    gpio0_set_input(qts, 20, 0);
+    gpio0_set_input(qts, 20, 1);
+
+    g_assert_cmphex(gpio0_raw_intstatus(qts) & BIT(20), ==, BIT(20));
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_ddr_output_no_int(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 25;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, BIT(25));
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(25));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(25));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(25));
+
+    gpio0_set_input(qts, 25, 0);
+    gpio0_set_input(qts, 25, 1);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_ctl_hw_no_int(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 27;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_SWPORTA_CTL, BIT(27));
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(27));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(27));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(27));
+
+    gpio0_set_input(qts, 27, 0);
+    gpio0_set_input(qts, 27, 1);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_gpio_plic(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO0_IRQ_BASE + 6;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(6));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(6));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(6));
+
+    gpio0_set_input(qts, 6, 0);
+    gpio0_set_input(qts, 6, 1);
+
+    g_assert_true(plic_irq_pending(qts, gpio_irq));
+
+    /*
+     * EOI clears the GPIO-side raw status, then claim/complete
+     * clears the sticky PLIC pending bit and returns the claimed
+     * IRQ id.
+     */
+    gpio0_write(qts, K230_GPIO_PORTA_EOI, BIT(6));
+    plic_claim_complete(qts, gpio_irq);
+    g_assert_false(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_multiple_irqs(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int irq_a = GPIO0_IRQ_BASE + 1;
+    int irq_b = GPIO0_IRQ_BASE + 9;
+
+    qtest_writel(qts, PLIC_PRIORITY + irq_a * 4, 1);
+    qtest_writel(qts, PLIC_PRIORITY + irq_b * 4, 1);
+    qtest_writel(qts, PLIC_THRESHOLD, 0);
+    qtest_writel(qts, PLIC_ENABLE + (irq_a / 32) * 4,
+                 BIT(irq_a % 32) | BIT(irq_b % 32));
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(1) | BIT(9));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(1) | BIT(9));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(1) | BIT(9));
+
+    gpio0_set_input(qts, 1, 0);
+    gpio0_set_input(qts, 9, 0);
+
+    gpio0_set_input(qts, 1, 1);
+    g_assert_true(plic_irq_pending(qts, irq_a));
+    g_assert_false(plic_irq_pending(qts, irq_b));
+
+    gpio0_set_input(qts, 9, 1);
+    g_assert_true(plic_irq_pending(qts, irq_a));
+    g_assert_true(plic_irq_pending(qts, irq_b));
+
+    qtest_quit(qts);
+}
+
+static void test_gpio1_plic(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int gpio_irq = GPIO1_IRQ_BASE + 5;
+
+    plic_enable_irq(qts, gpio_irq);
+
+    qtest_writel(qts, K230_GPIO1_BASE + K230_GPIO_SWPORTA_DDR, 0);
+    qtest_writel(qts, K230_GPIO1_BASE + K230_GPIO_INTTYPE_LEVEL, BIT(5));
+    qtest_writel(qts, K230_GPIO1_BASE + K230_GPIO_INT_POLARITY, BIT(5));
+    qtest_writel(qts, K230_GPIO1_BASE + K230_GPIO_INTMASK, 0);
+    qtest_writel(qts, K230_GPIO1_BASE + K230_GPIO_INTEN, BIT(5));
+
+    gpio1_set_input(qts, 5, 0);
+    gpio1_set_input(qts, 5, 1);
+
+    g_assert_true(plic_irq_pending(qts, gpio_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_ddr_switch_output_to_input(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int level_irq = GPIO0_IRQ_BASE + 12;
+    int edge_irq = GPIO0_IRQ_BASE + 13;
+
+    qtest_writel(qts, PLIC_PRIORITY + level_irq * 4, 1);
+    qtest_writel(qts, PLIC_PRIORITY + edge_irq * 4, 1);
+    qtest_writel(qts, PLIC_THRESHOLD, 0);
+    qtest_writel(qts, PLIC_ENABLE + (level_irq / 32) * 4,
+                 BIT(level_irq % 32) | BIT(edge_irq % 32));
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, BIT(12) | BIT(13));
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(13));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(12) | BIT(13));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(12) | BIT(13));
+
+    gpio0_set_input(qts, 12, 0);
+    gpio0_set_input(qts, 13, 0);
+
+    gpio0_set_input(qts, 12, 1);
+    gpio0_set_input(qts, 13, 1);
+
+    g_assert_false(plic_irq_pending(qts, level_irq));
+    g_assert_false(plic_irq_pending(qts, edge_irq));
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+
+    g_assert_true(plic_irq_pending(qts, level_irq));
+    g_assert_false(plic_irq_pending(qts, edge_irq));
+
+    qtest_quit(qts);
+}
+
+static void test_ctl_switch_hw_to_sw(void)
+{
+    QTestState *qts = qtest_init("-machine k230");
+    int level_irq = GPIO0_IRQ_BASE + 14;
+    int edge_irq = GPIO0_IRQ_BASE + 16;
+
+    qtest_writel(qts, PLIC_PRIORITY + level_irq * 4, 1);
+    qtest_writel(qts, PLIC_PRIORITY + edge_irq * 4, 1);
+    qtest_writel(qts, PLIC_THRESHOLD, 0);
+    qtest_writel(qts, PLIC_ENABLE + (level_irq / 32) * 4,
+                 BIT(level_irq % 32) | BIT(edge_irq % 32));
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_DDR, 0);
+    gpio0_write(qts, K230_GPIO_SWPORTA_CTL, BIT(14) | BIT(16));
+    gpio0_write(qts, K230_GPIO_INTTYPE_LEVEL, BIT(16));
+    gpio0_write(qts, K230_GPIO_INT_POLARITY, BIT(14) | BIT(16));
+    gpio0_write(qts, K230_GPIO_INTMASK, 0);
+    gpio0_write(qts, K230_GPIO_INTEN, BIT(14) | BIT(16));
+
+    gpio0_set_input(qts, 14, 0);
+    gpio0_set_input(qts, 16, 0);
+
+    gpio0_set_input(qts, 14, 1);
+    gpio0_set_input(qts, 16, 1);
+
+    g_assert_false(plic_irq_pending(qts, level_irq));
+    g_assert_false(plic_irq_pending(qts, edge_irq));
+
+    gpio0_write(qts, K230_GPIO_SWPORTA_CTL, 0);
+
+    g_assert_true(plic_irq_pending(qts, level_irq));
+    g_assert_false(plic_irq_pending(qts, edge_irq));
+
+    qtest_quit(qts);
+}
+
+int main(int argc, char *argv[])
+{
+    g_test_init(&argc, &argv, NULL);
+
+    qtest_add_func("/k230-gpio/reset_values", test_reset_values);
+    qtest_add_func("/k230-gpio/edge_rising", test_edge_rising);
+    qtest_add_func("/k230-gpio/edge_falling", test_edge_falling);
+    qtest_add_func("/k230-gpio/both_edge", test_both_edge);
+    qtest_add_func("/k230-gpio/level_high", test_level_high);
+    qtest_add_func("/k230-gpio/level_low", test_level_low);
+    qtest_add_func("/k230-gpio/eoi_clear", test_eoi_clear);
+    qtest_add_func("/k230-gpio/inten_disabled", test_inten_disabled);
+    qtest_add_func("/k230-gpio/intmask", test_intmask);
+    qtest_add_func("/k230-gpio/ddr_output_no_int", test_ddr_output_no_int);
+    qtest_add_func("/k230-gpio/ctl_hw_no_int", test_ctl_hw_no_int);
+    qtest_add_func("/k230-gpio/gpio_plic", test_gpio_plic);
+    qtest_add_func("/k230-gpio/multiple_irqs", test_multiple_irqs);
+    qtest_add_func("/k230-gpio/gpio1_plic", test_gpio1_plic);
+    qtest_add_func("/k230-gpio/ddr_switch_out_to_in",
+                   test_ddr_switch_output_to_input);
+    qtest_add_func("/k230-gpio/ctl_switch_hw_to_sw",
+                   test_ctl_switch_hw_to_sw);
+
+    return g_test_run();
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 56ff860e21..cec2f818ae 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -297,7 +297,7 @@ 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-wdt-test', 'k230-gpio-test'] : [])
 
 qtests_hexagon = ['boot-serial-test']
 
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.