]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
irqchip/ast2700-intc: Add KUnit tests for route resolution
authorRyan Chen <ryan_chen@aspeedtech.com>
Tue, 7 Apr 2026 03:08:06 +0000 (11:08 +0800)
committerThomas Gleixner <tglx@kernel.org>
Thu, 30 Apr 2026 10:53:04 +0000 (12:53 +0200)
Add a KUnit suite for aspeed_intc0_resolve_route().

Cover invalid arguments, invalid domain/range data, connected and
disconnected mappings, and malformed upstream range cases.

Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260407-irqchip-v5-3-c0b0a300a057@aspeedtech.com
drivers/irqchip/.kunitconfig [new file with mode: 0644]
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-ast2700-intc0-test.c [new file with mode: 0644]
drivers/irqchip/irq-ast2700-intc0.c

diff --git a/drivers/irqchip/.kunitconfig b/drivers/irqchip/.kunitconfig
new file mode 100644 (file)
index 0000000..00a1270
--- /dev/null
@@ -0,0 +1,5 @@
+CONFIG_KUNIT=y
+CONFIG_OF=y
+CONFIG_COMPILE_TEST=y
+CONFIG_ASPEED_AST2700_INTC=y
+CONFIG_ASPEED_AST2700_INTC_TEST=y
index cb446e1234579d4fbaedf5a67a7ebc87480ffcd5..387beef4a89db352bfb8dddae7335b9b1972496a 100644 (file)
@@ -122,6 +122,17 @@ config ASPEED_AST2700_INTC
 
          If unsure, say N.
 
+config ASPEED_AST2700_INTC_TEST
+       bool "Tests for the ASPEED AST2700 Interrupt Controller"
+       depends on ASPEED_AST2700_INTC && KUNIT=y
+       default KUNIT_ALL_TESTS
+       help
+         Enable KUnit tests for AST2700 INTC route resolution.
+         The tests exercise error handling and route selection paths.
+         This option is intended for test builds.
+
+         If unsure, say N.
+
 config ATMEL_AIC_IRQ
        bool
        select GENERIC_IRQ_CHIP
index 62790663f982323eff3160364cac0d12141ea194..ac04a4b977976db490869c0130690aa82669600d 100644 (file)
@@ -90,6 +90,7 @@ obj-$(CONFIG_MVEBU_SEI)                       += irq-mvebu-sei.o
 obj-$(CONFIG_LS_EXTIRQ)                        += irq-ls-extirq.o
 obj-$(CONFIG_LS_SCFG_MSI)              += irq-ls-scfg-msi.o
 obj-$(CONFIG_ASPEED_AST2700_INTC)      += irq-ast2700.o irq-ast2700-intc0.o irq-ast2700-intc1.o
+obj-$(CONFIG_ASPEED_AST2700_INTC_TEST) += irq-ast2700-intc0-test.o
 obj-$(CONFIG_ARCH_ASPEED)              += irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-aspeed-scu-ic.o
 obj-$(CONFIG_ARCH_ASPEED)              += irq-aspeed-intc.o
 obj-$(CONFIG_STM32MP_EXTI)             += irq-stm32mp-exti.o
