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