1 /*
2  * Copyright 2023 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "test_shared_irq.h"
8 
9 struct shared_irq_fixture {
10 	unsigned int irq1;
11 	unsigned int irq2;
12 	unsigned int irq1_table_idx;
13 	unsigned int irq2_table_idx;
14 	unsigned int irq_priority;
15 };
16 
17 static struct shared_irq_fixture fixture;
18 
reset_test_vector(void)19 static void reset_test_vector(void)
20 {
21 	int i;
22 
23 	for (i = 0; i < TEST_VECTOR_SIZE; i++) {
24 		test_vector[i] = 0;
25 	}
26 }
27 
dynamic_shared_irq_suite_after(void * data)28 static void dynamic_shared_irq_suite_after(void *data)
29 {
30 	ARG_UNUSED(data);
31 
32 	/* note: no need to check the state of the SW ISR tables after
33 	 * all these disconnect operations. If there's something wrong
34 	 * it should be detected by dynamic_shared_irq_suite_before().
35 	 */
36 	arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority,
37 				    test_isr_0, 0, 0);
38 	arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority,
39 				    test_isr_1, (void *)1, 0);
40 	arch_irq_disconnect_dynamic(fixture.irq2, fixture.irq_priority,
41 				    test_isr_2, (void *)2, 0);
42 }
43 
dummy_isr(const void * data)44 static void dummy_isr(const void *data)
45 {
46 	ARG_UNUSED(data);
47 
48 	test_vector[0] = TEST_DUMMY_ISR_VAL;
49 }
50 
get_irq_slot(unsigned int start)51 static unsigned int get_irq_slot(unsigned int start)
52 {
53 	unsigned int i, table_idx;
54 
55 	for (i = start; i <= CONFIG_GEN_IRQ_START_VECTOR + CONFIG_NUM_IRQS - 1; i++) {
56 		table_idx = i - CONFIG_GEN_IRQ_START_VECTOR;
57 
58 		if (_sw_isr_table[table_idx].isr == &z_irq_spurious) {
59 			test_vector[0] = 0;
60 
61 			/* check to see if we can trigger this IRQ */
62 			arch_irq_connect_dynamic(i, IRQ_PRIORITY, dummy_isr,
63 						 NULL, 0);
64 			irq_enable(i);
65 			trigger_irq(i);
66 
67 			/* wait a bit */
68 			k_busy_wait(100);
69 
70 			if (test_vector[0] == TEST_DUMMY_ISR_VAL) {
71 				/* found a valid INTID */
72 				irq_disable(i);
73 
74 				arch_irq_disconnect_dynamic(i, IRQ_PRIORITY,
75 							    dummy_isr, NULL, 0);
76 				return i;
77 			}
78 		}
79 	}
80 
81 	return TEST_INVALID_IRQ;
82 }
83 
dynamic_shared_irq_suite_setup(void)84 static void *dynamic_shared_irq_suite_setup(void)
85 {
86 	fixture.irq1 = get_irq_slot(CONFIG_GEN_IRQ_START_VECTOR);
87 	zassert_true(fixture.irq1 != TEST_INVALID_IRQ,
88 		     "no suitable value found for irq1");
89 	fixture.irq2 = get_irq_slot(fixture.irq1 + 1);
90 	zassert_true(fixture.irq2 != TEST_INVALID_IRQ,
91 		     "no suitable value found for irq2");
92 	fixture.irq_priority = IRQ_PRIORITY;
93 
94 	fixture.irq1_table_idx = fixture.irq1 - CONFIG_GEN_IRQ_START_VECTOR;
95 	fixture.irq2_table_idx = fixture.irq2 - CONFIG_GEN_IRQ_START_VECTOR;
96 
97 	return NULL;
98 }
99 
100 #if defined(CONFIG_RISCV_RESERVED_IRQ_ISR_TABLES_OFFSET)
101 #define TABLE_OFFSET CONFIG_RISCV_RESERVED_IRQ_ISR_TABLES_OFFSET
102 #else
103 #define TABLE_OFFSET 0
104 #endif
105 
dynamic_shared_irq_suite_before(void * data)106 static void dynamic_shared_irq_suite_before(void *data)
107 {
108 	ARG_UNUSED(data);
109 
110 	arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority,
111 				 test_isr_0, 0, 0);
112 
113 	zassert_true(_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET].isr == test_isr_0,
114 		     "wrong _sw_isr_table ISR at irq1");
115 	zassert_true(!_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET].arg,
116 		     "wrong _sw_isr_table argument at irq1");
117 	zassert_true(!z_shared_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET].client_num,
118 		     "wrong client number at irq1");
119 
120 	arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority,
121 				 test_isr_1, (void *)1, 0);
122 
123 	zassert_true(_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET].isr == z_shared_isr,
124 		     "wrong _sw_isr_table ISR at irq1");
125 	zassert_true(_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET].arg ==
126 		     &z_shared_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET],
127 		     "wrong _sw_isr_table argument at irq1");
128 	zassert_true(z_shared_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET].client_num == 2,
129 		     "wrong client number at irq1");
130 
131 	zassert_true(client_exists_at_index(test_isr_0, 0, fixture.irq1_table_idx + TABLE_OFFSET,
132 					    0),
133 		     "unexpected client data for irq1, index 0");
134 	zassert_true(client_exists_at_index(test_isr_1, (void *)1,
135 		     fixture.irq1_table_idx + TABLE_OFFSET, 1),
136 		     "unexpected client data for irq1, index 1");
137 
138 	arch_irq_connect_dynamic(fixture.irq2, fixture.irq_priority,
139 				 test_isr_2, (void *)2, 0);
140 
141 	zassert_true(_sw_isr_table[fixture.irq2_table_idx + TABLE_OFFSET].isr == test_isr_2,
142 		     "wrong _sw_isr_table ISR at irq2");
143 	zassert_true(_sw_isr_table[fixture.irq2_table_idx + TABLE_OFFSET].arg == (void *)2,
144 		     "wrong _sw_isr_table argument at irq2");
145 	zassert_true(!z_shared_sw_isr_table[fixture.irq2_table_idx + TABLE_OFFSET].client_num,
146 		     "wrong client number at irq2");
147 
148 	reset_test_vector();
149 }
150 
151 /**
152  * @brief Test writing to a vector with a shared interrupt
153  *
154  * @ingroup kernel_interrupt_tests
155  *
156  * @details This tests if interrupts are dynamically shared successfully
157  * (i.e: multiple ISR/arg pairs are called whenever the interrupt
158  * they were registered for is triggered).
159  */
ZTEST(shared_irq_feature,test_dynamic_shared_irq_write)160 ZTEST(shared_irq_feature, test_dynamic_shared_irq_write)
161 {
162 	int i;
163 
164 	irq_enable(fixture.irq1);
165 	irq_enable(fixture.irq2);
166 
167 	trigger_irq(fixture.irq1);
168 	trigger_irq(fixture.irq2);
169 
170 	/* wait 5ms before checking the results */
171 	k_busy_wait(5000);
172 
173 	for (i = 0; i < TEST_VECTOR_SIZE; i++) {
174 		zassert_true(test_vector[i] == result_vector[i],
175 			     "wrong test_vector value at %d: 0x%x vs 0x%x",
176 			     i, test_vector[i], result_vector[i]);
177 	}
178 
179 	irq_disable(fixture.irq1);
180 	irq_disable(fixture.irq2);
181 }
182 
183 /**
184  * @brief Test writing to a vector after an ISR/arg disconnect.
185  *
186  * @ingroup kernel_interrupt_tests
187  *
188  * @details This tests if ISR/arg pairs are disconnected successfully
189  * and the interrupts are "unshared" whenever a single ISR/arg pair is
190  * left.
191  */
ZTEST(shared_irq_feature,test_dynamic_shared_irq_disconnect_write)192 ZTEST(shared_irq_feature, test_dynamic_shared_irq_disconnect_write)
193 {
194 	int i;
195 
196 	/* remove test_isr_0/NULL pair. After this statement we expect
197 	 * irq1 to be unshared.
198 	 */
199 	arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority,
200 				    test_isr_0, 0, 0);
201 
202 	zassert_true(_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET].isr == test_isr_1,
203 		     "wrong _sw_isr_table ISR at irq1");
204 	zassert_true(_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET].arg == (void *)1,
205 		     "wrong _sw_isr_table arg at irq1");
206 	zassert_true(!z_shared_sw_isr_table[fixture.irq1_table_idx + TABLE_OFFSET].client_num,
207 		     "wrong client number at irq1");
208 
209 	irq_enable(fixture.irq1);
210 	trigger_irq(fixture.irq1);
211 
212 	/* wait 5ms before checking the results */
213 	k_busy_wait(5000);
214 
215 	for (i = 0; i < TEST_VECTOR_SIZE; i++) {
216 		if (i == 1) {
217 			zassert_true(test_vector[i] == result_vector[i],
218 				     "wrong test_vector at %d: 0x%x vs 0x%x",
219 				     i, test_vector[i], result_vector[i]);
220 			continue;
221 		}
222 
223 		zassert_true(!test_vector[i],
224 			     "wrong test_vector value at %d: 0x%x vs 0x%x",
225 			     i, test_vector[i], result_vector[i]);
226 	}
227 
228 	irq_disable(fixture.irq1);
229 }
230 
231 ZTEST_SUITE(shared_irq_feature, NULL,
232 	    dynamic_shared_irq_suite_setup,
233 	    dynamic_shared_irq_suite_before,
234 	    dynamic_shared_irq_suite_after,
235 	    NULL);
236