Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/*
 * Copyright (c) 2015 Intel corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief Public interface for configuring interrupts
 */
#ifndef ZEPHYR_INCLUDE_IRQ_H_
#define ZEPHYR_INCLUDE_IRQ_H_

/* Pull in the arch-specific implementations */
#include <arch/cpu.h>

#ifndef _ASMLANGUAGE
#include <toolchain.h>
#include <zephyr/types.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @defgroup isr_apis Interrupt Service Routine APIs
 * @ingroup kernel_apis
 * @{
 */

/**
 * @brief Initialize an interrupt handler.
 *
 * This routine initializes an interrupt handler for an IRQ. The IRQ must be
 * subsequently enabled before the interrupt handler begins servicing
 * interrupts.
 *
 * @warning
 * Although this routine is invoked at run-time, all of its arguments must be
 * computable by the compiler at build time.
 *
 * @param irq_p IRQ line number.
 * @param priority_p Interrupt priority.
 * @param isr_p Address of interrupt service routine.
 * @param isr_param_p Parameter passed to interrupt service routine.
 * @param flags_p Architecture-specific IRQ configuration flags..
 */
#define IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \
	ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p)

/**
 * Configure a dynamic interrupt.
 *
 * Use this instead of IRQ_CONNECT() if arguments cannot be known at build time.
 *
 * @param irq IRQ line number
 * @param priority Interrupt priority
 * @param routine Interrupt service routine
 * @param parameter ISR parameter
 * @param flags Arch-specific IRQ configuration flags
 *
 * @return The vector assigned to this interrupt
 */
static inline int
irq_connect_dynamic(unsigned int irq, unsigned int priority,
		    void (*routine)(const void *parameter),
		    const void *parameter, uint32_t flags)
{
	return arch_irq_connect_dynamic(irq, priority, routine, parameter,
					flags);
}

/**
 * @brief Initialize a 'direct' interrupt handler.
 *
 * This routine initializes an interrupt handler for an IRQ. The IRQ must be
 * subsequently enabled via irq_enable() before the interrupt handler begins
 * servicing interrupts.
 *
 * These ISRs are designed for performance-critical interrupt handling and do
 * not go through common interrupt handling code. They must be implemented in
 * such a way that it is safe to put them directly in the vector table.  For
 * ISRs written in C, The ISR_DIRECT_DECLARE() macro will do this
 * automatically. For ISRs written in assembly it is entirely up to the
 * developer to ensure that the right steps are taken.
 *
 * This type of interrupt currently has a few limitations compared to normal
 * Zephyr interrupts:
 * - No parameters are passed to the ISR.
 * - No stack switch is done, the ISR will run on the interrupted context's
 *   stack, unless the architecture automatically does the stack switch in HW.
 * - Interrupt locking state is unchanged from how the HW sets it when the ISR
 *   runs. On arches that enter ISRs with interrupts locked, they will remain
 *   locked.
 * - Scheduling decisions are now optional, controlled by the return value of
 *   ISRs implemented with the ISR_DIRECT_DECLARE() macro
 * - The call into the OS to exit power management idle state is now optional.
 *   Normal interrupts always do this before the ISR is run, but when it runs
 *   is now controlled by the placement of a ISR_DIRECT_PM() macro, or omitted
 *   entirely.
 *
 * @warning
 * Although this routine is invoked at run-time, all of its arguments must be
 * computable by the compiler at build time.
 *
 * @param irq_p IRQ line number.
 * @param priority_p Interrupt priority.
 * @param isr_p Address of interrupt service routine.
 * @param flags_p Architecture-specific IRQ configuration flags.
 */
#define IRQ_DIRECT_CONNECT(irq_p, priority_p, isr_p, flags_p) \
	ARCH_IRQ_DIRECT_CONNECT(irq_p, priority_p, isr_p, flags_p)

/**
 * @brief Common tasks before executing the body of an ISR
 *
 * This macro must be at the beginning of all direct interrupts and performs
 * minimal architecture-specific tasks before the ISR itself can run. It takes
 * no arguments and has no return value.
 */
#define ISR_DIRECT_HEADER() ARCH_ISR_DIRECT_HEADER()

/**
 * @brief Common tasks before exiting the body of an ISR
 *
 * This macro must be at the end of all direct interrupts and performs
 * minimal architecture-specific tasks like EOI. It has no return value.
 *
 * In a normal interrupt, a check is done at end of interrupt to invoke
 * z_swap() logic if the current thread is preemptible and there is another
 * thread ready to run in the kernel's ready queue cache. This is now optional
 * and controlled by the check_reschedule argument. If unsure, set to nonzero.
 * On systems that do stack switching and nested interrupt tracking in software,
 * z_swap() should only be called if this was a non-nested interrupt.
 *
 * @param check_reschedule If nonzero, additionally invoke scheduling logic
 */