diff --git a/drivers/irqchip/irq-ast2700-intc0-test.c b/drivers/irqchip/irq-ast2700-intc0-test.c
new file mode 100644 (file)
index 0000000..d497845
--- /dev/null
@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  Copyright (C) 2026 Code Construct
+ */
+#include <kunit/test.h>
+
+#include "irq-ast2700.h"
+
+static void aspeed_intc0_resolve_route_bad_args(struct kunit *test)
+{
+       static const struct aspeed_intc_interrupt_range c1ranges[] = { 0 };
+       static const u32 c1outs[] = { 0 };
+       struct aspeed_intc_interrupt_range resolved;
+       const struct irq_domain c0domain = { 0 };
+       int rc;
+
+       rc = aspeed_intc0_resolve_route(NULL, 0, c1outs, 0, c1ranges, NULL);
+       KUNIT_EXPECT_EQ(test, rc, -EINVAL);
+
+       rc = aspeed_intc0_resolve_route(&c0domain, 0, c1outs,
+                                       ARRAY_SIZE(c1ranges), c1ranges,
+                                       &resolved);
+       KUNIT_EXPECT_EQ(test, rc, -ENOENT);
+
+       rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs,
+                                       0, c1ranges, &resolved);
+       KUNIT_EXPECT_EQ(test, rc, -ENOENT);
+}
+
+static int gicv3_fwnode_read_string_array(const struct fwnode_handle *fwnode,
+                                         const char *propname, const char **val, size_t nval)
+{
+       if (!propname)
+               return -EINVAL;
+
+       if (!val)
+               return 1;
+
+       if (WARN_ON(nval != 1))
+               return -EOVERFLOW;
+
+       *val = "arm,gic-v3";
+       return 1;
+}
+
+static const struct fwnode_operations arm_gicv3_fwnode_ops = {
+       .property_read_string_array = gicv3_fwnode_read_string_array,
+};
+
+static void aspeed_intc_resolve_route_invalid_c0domain(struct kunit *test)
+{
+       struct device_node intc0_node = {
+               .fwnode = { .ops = &arm_gicv3_fwnode_ops },
+       };
+       const struct irq_domain c0domain = { .fwnode = &intc0_node.fwnode };
+       static const struct aspeed_intc_interrupt_range c1ranges[] = { 0 };
+       static const u32 c1outs[] = { 0 };
+       struct aspeed_intc_interrupt_range resolved;
+       int rc;
+
+       rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs,
+                                       ARRAY_SIZE(c1ranges), c1ranges,
+                                       &resolved);
+       KUNIT_EXPECT_NE(test, rc, 0);
+}
+
+static int
+aspeed_intc0_fwnode_read_string_array(const struct fwnode_handle *fwnode_handle,
+                                     const char *propname, const char **val,
+                                     size_t nval)
+{
+       if (!propname)
+               return -EINVAL;
+
+       if (!val)
+               return 1;
+
+       if (WARN_ON(nval != 1))
+               return -EOVERFLOW;
+
+       *val = "aspeed,ast2700-intc0";
+       return nval;
+}
+
+static const struct fwnode_operations intc0_fwnode_ops = {
+       .property_read_string_array = aspeed_intc0_fwnode_read_string_array,
+};
+
+static void
+aspeed_intc0_resolve_route_c1i1o1c0i1o1_connected(struct kunit *test)
+{
+       struct device_node intc0_node = {
+               .fwnode = { .ops = &intc0_fwnode_ops },
+       };
+       struct aspeed_intc_interrupt_range c1ranges[] = {
+               {
+                       .start = 0,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = &intc0_node.fwnode,
+                               .param_count = 1,
+                               .param = { 128 }
+                       }
+               }
+       };
+       static const u32 c1outs[] = { 0 };
+       struct aspeed_intc_interrupt_range resolved;
+       struct aspeed_intc_interrupt_range intc0_ranges[] = {
+               {
+                       .start = 128,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = NULL,
+                               .param_count = 0,
+                               .param = { 0 },
+                       }
+               }
+       };
+       struct aspeed_intc0 intc0 = {
+               .ranges = { .ranges = intc0_ranges, .nranges = ARRAY_SIZE(intc0_ranges), }
+       };
+       const struct irq_domain c0domain = {
+               .host_data = &intc0,
+               .fwnode = &intc0_node.fwnode
+       };
+       int rc;
+
+       rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs,
+                                       ARRAY_SIZE(c1ranges), c1ranges,
+                                       &resolved);
+       KUNIT_EXPECT_EQ(test, rc, 0);
+       KUNIT_EXPECT_EQ(test, resolved.start, 0);
+       KUNIT_EXPECT_EQ(test, resolved.count, 1);
+       KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 128);
+}
+
+static void
+aspeed_intc0_resolve_route_c1i1o1c0i1o1_disconnected(struct kunit *test)
+{
+       struct device_node intc0_node = {
+               .fwnode = { .ops = &intc0_fwnode_ops },
+       };
+       struct aspeed_intc_interrupt_range c1ranges[] = {
+               {
+                       .start = 0,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = &intc0_node.fwnode,
+                               .param_count = 1,
+                               .param = { 128 }
+                       }
+               }
+       };
+       static const u32 c1outs[] = { 0 };
+       struct aspeed_intc_interrupt_range resolved;
+       struct aspeed_intc_interrupt_range intc0_ranges[] = {
+               {
+                       .start = 129,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = NULL,
+                               .param_count = 0,
+                               .param = { 0 },
+                       }
+               }
+       };
+       struct aspeed_intc0 intc0 = {
+               .ranges = {
+                       .ranges = intc0_ranges,
+                       .nranges = ARRAY_SIZE(intc0_ranges),
+               }
+       };
+       const struct irq_domain c0domain = {
+               .host_data = &intc0,
+               .fwnode = &intc0_node.fwnode
+       };
+       int rc;
+
+       rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs,
+                                       ARRAY_SIZE(c1ranges), c1ranges,
+                                       &resolved);
+       KUNIT_EXPECT_NE(test, rc, 0);
+}
+
+static void aspeed_intc0_resolve_route_c1i1o1mc0i1o1(struct kunit *test)
+{
+       struct device_node intc0_node = {
+               .fwnode = { .ops = &intc0_fwnode_ops },
+       };
+       struct aspeed_intc_interrupt_range c1ranges[] = {
+               {
+                       .start = 0,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = &intc0_node.fwnode,
+                               .param_count = 1,
+                               .param = { 480 }
+                       }
+               }
+       };
+       static const u32 c1outs[] = { 0 };
+       struct aspeed_intc_interrupt_range resolved;
+       struct aspeed_intc_interrupt_range intc0_ranges[] = {
+               {
+                       .start = 192,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = NULL,
+                               .param_count = 0,
+                               .param = { 0 },
+                       }
+               }
+       };
+       struct aspeed_intc0 intc0 = {
+               .ranges = {
+                       .ranges = intc0_ranges,
+                       .nranges = ARRAY_SIZE(intc0_ranges),
+               }
+       };
+       const struct irq_domain c0domain = {
+               .host_data = &intc0,
+               .fwnode = &intc0_node.fwnode
+       };
+       int rc;
+
+       rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs,
+                                       ARRAY_SIZE(c1ranges), c1ranges,
+                                       &resolved);
+       KUNIT_EXPECT_EQ(test, rc, 0);
+       KUNIT_EXPECT_EQ(test, resolved.start, 0);
+       KUNIT_EXPECT_EQ(test, resolved.count, 1);
+       KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 480);
+}
+
+static void aspeed_intc0_resolve_route_c1i2o2mc0i1o1(struct kunit *test)
+{
+       struct device_node intc0_node = {
+               .fwnode = { .ops = &intc0_fwnode_ops },
+       };
+       struct aspeed_intc_interrupt_range c1ranges[] = {
+               {
+                       .start = 0,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = &intc0_node.fwnode,
+                               .param_count = 1,
+                               .param = { 480 }
+                       }
+               },
+               {
+                       .start = 1,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = &intc0_node.fwnode,
+                               .param_count = 1,
+                               .param = { 510 }
+                       }
+               }
+       };
+       static const u32 c1outs[] = { 1 };
+       struct aspeed_intc_interrupt_range resolved;
+       static struct aspeed_intc_interrupt_range intc0_ranges[] = {
+               {
+                       .start = 208,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = NULL,
+                               .param_count = 0,
+                               .param = { 0 },
+                       }
+               }
+       };
+       struct aspeed_intc0 intc0 = {
+               .ranges = {
+                       .ranges = intc0_ranges,
+                       .nranges = ARRAY_SIZE(intc0_ranges),
+               }
+       };
+       const struct irq_domain c0domain = {
+               .host_data = &intc0,
+               .fwnode = &intc0_node.fwnode
+       };
+       int rc;
+
+       rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs,
+                                       ARRAY_SIZE(c1ranges), c1ranges,
+                                       &resolved);
+       KUNIT_EXPECT_EQ(test, rc, 0);
+       KUNIT_EXPECT_EQ(test, resolved.start, 1);
+       KUNIT_EXPECT_EQ(test, resolved.count, 1);
+       KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 510);
+}
+
+static void aspeed_intc0_resolve_route_c1i1o1mc0i2o1(struct kunit *test)
+{
+       struct device_node intc0_node = {
+               .fwnode = { .ops = &intc0_fwnode_ops },
+       };
+       struct aspeed_intc_interrupt_range c1ranges[] = {
+               {
+                       .start = 0,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = &intc0_node.fwnode,
+                               .param_count = 1,
+                               .param = { 510 }
+                       }
+               },
+       };
+       static const u32 c1outs[] = { 0 };
+       struct aspeed_intc_interrupt_range resolved;
+       static struct aspeed_intc_interrupt_range intc0_ranges[] = {
+               {
+                       .start = 192,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = NULL,
+                               .param_count = 0,
+                               .param = {0},
+                       }
+               },
+               {
+                       .start = 208,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = NULL,
+                               .param_count = 0,
+                               .param = {0},
+                       }
+               }
+       };
+       struct aspeed_intc0 intc0 = {
+               .ranges = {
+                       .ranges = intc0_ranges,
+                       .nranges = ARRAY_SIZE(intc0_ranges),
+               }
+       };
+       const struct irq_domain c0domain = {
+               .host_data = &intc0,
+               .fwnode = &intc0_node.fwnode
+       };
+       int rc;
+
+       rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs,
+                                       ARRAY_SIZE(c1ranges), c1ranges,
+                                       &resolved);
+       KUNIT_EXPECT_EQ(test, rc, 0);
+       KUNIT_EXPECT_EQ(test, resolved.start, 0);
+       KUNIT_EXPECT_EQ(test, resolved.count, 1);
+       KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 510);
+}
+
+static void aspeed_intc0_resolve_route_c1i1o2mc0i1o1_invalid(struct kunit *test)
+{
+       struct device_node intc0_node = {
+               .fwnode = { .ops = &intc0_fwnode_ops },
+       };
+       struct aspeed_intc_interrupt_range c1ranges[] = {
+               {
+                       .start = 0,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = &intc0_node.fwnode,
+                               .param_count = 1,
+                               .param = { 480 }
+                       }
+               }
+       };
+       static const u32 c1outs[] = {
+               AST2700_INTC_INVALID_ROUTE, 0
+       };
+       struct aspeed_intc_interrupt_range resolved;
+       struct aspeed_intc_interrupt_range intc0_ranges[] = {
+               {
+                       .start = 192,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = NULL,
+                               .param_count = 0,
+                               .param = { 0 },
+                       }
+               }
+       };
+       struct aspeed_intc0 intc0 = {
+               .ranges = {
+                       .ranges = intc0_ranges,
+                       .nranges = ARRAY_SIZE(intc0_ranges),
+               }
+       };
+       const struct irq_domain c0domain = {
+               .host_data = &intc0,
+               .fwnode = &intc0_node.fwnode
+       };
+       int rc;
+
+       rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs,
+                                       ARRAY_SIZE(c1ranges), c1ranges,
+                                       &resolved);
+       KUNIT_EXPECT_EQ(test, rc, 1);
+       KUNIT_EXPECT_EQ(test, resolved.start, 0);
+       KUNIT_EXPECT_EQ(test, resolved.count, 1);
+       KUNIT_EXPECT_EQ(test, resolved.upstream.param[0], 480);
+}
+
+static void
+aspeed_intc0_resolve_route_c1i1o1mc0i1o1_bad_range_upstream(struct kunit *test)
+{
+       struct device_node intc0_node = {
+               .fwnode = { .ops = &intc0_fwnode_ops },
+       };
+       struct aspeed_intc_interrupt_range c1ranges[] = {
+               {
+                       .start = 0,
+                       .count = 1,
+                       .upstream = {
+                               .fwnode = &intc0_node.fwnode,
+                               .param_count = 0,
+                               .param = { 0 }
+                       }
+               }
+       };
+       static const u32 c1outs[] = { 0 };
+       struct aspeed_intc_interrupt_range resolved;
+       struct aspeed_intc_interrupt_range intc0_ranges[] = {
+               {
+                       .start = 0,
+                       .count = 0,
+                       .upstream = {
+                               .fwnode = NULL,
+                               .param_count = 0,
+                               .param = { 0 },
+                       }
+               }
+       };
+       struct aspeed_intc0 intc0 = {
+               .ranges = {
+                       .ranges = intc0_ranges,
+                       .nranges = ARRAY_SIZE(intc0_ranges),
+               }
+       };
+       const struct irq_domain c0domain = {
+               .host_data = &intc0,
+               .fwnode = &intc0_node.fwnode
+       };
+       int rc;
+
+       rc = aspeed_intc0_resolve_route(&c0domain, ARRAY_SIZE(c1outs), c1outs,
+                                       ARRAY_SIZE(c1ranges), c1ranges,
+                                       &resolved);
+       KUNIT_EXPECT_NE(test, rc, 0);
+}
+
+static struct kunit_case ast2700_intc0_test_cases[] = {
+       KUNIT_CASE(aspeed_intc0_resolve_route_bad_args),
+       KUNIT_CASE(aspeed_intc_resolve_route_invalid_c0domain),
+       KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1c0i1o1_connected),
+       KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1c0i1o1_disconnected),
+       KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1mc0i1o1),
+       KUNIT_CASE(aspeed_intc0_resolve_route_c1i2o2mc0i1o1),
+       KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1mc0i2o1),
+       KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o2mc0i1o1_invalid),
+       KUNIT_CASE(aspeed_intc0_resolve_route_c1i1o1mc0i1o1_bad_range_upstream),
+       {},
+};
+
+static struct kunit_suite ast2700_intc0_test_suite = {
+       .name = "ast2700-intc0",
+       .test_cases = ast2700_intc0_test_cases,
+};
+
+kunit_test_suite(ast2700_intc0_test_suite);
+
+MODULE_LICENSE("GPL");
index 65e17b2dc6fa989d70cdc18afca28534295b6486..14b8b88f1179ba5fa8b38ada8deb1e59e827fb9c 100644 (file)
@@ -311,7 +311,8 @@ int aspeed_intc0_resolve_route(const struct irq_domain *c0domain, size_t nc1outs
        if (nc1outs == 0 || nc1ranges == 0)
                return -ENOENT;
 
-       if (!fwnode_device_is_compatible(c0domain->fwnode, "aspeed,ast2700-intc0"))
+       if (!IS_ENABLED(CONFIG_ASPEED_AST2700_INTC_TEST) &&
+           !fwnode_device_is_compatible(c0domain->fwnode, "aspeed,ast2700-intc0"))
                return -ENODEV;
 
        intc0 = c0domain->host_data;