1 /*
2  * Copyright (c) 2024 Meta
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "sw_isr_common.h"
8 
9 #include <stdint.h>
10 
11 #include <zephyr/irq_multilevel.h>
12 #include <zephyr/sys/iterable_sections.h>
13 #include <zephyr/ztest.h>
14 
15 #ifdef CONFIG_DUMP_INTC_TABLE
16 #define DEBUG_PRINT(...) PRINT(__VA_ARGS__)
17 #else
18 #define DEBUG_PRINT(...)
19 #endif
20 
21 /**
22  * Fake device pointers
23  */
24 /* Device pointer to level 2 intc 1 */
25 #define INTC_1_DEV UINT_TO_POINTER(21)
26 /* Device pointer to level 2 intc 2 */
27 #define INTC_2_DEV UINT_TO_POINTER(22)
28 /* Device pointer to level 3 intc 3*/
29 #define INTC_3_DEV UINT_TO_POINTER(31)
30 /* Device pointer to level 3 intc 4 */
31 #define INTC_4_DEV UINT_TO_POINTER(32)
32 
33 /**
34  * Interrupt controller's local IRQ
35  */
36 /* Local IRQ of level 2 intc 1 */
37 #define INTC_1_IRQ 4
38 /* Local IRQ of level 2 intc 2 */
39 #define INTC_2_IRQ 5
40 /* Local IRQ of level 3 intc 3 */
41 #define INTC_3_IRQ 9
42 /* Local IRQ of level 3 intc 4 */
43 #define INTC_4_IRQ 10
44 
45 /**
46  * Interrupt controller's IRQ in Zephyr format
47  */
48 /* Zephyr IRQ of level 2 intc 1 */
49 #define INTC_1_IRQN INTC_1_IRQ
50 /* Zephyr IRQ of level 2 intc 2 */
51 #define INTC_2_IRQN INTC_2_IRQ
52 /* Zephyr IRQ of level 3 intc 3*/
53 #define INTC_3_IRQN (IRQ_TO_L2(INTC_3_IRQ) | INTC_1_IRQN)
54 /* Zephyr IRQ of level 3 intc 4 */
55 #define INTC_4_IRQN (IRQ_TO_L2(INTC_4_IRQ) | INTC_2_IRQN)
56 
57 /**
58  *  Register all interrupt controller with the intc table
59  */
60 /* Helper to calculate the stride for each intc in the ISR table */
61 #define INTC_COUNT(n) (n * CONFIG_MAX_IRQ_PER_AGGREGATOR)
62 #define INTC_1_OFFSET INTC_COUNT(1)
63 #define INTC_2_OFFSET INTC_COUNT(2)
64 #define INTC_3_OFFSET INTC_COUNT(3)
65 #define INTC_4_OFFSET INTC_COUNT(4)
66 IRQ_PARENT_ENTRY_DEFINE(intc_l2_1, INTC_1_DEV, INTC_1_IRQN, INTC_1_OFFSET, 2);
67 IRQ_PARENT_ENTRY_DEFINE(intc_l2_2, INTC_2_DEV, INTC_2_IRQN, INTC_2_OFFSET, 2);
68 IRQ_PARENT_ENTRY_DEFINE(intc_l3_3, INTC_3_DEV, INTC_3_IRQN, INTC_3_OFFSET, 3);
69 IRQ_PARENT_ENTRY_DEFINE(intc_l3_4, INTC_4_DEV, INTC_4_IRQN, INTC_4_OFFSET, 3);
70 
71 /**
72  *  Test IRQs in local format
73  */
74 #define TEST_IRQ_1 2
75 #define TEST_IRQ_2 3
76 #define TEST_IRQ_3 4
77 #define TEST_IRQ_4 5
78 
79 /**
80  *  Test IRQs in Zephyr format
81  */
82 /* TEST_IRQ_1 handled by intc_l2_1 */
83 #define TEST_IRQN_1 (IRQ_TO_L2(TEST_IRQ_1) | INTC_1_IRQN)
84 /* TEST_IRQ_2 handled by intc_l2_2 */
85 #define TEST_IRQN_2 (IRQ_TO_L2(TEST_IRQ_2) | INTC_2_IRQN)
86 /* TEST_IRQ_3 handled by intc_l3_1 */
87 #define TEST_IRQN_3 (IRQ_TO_L3(TEST_IRQ_3) | INTC_3_IRQN)
88 /* TEST_IRQ_4 handled by intc_l3_2 */
89 #define TEST_IRQN_4 (IRQ_TO_L3(TEST_IRQ_4) | INTC_4_IRQN)
90 
ZTEST(intc_multi_level_backend,test_irq_from_device)91 ZTEST(intc_multi_level_backend, test_irq_from_device)
92 {
93 	/* degenerate cases */
94 	if (!IS_ENABLED(CONFIG_ASSERT)) {
95 		/* Return 0 if dev not found in the LUT */
96 		zassert_equal(z_get_sw_isr_irq_from_device(UINT_TO_POINTER(42)), 0);
97 	}
98 
99 	zassert_equal(z_get_sw_isr_irq_from_device(INTC_1_DEV), INTC_1_IRQN);
100 	zassert_equal(z_get_sw_isr_irq_from_device(INTC_2_DEV), INTC_2_IRQN);
101 	zassert_equal(z_get_sw_isr_irq_from_device(INTC_3_DEV), INTC_3_IRQN);
102 	zassert_equal(z_get_sw_isr_irq_from_device(INTC_4_DEV), INTC_4_IRQN);
103 }
104 
ZTEST(intc_multi_level_backend,test_device_from_irq)105 ZTEST(intc_multi_level_backend, test_device_from_irq)
106 {
107 	/* degenerate cases */
108 	if (!IS_ENABLED(CONFIG_ASSERT)) {
109 		/* Return NULL if can't find anything to handle the IRQ */
110 		zassert_equal_ptr(z_get_sw_isr_device_from_irq(IRQ_TO_L2(9) | 8), NULL);
111 	}
112 
113 	zassert_equal_ptr(z_get_sw_isr_device_from_irq(TEST_IRQN_1), INTC_1_DEV);
114 	zassert_equal_ptr(z_get_sw_isr_device_from_irq(TEST_IRQN_2), INTC_2_DEV);
115 	zassert_equal_ptr(z_get_sw_isr_device_from_irq(TEST_IRQN_3), INTC_3_DEV);
116 	zassert_equal_ptr(z_get_sw_isr_device_from_irq(TEST_IRQN_4), INTC_4_DEV);
117 }
118 
ZTEST(intc_multi_level_backend,test_table_idx_from_irq)119 ZTEST(intc_multi_level_backend, test_table_idx_from_irq)
120 {
121 	/* degenerate cases */
122 	if (!IS_ENABLED(CONFIG_ASSERT)) {
123 		/* 2nd level aggregator that doesn't exist */
124 		const unsigned int first_level_agg = 8;
125 		const unsigned int unhandled_irqn = IRQ_TO_L2(TEST_IRQ_1) | first_level_agg;
126 
127 		zassert_equal(z_get_sw_isr_table_idx(unhandled_irqn),
128 			      unhandled_irqn - CONFIG_GEN_IRQ_START_VECTOR);
129 
130 		/* local_irq exceeded CONFIG_MAX_IRQ_PER_AGGREGATOR */
131 		const unsigned int local_irq = CONFIG_MAX_IRQ_PER_AGGREGATOR + 1;
132 		const unsigned int overflown_irqn = IRQ_TO_L2(local_irq) | INTC_1_IRQN;
133 
134 		zassert_equal(z_get_sw_isr_table_idx(overflown_irqn),
135 			      local_irq + INTC_1_OFFSET - CONFIG_GEN_IRQ_START_VECTOR);
136 
137 		/* Overflow SW ISR table */
138 		const unsigned int local_irq2 = (CONFIG_MAX_IRQ_PER_AGGREGATOR - 1);
139 		const unsigned int overflown_irqn2 = IRQ_TO_L3(local_irq2) | INTC_4_IRQN;
140 
141 		zassert_equal(z_get_sw_isr_table_idx(overflown_irqn2),
142 			      local_irq2 + INTC_4_OFFSET - CONFIG_GEN_IRQ_START_VECTOR);
143 	}
144 
145 	/* Level 1 */
146 	zassert_equal(z_get_sw_isr_table_idx(INTC_1_IRQN),
147 		      INTC_1_IRQN - CONFIG_GEN_IRQ_START_VECTOR);
148 	zassert_equal(z_get_sw_isr_table_idx(1), 1 - CONFIG_GEN_IRQ_START_VECTOR);
149 
150 	/* Level 2 */
151 	zassert_equal(z_get_sw_isr_table_idx(TEST_IRQN_1),
152 		      TEST_IRQ_1 + INTC_1_OFFSET - CONFIG_GEN_IRQ_START_VECTOR);
153 	zassert_equal(z_get_sw_isr_table_idx(TEST_IRQN_2),
154 		      TEST_IRQ_2 + INTC_2_OFFSET - CONFIG_GEN_IRQ_START_VECTOR);
155 
156 	/* Level 3 */
157 	zassert_equal(z_get_sw_isr_table_idx(TEST_IRQN_3),
158 		      TEST_IRQ_3 + INTC_3_OFFSET - CONFIG_GEN_IRQ_START_VECTOR);
159 	zassert_equal(z_get_sw_isr_table_idx(TEST_IRQN_4),
160 		      TEST_IRQ_4 + INTC_4_OFFSET - CONFIG_GEN_IRQ_START_VECTOR);
161 }
162 
setup(void)163 static void *setup(void)
164 {
165 	DEBUG_PRINT("=============== intc table ===============\n");
166 	DEBUG_PRINT("         dev |  level |    irq |  offset\n");
167 	DEBUG_PRINT("==========================================\n");
168 	STRUCT_SECTION_FOREACH_ALTERNATE(intc_table, _irq_parent_entry, intc)
169 	{
170 		DEBUG_PRINT("%12p | %6u | %6X | %7u\n", intc->dev, intc->level, intc->irq,
171 			    intc->offset);
172 	}
173 	DEBUG_PRINT("==========================================\n");
174 
175 	return NULL;
176 }
177 
178 ZTEST_SUITE(intc_multi_level_backend, NULL, setup, NULL, NULL, NULL);
179