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