irqchip/ast2700-intc: Add KUnit tests for route resolution

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
This commit is contained in:
Ryan Chen
2026-04-07 11:08:06 +08:00
committed by Thomas Gleixner
parent 07825e4151
commit 46e39ee92d
5 changed files with 492 additions and 1 deletions

View File

@@ -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

View 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

View 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

View File

@@ -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");

View 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;