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