/***************************************************************************** | |
* © 2015 Microchip Technology Inc. and its subsidiaries. | |
* You may use this software and any derivatives exclusively with | |
* Microchip products. | |
* 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, OR ITS INTERACTION WITH MICROCHIP | |
* PRODUCTS, COMBINATION WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION. | |
* 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. | |
* MICROCHIP PROVIDES THIS SOFTWARE CONDITIONALLY UPON YOUR ACCEPTANCE | |
* OF THESE TERMS. | |
****************************************************************************** | |
Version Control Information (Perforce) | |
****************************************************************************** | |
$Revision: #1 $ | |
$DateTime: 2016/09/22 08:03:49 $ | |
$Author: pramans $ | |
Last Change: Renamed ecia_init to interrupt_init | |
******************************************************************************/ | |
/** @file interrupt_api.c | |
* \brief Interrupt APIs Source File | |
* \author jvasanth | |
* | |
* This file implements the Interrupt Source file | |
******************************************************************************/ | |
/** @defgroup Interrupt | |
* @{ | |
*/ | |
#include "common_lib.h" | |
#include "interrupt.h" | |
#include "..\pcr\pcr.h" | |
static uint8_t interrupt_is_girq_direct(uint8_t girq_num); | |
/* ------------------------------------------------------------------------------- */ | |
/* NVIC,ECIA Routing Policy for Direct Mode */ | |
/* ------------------------------------------------------------------------------- */ | |
/* In Direct Mode, some interrupts could be configured to be used as aggregated. | |
* Configuration: | |
* 1. Always set ECS Interrupt Direct enable bit. | |
* 2. If GIRQn aggregated set Block Enable bit. | |
* 3. If GIRQn direct then clear Block Enable bit and enable individual NVIC inputs. | |
* Switching issues: | |
* Aggregate enable/disable requires set/clear single GIRQn bit in GIRQ Block En/Clr registers. | |
* Also requires set/clear of individual NVIC Enables. | |
* | |
* Note: interrupt_is_girq_direct() internal function uses this policy to detect | |
* if any interrupt is configured as direct or aggregated | |
*/ | |
/** Initialize EC Interrupt Aggregator | |
* @param mode 1 - Direct Map mode, 0 - Fully Aggregated Mode | |
* @param girq_bitmask - BitMask of GIRQ to be configured as aggregated | |
* This parameter is only applicable in direct mode. | |
* @note All GPIO's and wake capable sources are always | |
* aggregated! GPIO's interrupts will still work in direct mode. | |
* Block wakes are not be routed to the processor in direct | |
* mode. | |
* Note2: This function disables and enables global interrupt | |
*/ | |
void interrupt_init(uint8_t mode, uint32_t girq_bitmask) | |
{ | |
uint32_t isave; | |
isave = __get_PRIMASK(); | |
__disable_irq(); | |
//Clear Sleep for Interrupt block | |
pcr_sleep_enable(PCR_INT, 0); | |
interrupt_mode_set(mode); | |
p_interrupt_ecia_girqs_enable_reset(); | |
p_interrupt_nvic_enpend_clr(); | |
p_interrupt_nvic_priorities_default_set(); | |
if (mode) | |
{//If direct mode, enable specific GIRQs to be aggregated | |
p_interrupt_ecia_block_enable_bitmask_set(girq_bitmask); | |
} | |
if (!isave) { | |
__enable_irq(); | |
} | |
} | |
/** Set interrupt routing mode to aggregated or direct. | |
* @param mode 1 = Direct (except GPIO & wake), 0 = All Aggregated | |
* @note In direct mode, one could enable certain GIRQs as aggregated using | |
* p_interrupt_ecia_block_enable_set function | |
*/ | |
void interrupt_mode_set(uint8_t mode) | |
{ | |
if (mode) | |
{ | |
p_interrupt_ecia_block_enable_all_clr(); | |
} | |
else | |
{ | |
p_interrupt_ecia_block_enable_all_set(); | |
} | |
p_interrupt_control_set(mode); | |
} | |
/** Clears all individual interrupts Enables and Source in ECIA, | |
* and Clears all NVIC external enables and pending bits | |
*/ | |
void interrupt_reset(void) | |
{ | |
p_interrupt_ecia_girqs_enable_reset(); | |
p_interrupt_ecia_girqs_source_reset(); | |
p_interrupt_nvic_enpend_clr(); | |
} | |
/** Enables interrupt for a device | |
* @param dev_iroute - source IROUTING information | |
* @note This function disables and enables global interrupt | |
*/ | |
void interrupt_device_enable(uint32_t dev_iroute) | |
{ | |
uint32_t isave; | |
IRQn_Type nvic_num; | |
uint8_t girq_num; | |
uint8_t ia_bitpos; | |
girq_num = (uint8_t)(dev_iroute >> (ECIA_GIRQ_ID_BITPOS)) & 0x1Fu; | |
ia_bitpos = (uint8_t)(dev_iroute >> (ECIA_GIRQ_BIT_BITPOS)) & 0x1Fu; | |
if (interrupt_is_girq_direct(girq_num)) | |
{ // GIRQ is hooked direct | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
else | |
{ // GIRQ is aggregated | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_IA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
isave = __get_PRIMASK(); | |
__disable_irq(); | |
NVIC_EnableIRQ(nvic_num); | |
p_interrupt_ecia_girq_enable_set(girq_num, ia_bitpos); | |
__DSB(); | |
if (!isave) { | |
__enable_irq(); | |
} | |
} | |
/** Disables interrupt for a device | |
* @param dev_iroute - source IROUTING information | |
* @note This function disables and enables global interrupt | |
*/ | |
void interrupt_device_disable(uint32_t dev_iroute) | |
{ | |
uint32_t isave; | |
IRQn_Type nvic_num; | |
uint8_t girq_num; | |
uint8_t ia_bitpos; | |
girq_num = (uint8_t)(dev_iroute >> (ECIA_GIRQ_ID_BITPOS)) & 0x1Fu; | |
ia_bitpos = (uint8_t)(dev_iroute >> (ECIA_GIRQ_BIT_BITPOS)) & 0x1Fu; | |
isave = __get_PRIMASK(); | |
__disable_irq(); | |
if (interrupt_is_girq_direct(girq_num)) | |
{ // GIRQ is hooked direct | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_NVIC_ID_BITPOS)) & 0xFFul); | |
NVIC_DisableIRQ(nvic_num); | |
} | |
p_interrupt_ecia_girq_enable_clr(girq_num, ia_bitpos); | |
__DSB(); | |
if (!isave) { | |
__enable_irq(); | |
} | |
} | |
/** ecia_is_girq_direct - Return true if GIRQn sources can be directly | |
* connected to the NVIC - based on ECS->INTERRUPT_CONTROL and GIRQ block enable | |
* @param girq_num - enum MEC_GIRQ_IDS | |
* @return 1 if GIRQn sources can be directly routed to the NVIC, else 0 | |
*/ | |
static uint8_t interrupt_is_girq_direct(uint8_t girq_num) | |
{ | |
uint32_t bpos; | |
uint8_t retVal; | |
uint8_t girq_block_enabled; | |
retVal = 0; | |
bpos = (girq_num & 0x1Fu) + 8u; | |
if ((ECIA_GIRQ_DIRECT_BITMAP) & (1ul << bpos)) | |
{ | |
if (p_interrupt_control_get()) | |
{// direct NVIC enabled | |
girq_block_enabled = p_interrupt_ecia_block_enable_get(girq_num); | |
if (!girq_block_enabled) | |
{ | |
retVal = 1; | |
} | |
} | |
} | |
return retVal; | |
} | |
/* ------------------------------------------------------------------------------- */ | |
/* ECIA APIs using device IROUTE() as input */ | |
/* ------------------------------------------------------------------------------- */ | |
/** Clear Source in the ECIA for the device | |
* @param devi - device IROUTING value | |
*/ | |
void interrupt_device_ecia_source_clear(const uint32_t dev_iroute) | |
{ | |
uint8_t girq_num; | |
uint8_t ia_bit_pos; | |
girq_num = (uint8_t)(dev_iroute >> (ECIA_GIRQ_ID_BITPOS)) & 0x1Fu; | |
ia_bit_pos = (uint8_t)(dev_iroute >> (ECIA_GIRQ_BIT_BITPOS)) & 0x1Fu; | |
p_interrupt_ecia_girq_source_clr(girq_num, ia_bit_pos); | |
__DSB(); | |
} | |
/** Get the Source bit in the ECIA for the device | |
* @param devi - device IROUTING value | |
* @return 0 if source bit not set; else non-zero value | |
*/ | |
uint32_t interrupt_device_ecia_source_get(const uint32_t dev_iroute) | |
{ | |
uint8_t girq_num; | |
uint8_t ia_bit_pos; | |
uint8_t retVal; | |
girq_num = (uint8_t)(dev_iroute >> (ECIA_GIRQ_ID_BITPOS)) & 0x1Fu; | |
ia_bit_pos = (uint8_t)(dev_iroute >> (ECIA_GIRQ_BIT_BITPOS)) & 0x1Fu; | |
retVal = p_interrupt_ecia_girq_source_get(girq_num, ia_bit_pos); | |
return retVal; | |
} | |
/** Get the Result bit in the ECIA for the device | |
* @param devi - device IROUTING value | |
* @return 0 if result bit not set; else non-zero value | |
*/ | |
uint32_t interrupt_device_ecia_result_get(const uint32_t dev_iroute) | |
{ | |
uint8_t girq_num; | |
uint8_t ia_bit_pos; | |
uint8_t retVal; | |
girq_num = (uint8_t)(dev_iroute >> (ECIA_GIRQ_ID_BITPOS)) & 0x1Fu; | |
ia_bit_pos = (uint8_t)(dev_iroute >> (ECIA_GIRQ_BIT_BITPOS)) & 0x1Fu; | |
retVal = p_interrupt_ecia_girq_result_get(girq_num, ia_bit_pos); | |
return retVal; | |
} | |
/* ------------------------------------------------------------------------------- */ | |
/* NVIC APIs using device IROUTE() as input */ | |
/* ------------------------------------------------------------------------------- */ | |
/* Note that if the device interrupt is aggregated, then these APIs would affect the | |
* NVIC corresponding to the aggregated GIRQ | |
*/ | |
/** Enable/Disable the NVIC (in the NVIC controller) for the device | |
* @param dev_iroute : source IROUTING information (encoded in a uint32_t) | |
* @param en_flag : 1 = Enable the NVIC IRQ, 0 = Disable the NVIC IRQ | |
* @note 1. Recommended to use interrupt_device_enable, interrupt_device_disable | |
* to enable/disable interrupts for the device, since those APIs configure ECIA as well | |
* 2. This function disables and enables global interrupt | |
*/ | |
void interrupt_device_nvic_enable(uint32_t dev_iroute, uint8_t en_flag) | |
{ | |
uint32_t isave; | |
IRQn_Type nvic_num; | |
if (p_interrupt_control_get()) | |
{ // direct | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
else // fully aggregated | |
{ | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_IA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
isave = __get_PRIMASK(); | |
__disable_irq(); | |
p_interrupt_nvic_enable(nvic_num, en_flag); | |
if (!isave) { | |
__enable_irq(); | |
} | |
} | |
/** Set NVIC priority for specified peripheral interrupt | |
* @param dev_iroute - source IROUTING information (encoded in a uint32_t) | |
* @param nvic_pri - NVIC Priority | |
* @note If ECIA is in aggregated mode, the priority affects all interrupt | |
* sources in the GIRQ. | |
*/ | |
void interrupt_device_nvic_priority_set(const uint32_t dev_iroute, const uint8_t nvic_pri) | |
{ | |
IRQn_Type nvic_num; | |
if (p_interrupt_control_get()) | |
{ // direct | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
else // fully aggregated | |
{ | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_IA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
NVIC_SetPriority(nvic_num, (uint32_t)nvic_pri); | |
} | |
/** Return NVIC priority for the device's interrupt | |
* @param dev_iroute - source IROUTING information | |
* @return uint32_t NVIC priority | |
*/ | |
uint32_t interrupt_device_nvic_priority_get(const uint32_t dev_iroute) | |
{ | |
IRQn_Type nvic_num; | |
uint32_t nvic_priority; | |
if (p_interrupt_control_get()) | |
{ // direct | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
else // fully aggregated | |
{ | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_IA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
nvic_priority = NVIC_GetPriority(nvic_num); | |
return nvic_priority; | |
} | |
/** Return NVIC pending for the device | |
* @param dev_iroute - source IROUTING information | |
* @return uint8_t 0(not pending), 1 (pending in NVIC) | |
* | |
*/ | |
uint8_t interrupt_device_nvic_pending_get(const uint32_t dev_iroute) | |
{ | |
IRQn_Type nvic_num; | |
uint8_t nvic_pending; | |
if (p_interrupt_control_get()) | |
{ // direct | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
else // fully aggregated | |
{ | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_IA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
nvic_pending = (uint8_t)(NVIC_GetPendingIRQ(nvic_num)); | |
return nvic_pending; | |
} | |
/** Set NVIC pending for interrupt source | |
* @param dev_iroute - source IROUTING information | |
*/ | |
void interrupt_device_nvic_pending_set(const uint32_t dev_iroute) | |
{ | |
IRQn_Type nvic_num; | |
if (p_interrupt_control_get()) | |
{ // direct | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
else // fully aggregated | |
{ | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_IA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
NVIC_SetPendingIRQ(nvic_num); | |
} | |
/** Clears NVIC pending for interrupt source | |
* @param dev_iroute - source IROUTING information | |
* @return uint8_t 0(not pending), 1 (pending in NVIC) - before clear | |
* @note This function disables and enables global interrupt | |
*/ | |
uint8_t interrupt_device_nvic_pending_clear(const uint32_t dev_iroute) | |
{ | |
uint32_t was_masked; | |
IRQn_Type nvic_num; | |
uint8_t pending; | |
if (p_interrupt_control_get()) | |
{ // direct | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
else // fully aggregated | |
{ | |
nvic_num = (IRQn_Type)((dev_iroute >> (ECIA_IA_NVIC_ID_BITPOS)) & 0xFFul); | |
} | |
was_masked = __get_PRIMASK(); | |
__disable_irq(); | |
pending = (uint8_t)(NVIC_GetPendingIRQ(nvic_num)); | |
NVIC_ClearPendingIRQ(nvic_num); | |
__DSB(); | |
if (!was_masked) { | |
__enable_irq(); | |
} | |
return pending; | |
} | |
/* ------------------------------------------------------------------------------- */ | |
/* end interrupt_api.c */ | |
/** @} | |
*/ |