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