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