summaryrefslogtreecommitdiff
path: root/atmel-samd/asf/common/utils/interrupt/interrupt_avr32.h
blob: 66f2fa2c2fed537d45029f297f29a97c114b7071 (plain)
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
/**
 * \file
 *
 * \brief Global interrupt management for 32-bit AVR
 *
 * Copyright (c) 2010-2016 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \asf_license_stop
 *
 */
/*
 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
 */
#ifndef UTILS_INTERRUPT_INTERRUPT_H
#define UTILS_INTERRUPT_INTERRUPT_H

#include <compiler.h>
#include <preprocessor/tpaste.h>

/**
 * \weakgroup interrupt_group
 *
 * @{
 */

//! Pointer to interrupt handler.
#if (defined __GNUC__)
typedef void (*__int_handler)(void);
#elif (defined __ICCAVR32__)
typedef void (__interrupt *__int_handler)(void);
#endif

/**
 * \name Interrupt Service Routine definition and registration
 *
 * @{
 */
#if defined(__GNUC__) || defined(__DOXYGEN__)

/**
 * \brief Macro to declare an interrupt service routine
 *
 * With GCC, this macro only causes the function to be defined as an interrupt
 * service routine, i.e., it does not add any initialization code. A valid
 * function name is required for use with \ref irq_register_handler.
 *
 * With IAR, this macro defines the function as an interrupt service routine and
 * causes the compiler to add initialization code for the interrupt controller
 * (INTC). The interrupt group and level, as well as a valid function name are
 * therefore required.\n
 * \note If \ref CONFIG_INTERRUPT_FORCE_INTC is defined, only a valid function
 * name is required for use with \ref irq_register_handler. The initialization
 * code will be handled by the interrupt controller itself.
 *
 * Usage:
 * \code
	ISR(foo_irq_handler, AVR32_xxx_IRQ_GROUP, n)
	{
	     // Function definition
	     ...
	}
\endcode
 *
 * \param func Name for the function, needed by \ref irq_register_handler.
 * \param int_grp Interrupt group to define service routine for.
 * \param int_lvl Priority level to set for the interrupt group, in the range
 * \c 0 to \c 3.
 *
 * \note The interrupt groups can be found in the device header files for the
 * IAR toolchain (avr32/io\<part\>.h).
 *
 * \todo Update to use IRQ numbers when these are made available in the
 * device header files of both IAR and GCC.
 */
#  define ISR(func, int_grp, int_lvl)    \
	__attribute__((__interrupt__)) static void func (void)

#elif defined(__ICCAVR32__) && defined(CONFIG_INTERRUPT_FORCE_INTC)
#  define ISR(func, int_grp, int_lvl) \
		__interrupt static void func (void)

#elif defined(__ICCAVR32__)
#  define ISR0(...) _Pragma(#__VA_ARGS__)
#  define ISR(func, int_grp, int_lvl)                                          \
		ISR0(handler=int_grp, int_lvl)                                 \
		__interrupt static void func (void)
#endif

#if defined(__GNUC__) || defined(__DOXYGEN__) || defined(CONFIG_INTERRUPT_FORCE_INTC)
#  include <intc.h>

/**
 * \brief Initialize interrupt vectors
 *
 * With GCC, this macro adds code for initialization of the interrupt vectors
 * with the driver for the interrupt controller (INTC).
 *
 * With IAR and unless \ref CONFIG_INTERRUPT_FORCE_INTC is defined this macro
 * adds no code, since initialization of the INTC is handled by the compiler.
 * \note Defining \ref CONFIG_INTERRUPT_FORCE_INTC will force the use of the
 * INTC driver, replacing the compiler built-in interrupt handler.
 *
 * This must be called prior to \ref irq_register_handler.
 */
#  define irq_initialize_vectors()    INTC_init_interrupts()

/**
 * \brief Register handler for interrupt
 *
 * With GCC, this macro adds code for registering an interrupt handler with the
 * driver for the interrupt controller (INTC).
 *
 * With IAR and unless \ref CONFIG_INTERRUPT_FORCE_INTC is defined this macro
 * adds no code, since initialization of the INTC is handled by the compiler.
 * \note Defining \ref CONFIG_INTERRUPT_FORCE_INTC will force the use of the
 * INTC driver, replacing the compiler built-in interrupt handler.
 *
 * \param func Name of handler function to register for interrupt.
 * \param int_num Number of the interrupt line to register function for.
 * \param int_lvl Priority level to set for the interrupt's group, in the range
 * \c 0 to \c 3.
 *
 * Usage:
 * \code
	irq_initialize_vectors();
	irq_register_handler(foo_irq_handler, AVR32_xxx_IRQ, n);
\endcode
 *
 * \note The function \a func must be defined with the \ref ISR macro.
 * \note The interrupt line number can be found in the device header files for
 * the GCC toolchain (avr32/\<part\>.h).
 */
#  define irq_register_handler(func, int_num, int_lvl)                         \
	INTC_register_interrupt(func, int_num,                                 \
			TPASTE2(AVR32_INTC_INT, int_lvl))

#elif defined(__ICCAVR32__)
#  define irq_initialize_vectors()                        do{ } while(0)
#  define irq_register_handler(func, int_num, int_lvl)    do{ } while(0)
#endif

//@}

