mirror of https://github.com/EspoTek/Labrador.git
353 lines
10 KiB
C
353 lines
10 KiB
C
/**
|
|
* \file
|
|
*
|
|
* \brief Programmable Multilevel Interrupt Controller driver
|
|
*
|
|
* Copyright (c) 2010-2015 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 PMIC_H
|
|
#define PMIC_H
|
|
|
|
#include <compiler.h>
|
|
#include <ccp.h>
|
|
|
|
/**
|
|
* \defgroup pmic_group Programmable Multilevel Interrupt Controller
|
|
*
|
|
* See \ref xmega_pmic_quickstart.
|
|
*
|
|
* This is a low-level driver implementation for the AVR XMEGA Programmable
|
|
* Multilevel Interrupt Controller.
|
|
*
|
|
* \note If these functions are used in interrupt service routines (ISRs), any
|
|
* non-ISR code or ISR code for lower level interrupts must ensure that the
|
|
* operations are atomic, i.e., by disabling interrupts during the function
|
|
* calls.
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* \brief Interrupt level bitmasks
|
|
*
|
|
* \note These may be OR'ed, e.g., if multiple levels are to be enabled or
|
|
* disabled.
|
|
*/
|
|
enum pmic_level {
|
|
PMIC_LVL_LOW = PMIC_LOLVLEN_bm, //!< Low-level interrupts
|
|
PMIC_LVL_MEDIUM = PMIC_MEDLVLEN_bm, //!< Medium-level interrupts
|
|
PMIC_LVL_HIGH = PMIC_HILVLEN_bm, //!< High-level interrupts
|
|
/**
|
|
* \brief Non-maskable interrupts
|
|
* \note These cannot be enabled nor disabled.
|
|
*/
|
|
PMIC_LVL_NMI = PMIC_NMIEX_bp,
|
|
};
|
|
|
|
//! Interrupt vector locations
|
|
enum pmic_vector {
|
|
PMIC_VEC_APPLICATION, //!< Application section
|
|
PMIC_VEC_BOOT, //!< Boot section
|
|
PMIC_NR_OF_VECTORS, //!< Number of interrupt vector locations
|
|
};
|
|
|
|
//! Interrupt scheduling schemes
|
|
enum pmic_schedule {
|
|
PMIC_SCH_FIXED_PRIORITY, //!< Default, fixed priority scheduling
|
|
PMIC_SCH_ROUND_ROBIN, //!< Round-robin scheduling
|
|
PMIC_NR_OF_SCHEDULES, //!< Number of interrupt scheduling schemes
|
|
};
|
|
|
|
/**
|
|
* \brief Initialize the PMIC
|
|
*
|
|
* Enables all interrupt levels, with vectors located in the application section
|
|
* and fixed priority scheduling.
|
|
*/
|
|
static inline void pmic_init(void)
|
|
{
|
|
PMIC.CTRL = PMIC_LVL_LOW | PMIC_LVL_MEDIUM |
|
|
PMIC_LVL_HIGH;
|
|
}
|
|
|
|
/**
|
|
* \brief Enable interrupts with specified \a level(s).
|
|
*
|
|
* \param level Interrupt level(s) to enable.
|
|
*/
|
|
static inline void pmic_enable_level(enum pmic_level level)
|
|
{
|
|
Assert((level & PMIC_LVL_NMI));
|
|
|
|
PMIC.CTRL |= level;
|
|
}
|
|
|
|
/**
|
|
* \brief Disable interrupts with specified \a level(s).
|
|
*
|
|
* \param level Interrupt level(s) to disable.
|
|
*/
|
|
static inline void pmic_disable_level(enum pmic_level level)
|
|
{
|
|
Assert((level & PMIC_LVL_NMI));
|
|
|
|
PMIC.CTRL &= ~level;
|
|
}
|
|
|
|
/**
|
|
* \brief Check if specified interrupt \a level(s) is enabled.
|
|
*
|
|
* \param level Interrupt level(s) to check.
|
|
*
|
|
* \return True if interrupt level(s) is enabled.
|
|
*/
|
|
static inline bool pmic_level_is_enabled(enum pmic_level level)
|
|
{
|
|
Assert((level & PMIC_LVL_NMI));
|
|
|
|
return PMIC.CTRL & level;
|
|
}
|
|
|
|
/**
|
|
* \brief Get currently enabled level(s)
|
|
*
|
|
* \return Bitmask with currently enabled levels.
|
|
*/
|
|
static inline enum pmic_level pmic_get_enabled_levels(void)
|
|
{
|
|
return (enum pmic_level)(PMIC.CTRL & (PMIC_LVL_LOW | PMIC_LVL_MEDIUM
|
|
| PMIC_LVL_HIGH));
|
|
}
|
|
|
|
/**
|
|
* \brief Check if an interrupt level(s) is currently executing.
|
|
*
|
|
* \param level Interrupt level(s) to check.
|
|
*
|
|
* \return True if interrupt level(s) is currently executing.
|
|
*/
|
|
static inline bool pmic_level_is_executing(enum pmic_level level)
|
|
{
|
|
return PMIC.STATUS & level;
|
|
}
|
|
|
|
/**
|
|
* \brief Set interrupt scheduling for low-level interrupts.
|
|
*
|
|
* \param schedule Interrupt scheduling method to set.
|
|
*
|
|
* \note The low-priority vector, INTPRI, must be set to 0 when round-robin
|
|
* scheduling is disabled to return to default interrupt priority order.
|
|
*/
|
|
static inline void pmic_set_scheduling(enum pmic_schedule schedule)
|
|
{
|
|
Assert(schedule < PMIC_NR_OF_SCHEDULES);
|
|
|
|
switch (schedule) {
|
|
case PMIC_SCH_FIXED_PRIORITY:
|
|
PMIC.CTRL &= ~PMIC_RREN_bm;
|
|
PMIC.INTPRI = 0;
|
|
break;
|
|
|
|
case PMIC_SCH_ROUND_ROBIN:
|
|
PMIC.CTRL |= PMIC_RREN_bm;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* \brief Set location of interrupt vectors.
|
|
*
|
|
* \param vector Location to use for interrupt vectors.
|
|
*/
|
|
static inline void pmic_set_vector_location(enum pmic_vector vector)
|
|
{
|
|
uint8_t ctrl = PMIC.CTRL;
|
|
|
|
Assert(vector < PMIC_NR_OF_VECTORS);
|
|
|
|
switch (vector) {
|
|
case PMIC_VEC_APPLICATION:
|
|
ctrl &= ~PMIC_IVSEL_bm;
|
|
break;
|
|
|
|
case PMIC_VEC_BOOT:
|
|
ctrl |= PMIC_IVSEL_bm;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ccp_write_io((uint8_t*)&PMIC.CTRL, ctrl);
|
|
}
|
|
|
|
//! @}
|
|
|
|
/**
|
|
* \page xmega_pmic_quickstart Quick start guide for PMIC driver
|
|
*
|
|
* This is the quick start guide for the \ref pmic_group "PMIC driver" and
|
|
* the closely related \ref interrupt_group "global interrupt driver", with
|
|
* step-by-step instructions on how to configure and use the drivers in a
|
|
* selection of use cases.
|
|
*
|
|
* The use cases contain several code fragments. The code fragments in the
|
|
* steps for setup can be copied into a custom initialization function, while
|
|
* the steps for usage can be copied into, e.g., the main application function.
|
|
*
|
|
* \section pmic_basic_use_case Basic use case
|
|
* In this basic use case, the PMIC is configured for:
|
|
* - all interrupt levels enabled
|
|
* - round-robin scheduling
|
|
*
|
|
* This will allow for interrupts from other modules being used.
|
|
*
|
|
* \section pmic_basic_use_case_setup Setup steps
|
|
*
|
|
* \subsection pmic_basic_use_case_setup_prereq Prerequisites
|
|
* For the setup code of this use case to work, the following must
|
|
* be added to the project:
|
|
* -# Interrupts for the module requiring the PMIC module have to be
|
|
* enabled.
|
|
* -# An Interrupt Service Routine (ISR) for a given interrupt vector has to be
|
|
* defined, where the interrupt vectors available are defined by toolchain and
|
|
* listed in the subsection 'Interrupt Vector Summary' in the data sheet.
|
|
* \code
|
|
ISR(interrupt_vector){
|
|
//Interrupt Service Routine
|
|
}
|
|
\endcode
|
|
*
|
|
* \subsection pmic_basic_use_case_setup_code Example code
|
|
* Add to the initialization code:
|
|
* \code
|
|
pmic_init();
|
|
pmic_set_scheduling(PMIC_SCH_ROUND_ROBIN);
|
|
cpu_irq_enable();
|
|
\endcode
|
|
*
|
|
* \subsection pmic_basic_use_case_setup_flow Workflow
|
|
* -# call the PMIC driver's own init function to enable all interrupt levels:
|
|
* - \code pmic_init(); \endcode
|
|
* -# enable round-robin instead of fixed priority interrupt scheduling:
|
|
* - \code pmic_set_scheduling(PMIC_SCH_ROUND_ROBIN); \endcode
|
|
* -# enable interrupts globally:
|
|
* - \code cpu_irq_enable(); \endcode
|
|
* - \attention Interrupts will not trigger without this step.
|
|
*
|
|
* \section pmic_use_cases Advanced use cases
|
|
* For more advanced use of the PMIC driver, see the following use cases:
|
|
* - \subpage pmic_use_case_1 : atomic operations
|
|
*/
|
|
|
|
/**
|
|
* \page pmic_use_case_1 Use case #1
|
|
*
|
|
* In this use case, the PMIC is configured for:
|
|
* - all interrupt levels enabled
|
|
*
|
|
* This will allow for interrupts from other modules being used.
|
|
*
|
|
* This use case shows how to make an operation which consists of multiple
|
|
* instructions uninterruptible, i.e., into an atomic operation. This is often
|
|
* necessary if there is a risk that data can be accessed by interrupt handlers
|
|
* while other code is accessing it, and at least one of them modifies it.
|
|
*
|
|
* \section pmic_use_case_1_setup Setup steps
|
|
*
|
|
* \subsection pmic_basic_use_case_setup_prereq Prerequisites
|
|
* For the setup code of this use case to work, the following must
|
|
* be added to the project:
|
|
* -# Interrupts for the module requiring the PMIC module have to be
|
|
* enabled.
|
|
* -# An Interrupt Service Routine (ISR) for a given interrupt vector has to be
|
|
* defined, where the interrupt vectors available are defined by toolchain and
|
|
* listed in the subsection 'Interrupt Vector Summary' in the data sheet.
|
|
* \code
|
|
ISR(interrupt_vector){
|
|
//Interrupt Service Routine
|
|
}
|
|
\endcode
|
|
*
|
|
* \subsection pmic_use_case_1_setup_code Example code
|
|
* Add to application initialization:
|
|
* \code
|
|
pmic_init();
|
|
cpu_irq_enable();
|
|
\endcode
|
|
*
|
|
* \subsection pmic_use_case_1_setup_flow Workflow
|
|
* -# call the PMIC driver's own init function to enable all interrupt levels:
|
|
* - \code pmic_init(); \endcode
|
|
* -# set global interrupt enable flag:
|
|
* - \code cpu_irq_enable(); \endcode
|
|
*
|
|
* \section pmic_use_case_1_usage Usage steps
|
|
*
|
|
* \subsection pmic_use_case_1_usage_code Example code
|
|
* \code
|
|
Add to application:
|
|
void atomic_operation(void)
|
|
{
|
|
irqflags_t flags;
|
|
|
|
flags = cpu_irq_save();
|
|
|
|
// Uninterruptible block of code
|
|
|
|
cpu_irq_restore(flags);
|
|
}
|
|
\endcode
|
|
*
|
|
* \subsection pmic_use_case_1_usage_flow Workflow
|
|
* -# allocate temporary storage for interrupt enable:
|
|
* - \code irqflags_t flags; \endcode
|
|
* -# clear global interrupt enable flag while saving its previous state:
|
|
* - \code flags = cpu_irq_save(); \endcode
|
|
* -# restore the previous state of global interrupt flag after operation:
|
|
* - \code cpu_irq_restore(flags); \endcode
|
|
*/
|
|
|
|
#endif /* PMIC_H */
|