1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * Interrupt management.
9 */
10
11 #include <fwk_arch.h>
12 #include <fwk_assert.h>
13 #include <fwk_interrupt.h>
14 #include <fwk_macros.h>
15 #include <fwk_mm.h>
16 #include <fwk_noreturn.h>
17 #include <fwk_status.h>
18
19 #include <arch_exceptions.h>
20
21 #include <fmw_cmsis.h>
22
23 #include <limits.h>
24 #include <stdbool.h>
25 #include <stddef.h>
26 #include <stdint.h>
27 #include <string.h>
28
29 /* We use short enums, so avoid truncation of larger unsigned IRQ numbers */
30 #define IRQN_TYPE_MAX \
31 ((uint64_t)(((UINT64_C(1) << (sizeof(IRQn_Type) * CHAR_BIT)) - 1U) / 2U))
32
33 static uint32_t isr_count;
34 static uint32_t irq_count;
35
36 static_assert(
37 UINT32_MAX >= IRQN_TYPE_MAX,
38 "`uint32_t` cannot hold all possible IRQ numbers");
39
40 static_assert(
41 UINT_MAX >= IRQN_TYPE_MAX,
42 "`unsigned int` cannot hold all possible IRQ numbers");
43
44 static_assert(
45 sizeof(IRQn_Type) >= sizeof(int16_t),
46 "`IRQn_Type` cannot hold all possible IRQ numbers");
47
48 /*
49 * For interrupts with parameters, their entry in the vector table points to a
50 * global handler that calls a registered function in the callback table with a
51 * corresponding parameter. Entries in the vector table for interrupts without
52 * parameters point directly to the handler functions.
53 *
54 * Entry indices are offset by -1 relative to their interrupt numbers, as no
55 * interrupt may have an interrupt number of zero.
56 */
57 struct irq_callback {
58 void (*func)(uintptr_t param);
59 uintptr_t param;
60 };
61
62 static struct irq_callback *callback;
63
irq_global(void)64 static void irq_global(void)
65 {
66 struct irq_callback *entry = &callback[__get_IPSR() - 1];
67
68 entry->func(entry->param);
69 }
70
global_enable(void)71 static int global_enable(void)
72 {
73 __enable_irq();
74
75 return FWK_SUCCESS;
76 }
77
global_disable(void)78 static int global_disable(void)
79 {
80 __disable_irq();
81
82 return FWK_SUCCESS;
83 }
84
is_enabled(unsigned int interrupt,bool * enabled)85 static int is_enabled(unsigned int interrupt, bool *enabled)
86 {
87 if (interrupt >= irq_count) {
88 return FWK_E_PARAM;
89 }
90
91 *enabled = NVIC_GetEnableIRQ((enum IRQn)interrupt) != 0;
92
93 return FWK_SUCCESS;
94 }
95
enable(unsigned int interrupt)96 static int enable(unsigned int interrupt)
97 {
98 if (interrupt >= irq_count) {
99 return FWK_E_PARAM;
100 }
101
102 NVIC_EnableIRQ((enum IRQn)interrupt);
103
104 return FWK_SUCCESS;
105 }
106
disable(unsigned int interrupt)107 static int disable(unsigned int interrupt)
108 {
109 if (interrupt >= irq_count) {
110 return FWK_E_PARAM;
111 }
112
113 NVIC_DisableIRQ((enum IRQn)interrupt);
114
115 return FWK_SUCCESS;
116 }
117
is_pending(unsigned int interrupt,bool * pending)118 static int is_pending(unsigned int interrupt, bool *pending)
119 {
120 if (interrupt >= irq_count) {
121 return FWK_E_PARAM;
122 }
123
124 *pending = NVIC_GetPendingIRQ((enum IRQn)interrupt) != 0;
125
126 return FWK_SUCCESS;
127 }
128
set_pending(unsigned int interrupt)129 static int set_pending(unsigned int interrupt)
130 {
131 if (interrupt >= irq_count) {
132 return FWK_E_PARAM;
133 }
134
135 NVIC_SetPendingIRQ((enum IRQn)interrupt);
136
137 return FWK_SUCCESS;
138 }
139
clear_pending(unsigned int interrupt)140 static int clear_pending(unsigned int interrupt)
141 {
142 if (interrupt >= irq_count) {
143 return FWK_E_PARAM;
144 }
145
146 NVIC_ClearPendingIRQ((enum IRQn)interrupt);
147
148 return FWK_SUCCESS;
149 }
150
set_isr_irq(unsigned int interrupt,void (* isr)(void))151 static int set_isr_irq(unsigned int interrupt, void (*isr)(void))
152 {
153 if (interrupt >= irq_count) {
154 return FWK_E_PARAM;
155 }
156
157 NVIC_SetVector((enum IRQn)interrupt, (uint32_t)isr);
158
159 return FWK_SUCCESS;
160 }
161
set_isr_irq_param(unsigned int interrupt,void (* isr)(uintptr_t param),uintptr_t parameter)162 static int set_isr_irq_param(
163 unsigned int interrupt,
164 void (*isr)(uintptr_t param),
165 uintptr_t parameter)
166 {
167 struct irq_callback *entry;
168 if (interrupt >= irq_count) {
169 return FWK_E_PARAM;
170 }
171
172 entry = &callback[NVIC_USER_IRQ_OFFSET + interrupt - 1];
173 entry->func = isr;
174 entry->param = parameter;
175
176 NVIC_SetVector((enum IRQn)interrupt, (uint32_t)irq_global);
177
178 return FWK_SUCCESS;
179 }
180
set_isr_nmi(void (* isr)(void))181 static int set_isr_nmi(void (*isr)(void))
182 {
183 NVIC_SetVector(NonMaskableInt_IRQn, (uint32_t)isr);
184
185 return FWK_SUCCESS;
186 }
187
set_isr_nmi_param(void (* isr)(uintptr_t param),uintptr_t parameter)188 static int set_isr_nmi_param(void (*isr)(uintptr_t param), uintptr_t parameter)
189 {
190 struct irq_callback *entry;
191
192 entry = &callback[NVIC_USER_IRQ_OFFSET + (int)NonMaskableInt_IRQn - 1];
193 entry->func = isr;
194 entry->param = parameter;
195
196 NVIC_SetVector(NonMaskableInt_IRQn, (uint32_t)irq_global);
197
198 return FWK_SUCCESS;
199 }
200
set_isr_fault(void (* isr)(void))201 static int set_isr_fault(void (*isr)(void))
202 {
203 NVIC_SetVector(HardFault_IRQn, (uint32_t)isr);
204 NVIC_SetVector(MemoryManagement_IRQn, (uint32_t)isr);
205 NVIC_SetVector(BusFault_IRQn, (uint32_t)isr);
206 NVIC_SetVector(UsageFault_IRQn, (uint32_t)isr);
207
208 return FWK_SUCCESS;
209 }
210
get_current(unsigned int * interrupt)211 static int get_current(unsigned int *interrupt)
212 {
213 *interrupt = __get_IPSR();
214
215 /* Not an interrupt */
216 if (*interrupt == 0) {
217 return FWK_E_STATE;
218 }
219
220 if (*interrupt == (NVIC_USER_IRQ_OFFSET + (int)NonMaskableInt_IRQn)) {
221 *interrupt = FWK_INTERRUPT_NMI;
222 } else if (*interrupt < NVIC_USER_IRQ_OFFSET) {
223 *interrupt = FWK_INTERRUPT_EXCEPTION;
224 } else {
225 *interrupt -= NVIC_USER_IRQ_OFFSET;
226 }
227
228 return FWK_SUCCESS;
229 }
230
is_interrupt_context(void)231 static bool is_interrupt_context(void)
232 {
233 /* Not an interrupt */
234 if (__get_IPSR() == 0) {
235 return false;
236 }
237
238 return true;
239 }
240
241 static const struct fwk_arch_interrupt_driver arch_nvic_driver = {
242 .global_enable = global_enable,
243 .global_disable = global_disable,
244 .is_enabled = is_enabled,
245 .enable = enable,
246 .disable = disable,
247 .is_pending = is_pending,
248 .set_pending = set_pending,
249 .clear_pending = clear_pending,
250 .set_isr_irq = set_isr_irq,
251 .set_isr_irq_param = set_isr_irq_param,
252 .set_isr_nmi = set_isr_nmi,
253 .set_isr_nmi_param = set_isr_nmi_param,
254 .set_isr_fault = set_isr_fault,
255 .get_current = get_current,
256 .is_interrupt_context = is_interrupt_context,
257 };
258
irq_invalid(void)259 static void irq_invalid(void)
260 {
261 (void)disable(__get_IPSR());
262 }
263
arch_nvic_init(const struct fwk_arch_interrupt_driver ** driver)264 int arch_nvic_init(const struct fwk_arch_interrupt_driver **driver)
265 {
266 uint32_t ictr_intlinesnum;
267 uint32_t align_entries;
268 uint32_t align_word;
269 uint32_t *vector;
270 uint32_t irq;
271
272 if (driver == NULL) {
273 return FWK_E_PARAM;
274 }
275
276 /* Find the number of interrupt lines implemented in hardware */
277 ictr_intlinesnum = SCnSCB->ICTR & SCnSCB_ICTR_INTLINESNUM_Msk;
278 irq_count = (ictr_intlinesnum + 1) * 32;
279 isr_count = NVIC_USER_IRQ_OFFSET + irq_count;
280
281 /*
282 * irq_count holds the amount of IRQ meanwhile IRQN_TYPE_MAX holds the
283 * maximum IRQ number (for type size), that is the reason there is a +1 in
284 * the comparison.
285 */
286 fwk_assert(irq_count <= (IRQN_TYPE_MAX + 1));
287
288 /*
289 * Allocate and initialize a table for the callback functions and their
290 * corresponding parameters.
291 */
292 callback = fwk_mm_calloc(isr_count, sizeof(callback[0]));
293
294 /*
295 * The base address for the vector table must align on the number of
296 * entries in the table, corresponding to a word boundary rounded up to the
297 * next power of two.
298 *
299 * For example, for a vector table with 48 entries, the base address must be
300 * on a 64-word boundary.
301 */
302
303 /* Calculate the next power of two */
304 align_entries = UINT32_C(1) << (32U - __CLZ(isr_count - 1U));
305
306 /* Calculate alignment on a word boundary */
307 align_word = align_entries * sizeof(vector[0]);
308
309 /* Allocate and wipe the new vector table */
310 vector = fwk_mm_calloc_aligned(align_word, isr_count, sizeof(vector[0]));
311
312 /* Copy the processor exception table over to the new vector table */
313 (void)memcpy(
314 vector,
315 (const void *)SCB->VTOR,
316 NVIC_USER_IRQ_OFFSET * sizeof(vector[0]));
317
318 __DMB();
319
320 __disable_irq();
321
322 /* Switch to the new vector table */
323 SCB->VTOR = (uint32_t)vector;
324
325 /* Initialize IRQs */
326 for (irq = 0; irq < irq_count; irq++) {
327 /* Ensure IRQs are disabled during boot sequence */
328 NVIC_DisableIRQ((IRQn_Type)irq);
329 NVIC_ClearPendingIRQ((IRQn_Type)irq);
330
331 /* Initialize all IRQ entries to point to the irq_invalid() handler */
332 NVIC_SetVector((IRQn_Type)irq, (uint32_t)irq_invalid);
333 }
334
335 __enable_irq();
336
337 /* Enable the Usage, Bus and Memory faults which are disabled by default */
338 SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk | SCB_SHCSR_BUSFAULTENA_Msk |
339 SCB_SHCSR_USGFAULTENA_Msk;
340
341 *driver = &arch_nvic_driver;
342
343 return FWK_SUCCESS;
344 }
345