342 lines
11 KiB
C
342 lines
11 KiB
C
|
/**
|
||
|
* \file
|
||
|
*
|
||
|
* \brief Global interrupt management for 32-bit AVR
|
||
|
*
|
||
|
* Copyright (c) 2010-2018 Microchip Technology Inc. and its subsidiaries.
|
||
|
*
|
||
|
* \asf_license_start
|
||
|
*
|
||
|
* \page License
|
||
|
*
|
||
|
* Subject to your compliance with these terms, you may use Microchip
|
||
|
* software and any derivatives exclusively with Microchip products.
|
||
|
* It is your responsibility to comply with third party license terms applicable
|
||
|
* to your use of third party software (including open source software) that
|
||
|
* may accompany Microchip software.
|
||
|
*
|
||
|
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
|
||
|
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
|
||
|
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
|
||
|
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
|
||
|
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
|
||
|
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
|
||
|
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
|
||
|
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
|
||
|
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
|
||
|
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
|
||
|
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
|
||
|
*
|
||
|
* \asf_license_stop
|
||
|
*
|
||
|
*/
|
||
|
/*
|
||
|
* Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip 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 */
|