Contributors: 1
Author Tokens Token Proportion Commits Commit Proportion
Ryan Chen 2216 100.00% 2 100.00%
Total 2216 2


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