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 
dynamic_shared_irq_suite_before(void * data)100 static void dynamic_shared_irq_suite_before(void *data)
101 {
102 	ARG_UNUSED(data);
103 
104 	arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority,
105 				 test_isr_0, 0, 0);
106 
107 	zassert_true(_sw_isr_table[fixture.irq1_table_idx].isr == test_isr_0,
108 		     "wrong _sw_isr_table ISR at irq1");
109 	zassert_true(!_sw_isr_table[fixture.irq1_table_idx].arg,
110 		     "wrong _sw_isr_table argument at irq1");
111 	zassert_true(!z_shared_sw_isr_table[fixture.irq1_table_idx].client_num,
112 		     "wrong client number at irq1");
113 
114 	arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority,
115 				 test_isr_1, (void *)1, 0);
116 
117 	zassert_true(_sw_isr_table[fixture.irq1_table_idx].isr == z_shared_isr,
118 		     "wrong _sw_isr_table ISR at irq1");
119 	zassert_true(_sw_isr_table[fixture.irq1_table_idx].arg ==
120 		     &z_shared_sw_isr_table[fixture.irq1_table_idx],
121 		     "wrong _sw_isr_table argument at irq1");
122 	zassert_true(z_shared_sw_isr_table[fixture.irq1_table_idx].client_num == 2,
123 		     "wrong client number at irq1");
124 
125 	zassert_true(client_exists_at_index(test_isr_0, 0, fixture.irq1_table_idx, 0),
126 		     "unexpected client data for irq1, index 0");
127 	zassert_true(client_exists_at_index(test_isr_1, (void *)1, fixture.irq1_table_idx, 1),
128 		     "unexpected client data for irq1, index 1");
129 
130 	arch_irq_connect_dynamic(fixture.irq2, fixture.irq_priority,
131 				 test_isr_2, (void *)2, 0);
132 
133 	zassert_true(_sw_isr_table[fixture.irq2_table_idx].isr == test_isr_2,
134 		     "wrong _sw_isr_table ISR at irq2");
135 	zassert_true(_sw_isr_table[fixture.irq2_table_idx].arg == (void *)2,
136 		     "wrong _sw_isr_table argument at irq2");
137 	zassert_true(!z_shared_sw_isr_table[fixture.irq2_table_idx].client_num,
138 		     "wrong client number at irq2");
139 
140 	reset_test_vector();
141 }
142 
143 /**
144  * @brief Test writing to a vector with a shared interrupt
145  *
146  * @ingroup kernel_interrupt_tests
147  *
148  * @details This tests if interrupts are dynamically shared successfully
149  * (i.e: multiple ISR/arg pairs are called whenever the interrupt
150  * they were registered for is triggered).
151  */
ZTEST(shared_irq_feature,test_dynamic_shared_irq_write)152 ZTEST(shared_irq_feature, test_dynamic_shared_irq_write)
153 {
154 	int i;
155 
156 	irq_enable(fixture.irq1);
157 	irq_enable(fixture.irq2);
158 
159 	trigger_irq(fixture.irq1);
160 	trigger_irq(fixture.irq2);
161 
162 	/* wait 5ms before checking the results */
163 	k_busy_wait(5000);
164 
165 	for (i = 0; i < TEST_VECTOR_SIZE; i++) {
166 		zassert_true(test_vector[i] == result_vector[i],
167 			     "wrong test_vector value at %d: 0x%x vs 0x%x",
168 			     i, test_vector[i], result_vector[i]);
169 	}
170 
171 	irq_disable(fixture.irq1);
172 	irq_disable(fixture.irq2);
173 }
174 
175 /**
176  * @brief Test writing to a vector after an ISR/arg disconnect.
177  *
178  * @ingroup kernel_interrupt_tests
179  *
180  * @details This tests if ISR/arg pairs are disconnected successfully
181  * and the interrupts are "unshared" whenever a single ISR/arg pair is
182  * left.
183  */
ZTEST(shared_irq_feature,test_dynamic_shared_irq_disconnect_write)184 ZTEST(shared_irq_feature, test_dynamic_shared_irq_disconnect_write)
185 {
186 	int i;
187 
188 	/* remove test_isr_0/NULL pair. After this statement we expect
189 	 * irq1 to be unshared.
190 	 */
191 	arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority,
192 				    test_isr_0, 0, 0);
193 
194 	zassert_true(_sw_isr_table[fixture.irq1_table_idx].isr == test_isr_1,
195 		     "wrong _sw_isr_table ISR at irq1");
196 	zassert_true(_sw_isr_table[fixture.irq1_table_idx].arg == (void *)1,
197 		     "wrong _sw_isr_table arg at irq1");
198 	zassert_true(!z_shared_sw_isr_table[fixture.irq1_table_idx].client_num,
199 		     "wrong client number at irq1");
200 
201 	irq_enable(fixture.irq1);
202 	trigger_irq(fixture.irq1);
203 
204 	/* wait 5ms before checking the results */
205 	k_busy_wait(5000);
206 
207 	for (i = 0; i < TEST_VECTOR_SIZE; i++) {
208 		if (i == 1) {
209 			zassert_true(test_vector[i] == result_vector[i],
210 				     "wrong test_vector at %d: 0x%x vs 0x%x",
211 				     i, test_vector[i], result_vector[i]);
212 			continue;
213 		}
214 
215 		zassert_true(!test_vector[i],
216 			     "wrong test_vector value at %d: 0x%x vs 0x%x",
217 			     i, test_vector[i], result_vector[i]);
218 	}
219 
220 	irq_disable(fixture.irq1);
221 }
222 
223 ZTEST_SUITE(shared_irq_feature, NULL,
224 	    dynamic_shared_irq_suite_setup,
225 	    dynamic_shared_irq_suite_before,
226 	    dynamic_shared_irq_suite_after,
227 	    NULL);
228