rt-thread/bsp/avr32/software_framework/utils/interrupt/interrupt_avr32.h

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 */