1 /*
2  * Copyright (c) 2021 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/ztest.h>
8 #include <zephyr/interrupt_util.h>
9 #include <zephyr/irq.h>
10 
11 /*
12  * Other arch has already been tested in testcase of gen_isr_table,
13  * so we only test x86 series here.
14  */
15 
16 #define TEST_IRQ_LINE_1	27
17 #define TEST_IRQ_LINE_2	28
18 
19 #define TEST_IRQ_PRIO	2
20 
21 
22 volatile uint32_t reg_int_executed[2];
23 
isr_comm(const void * param)24 void isr_comm(const void *param)
25 {
26 	int choice = POINTER_TO_INT(param);
27 
28 	switch (choice) {
29 	case TEST_IRQ_LINE_1:
30 		reg_int_executed[0]++;
31 	break;
32 	case TEST_IRQ_LINE_2:
33 		reg_int_executed[1]++;
34 	break;
35 
36 	default:
37 	break;
38 	}
39 }
40 
41 /**
42  * @brief Test regular interrupt
43  *
44  * @details Validate regular interrupt works as expected.
45  * - Register two regular interrupt at build time.
46  * - Trigger interrupt and check if isr handler has executed or not.
47  * - Also check irq_enable and irq_disable works.
48  *
49  * @ingroup kernel_interrupt_tests
50  *
51  * @see IRQ_CONNECT(), irq_enable(), irq_disable(),
52  * irq_unlock(),
53  */
ZTEST(interrupt_feature,test_isr_regular)54 ZTEST(interrupt_feature, test_isr_regular)
55 {
56 	int trig_vec1, trig_vec2;
57 
58 	IRQ_CONNECT(TEST_IRQ_LINE_1, TEST_IRQ_PRIO, isr_comm, (void *)TEST_IRQ_LINE_1, 0);
59 	IRQ_CONNECT(TEST_IRQ_LINE_2, TEST_IRQ_PRIO, isr_comm, (void *)TEST_IRQ_LINE_2, 0);
60 
61 	trig_vec1 = Z_IRQ_TO_INTERRUPT_VECTOR(TEST_IRQ_LINE_1);
62 	trig_vec2 = Z_IRQ_TO_INTERRUPT_VECTOR(TEST_IRQ_LINE_2);
63 
64 	TC_PRINT("irq(%d)=vector(%d)\n", TEST_IRQ_LINE_1, trig_vec1);
65 	TC_PRINT("irq(%d)=vector(%d)\n", TEST_IRQ_LINE_2, trig_vec2);
66 
67 	irq_enable(TEST_IRQ_LINE_1);
68 	irq_enable(TEST_IRQ_LINE_2);
69 
70 	trigger_irq(trig_vec1);
71 
72 	zassert_true(reg_int_executed[0] == 1 &&
73 			reg_int_executed[1] == 0,
74 			"ISR1 should execute");
75 
76 	trigger_irq(trig_vec2);
77 
78 	zassert_true(reg_int_executed[0] == 1 &&
79 			reg_int_executed[1] == 1,
80 			"Both ISR should execute");
81 
82 	unsigned int key = irq_lock();
83 
84 	/* trigger under irq locked */
85 	trigger_irq(trig_vec1);
86 	trigger_irq(trig_vec2);
87 
88 	zassert_true(reg_int_executed[0] == 1 &&
89 			reg_int_executed[1] == 1,
90 			"Both ISR should not execute again(%d)(%d)",
91 			reg_int_executed[0], reg_int_executed[1]);
92 
93 	irq_unlock(key);
94 
95 #ifdef CONFIG_BOARD_QEMU_X86
96 	/* QEMU seems to have an issue in that interrupts seem to post on
97 	 * the instruction after the 'sti' that is part of irq_unlock().  This
98 	 * can cause an issue if the instruction after the 'sti' ends up looking
99 	 * at the state that the ISR is suppose to update.  This has been shown
100 	 * to happen when building this test for LLVM.
101 	 *
102 	 * Adding a nop instruction allows QEMU to post the ISR before any state
103 	 * gets examined as a workaround.
104 	 *
105 	 * See GitHub issue zephyrproject-rtos/sdk-ng#629 for the qemu bug.
106 	 */
107 	arch_nop();
108 #endif
109 
110 	/* interrupt serve after irq unlocked */
111 	zassert_true(reg_int_executed[0] == 2 &&
112 			reg_int_executed[1] == 2,
113 			"Both ISR should execute again(%d)(%d)",
114 			reg_int_executed[0], reg_int_executed[1]);
115 
116 	/* trigger after irq unlocked */
117 	trigger_irq(trig_vec1);
118 	trigger_irq(trig_vec2);
119 
120 	zassert_true(reg_int_executed[0] == 3 &&
121 			reg_int_executed[1] == 3,
122 			"Both ISR should execute again(%d)(%d)",
123 			reg_int_executed[0], reg_int_executed[1]);
124 }
125