mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-05-31 23:08:22 -04:00
thermosphere: handle physical IRQs
This commit is contained in:
parent
271d2a0ddb
commit
0a9a8c2f15
10 changed files with 267 additions and 12 deletions
163
thermosphere/src/irq.c
Normal file
163
thermosphere/src/irq.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue