1
0
Fork 0
mirror of https://github.com/Atmosphere-NX/Atmosphere.git synced 2024-11-15 08:26:56 +00:00
Atmosphere/thermosphere/src/irq.c

164 lines
4.8 KiB
C
Raw Normal View History

2019-08-10 23:56:49 +01:00
/*
* Copyright (c) 2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "irq.h"
#include "platform/interrupt_config.h"
#include "core_ctx.h"
#include "debug_log.h"
IrqManager g_irqManager = {0};
static void initGic(void)
{
// Reinits the GICD and GICC (for non-secure mode, obviously)
if (currentCoreCtx->isBootCore && !currentCoreCtx->warmboot) {
initGicV2Pointers(&g_irqManager.gic);
// Disable interrupt handling & global interrupt distribution
g_irqManager.gic.gicd->ctlr = 0;
// Get some info
g_irqManager.numSharedInterrupts = 32 * (g_irqManager.gic.gicd->typer & 0x1F); // number of interrupt lines / 32
// unimplemented priority bits (lowest significant) are RAZ/WI
g_irqManager.gic.gicd->ipriorityr[0] = 0xFF;
g_irqManager.numPriorityLevels = (u8)BIT(__builtin_popcount(g_irqManager.gic.gicd->ipriorityr[0]));
// same thing for CPU interfaces targets (save for ITARGETSR registers corresponding to SGIs)
g_irqManager.gic.gicd->itargetsr[32] = 0xFF;
g_irqManager.numCpuInterfaces = (u8)__builtin_popcount(g_irqManager.gic.gicd->itargetsr[32]);
}
volatile ArmGicV2Controller *gicc = g_irqManager.gic.gicc;
volatile ArmGicV2Distributor *gicd = g_irqManager.gic.gicd;
// Only one core will reset the GIC state for the shared peripheral interrupts
u32 numInterrupts = 32;
if (currentCoreCtx->isBootCore) {
numInterrupts += g_irqManager.numSharedInterrupts;
}
// Filter all interrupts
gicc->pmr = 0;
// Disable interrupt preemption
gicc->bpr = 7;
// Note: the GICD I...n regs are banked for private interrupts
// Disable all interrupts, clear active status, clear pending status, also clear secure regs igroupr to be sure
for (u32 i = 0; i < numInterrupts / 32; i++) {
gicd->icenabler[i] = 0xFFFFFFFF;
gicd->icactiver[i] = 0xFFFFFFFF;
gicd->icpendr[i] = 0xFFFFFFFF;
gicd->igroupr[i] = 0x00000000;
}
// Set priorities to lowest
for (u32 i = 0; i < numInterrupts; i++) {
gicd->ipriorityr[i] = 0xFF;
}
// Reset icfgr, itargetsr for shared peripheral interrupts
for (u32 i = 32 / 16; i < numInterrupts / 16; i++) {
gicd->icfgr[i] = 0;
}
for (u32 i = 32; i < numInterrupts; i++) {
gicd->itargetsr[i] = 0;
}
// Now, reenable interrupts
// Enable the distributor
if (currentCoreCtx->isBootCore) {
gicd->ctlr = 1;
}
// Enable the CPU interface. Set EOIModeNS=1 (split prio drop & deactivate priority)
gicc->ctlr = BIT(9) | 1;
// Disable interrupt filtering
gicc->pmr = 0xFF;
currentCoreCtx->gicInterfaceId = gicd->itargetsr[0];
}
static void configureInterrupt(u16 id, u8 prio, bool isLevelSensitive)
{
volatile ArmGicV2Distributor *gicd = g_irqManager.gic.gicd;
gicd->icenabler[id / 32] |= BIT(id % 32);
if (id >= 32) {
gicd->icfgr[id / 16] &= ~3 << (2 * (id % 16));
gicd->icfgr[id / 16] |= (!isLevelSensitive ? 2 : 0) << (2 * (id % 16));
}
if (id >= 16) {
gicd->itargetsr[id] |= currentCoreCtx->gicInterfaceId;
}
gicd->icpendr[id / 32] |= BIT(id % 32);
gicd->ipriorityr[id] = prio;
gicd->isenabler[id / 32] |= BIT(id % 32);
}
void initIrq(void)
{
u64 flags = recursiveSpinlockLockMaskIrq(&g_irqManager.lock);
initGic();
// Configure the interrupts we use here
configureInterrupt(0, 0, false);
configureInterrupt(GIC_IRQID_MAINTENANCE, 0, true);
recursiveSpinlockUnlockRestoreIrq(&g_irqManager.lock, flags);
}
void handleIrqException(ExceptionStackFrame *frame, bool isLowerEl, bool isA32)
{
(void)isLowerEl;
(void)isA32;
volatile ArmGicV2Controller *gicc = g_irqManager.gic.gicc;
// Acknowledge the interrupt. Interrupt goes from pending to active.
u32 iar = gicc->iar;
u32 irqId = iar & 0x3FF;
DEBUG("Received irq %x\n", irqId);
if (irqId == GIC_IRQID_SPURIOUS) {
// Spurious interrupt received
return;
}
bool isGuestInterrupt = false;
// TODO: handle the interrupt if it's a host interrupt
// Priority drop
gicc->eoir = iar;
if (!isGuestInterrupt) {
// Deactivate the interrupt
gicc->dir = iar;
} else {
// TODO
}
}