From nobody Thu Apr 2 09:12:32 2026 Received: from TWMBX01.aspeed.com (mail.aspeedtech.com [211.20.114.72]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5BF5A391505; Mon, 30 Mar 2026 06:37:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774852651; cv=none; b=ftfxP6geWVVw/9iXYrnjkSkPFdHtl2u4c3epZI9JD4IUdLB1CVHVPEIiX01Oixxx/TyR3Oq59K6VSg1hXgTEqA1pu+HrvDcrM6fmeUaebGB6spUdCVlkmlXHH4MfZ9pvT1M8ZpU3t5sV9NJTbn5tjbAgYq6h2dPQ59FPCPMcpkU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774852651; c=relaxed/simple; bh=oB9RyZS3+772u0r2y3OmDdlibp4XBcBxID5nPLaEA68=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=ULcubFpqV1XEzlHXzFO5/HYQhEIafElRoGLSdhqU9LgdELdPv0WSj7LLpAfzFje4uHMWdMV9wCkzRBU7apPB55d1aI0EpBlLG7pfYnqLnXZlEnV2uapkEUmpQT9YmaE2pLuDmUHamfacNy7zQW/WqV3rKvppzCeWOh1KnG79Rf8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Mon, 30 Mar 2026 14:32:11 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Mon, 30 Mar 2026 14:32:11 +0800 From: Ryan Chen Date: Mon, 30 Mar 2026 14:32:12 +0800 Subject: [PATCH v4 3/4] irqchip/ast2700-intc: Add KUnit tests for route resolution Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20260330-irqchip-v4-3-3c0f1620cc06@aspeedtech.com> References: <20260330-irqchip-v4-0-3c0f1620cc06@aspeedtech.com> In-Reply-To: <20260330-irqchip-v4-0-3c0f1620cc06@aspeedtech.com> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , "Andrew Jeffery" , Paul Walmsley , "Palmer Dabbelt" , Albert Ou , "Alexandre Ghiti" , Thomas Gleixner , Thomas Gleixner CC: , , , , , Ryan Chen X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1774852330; l=14766; i=ryan_chen@aspeedtech.com; s=20251126; h=from:subject:message-id; bh=oB9RyZS3+772u0r2y3OmDdlibp4XBcBxID5nPLaEA68=; b=mZKfdNK4E99WvDkbbUua4z+WPC1acEvSxmCXc1+CGYyH5Rs5PuobYG9IEeBq40jRi4X/82loF nsCf1yvgyA2CT4kjFiHD0K5QtBcT9ZqWQsqZbyIPcgx79JEwAqTu1Y7 X-Developer-Key: i=ryan_chen@aspeedtech.com; a=ed25519; pk=Xe73xY6tcnkuRjjbVAB/oU30KdB3FvG4nuJuILj7ZVc= 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 --- Changes in v4: - fix warning: the frame size of 1296 bytes is larger than 1280 bytes. Changes in v2: - add line break before include "irq-ast2700.h" - remove pointless newline. - rename arm_gicv3_fwnode_read_string_array to gicv3_fwnode_read_string_array - add .kunitconfig file --- drivers/irqchip/.kunitconfig | 5 + drivers/irqchip/Kconfig | 11 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-ast2700-intc0-test.c | 473 +++++++++++++++++++++++++++= ++++ 4 files changed, 490 insertions(+) diff --git a/drivers/irqchip/.kunitconfig b/drivers/irqchip/.kunitconfig new file mode 100644 index 000000000000..00a12703f635 --- /dev/null +++ b/drivers/irqchip/.kunitconfig @@ -0,0 +1,5 @@ +CONFIG_KUNIT=3Dy +CONFIG_OF=3Dy +CONFIG_COMPILE_TEST=3Dy +CONFIG_ASPEED_AST2700_INTC=3Dy +CONFIG_ASPEED_AST2700_INTC_TEST=3Dy diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 0156fee89b2c..143af3f30a4b 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -122,6 +122,17 @@ config ASPEED_AST2700_INTC =20 If unsure, say N. =20 +config ASPEED_AST2700_INTC_TEST + bool "Tests for the ASPEED AST2700 Interrupt Controller" + depends on ASPEED_AST2700_INTC && KUNIT=3Dy + 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 diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 62790663f982..ac04a4b97797 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -90,6 +90,7 @@ obj-$(CONFIG_MVEBU_SEI) +=3D irq-mvebu-sei.o obj-$(CONFIG_LS_EXTIRQ) +=3D irq-ls-extirq.o obj-$(CONFIG_LS_SCFG_MSI) +=3D irq-ls-scfg-msi.o obj-$(CONFIG_ASPEED_AST2700_INTC) +=3D irq-ast2700.o irq-ast2700-intc0.o i= rq-ast2700-intc1.o +obj-$(CONFIG_ASPEED_AST2700_INTC_TEST) +=3D irq-ast2700-intc0-test.o obj-$(CONFIG_ARCH_ASPEED) +=3D irq-aspeed-vic.o irq-aspeed-i2c-ic.o irq-a= speed-scu-ic.o obj-$(CONFIG_ARCH_ASPEED) +=3D irq-aspeed-intc.o obj-$(CONFIG_STM32MP_EXTI) +=3D 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 index 000000000000..d49784509ac7 --- /dev/null +++ b/drivers/irqchip/irq-ast2700-intc0-test.c @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2026 Code Construct + */ +#include + +#include "irq-ast2700.h" + +static void aspeed_intc0_resolve_route_bad_args(struct kunit *test) +{ + static const struct aspeed_intc_interrupt_range c1ranges[] =3D { 0 }; + static const u32 c1outs[] =3D { 0 }; + struct aspeed_intc_interrupt_range resolved; + const struct irq_domain c0domain =3D { 0 }; + int rc; + + rc =3D aspeed_intc0_resolve_route(NULL, 0, c1outs, 0, c1ranges, NULL); + KUNIT_EXPECT_EQ(test, rc, -EINVAL); + + rc =3D aspeed_intc0_resolve_route(&c0domain, 0, c1outs, + ARRAY_SIZE(c1ranges), c1ranges, + &resolved); + KUNIT_EXPECT_EQ(test, rc, -ENOENT); + + rc =3D 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 *fwno= de, + const char *propname, const char **val, size_t nval) +{ + if (!propname) + return -EINVAL; + + if (!val) + return 1; + + if (WARN_ON(nval !=3D 1)) + return -EOVERFLOW; + + *val =3D "arm,gic-v3"; + return 1; +} + +static const struct fwnode_operations arm_gicv3_fwnode_ops =3D { + .property_read_string_array =3D gicv3_fwnode_read_string_array, +}; + +static void aspeed_intc_resolve_route_invalid_c0domain(struct kunit *test) +{ + struct device_node intc0_node =3D { + .fwnode =3D { .ops =3D &arm_gicv3_fwnode_ops }, + }; + const struct irq_domain c0domain =3D { .fwnode =3D &intc0_node.fwnode }; + static const struct aspeed_intc_interrupt_range c1ranges[] =3D { 0 }; + static const u32 c1outs[] =3D { 0 }; + struct aspeed_intc_interrupt_range resolved; + int rc; + + rc =3D 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_h= andle, + const char *propname, const char **val, + size_t nval) +{ + if (!propname) + return -EINVAL; + + if (!val) + return 1; + + if (WARN_ON(nval !=3D 1)) + return -EOVERFLOW; + + *val =3D "aspeed,ast2700-intc0"; + return nval; +} + +static const struct fwnode_operations intc0_fwnode_ops =3D { + .property_read_string_array =3D aspeed_intc0_fwnode_read_string_array, +}; + +static void +aspeed_intc0_resolve_route_c1i1o1c0i1o1_connected(struct kunit *test) +{ + struct device_node intc0_node =3D { + .fwnode =3D { .ops =3D &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] =3D { + { + .start =3D 0, + .count =3D 1, + .upstream =3D { + .fwnode =3D &intc0_node.fwnode, + .param_count =3D 1, + .param =3D { 128 } + } + } + }; + static const u32 c1outs[] =3D { 0 }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] =3D { + { + .start =3D 128, + .count =3D 1, + .upstream =3D { + .fwnode =3D NULL, + .param_count =3D 0, + .param =3D { 0 }, + } + } + }; + struct aspeed_intc0 intc0 =3D { + .ranges =3D { .ranges =3D intc0_ranges, .nranges =3D ARRAY_SIZE(intc0_ra= nges), } + }; + const struct irq_domain c0domain =3D { + .host_data =3D &intc0, + .fwnode =3D &intc0_node.fwnode + }; + int rc; + + rc =3D 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 =3D { + .fwnode =3D { .ops =3D &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] =3D { + { + .start =3D 0, + .count =3D 1, + .upstream =3D { + .fwnode =3D &intc0_node.fwnode, + .param_count =3D 1, + .param =3D { 128 } + } + } + }; + static const u32 c1outs[] =3D { 0 }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] =3D { + { + .start =3D 129, + .count =3D 1, + .upstream =3D { + .fwnode =3D NULL, + .param_count =3D 0, + .param =3D { 0 }, + } + } + }; + struct aspeed_intc0 intc0 =3D { + .ranges =3D { + .ranges =3D intc0_ranges, + .nranges =3D ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain =3D { + .host_data =3D &intc0, + .fwnode =3D &intc0_node.fwnode + }; + int rc; + + rc =3D 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 =3D { + .fwnode =3D { .ops =3D &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] =3D { + { + .start =3D 0, + .count =3D 1, + .upstream =3D { + .fwnode =3D &intc0_node.fwnode, + .param_count =3D 1, + .param =3D { 480 } + } + } + }; + static const u32 c1outs[] =3D { 0 }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] =3D { + { + .start =3D 192, + .count =3D 1, + .upstream =3D { + .fwnode =3D NULL, + .param_count =3D 0, + .param =3D { 0 }, + } + } + }; + struct aspeed_intc0 intc0 =3D { + .ranges =3D { + .ranges =3D intc0_ranges, + .nranges =3D ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain =3D { + .host_data =3D &intc0, + .fwnode =3D &intc0_node.fwnode + }; + int rc; + + rc =3D 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 =3D { + .fwnode =3D { .ops =3D &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] =3D { + { + .start =3D 0, + .count =3D 1, + .upstream =3D { + .fwnode =3D &intc0_node.fwnode, + .param_count =3D 1, + .param =3D { 480 } + } + }, + { + .start =3D 1, + .count =3D 1, + .upstream =3D { + .fwnode =3D &intc0_node.fwnode, + .param_count =3D 1, + .param =3D { 510 } + } + } + }; + static const u32 c1outs[] =3D { 1 }; + struct aspeed_intc_interrupt_range resolved; + static struct aspeed_intc_interrupt_range intc0_ranges[] =3D { + { + .start =3D 208, + .count =3D 1, + .upstream =3D { + .fwnode =3D NULL, + .param_count =3D 0, + .param =3D { 0 }, + } + } + }; + struct aspeed_intc0 intc0 =3D { + .ranges =3D { + .ranges =3D intc0_ranges, + .nranges =3D ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain =3D { + .host_data =3D &intc0, + .fwnode =3D &intc0_node.fwnode + }; + int rc; + + rc =3D 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 =3D { + .fwnode =3D { .ops =3D &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] =3D { + { + .start =3D 0, + .count =3D 1, + .upstream =3D { + .fwnode =3D &intc0_node.fwnode, + .param_count =3D 1, + .param =3D { 510 } + } + }, + }; + static const u32 c1outs[] =3D { 0 }; + struct aspeed_intc_interrupt_range resolved; + static struct aspeed_intc_interrupt_range intc0_ranges[] =3D { + { + .start =3D 192, + .count =3D 1, + .upstream =3D { + .fwnode =3D NULL, + .param_count =3D 0, + .param =3D {0}, + } + }, + { + .start =3D 208, + .count =3D 1, + .upstream =3D { + .fwnode =3D NULL, + .param_count =3D 0, + .param =3D {0}, + } + } + }; + struct aspeed_intc0 intc0 =3D { + .ranges =3D { + .ranges =3D intc0_ranges, + .nranges =3D ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain =3D { + .host_data =3D &intc0, + .fwnode =3D &intc0_node.fwnode + }; + int rc; + + rc =3D 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 =3D { + .fwnode =3D { .ops =3D &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] =3D { + { + .start =3D 0, + .count =3D 1, + .upstream =3D { + .fwnode =3D &intc0_node.fwnode, + .param_count =3D 1, + .param =3D { 480 } + } + } + }; + static const u32 c1outs[] =3D { + AST2700_INTC_INVALID_ROUTE, 0 + }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] =3D { + { + .start =3D 192, + .count =3D 1, + .upstream =3D { + .fwnode =3D NULL, + .param_count =3D 0, + .param =3D { 0 }, + } + } + }; + struct aspeed_intc0 intc0 =3D { + .ranges =3D { + .ranges =3D intc0_ranges, + .nranges =3D ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain =3D { + .host_data =3D &intc0, + .fwnode =3D &intc0_node.fwnode + }; + int rc; + + rc =3D 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 =3D { + .fwnode =3D { .ops =3D &intc0_fwnode_ops }, + }; + struct aspeed_intc_interrupt_range c1ranges[] =3D { + { + .start =3D 0, + .count =3D 1, + .upstream =3D { + .fwnode =3D &intc0_node.fwnode, + .param_count =3D 0, + .param =3D { 0 } + } + } + }; + static const u32 c1outs[] =3D { 0 }; + struct aspeed_intc_interrupt_range resolved; + struct aspeed_intc_interrupt_range intc0_ranges[] =3D { + { + .start =3D 0, + .count =3D 0, + .upstream =3D { + .fwnode =3D NULL, + .param_count =3D 0, + .param =3D { 0 }, + } + } + }; + struct aspeed_intc0 intc0 =3D { + .ranges =3D { + .ranges =3D intc0_ranges, + .nranges =3D ARRAY_SIZE(intc0_ranges), + } + }; + const struct irq_domain c0domain =3D { + .host_data =3D &intc0, + .fwnode =3D &intc0_node.fwnode + }; + int rc; + + rc =3D 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[] =3D { + 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 =3D { + .name =3D "ast2700-intc0", + .test_cases =3D ast2700_intc0_test_cases, +}; + +kunit_test_suite(ast2700_intc0_test_suite); + +MODULE_LICENSE("GPL"); --=20 2.34.1