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