1 /*
2  * Copyright (c) 2014 Wind River Systems, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief ARCv2 interrupt management
10  *
11  *
12  * Interrupt management:
13  *
14  * - enabling/disabling
15  *
16  * An IRQ number passed to the @a irq parameters found in this file is a
17  * number from 16 to last IRQ number on the platform.
18  */
19 
20 #include <zephyr/kernel.h>
21 #include <zephyr/arch/cpu.h>
22 #include <zephyr/sys/__assert.h>
23 #include <zephyr/toolchain.h>
24 #include <zephyr/linker/sections.h>
25 #include <zephyr/sw_isr_table.h>
26 #include <zephyr/irq.h>
27 #include <zephyr/sys/printk.h>
28 
29 
30 /*
31  * storage space for the interrupt stack of fast_irq
32  */
33 #if defined(CONFIG_ARC_FIRQ_STACK)
34 #if defined(CONFIG_SMP)
35 K_KERNEL_STACK_ARRAY_DEFINE(_firq_interrupt_stack, CONFIG_MP_MAX_NUM_CPUS,
36 			    CONFIG_ARC_FIRQ_STACK_SIZE);
37 #else
38 K_KERNEL_STACK_DEFINE(_firq_interrupt_stack, CONFIG_ARC_FIRQ_STACK_SIZE);
39 #endif
40 
41 /**
42  * @brief Set the stack pointer for firq handling
43  */
z_arc_firq_stack_set(void)44 void z_arc_firq_stack_set(void)
45 {
46 #ifdef CONFIG_SMP
47 	char *firq_sp = Z_KERNEL_STACK_BUFFER(
48 		  _firq_interrupt_stack[z_arc_v2_core_id()]) +
49 		  CONFIG_ARC_FIRQ_STACK_SIZE;
50 #else
51 	char *firq_sp = Z_KERNEL_STACK_BUFFER(_firq_interrupt_stack) +
52 		  CONFIG_ARC_FIRQ_STACK_SIZE;
53 #endif
54 
55 /* the z_arc_firq_stack_set must be called when irq diasbled, as
56  * it can be called not only in the init phase but also other places
57  */
58 	unsigned int key = arch_irq_lock();
59 
60 	__asm__ volatile (
61 /* only ilink will not be banked, so use ilink as channel
62  * between 2 banks
63  */
64 	"mov %%ilink, %0\n\t"
65 	"lr %0, [%1]\n\t"
66 	"or %0, %0, %2\n\t"
67 	"kflag %0\n\t"
68 	"mov %%sp, %%ilink\n\t"
69 /* switch back to bank0, use ilink to avoid the pollution of
70  * bank1's gp regs.
71  */
72 	"lr %%ilink, [%1]\n\t"
73 	"and %%ilink, %%ilink, %3\n\t"
74 	"kflag %%ilink\n\t"
75 	:
76 	: "r"(firq_sp), "i"(_ARC_V2_STATUS32),
77 	  "i"(_ARC_V2_STATUS32_RB(1)),
78 	  "i"(~_ARC_V2_STATUS32_RB(7))
79 	);
80 	arch_irq_unlock(key);
81 }
82 #endif
83 
84 /*
85  * ARC CPU interrupt controllers hierarchy.
86  *
87  * Single-core (UP) case:
88  *
89  *   --------------------------
90  *   |  CPU core 0            |
91  *   --------------------------
92  *   |  core 0 (private)      |
93  *   |  interrupt controller  |
94  *   --------------------------
95  *               |
96  *      [internal interrupts]
97  *      [external interrupts]
98  *
99  *
100  * Multi-core (SMP) case:
101  *
102  *   --------------------------               --------------------------
103  *   |  CPU core 0            |               |  CPU core 1            |
104  *   --------------------------               --------------------------
105  *   |  core 0 (private)      |               |  core 1 (private)      |
106  *   |  interrupt controller  |               |  interrupt controller  |
107  *   --------------------------               --------------------------
108  *     |    |      |                                |     |      |
109  *     |    | [core 0 private internal interrupts]  |     |   [core 1 private internal interrupts]
110  *     |    |                                       |     |
111  *     |    |                                       |     |
112  *     |   -------------------------------------------    |
113  *     |   |     IDU (Interrupt Distribution Unit)   |    |
114  *     |   -------------------------------------------    |
115  *     |                       |                          |
116  *     |          [common (shared) interrupts]            |
117  *     |                                                  |
118  *     |                                                  |
119  *   [core 0 private external interrupts]               [core 1 private external interrupts]
120  *
121  *
122  *
123  *  The interrupts are grouped in HW in the same order - firstly internal interrupts
124  *  (with lowest line numbers in IVT), than common interrupts (if present), than external
125  *  interrupts (with highest line numbers in IVT).
126  *
127  *  NOTE: in case of SMP system we currently support in Zephyr only private internal and common
128  *  interrupts, so the core-private external interrupts are currently not supported for SMP.
129  */
130 
131 /**
132  * @brief Enable an interrupt line
133  *
134  * Clear possible pending interrupts on the line, and enable the interrupt
135  * line. After this call, the CPU will receive interrupts for the specified
136  * @a irq.
137  */
138 void arch_irq_enable(unsigned int irq);
139 
140 /**
141  * @brief Disable an interrupt line
142  *
143  * Disable an interrupt line. After this call, the CPU will stop receiving
144  * interrupts for the specified @a irq.
145  */
146 void arch_irq_disable(unsigned int irq);
147 
148 /**
149  * @brief Return IRQ enable state
150  *
151  * @param irq IRQ line
152  * @return interrupt enable state, true or false
153  */
154 int arch_irq_is_enabled(unsigned int irq);
155 
156 #ifdef CONFIG_ARC_CONNECT
157 
158 #define IRQ_NUM_TO_IDU_NUM(id)		((id) - ARC_CONNECT_IDU_IRQ_START)
159 #define IRQ_IS_COMMON(id)		((id) >= ARC_CONNECT_IDU_IRQ_START)
160 
arch_irq_enable(unsigned int irq)161 void arch_irq_enable(unsigned int irq)
162 {
163 	if (IRQ_IS_COMMON(irq)) {
164 		z_arc_connect_idu_set_mask(IRQ_NUM_TO_IDU_NUM(irq), 0x0);
165 	} else {
166 		z_arc_v2_irq_unit_int_enable(irq);
167 	}
168 }
169 
arch_irq_disable(unsigned int irq)170 void arch_irq_disable(unsigned int irq)
171 {
172 	if (IRQ_IS_COMMON(irq)) {
173 		z_arc_connect_idu_set_mask(IRQ_NUM_TO_IDU_NUM(irq), 0x1);
174 	} else {
175 		z_arc_v2_irq_unit_int_disable(irq);
176 	}
177 }
178 
arch_irq_is_enabled(unsigned int irq)179 int arch_irq_is_enabled(unsigned int irq)
180 {
181 	if (IRQ_IS_COMMON(irq)) {
182 		return !z_arc_connect_idu_read_mask(IRQ_NUM_TO_IDU_NUM(irq));
183 	} else {
184 		return z_arc_v2_irq_unit_int_enabled(irq);
185 	}
186 }
187 #else
arch_irq_enable(unsigned int irq)188 void arch_irq_enable(unsigned int irq)
189 {
190 	z_arc_v2_irq_unit_int_enable(irq);
191 }
192 
arch_irq_disable(unsigned int irq)193 void arch_irq_disable(unsigned int irq)
194 {
195 	z_arc_v2_irq_unit_int_disable(irq);
196 }
197 
arch_irq_is_enabled(unsigned int irq)198 int arch_irq_is_enabled(unsigned int irq)
199 {
200 	return z_arc_v2_irq_unit_int_enabled(irq);
201 }
202 #endif /* CONFIG_ARC_CONNECT */
203 
204 /**
205  * @internal
206  *
207  * @brief Set an interrupt's priority
208  *
209  * Lower values take priority over higher values. Special case priorities are
210  * expressed via mutually exclusive flags.
211 
212  * The priority is verified if ASSERT_ON is enabled; max priority level
213  * depends on CONFIG_NUM_IRQ_PRIO_LEVELS.
214  */
215 
z_irq_priority_set(unsigned int irq,unsigned int prio,uint32_t flags)216 void z_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags)
217 {
218 	ARG_UNUSED(flags);
219 
220 	__ASSERT(prio < CONFIG_NUM_IRQ_PRIO_LEVELS,
221 		 "invalid priority %d for irq %d", prio, irq);
222 /* 0 -> CONFIG_NUM_IRQ_PRIO_LEVELS allocated to secure world
223  * left prio levels allocated to normal world
224  */
225 #if defined(CONFIG_ARC_SECURE_FIRMWARE)
226 	prio = prio < ARC_N_IRQ_START_LEVEL ?
227 		prio : (ARC_N_IRQ_START_LEVEL - 1);
228 #elif defined(CONFIG_ARC_NORMAL_FIRMWARE)
229 	prio = prio < ARC_N_IRQ_START_LEVEL ?
230 		 ARC_N_IRQ_START_LEVEL : prio;
231 #endif
232 	z_arc_v2_irq_unit_prio_set(irq, prio);
233 }
234 
235 /**
236  * @brief Spurious interrupt handler
237  *
238  * Installed in all dynamic interrupt slots at boot time. Throws an error if
239  * called.
240  */
241 
z_irq_spurious(const void * unused)242 void z_irq_spurious(const void *unused)
243 {
244 	ARG_UNUSED(unused);
245 	z_fatal_error(K_ERR_SPURIOUS_IRQ, NULL);
246 }
247 
248 #ifdef CONFIG_DYNAMIC_INTERRUPTS
arch_irq_connect_dynamic(unsigned int irq,unsigned int priority,void (* routine)(const void * parameter),const void * parameter,uint32_t flags)249 int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority,
250 			     void (*routine)(const void *parameter),
251 			     const void *parameter, uint32_t flags)
252 {
253 	z_isr_install(irq, routine, parameter);
254 	z_irq_priority_set(irq, priority, flags);
255 	return irq;
256 }
257 #endif /* CONFIG_DYNAMIC_INTERRUPTS */
258