#if (defined __GNUC__)
#  define cpu_irq_enable()                             \
	do {                                           \
		barrier();                             \
		__builtin_csrf(AVR32_SR_GM_OFFSET);    \
	} while (0)
#  define cpu_irq_disable()                            \
	do {                                           \
		__builtin_ssrf(AVR32_SR_GM_OFFSET);    \
		barrier();                             \
	} while (0)
#elif (defined __ICCAVR32__)
#  if (defined CONFIG_INTERRUPT_FORCE_INTC)
#    define cpu_irq_enable()                                 \
	do {                                                 \
		barrier();                                   \
		__clear_status_flag(AVR32_SR_GM_OFFSET);     \
	} while(0)
#    define cpu_irq_disable()                                \
	do {                                                 \
		__set_status_flag(AVR32_SR_GM_OFFSET);       \
		barrier();                                   \
	} while (0)
#  else
#    define cpu_irq_enable()     __enable_interrupt()
#    define cpu_irq_disable()    __disable_interrupt()
#  endif
#endif

typedef uint32_t irqflags_t;

static inline irqflags_t cpu_irq_save(void)
{
	volatile irqflags_t flags;

	flags = sysreg_read(AVR32_SR);
	cpu_irq_disable();

	return flags;
}

static inline bool cpu_irq_is_enabled_flags(irqflags_t flags)
{
	return !(flags & AVR32_SR_GM_MASK);
}

static inline void cpu_irq_restore(irqflags_t flags)
{
	barrier();

   /* Restore the global IRQ mask status flag if it was previously set */
   if ( cpu_irq_is_enabled_flags(flags) ) {
      cpu_irq_enable();
   }

	barrier();
}

#define cpu_irq_is_enabled()    cpu_irq_is_enabled_flags(sysreg_read(AVR32_SR))

//! \name Global interrupt levels
//@{

/**
 * \brief Check if interrupt level is enabled in supplied flags
 *
 * \param flags State of interrupt flags.
 * \param level Bit position for interrupt level.
 *
 * \return True if interrupt level is enabled.
 */
static inline bool cpu_irq_level_is_enabled_flags(irqflags_t flags,
		uint32_t level)
{
	return !(flags & (1 << level));
}

/**
 * \brief Check if interrupt level is enabled
 *
 * \param level Interrupt level (0 to 3).
 *
 * \return True if interrupt level \a level is enabled.
 *
 * \note The interrupt level must be known at compile time.
 */
#define cpu_irq_level_is_enabled(level)                                  \
	cpu_irq_level_is_enabled_flags(sysreg_read(AVR32_SR),             \
			TPASTE3(AVR32_SR_I, level, M_OFFSET))

#if defined(__GNUC__) || defined(__DOXYGEN__)
/**
 * \brief Enable interrupt level
 *
 * \param level Interrupt level to enable (0 to 3).
 *
 * \note The interrupt level must be known at compile time.
 */
#  define cpu_irq_enable_level(level)                                    \
	do {                                                             \
		barrier();                                               \
		__builtin_csrf(TPASTE3(AVR32_SR_I, level, M_OFFSET));    \
	} while (0)

/**
 * \brief Disable interrupt level
 *
 * \param level Interrupt level to disable (0 to 3).
 *
 * \note The interrupt level must be known at compile time.
 */
#  define cpu_irq_disable_level(level)                                   \
	do {                                                             \
		__builtin_ssrf(TPASTE3(AVR32_SR_I, level, M_OFFSET));    \
		barrier();                                               \
	} while (0)

#elif (defined __ICCAVR32__)
#  define cpu_irq_enable_level(level)                                          \
	do {                                                                   \
		barrier();                                                     \
		__clear_status_flag(TPASTE3(AVR32_SR_I, level, M_OFFSET));     \
	} while(0)
#  define cpu_irq_disable_level(level)                                         \
	do {                                                                   \
		__set_status_flag(TPASTE3(AVR32_SR_I, level, M_OFFSET));       \
		barrier();                                                     \
	} while (0)
#endif

//@}

//@}

/**
 * \weakgroup interrupt_deprecated_group
 * @{
 */

#define Enable_global_interrupt()            cpu_irq_enable()
#define Disable_global_interrupt()           cpu_irq_disable()
#define Is_global_interrupt_enabled()        cpu_irq_is_enabled()

#define Enable_interrupt_level(level)        cpu_irq_enable_level(level)
#define Disable_interrupt_level(level)       cpu_irq_disable_level(level)
#define Is_interrupt_level_enabled(level)    cpu_irq_level_is_enabled(level)

/**
 * \name Interrupt protection of code sections
 * \note Use \ref cpu_irq_save and \ref cpu_irq_restore instead of these macros.
 * @{
 */

/**
 * \brief Start section with code protected against interrupts
 */
#define AVR32_ENTER_CRITICAL_REGION()                                          \
	{                                                                      \
		bool global_interrupt_enabled = Is_global_interrupt_enabled(); \
		Disable_global_interrupt();

/**
 * \brief End section with code protected against interrupts
 *
 * \note This macro must always be used in conjunction with
 * \ref AVR32_ENTER_CRITICAL_REGION so that interrupts are enabled again.
 */
#define AVR32_LEAVE_CRITICAL_REGION()                                          \
		if (global_interrupt_enabled) Enable_global_interrupt();       \
	}

//@}

//@}

#endif /* UTILS_INTERRUPT_INTERRUPT_H */