1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #ifndef INTERRUPT_UTIL_H_
8 #define INTERRUPT_UTIL_H_
9
10 #define MS_TO_US(ms) (ms * USEC_PER_MSEC)
11
12 #if defined(CONFIG_CPU_CORTEX_M)
13 #include <cmsis_core.h>
14
get_available_nvic_line(uint32_t initial_offset)15 static inline uint32_t get_available_nvic_line(uint32_t initial_offset)
16 {
17 int i;
18
19 for (i = initial_offset - 1; i >= 0; i--) {
20
21 if (NVIC_GetEnableIRQ(i) == 0) {
22 /*
23 * Interrupts configured statically with IRQ_CONNECT(.)
24 * are automatically enabled. NVIC_GetEnableIRQ()
25 * returning false, here, implies that the IRQ line is
26 * either not implemented or it is not enabled, thus,
27 * currently not in use by Zephyr.
28 */
29
30 /* Set the NVIC line to pending. */
31 NVIC_SetPendingIRQ(i);
32
33 if (NVIC_GetPendingIRQ(i)) {
34 /*
35 * If the NVIC line is pending, it is
36 * guaranteed that it is implemented; clear the
37 * line.
38 */
39 NVIC_ClearPendingIRQ(i);
40
41 if (!NVIC_GetPendingIRQ(i)) {
42 /*
43 * If the NVIC line can be successfully
44 * un-pended, it is guaranteed that it
45 * can be used for software interrupt
46 * triggering. Return the NVIC line
47 * number.
48 */
49 break;
50 }
51 }
52 }
53 }
54
55 zassert_true(i >= 0, "No available IRQ line\n");
56
57 return i;
58 }
59
trigger_irq(int irq)60 static inline void trigger_irq(int irq)
61 {
62 printk("Triggering irq : %d\n", irq);
63 #if defined(CONFIG_SOC_TI_LM3S6965_QEMU) || defined(CONFIG_CPU_CORTEX_M0) \
64 || defined(CONFIG_CPU_CORTEX_M0PLUS) || defined(CONFIG_CPU_CORTEX_M1)\
65 || defined(CONFIG_ARMV6_M_ARMV8_M_BASELINE)
66 /* QEMU does not simulate the STIR register: this is a workaround */
67 NVIC_SetPendingIRQ(irq);
68 #else
69 NVIC->STIR = irq;
70 #endif
71 }
72
73 #elif defined(CONFIG_GIC)
74 #include <zephyr/drivers/interrupt_controller/gic.h>
75 #include <zephyr/dt-bindings/interrupt-controller/arm-gic.h>
76
trigger_irq(int irq)77 static inline void trigger_irq(int irq)
78 {
79 printk("Triggering irq : %d\n", irq);
80
81 /* Ensure that the specified IRQ number is a valid SGI interrupt ID */
82 zassert_true(irq <= 15, "%u is not a valid SGI interrupt ID", irq);
83
84 /*
85 * Generate a software generated interrupt and forward it to the
86 * requesting CPU.
87 */
88 #if CONFIG_GIC_VER <= 2
89 sys_write32(GICD_SGIR_TGTFILT_REQONLY | GICD_SGIR_SGIINTID(irq),
90 GICD_SGIR);
91 #else
92 uint64_t mpidr = GET_MPIDR();
93 uint8_t aff0 = MPIDR_AFFLVL(mpidr, 0);
94
95 gic_raise_sgi(irq, mpidr, BIT(aff0));
96 #endif
97 }
98
99 #elif defined(CONFIG_ARC)
trigger_irq(int irq)100 static inline void trigger_irq(int irq)
101 {
102 printk("Triggering irq : %d\n", irq);
103 z_arc_v2_aux_reg_write(_ARC_V2_AUX_IRQ_HINT, irq);
104 }
105
106 #elif defined(CONFIG_X86)
107
108 #ifdef CONFIG_X2APIC
109 #include <zephyr/drivers/interrupt_controller/loapic.h>
110 #define VECTOR_MASK 0xFF
111 #else
112 #include <zephyr/sys/arch_interface.h>
113 #define LOAPIC_ICR_IPI_TEST 0x00004000U
114 #endif
115
116 /*
117 * We can emulate the interrupt by sending the IPI to
118 * core itself by the LOAPIC for x86 platform.
119 *
120 * In APIC mode, Write LOAPIC's ICR to trigger IPI,
121 * the LOAPIC_ICR_IPI_TEST 0x00004000U means:
122 * Delivery Mode: Fixed
123 * Destination Mode: Physical
124 * Level: Assert
125 * Trigger Mode: Edge
126 * Destination Shorthand: No Shorthand
127 * Destination: depends on cpu_id
128 *
129 * In X2APIC mode, this no longer works. We emulate the
130 * interrupt by writing the IA32_X2APIC_SELF_IPI MSR
131 * to send IPI to the core itself via LOAPIC also.
132 * According to SDM vol.3 chapter 10.12.11, the bit[7:0]
133 * for setting the vector is only needed.
134 */
trigger_irq(int vector)135 static inline void trigger_irq(int vector)
136 {
137 uint8_t i;
138
139 #ifdef CONFIG_X2APIC
140 x86_write_x2apic(LOAPIC_SELF_IPI, ((VECTOR_MASK & vector)));
141 #else
142
143 #ifdef CONFIG_SMP
144 int cpu_id = arch_curr_cpu()->id;
145 #else
146 int cpu_id = 0;
147 #endif
148 z_loapic_ipi(cpu_id, LOAPIC_ICR_IPI_TEST, vector);
149 #endif /* CONFIG_X2APIC */
150
151 /*
152 * add some nop operations here to cost some cycles to make sure
153 * the IPI interrupt is handled before do our check.
154 */
155 for (i = 0; i < 10; i++) {
156 arch_nop();
157 }
158 }
159
160 #elif defined(CONFIG_ARCH_POSIX)
161 #include <zephyr/arch/posix/posix_soc_if.h>
162
trigger_irq(int irq)163 static inline void trigger_irq(int irq)
164 {
165 posix_sw_set_pending_IRQ(irq);
166 }
167
168 #elif defined(CONFIG_RISCV)
trigger_irq(int irq)169 static inline void trigger_irq(int irq)
170 {
171 uint32_t mip;
172
173 __asm__ volatile ("csrrs %0, mip, %1\n"
174 : "=r" (mip)
175 : "r" (1 << irq));
176 }
177
178 #elif defined(CONFIG_XTENSA)
trigger_irq(int irq)179 static inline void trigger_irq(int irq)
180 {
181 z_xt_set_intset(BIT((unsigned int)irq));
182 }
183
184 #elif defined(CONFIG_SPARC)
185 extern void z_sparc_enter_irq(int);
186
trigger_irq(int irq)187 static inline void trigger_irq(int irq)
188 {
189 z_sparc_enter_irq(irq);
190 }
191
192 #elif defined(CONFIG_MIPS)
193 extern void z_mips_enter_irq(int);
194
trigger_irq(int irq)195 static inline void trigger_irq(int irq)
196 {
197 z_mips_enter_irq(irq);
198 }
199
200 #elif defined(CONFIG_CPU_CORTEX_R5) && defined(CONFIG_VIM)
201
202 extern void z_vim_arm_enter_irq(int);
203
trigger_irq(int irq)204 static inline void trigger_irq(int irq)
205 {
206 z_vim_arm_enter_irq(irq);
207 }
208
209 #else
210 /* So far, Nios II does not support this */
211 #define NO_TRIGGER_FROM_SW
212 #endif
213
214 #endif /* INTERRUPT_UTIL_H_ */
215