1 /*
2  * Copyright Meta Platforms, Inc. and its affiliates.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/arch/cpu.h>
9 #include <cmsis_core.h>
10 #include <zephyr/sys/barrier.h>
11 
12 unsigned int sw_irq_number = (unsigned int)(-1);
13 static volatile bool custom_init_called;
14 static volatile bool custom_enable_called;
15 static volatile bool custom_disable_called;
16 static volatile bool custom_set_priority_called;
17 static volatile bool custom_eoi_called;
18 static volatile bool irq_handler_called;
19 
20 /* Define out custom SoC interrupt controller interface methods.
21  * These closely match the normal Cortex-M implementations.
22  */
23 
24 #define NUM_IRQS_PER_REG  32
25 #define REG_FROM_IRQ(irq) (irq / NUM_IRQS_PER_REG)
26 #define BIT_FROM_IRQ(irq) (irq % NUM_IRQS_PER_REG)
27 
z_soc_irq_init(void)28 void z_soc_irq_init(void)
29 {
30 	int irq = 0;
31 
32 	for (; irq < CONFIG_NUM_IRQS; irq++) {
33 		NVIC_SetPriority((IRQn_Type)irq, _IRQ_PRIO_OFFSET);
34 	}
35 
36 	custom_init_called = true;
37 }
38 
z_soc_irq_enable(unsigned int irq)39 void z_soc_irq_enable(unsigned int irq)
40 {
41 	if (irq == sw_irq_number) {
42 		custom_enable_called = true;
43 	}
44 	NVIC_EnableIRQ((IRQn_Type)irq);
45 }
46 
z_soc_irq_disable(unsigned int irq)47 void z_soc_irq_disable(unsigned int irq)
48 {
49 	if (irq == sw_irq_number) {
50 		custom_disable_called = true;
51 	}
52 	NVIC_DisableIRQ((IRQn_Type)irq);
53 }
54 
z_soc_irq_is_enabled(unsigned int irq)55 int z_soc_irq_is_enabled(unsigned int irq)
56 {
57 	return NVIC->ISER[REG_FROM_IRQ(irq)] & BIT(BIT_FROM_IRQ(irq));
58 }
59 
z_soc_irq_eoi(unsigned int irq)60 void z_soc_irq_eoi(unsigned int irq)
61 {
62 	if (irq == sw_irq_number) {
63 		custom_eoi_called = true;
64 	}
65 }
66 
z_soc_irq_get_active(void)67 inline __attribute__((always_inline)) unsigned int z_soc_irq_get_active(void)
68 {
69 	return __get_IPSR();
70 }
71 
z_soc_irq_priority_set(unsigned int irq,unsigned int prio,uint32_t flags)72 void z_soc_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags)
73 {
74 	if (irq == sw_irq_number) {
75 		custom_set_priority_called = true;
76 	}
77 
78 	if (IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS) && (flags & IRQ_ZERO_LATENCY)) {
79 		prio = _EXC_ZERO_LATENCY_IRQS_PRIO;
80 	} else {
81 		prio += _IRQ_PRIO_OFFSET;
82 	}
83 
84 	NVIC_SetPriority((IRQn_Type)irq, prio);
85 }
86 
arm_isr_handler(const void * args)87 void arm_isr_handler(const void *args)
88 {
89 	ARG_UNUSED(args);
90 
91 #if defined(CONFIG_CPU_CORTEX_M) && defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING)
92 	/* Clear Floating Point Status and Control Register (FPSCR),
93 	 * to prevent from having the interrupt line set to pending again,
94 	 * in case FPU IRQ is selected by the test as "Available IRQ line"
95 	 */
96 #if defined(CONFIG_ARMV8_1_M_MAINLINE)
97 	/*
98 	 * For ARMv8.1-M with FPU, the FPSCR[18:16] LTPSIZE field must be set
99 	 * to 0b100 for "Tail predication not applied" as it's reset value
100 	 */
101 	__set_FPSCR(4 << FPU_FPDSCR_LTPSIZE_Pos);
102 #else
103 	__set_FPSCR(0);
104 #endif
105 #endif
106 
107 	/* IRQ numbers are offset by 16 on Cortex-M. */
108 	unsigned int this_irq = z_soc_irq_get_active() - 16;
109 
110 	TC_PRINT("Got IRQ: %u\n", this_irq);
111 
112 	zassert_equal(this_irq, sw_irq_number, "Unexpected active IRQ\n");
113 	irq_handler_called = true;
114 }
115 
116 /**
117  * @brief Test custom interrupt controller handling with CONFIG_ARM_CUSTOM_INTERRUPT_CONTROLLER.
118  * @addtogroup kernel_interrupt_tests
119  * @ingroup all_tests
120  * @{
121  */
122 
ZTEST(arm_custom_interrupt,test_arm_interrupt)123 ZTEST(arm_custom_interrupt, test_arm_interrupt)
124 {
125 	zassert_true(custom_init_called, "Custom IRQ init not called\n");
126 
127 	/* Determine an NVIC IRQ line that is not currently in use. */
128 	int i;
129 
130 	for (i = CONFIG_NUM_IRQS - 1; i >= 0; i--) {
131 		if (NVIC_GetEnableIRQ(i) == 0) {
132 			/*
133 			 * Interrupts configured statically with IRQ_CONNECT(.)
134 			 * are automatically enabled. NVIC_GetEnableIRQ()
135 			 * returning false, here, implies that the IRQ line is
136 			 * either not implemented or it is not enabled, thus,
137 			 * currently not in use by Zephyr.
138 			 */
139 
140 			/* Set the NVIC line to pending. */
141 			NVIC_SetPendingIRQ(i);
142 
143 			if (NVIC_GetPendingIRQ(i)) {
144 				/* If the NVIC line is pending, it is
145 				 * guaranteed that it is implemented; clear the
146 				 * line.
147 				 */
148 				NVIC_ClearPendingIRQ(i);
149 
150 				if (!NVIC_GetPendingIRQ(i)) {
151 					/*
152 					 * If the NVIC line can be successfully
153 					 * un-pended, it is guaranteed that it
154 					 * can be used for software interrupt
155 					 * triggering.
156 					 */
157 					break;
158 				}
159 			}
160 		}
161 	}
162 
163 	zassert_true(i >= 0, "No available IRQ line to use in the test\n");
164 
165 	TC_PRINT("Available IRQ line: %u\n", i);
166 	sw_irq_number = i;
167 
168 	zassert_false(custom_set_priority_called, "Custom set priority flag set\n");
169 	arch_irq_connect_dynamic(sw_irq_number, 0 /* highest priority */, arm_isr_handler, NULL, 0);
170 	zassert_true(custom_set_priority_called, "Custom set priority not called\n");
171 
172 	NVIC_ClearPendingIRQ(i);
173 
174 	zassert_false(arch_irq_is_enabled(sw_irq_number), "SW IRQ already enabled\n");
175 	zassert_false(custom_enable_called, "Custom IRQ enable flag is set\n");
176 	irq_enable(sw_irq_number);
177 	zassert_true(custom_enable_called, "Custom IRQ enable not called\n");
178 	zassert_true(arch_irq_is_enabled(sw_irq_number), "SW IRQ is not enabled\n");
179 
180 	for (int j = 1; j <= 3; j++) {
181 		custom_eoi_called = false;
182 		irq_handler_called = false;
183 		custom_set_priority_called = false;
184 
185 		/* Set the dynamic IRQ to pending state. */
186 		NVIC_SetPendingIRQ(i);
187 
188 		/*
189 		 * Instruction barriers to make sure the NVIC IRQ is
190 		 * set to pending state before 'test_flag' is checked.
191 		 */
192 		barrier_dsync_fence_full();
193 		barrier_isync_fence_full();
194 
195 		/* Returning here implies the thread was not aborted. */
196 
197 		/* Confirm test flag is set by the ISR handler. */
198 		zassert_true(custom_eoi_called, "Custom EOI handler not called\n");
199 		zassert_true(irq_handler_called, "ISR handler not called\n");
200 	}
201 
202 	zassert_false(custom_disable_called, "Custom IRQ disable flag is set\n");
203 	irq_disable(sw_irq_number);
204 	zassert_true(custom_disable_called, "Custom IRQ disable not called\n");
205 }
206 
207 /**
208  * @}
209  */
210