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 k_str_out_count(s) k_str_out((s), sizeof(s) - 1);
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 k_str_out_count("Triggering irq\n");
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 k_str_out_count("Triggering irq\n");
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), GICD_SGIR);
90 #else
91 uint64_t mpidr = GET_MPIDR();
92 uint8_t aff0 = MPIDR_AFFLVL(mpidr, 0);
93
94 gic_raise_sgi(irq, mpidr, BIT(aff0));
95 #endif
96 }
97
98 #elif defined(CONFIG_ARC)
trigger_irq(int irq)99 static inline void trigger_irq(int irq)
100 {
101 k_str_out_count("Triggering irq\n");
102 z_arc_v2_aux_reg_write(_ARC_V2_AUX_IRQ_HINT, irq);
103 }
104
105 #elif defined(CONFIG_X86)
106
107 #ifdef CONFIG_X2APIC
108 #include <zephyr/drivers/interrupt_controller/loapic.h>
109 #define VECTOR_MASK 0xFF
110 #else
111 #include <zephyr/arch/arch_interface.h>
112 #define LOAPIC_ICR_IPI_TEST 0x00004000U
113 #endif
114
115 /*
116 * We can emulate the interrupt by sending the IPI to
117 * core itself by the LOAPIC for x86 platform.
118 *
119 * In APIC mode, Write LOAPIC's ICR to trigger IPI,
120 * the LOAPIC_ICR_IPI_TEST 0x00004000U means:
121 * Delivery Mode: Fixed
122 * Destination Mode: Physical
123 * Level: Assert
124 * Trigger Mode: Edge
125 * Destination Shorthand: No Shorthand
126 * Destination: depends on cpu_id
127 *
128 * In X2APIC mode, this no longer works. We emulate the
129 * interrupt by writing the IA32_X2APIC_SELF_IPI MSR
130 * to send IPI to the core itself via LOAPIC also.
131 * According to SDM vol.3 chapter 10.12.11, the bit[7:0]
132 * for setting the vector is only needed.
133 */
trigger_irq(int vector)134 static inline void trigger_irq(int vector)
135 {
136 uint8_t i;
137
138 #ifdef CONFIG_X2APIC
139 x86_write_x2apic(LOAPIC_SELF_IPI, ((VECTOR_MASK & vector)));
140 #else
141
142 #ifdef CONFIG_SMP
143 int cpu_id = arch_curr_cpu()->id;
144 #else
145 int cpu_id = 0;
146 #endif
147 z_loapic_ipi(cpu_id, LOAPIC_ICR_IPI_TEST, vector);
148 #endif /* CONFIG_X2APIC */
149
150 /*
151 * add some nop operations here to cost some cycles to make sure
152 * the IPI interrupt is handled before do our check.
153 */
154 for (i = 0; i < 10; i++) {
155 arch_nop();
156 }
157 }
158
159 #elif defined(CONFIG_ARCH_POSIX)
160 #include <zephyr/arch/posix/posix_soc_if.h>
161
trigger_irq(int irq)162 static inline void trigger_irq(int irq)
163 {
164 posix_sw_set_pending_IRQ(irq);
165 }
166
167 #elif defined(CONFIG_RISCV)
168 #if defined(CONFIG_HAZARD3_INTC)
169 #include <hardware/irq.h>
170 #endif
171
172 #if defined(CONFIG_CLIC) || defined(CONFIG_NRFX_CLIC)
173 void riscv_clic_irq_set_pending(uint32_t irq);
trigger_irq(int irq)174 static inline void trigger_irq(int irq)
175 {
176 riscv_clic_irq_set_pending(irq);
177 }
178 #elif defined(CONFIG_HAZARD3_INTC)
trigger_irq(int irq)179 static inline void trigger_irq(int irq)
180 {
181 irq_set_pending(irq);
182 }
183 #else
trigger_irq(int irq)184 static inline void trigger_irq(int irq)
185 {
186 uint32_t mip;
187
188 __asm__ volatile("csrrs %0, mip, %1\n" : "=r"(mip) : "r"(1 << irq));
189 }
190 #endif
191 #elif defined(CONFIG_XTENSA)
trigger_irq(int irq)192 static inline void trigger_irq(int irq)
193 {
194 #if XCHAL_NUM_INTERRUPTS > 32
195 switch (irq >> 5) {
196 case 0:
197 z_xt_set_intset(1 << (irq & 0x1f));
198 break;
199 case 1:
200 z_xt_set_intset1(1 << (irq & 0x1f));
201 break;
202 #if XCHAL_NUM_INTERRUPTS > 64
203 case 2:
204 z_xt_set_intset2(1 << (irq & 0x1f));
205 break;
206 #endif
207 #if XCHAL_NUM_INTERRUPTS > 96
208 case 3:
209 z_xt_set_intset3(1 << (irq & 0x1f));
210 break;
211 #endif
212 default:
213 break;
214 }
215 #else
216 z_xt_set_intset(1 << irq);
217 #endif
218 }
219
220 #elif defined(CONFIG_SPARC)
221 extern void z_sparc_enter_irq(int);
222
trigger_irq(int irq)223 static inline void trigger_irq(int irq)
224 {
225 z_sparc_enter_irq(irq);
226 }
227
228 #elif defined(CONFIG_MIPS)
229 extern void z_mips_enter_irq(int);
230
trigger_irq(int irq)231 static inline void trigger_irq(int irq)
232 {
233 z_mips_enter_irq(irq);
234 }
235
236 #elif defined(CONFIG_CPU_CORTEX_R5) && defined(CONFIG_TI_VIM)
237
238 extern void z_vim_arm_enter_irq(int);
239
trigger_irq(int irq)240 static inline void trigger_irq(int irq)
241 {
242 z_vim_arm_enter_irq(irq);
243 }
244
245 #elif defined(CONFIG_RX)
246 #define IR_BASE_ADDRESS DT_REG_ADDR_BY_NAME(DT_NODELABEL(icu), IR)
trigger_irq(int irq)247 static inline void trigger_irq(int irq)
248 {
249 __ASSERT(irq < CONFIG_NUM_IRQS, "attempting to trigger invalid IRQ (%u)", irq);
250 __ASSERT(irq >= CONFIG_GEN_IRQ_START_VECTOR, "attempting to trigger reserved IRQ (%u)",
251 irq);
252 _sw_isr_table[irq - CONFIG_GEN_IRQ_START_VECTOR].isr(
253 _sw_isr_table[irq - CONFIG_GEN_IRQ_START_VECTOR].arg);
254 }
255
256 #else
257 #define NO_TRIGGER_FROM_SW
258 #endif
259
260 #endif /* INTERRUPT_UTIL_H_ */
261