405 lines
8.9 KiB
C
405 lines
8.9 KiB
C
/**************************************************************************//**
|
|
* @file irq_ctrl_gic.c
|
|
* @brief Interrupt controller handling implementation for GIC
|
|
* @version V1.0.1
|
|
* @date 9. April 2018
|
|
******************************************************************************/
|
|
/*
|
|
* Copyright (c) 2017 ARM Limited. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
|
* not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include "cmsis.h"
|
|
#include "irq_ctrl.h"
|
|
|
|
#if defined(__GIC_PRESENT) && (__GIC_PRESENT == 1U)
|
|
|
|
/// Number of implemented interrupt lines
|
|
#define IRQ_GIC_LINE_COUNT USER_IRQn_QTY
|
|
|
|
static IRQHandler_t IRQTable[IRQ_GIC_LINE_COUNT] = { 0U };
|
|
static uint32_t IRQ_ID0;
|
|
|
|
/// Initialize interrupt controller.
|
|
__WEAK int32_t IRQ_Initialize (void) {
|
|
uint32_t i;
|
|
|
|
for (i = 0U; i < IRQ_GIC_LINE_COUNT; i++) {
|
|
IRQTable[i] = (IRQHandler_t)NULL;
|
|
}
|
|
GIC_Enable();
|
|
return (0);
|
|
}
|
|
|
|
|
|
/// Register interrupt handler.
|
|
__WEAK int32_t IRQ_SetHandler (IRQn_ID_t irqn, IRQHandler_t handler) {
|
|
int32_t status;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
IRQTable[irqn] = handler;
|
|
status = 0;
|
|
} else {
|
|
status = -1;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/// Get the registered interrupt handler.
|
|
__WEAK IRQHandler_t IRQ_GetHandler (IRQn_ID_t irqn) {
|
|
IRQHandler_t h;
|
|
|
|
// Ignore CPUID field (software generated interrupts)
|
|
irqn &= 0x3FFU;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
h = IRQTable[irqn];
|
|
} else {
|
|
h = (IRQHandler_t)0;
|
|
}
|
|
|
|
return (h);
|
|
}
|
|
|
|
|
|
/// Enable interrupt.
|
|
__WEAK int32_t IRQ_Enable (IRQn_ID_t irqn) {
|
|
int32_t status;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
GIC_EnableIRQ ((IRQn_Type)irqn);
|
|
status = 0;
|
|
} else {
|
|
status = -1;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/// Disable interrupt.
|
|
__WEAK int32_t IRQ_Disable (IRQn_ID_t irqn) {
|
|
int32_t status;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
GIC_DisableIRQ ((IRQn_Type)irqn);
|
|
status = 0;
|
|
} else {
|
|
status = -1;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/// Get interrupt enable state.
|
|
__WEAK uint32_t IRQ_GetEnableState (IRQn_ID_t irqn) {
|
|
uint32_t enable;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
enable = GIC_GetEnableIRQ((IRQn_Type)irqn);
|
|
} else {
|
|
enable = 0U;
|
|
}
|
|
|
|
return (enable);
|
|
}
|
|
|
|
|
|
/// Configure interrupt request mode.
|
|
__WEAK int32_t IRQ_SetMode (IRQn_ID_t irqn, uint32_t mode) {
|
|
uint32_t val;
|
|
uint8_t cfg;
|
|
uint8_t secure;
|
|
uint8_t cpu;
|
|
int32_t status = 0;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
// Check triggering mode
|
|
val = (mode & IRQ_MODE_TRIG_Msk);
|
|
|
|
if (val == IRQ_MODE_TRIG_LEVEL) {
|
|
cfg = 0x00U;
|
|
} else if (val == IRQ_MODE_TRIG_EDGE) {
|
|
cfg = 0x02U;
|
|
} else {
|
|
cfg = 0x00U;
|
|
status = -1;
|
|
}
|
|
|
|
// Check interrupt type
|
|
val = mode & IRQ_MODE_TYPE_Msk;
|
|
|
|
if (val != IRQ_MODE_TYPE_IRQ) {
|
|
status = -1;
|
|
}
|
|
|
|
// Check interrupt domain
|
|
val = mode & IRQ_MODE_DOMAIN_Msk;
|
|
|
|
if (val == IRQ_MODE_DOMAIN_NONSECURE) {
|
|
secure = 0U;
|
|
} else {
|
|
// Check security extensions support
|
|
val = GIC_DistributorInfo() & (1UL << 10U);
|
|
|
|
if (val != 0U) {
|
|
// Security extensions are supported
|
|
secure = 1U;
|
|
} else {
|
|
secure = 0U;
|
|
status = -1;
|
|
}
|
|
}
|
|
|
|
// Check interrupt CPU targets
|
|
val = mode & IRQ_MODE_CPU_Msk;
|
|
|
|
if (val == IRQ_MODE_CPU_ALL) {
|
|
cpu = 0xFFU;
|
|
} else {
|
|
cpu = val >> IRQ_MODE_CPU_Pos;
|
|
}
|
|
|
|
// Apply configuration if no mode error
|
|
if (status == 0) {
|
|
GIC_SetConfiguration((IRQn_Type)irqn, cfg);
|
|
GIC_SetTarget ((IRQn_Type)irqn, cpu);
|
|
|
|
if (secure != 0U) {
|
|
GIC_SetGroup ((IRQn_Type)irqn, secure);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/// Get interrupt mode configuration.
|
|
__WEAK uint32_t IRQ_GetMode (IRQn_ID_t irqn) {
|
|
uint32_t mode;
|
|
uint32_t val;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
mode = IRQ_MODE_TYPE_IRQ;
|
|
|
|
// Get trigger mode
|
|
val = GIC_GetConfiguration((IRQn_Type)irqn);
|
|
|
|
if ((val & 2U) != 0U) {
|
|
// Corresponding interrupt is edge triggered
|
|
mode |= IRQ_MODE_TRIG_EDGE;
|
|
} else {
|
|
// Corresponding interrupt is level triggered
|
|
mode |= IRQ_MODE_TRIG_LEVEL;
|
|
}
|
|
|
|
// Get interrupt CPU targets
|
|
mode |= GIC_GetTarget ((IRQn_Type)irqn) << IRQ_MODE_CPU_Pos;
|
|
|
|
} else {
|
|
mode = IRQ_MODE_ERROR;
|
|
}
|
|
|
|
return (mode);
|
|
}
|
|
|
|
|
|
/// Get ID number of current interrupt request (IRQ).
|
|
__WEAK IRQn_ID_t IRQ_GetActiveIRQ (void) {
|
|
IRQn_ID_t irqn;
|
|
uint32_t prio;
|
|
|
|
/* Dummy read to avoid GIC 390 errata 801120 */
|
|
GIC_GetHighPendingIRQ();
|
|
|
|
irqn = GIC_AcknowledgePending();
|
|
|
|
__DSB();
|
|
|
|
/* Workaround GIC 390 errata 733075 (GIC-390_Errata_Notice_v6.pdf, 09-Jul-2014) */
|
|
/* The following workaround code is for a single-core system. It would be */
|
|
/* different in a multi-core system. */
|
|
/* If the ID is 0 or 0x3FE or 0x3FF, then the GIC CPU interface may be locked-up */
|
|
/* so unlock it, otherwise service the interrupt as normal. */
|
|
/* Special IDs 1020=0x3FC and 1021=0x3FD are reserved values in GICv1 and GICv2 */
|
|
/* so will not occur here. */
|
|
|
|
if ((irqn == 0) || (irqn >= 0x3FE)) {
|
|
/* Unlock the CPU interface with a dummy write to Interrupt Priority Register */
|
|
prio = GIC_GetPriority((IRQn_Type)0);
|
|
GIC_SetPriority ((IRQn_Type)0, prio);
|
|
|
|
__DSB();
|
|
|
|
if ((irqn == 0U) && ((GIC_GetIRQStatus ((IRQn_Type)irqn) & 1U) != 0U) && (IRQ_ID0 == 0U)) {
|
|
/* If the ID is 0, is active and has not been seen before */
|
|
IRQ_ID0 = 1U;
|
|
}
|
|
/* End of Workaround GIC 390 errata 733075 */
|
|
}
|
|
|
|
return (irqn);
|
|
}
|
|
|
|
|
|
/// Get ID number of current fast interrupt request (FIQ).
|
|
__WEAK IRQn_ID_t IRQ_GetActiveFIQ (void) {
|
|
return ((IRQn_ID_t)-1);
|
|
}
|
|
|
|
|
|
/// Signal end of interrupt processing.
|
|
__WEAK int32_t IRQ_EndOfInterrupt (IRQn_ID_t irqn) {
|
|
int32_t status;
|
|
IRQn_Type irq = (IRQn_Type)irqn;
|
|
|
|
irqn &= 0x3FFU;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
GIC_EndInterrupt (irq);
|
|
|
|
if (irqn == 0) {
|
|
IRQ_ID0 = 0U;
|
|
}
|
|
|
|
status = 0;
|
|
} else {
|
|
status = -1;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/// Set interrupt pending flag.
|
|
__WEAK int32_t IRQ_SetPending (IRQn_ID_t irqn) {
|
|
int32_t status;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
GIC_SetPendingIRQ ((IRQn_Type)irqn);
|
|
status = 0;
|
|
} else {
|
|
status = -1;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
/// Get interrupt pending flag.
|
|
__WEAK uint32_t IRQ_GetPending (IRQn_ID_t irqn) {
|
|
uint32_t pending;
|
|
|
|
if ((irqn >= 16) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
pending = GIC_GetPendingIRQ ((IRQn_Type)irqn);
|
|
} else {
|
|
pending = 0U;
|
|
}
|
|
|
|
return (pending & 1U);
|
|
}
|
|
|
|
|
|
/// Clear interrupt pending flag.
|
|
__WEAK int32_t IRQ_ClearPending (IRQn_ID_t irqn) {
|
|
int32_t status;
|
|
|
|
if ((irqn >= 16) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
GIC_ClearPendingIRQ ((IRQn_Type)irqn);
|
|
status = 0;
|
|
} else {
|
|
status = -1;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/// Set interrupt priority value.
|
|
__WEAK int32_t IRQ_SetPriority (IRQn_ID_t irqn, uint32_t priority) {
|
|
int32_t status;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
GIC_SetPriority ((IRQn_Type)irqn, priority);
|
|
status = 0;
|
|
} else {
|
|
status = -1;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/// Get interrupt priority.
|
|
__WEAK uint32_t IRQ_GetPriority (IRQn_ID_t irqn) {
|
|
uint32_t priority;
|
|
|
|
if ((irqn >= 0) && (irqn < (IRQn_ID_t)IRQ_GIC_LINE_COUNT)) {
|
|
priority = GIC_GetPriority ((IRQn_Type)irqn);
|
|
} else {
|
|
priority = IRQ_PRIORITY_ERROR;
|
|
}
|
|
|
|
return (priority);
|
|
}
|
|
|
|
|
|
/// Set priority masking threshold.
|
|
__WEAK int32_t IRQ_SetPriorityMask (uint32_t priority) {
|
|
GIC_SetInterfacePriorityMask (priority);
|
|
return (0);
|
|
}
|
|
|
|
|
|
/// Get priority masking threshold
|
|
__WEAK uint32_t IRQ_GetPriorityMask (void) {
|
|
return GIC_GetInterfacePriorityMask();
|
|
}
|
|
|
|
|
|
/// Set priority grouping field split point
|
|
__WEAK int32_t IRQ_SetPriorityGroupBits (uint32_t bits) {
|
|
int32_t status;
|
|
|
|
if (bits == IRQ_PRIORITY_Msk) {
|
|
bits = 7U;
|
|
}
|
|
|
|
if (bits < 8U) {
|
|
GIC_SetBinaryPoint (7U - bits);
|
|
status = 0;
|
|
} else {
|
|
status = -1;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
/// Get priority grouping field split point
|
|
__WEAK uint32_t IRQ_GetPriorityGroupBits (void) {
|
|
uint32_t bp;
|
|
|
|
bp = GIC_GetBinaryPoint() & 0x07U;
|
|
|
|
return (7U - bp);
|
|
}
|
|
|
|
#endif
|