#define ISR_DIRECT_FOOTER(check_reschedule) \
	ARCH_ISR_DIRECT_FOOTER(check_reschedule)

/**
 * @brief Perform power management idle exit logic
 *
 * This macro may optionally be invoked somewhere in between IRQ_DIRECT_HEADER()
 * and IRQ_DIRECT_FOOTER() invocations. It performs tasks necessary to
 * exit power management idle state. It takes no parameters and returns no
 * arguments. It may be omitted, but be careful!
 */
#define ISR_DIRECT_PM() ARCH_ISR_DIRECT_PM()

/**
 * @brief Helper macro to declare a direct interrupt service routine.
 *
 * This will declare the function in a proper way and automatically include
 * the ISR_DIRECT_FOOTER() and ISR_DIRECT_HEADER() macros. The function should
 * return nonzero status if a scheduling decision should potentially be made.
 * See ISR_DIRECT_FOOTER() for more details on the scheduling decision.
 *
 * For architectures that support 'regular' and 'fast' interrupt types, where
 * these interrupt types require different assembly language handling of
 * registers by the ISR, this will always generate code for the 'fast'
 * interrupt type.
 *
 * Example usage:
 *
 *     ISR_DIRECT_DECLARE(my_isr)
 *     {
 *             bool done = do_stuff();
 *             ISR_DIRECT_PM(); // done after do_stuff() due to latency concerns
 *             if (!done) {
 *                 return 0; // don't bother checking if we have to z_swap()
 *             }
 *
 *             k_sem_give(some_sem);
 *             return 1;
 *      }
 *
 * @param name symbol name of the ISR
 */
#define ISR_DIRECT_DECLARE(name) ARCH_ISR_DIRECT_DECLARE(name)

/**
 * @brief Lock interrupts.
 * @def irq_lock()
 *
 * This routine disables all interrupts on the CPU. It returns an unsigned
 * integer "lock-out key", which is an architecture-dependent indicator of
 * whether interrupts were locked prior to the call. The lock-out key must be
 * passed to irq_unlock() to re-enable interrupts.
 *
 * @note
 * This routine must also serve as a memory barrier to ensure the uniprocessor
 * implementation of `k_spinlock_t` is correct.
 *
 * This routine can be called recursively, as long as the caller keeps track
 * of each lock-out key that is generated. Interrupts are re-enabled by
 * passing each of the keys to irq_unlock() in the reverse order they were
 * acquired. (That is, each call to irq_lock() must be balanced by
 * a corresponding call to irq_unlock().)
 *
 * This routine can only be invoked from supervisor mode. Some architectures
 * (for example, ARM) will fail silently if invoked from user mode instead
 * of generating an exception.
 *
 * @note
 * This routine can be called by ISRs or by threads. If it is called by a
 * thread, the interrupt lock is thread-specific; this means that interrupts
 * remain disabled only while the thread is running. If the thread performs an
 * operation that allows another thread to run (for example, giving a semaphore
 * or sleeping for N milliseconds), the interrupt lock no longer applies and
 * interrupts may be re-enabled while other processing occurs. When the thread
 * once again becomes the current thread, the kernel re-establishes its
 * interrupt lock; this ensures the thread won't be interrupted until it has
 * explicitly released the interrupt lock it established.
 *
 * @warning
 * The lock-out key should never be used to manually re-enable interrupts
 * or to inspect or manipulate the contents of the CPU's interrupt bits.
 *
 * @return An architecture-dependent lock-out key representing the
 *         "interrupt disable state" prior to the call.
 */
#ifdef CONFIG_SMP
unsigned int z_smp_global_lock(void);
#define irq_lock() z_smp_global_lock()
#else
#define irq_lock() arch_irq_lock()
#endif

/**
 * @brief Unlock interrupts.
 * @def irq_unlock()
 *
 * This routine reverses the effect of a previous call to irq_lock() using
 * the associated lock-out key. The caller must call the routine once for
 * each time it called irq_lock(), supplying the keys in the reverse order
 * they were acquired, before interrupts are enabled.
 *
 * @note
 * This routine must also serve as a memory barrier to ensure the uniprocessor
 * implementation of `k_spinlock_t` is correct.
 *
 * This routine can only be invoked from supervisor mode. Some architectures
 * (for example, ARM) will fail silently if invoked from user mode instead
 * of generating an exception.
 *
 * @note Can be called by ISRs.
 *
 * @param key Lock-out key generated by irq_lock().
 *
 * @return N/A
 */
#ifdef CONFIG_SMP
void z_smp_global_unlock(unsigned int key);
#define irq_unlock(key) z_smp_global_unlock(key)
#else
#define irq_unlock(key) arch_irq_unlock(key)
#endif

/**
 * @brief Return IRQ level
 * @def irq_get_level()
 *
 * This routine returns the interrupt level number of the provided interrupt.
 *
 * @param irq IRQ number in its zephyr format
 *
 * @return 1 if IRQ level 1, 2 if IRQ level 2, 3 if IRQ level 3
 */
static inline unsigned int irq_get_level(unsigned int irq)
{
#if defined(CONFIG_3RD_LEVEL_INTERRUPTS)
	return ((irq >> 16) & 0xFF) != 0 ? 3 :
		(((irq >> 8) & 0xFF) == 0 ? 1 : 2);
#elif defined(CONFIG_2ND_LEVEL_INTERRUPTS)
	return ((irq >> 8) & 0xFF) == 0 ? 1 : 2;
#else
	ARG_UNUSED(irq);

	return 1;
#endif
}

#ifdef CONFIG_2ND_LEVEL_INTERRUPTS
/**
 * @brief Return the 2nd level interrupt number
 * @def irq_from_level_2()
 *
 * This routine returns the second level irq number of the zephyr irq
 * number passed in
 *
 * @param irq IRQ number in its zephyr format
 *
 * @return 2nd level IRQ number
 */
static inline unsigned int irq_from_level_2(unsigned int irq)
{
#ifdef CONFIG_3RD_LEVEL_INTERRUPTS
	return ((irq >> 8) & 0xFF) - 1;
#else
	return (irq >> 8) - 1;
#endif
}

/**
 * @brief Converts irq from level 1 to level 2 format
 * @def irq_to_level_2()
 *
 * This routine converts the input into the level 2 irq number format
 *
 * @note Values >= 0xFF are invalid
 *
 * @param irq IRQ number in its zephyr format
 *
 * @return 2nd level IRQ number
 */
static inline unsigned int irq_to_level_2(unsigned int irq)
{
	return (irq + 1) << 8;
}

/**
 * @brief Returns the parent IRQ of the level 2 raw IRQ number
 * @def irq_parent_level_2()
 *
 * The parent of a 2nd level interrupt is in the 1st byte
 *
 * @param irq IRQ number in its zephyr format
 *
 * @return 2nd level IRQ parent
 */
static inline unsigned int irq_parent_level_2(unsigned int irq)
{
	return irq & 0xFF;
}
#endif

#ifdef CONFIG_3RD_LEVEL_INTERRUPTS
/**
 * @brief Return the 3rd level interrupt number
 * @def irq_from_level_3()
 *
 * This routine returns the third level irq number of the zephyr irq
 * number passed in
 *
 * @param irq IRQ number in its zephyr format
 *
 * @return 3rd level IRQ number
 */
static inline unsigned int irq_from_level_3(unsigned int irq)
{
	return (irq >> 16) - 1;
}

/**
 * @brief Converts irq from level 1 to level 3 format
 * @def irq_to_level_3()
 *
 * This routine converts the input into the level 3 irq number format
 *
 * @note Values >= 0xFF are invalid
 *
 * @param irq IRQ number in its zephyr format
 *
 * @return 3rd level IRQ number
 */
static inline unsigned int irq_to_level_3(unsigned int irq)
{
	return (irq + 1) << 16;
}

/**
 * @brief Returns the parent IRQ of the level 3 raw IRQ number
 * @def irq_parent_level_3()
 *
 * The parent of a 3rd level interrupt is in the 2nd byte
 *
 * @param irq IRQ number in its zephyr format
 *
 * @return 3rd level IRQ parent
 */
static inline unsigned int irq_parent_level_3(unsigned int irq)
{
	return (irq >> 8) & 0xFF;
}
#endif

/**
 * @brief Enable an IRQ.
 *
 * This routine enables interrupts from source @a irq.
 *
 * @param irq IRQ line.
 *
 * @return N/A
 */
#define irq_enable(irq) arch_irq_enable(irq)

/**
 * @brief Disable an IRQ.
 *
 * This routine disables interrupts from source @a irq.
 *
 * @param irq IRQ line.
 *
 * @return N/A
 */
#define irq_disable(irq) arch_irq_disable(irq)

/**
 * @brief Get IRQ enable state.
 *
 * This routine indicates if interrupts from source @a irq are enabled.
 *
 * @param irq IRQ line.
 *
 * @return interrupt enable state, true or false
 */
#define irq_is_enabled(irq) arch_irq_is_enabled(irq)

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif /* ASMLANGUAGE */
#endif /* ZEPHYR_INCLUDE_IRQ_H_ */