2019-04-23 17:38:22 -03:00
|
|
|
/*
|
|
|
|
* This file is part of GodMode9
|
|
|
|
* Copyright (C) 2017-2019 Wolfvak
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that 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/>.
|
|
|
|
*/
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
#include <common.h>
|
2019-04-11 18:16:20 -03:00
|
|
|
#include <arm.h>
|
|
|
|
|
2020-11-18 16:47:00 -03:00
|
|
|
#include <stdatomic.h>
|
|
|
|
|
2019-04-11 18:16:20 -03:00
|
|
|
#include "arm/gic.h"
|
|
|
|
|
2020-11-18 16:47:00 -03:00
|
|
|
#include "system/event.h"
|
|
|
|
|
2019-04-11 18:16:20 -03:00
|
|
|
/* Generic Interrupt Controller Registers */
|
|
|
|
#define REG_GIC(cpu, o, t) REG_ARM_PMR(0x200 + ((cpu) * 0x100) + (o), t)
|
|
|
|
|
|
|
|
#define REG_GIC_CONTROL(c) (*REG_GIC(c, 0x00, u32))
|
|
|
|
#define REG_GIC_PRIOMASK(c) (*REG_GIC(c, 0x04, u32))
|
2020-07-19 11:59:52 -03:00
|
|
|
#define REG_GIC_POI(c) (*REG_GIC(c, 0x08, u32))
|
2019-04-11 18:16:20 -03:00
|
|
|
#define REG_GIC_IRQACK(c) (*REG_GIC(c, 0x0C, u32))
|
|
|
|
#define REG_GIC_IRQEND(c) (*REG_GIC(c, 0x10, u32))
|
|
|
|
#define REG_GIC_LASTPRIO(c) (*REG_GIC(c, 0x14, u32))
|
|
|
|
#define REG_GIC_PENDING(c) (*REG_GIC(c, 0x18, u32))
|
|
|
|
|
|
|
|
#define GIC_THIS_CPU_ALIAS (-1)
|
|
|
|
#define GIC_IRQ_SPURIOUS (1023)
|
|
|
|
|
|
|
|
/* Interrupt Distributor Registers */
|
|
|
|
#define REG_DIC(off, type) REG_ARM_PMR(0x1000 + (off), type)
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
#define REG_DIC_CONTROL (*REG_DIC(0x00, u32))
|
|
|
|
#define REG_DIC_TYPE (*REG_DIC(0x04, u32))
|
|
|
|
#define REG_DIC_SETENABLE REG_DIC(0x100, u32) // 32 intcfg per reg
|
2019-04-11 18:16:20 -03:00
|
|
|
#define REG_DIC_CLRENABLE REG_DIC(0x180, u32)
|
|
|
|
#define REG_DIC_SETPENDING REG_DIC(0x200, u32)
|
|
|
|
#define REG_DIC_CLRPENDING REG_DIC(0x280, u32)
|
2020-07-19 11:59:52 -03:00
|
|
|
#define REG_DIC_PRIORITY REG_DIC(0x400, u8) // 1 intcfg per reg (in upper 4 bits)
|
|
|
|
#define REG_DIC_TARGETCPU REG_DIC(0x800, u8) // 1 intcfg per reg
|
|
|
|
#define REG_DIC_CFGREG REG_DIC(0xC00, u32) // 16 intcfg per reg
|
|
|
|
#define REG_DIC_SOFTINT (*REG_DIC(0xF00, u32))
|
|
|
|
|
|
|
|
// used only on reset routines
|
|
|
|
#define REG_DIC_PRIORITY32 REG_DIC(0x400, u32) // 4 intcfg per reg (in upper 4 bits)
|
|
|
|
#define REG_DIC_TARGETCPU32 REG_DIC(0x800, u32) // 4 intcfg per reg
|
|
|
|
|
|
|
|
#define GIC_PRIO_NEVER32 \
|
|
|
|
(GIC_PRIO_NEVER | (GIC_PRIO_NEVER << 8) | \
|
|
|
|
(GIC_PRIO_NEVER << 16) | (GIC_PRIO_NEVER << 24))
|
|
|
|
#define GIC_PRIO_HIGH32 \
|
|
|
|
(GIC_PRIO_HIGHEST | (GIC_PRIO_HIGHEST << 8) | \
|
|
|
|
(GIC_PRIO_HIGHEST << 16) | (GIC_PRIO_HIGHEST << 24))
|
|
|
|
|
|
|
|
/* CPU source ID is present in Interrupt Acknowledge register? */
|
|
|
|
#define IRQN_SRC_MASK (0x7 << 10)
|
2019-04-11 18:16:20 -03:00
|
|
|
|
|
|
|
/* Interrupt Handling */
|
|
|
|
#define LOCAL_IRQS (32)
|
|
|
|
#define DIC_MAX_IRQ (LOCAL_IRQS + MAX_IRQ)
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
#define COREMASK_VALID(x) (((x) > 0) && ((x) < BIT(MAX_CPU)))
|
|
|
|
|
2019-04-11 18:16:20 -03:00
|
|
|
#define IRQN_IS_VALID(n) ((n) < DIC_MAX_IRQ)
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
static gicIrqHandler gicIrqHandlers[DIC_MAX_IRQ];
|
2020-11-18 16:47:00 -03:00
|
|
|
static _Atomic(u32) gicIrqPending[DIC_MAX_IRQ / 32];
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
static struct {
|
|
|
|
u8 tgt;
|
|
|
|
u8 prio;
|
|
|
|
} gicIrqConfig[DIC_MAX_IRQ];
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// gets used whenever a NULL pointer is passed to gicEnableInterrupt
|
|
|
|
static void gicDummyHandler(u32 irqn) { (void)irqn; return; }
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-08-02 11:40:18 -03:00
|
|
|
static const struct {
|
|
|
|
u8 low, high, mode;
|
|
|
|
} gicDefaultIrqCfg[] = {
|
|
|
|
{ .low = 0x00, .high = 0x1F, .mode = GIC_RISINGEDGE_NN },
|
|
|
|
{ .low = 0x20, .high = 0x23, .mode = GIC_LEVELHIGH_1N },
|
|
|
|
{ .low = 0x24, .high = 0x24, .mode = GIC_RISINGEDGE_1N },
|
|
|
|
{ .low = 0x25, .high = 0x27, .mode = GIC_LEVELHIGH_1N },
|
|
|
|
{ .low = 0x28, .high = 0x2D, .mode = GIC_RISINGEDGE_1N },
|
|
|
|
{ .low = 0x30, .high = 0x3B, .mode = GIC_LEVELHIGH_1N },
|
|
|
|
{ .low = 0x40, .high = 0x4E, .mode = GIC_RISINGEDGE_1N },
|
|
|
|
{ .low = 0x4F, .high = 0x4F, .mode = GIC_LEVELHIGH_1N },
|
|
|
|
{ .low = 0x50, .high = 0x57, .mode = GIC_RISINGEDGE_1N },
|
|
|
|
{ .low = 0x58, .high = 0x58, .mode = GIC_LEVELHIGH_1N },
|
|
|
|
{ .low = 0x59, .high = 0x75, .mode = GIC_RISINGEDGE_1N },
|
|
|
|
{ .low = 0x76, .high = 0x77, .mode = GIC_LEVELHIGH_1N },
|
|
|
|
{ .low = 0x78, .high = 0x78, .mode = GIC_RISINGEDGE_1N },
|
|
|
|
{ .low = 0x79, .high = 0x7d, .mode = GIC_LEVELHIGH_1N },
|
|
|
|
};
|
|
|
|
|
|
|
|
static u8 gicGetDefaultIrqCfg(u32 irqn) {
|
|
|
|
for (unsigned i = 0; i < countof(gicDefaultIrqCfg); i++) {
|
|
|
|
if ((irqn >= gicDefaultIrqCfg[i].low) && (irqn <= gicDefaultIrqCfg[i].high))
|
|
|
|
return gicDefaultIrqCfg[i].mode;
|
|
|
|
}
|
|
|
|
// TODO: would it be considerably faster to use bsearch?
|
|
|
|
return GIC_RISINGEDGE_1N;
|
|
|
|
}
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
void gicTopHandler(void)
|
2019-04-11 18:16:20 -03:00
|
|
|
{
|
2020-07-19 11:59:52 -03:00
|
|
|
while(1) {
|
2020-11-18 16:47:00 -03:00
|
|
|
u32 irqn, irqsource, index, mask;
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
/**
|
|
|
|
If more than one of these CPUs reads the Interrupt Acknowledge Register at the
|
|
|
|
same time, they can all acknowledge the same interrupt. The interrupt service
|
|
|
|
routine must ensure that only one of them tries to process the interrupt, with the
|
|
|
|
others returning after writing the ID to the End of Interrupt Register.
|
|
|
|
*/
|
2020-11-18 16:47:00 -03:00
|
|
|
irqsource = REG_GIC_IRQACK(GIC_THIS_CPU_ALIAS);
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-11-18 16:47:00 -03:00
|
|
|
if (irqsource == GIC_IRQ_SPURIOUS) // no further processing is needed
|
2019-04-11 18:16:20 -03:00
|
|
|
break;
|
|
|
|
|
2020-11-18 16:47:00 -03:00
|
|
|
irqn = irqsource & ~IRQN_SRC_MASK;
|
|
|
|
|
|
|
|
index = irqn / 32;
|
|
|
|
mask = BIT(irqn % 32);
|
|
|
|
atomic_fetch_or(&gicIrqPending[index], mask);
|
|
|
|
|
|
|
|
(gicIrqHandlers[irqn])(irqsource);
|
2020-07-19 11:59:52 -03:00
|
|
|
// if the id is < 16, the source CPU can be obtained from irqn
|
|
|
|
// if the handler isn't set, it'll try to branch to 0 and trigger a prefetch abort
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-11-18 16:47:00 -03:00
|
|
|
REG_GIC_IRQEND(GIC_THIS_CPU_ALIAS) = irqsource;
|
2019-04-11 18:16:20 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
void gicGlobalReset(void)
|
2019-04-11 18:16:20 -03:00
|
|
|
{
|
2020-07-19 11:59:52 -03:00
|
|
|
u32 dic_type;
|
|
|
|
unsigned gicn, intn;
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
dic_type = REG_DIC_TYPE;
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// number of local controllers
|
|
|
|
gicn = ((dic_type >> 5) & 3) + 1;
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// number of interrupt lines (up to 224 external + 32 fixed internal per CPU)
|
|
|
|
intn = ((dic_type & 7) + 1) * 32;
|
|
|
|
|
|
|
|
// clamp it down to the amount of CPUs designed to handle
|
2019-04-11 18:16:20 -03:00
|
|
|
if (gicn > MAX_CPU)
|
|
|
|
gicn = MAX_CPU;
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// clear the interrupt handler and config table
|
2020-11-18 16:47:00 -03:00
|
|
|
getEventIRQ()->reset();
|
2020-07-19 11:59:52 -03:00
|
|
|
memset(gicIrqHandlers, 0, sizeof(gicIrqHandlers));
|
|
|
|
memset(gicIrqConfig, 0, sizeof(gicIrqConfig));
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// disable all MP11 GICs
|
|
|
|
for (unsigned i = 0; i < gicn; i++)
|
2019-04-11 18:16:20 -03:00
|
|
|
REG_GIC_CONTROL(i) = 0;
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// disable the main DIC
|
2019-04-11 18:16:20 -03:00
|
|
|
REG_DIC_CONTROL = 0;
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// clear all external interrupts
|
|
|
|
for (unsigned i = 1; i < (intn / 32); i++) {
|
2019-04-11 18:16:20 -03:00
|
|
|
REG_DIC_CLRENABLE[i] = ~0;
|
|
|
|
REG_DIC_CLRPENDING[i] = ~0;
|
|
|
|
}
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// reset all external priorities to highest by default
|
|
|
|
// clear target processor regs
|
|
|
|
for (unsigned i = 4; i < (intn / 4); i++) {
|
|
|
|
REG_DIC_PRIORITY32[i] = GIC_PRIO_HIGH32;
|
|
|
|
REG_DIC_TARGETCPU32[i] = 0;
|
2019-04-11 18:16:20 -03:00
|
|
|
}
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// set all interrupts to active level triggered in N-N model
|
|
|
|
for (unsigned i = 16; i < (intn / 16); i++)
|
|
|
|
REG_DIC_CFGREG[i] = 0;
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// re enable the main DIC
|
2019-04-11 18:16:20 -03:00
|
|
|
REG_DIC_CONTROL = 1;
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
for (unsigned i = 0; i < gicn; i++) {
|
|
|
|
// compare all priority bits
|
2019-04-11 18:16:20 -03:00
|
|
|
REG_GIC_POI(i) = 3;
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// don't mask any interrupt with low priority
|
2019-04-11 18:16:20 -03:00
|
|
|
REG_GIC_PRIOMASK(i) = 0xF0;
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// enable all the MP11 GICs
|
2019-04-11 18:16:20 -03:00
|
|
|
REG_GIC_CONTROL(i) = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
void gicLocalReset(void)
|
2019-04-11 18:16:20 -03:00
|
|
|
{
|
|
|
|
u32 irq_s;
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// disable all local interrupts
|
2019-04-11 18:16:20 -03:00
|
|
|
REG_DIC_CLRENABLE[0] = ~0;
|
|
|
|
REG_DIC_CLRPENDING[0] = ~0;
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
for (unsigned i = 0; i < 4; i++) {
|
|
|
|
REG_DIC_PRIORITY32[i] = GIC_PRIO_HIGH32;
|
|
|
|
// local IRQs are always unmasked by default
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// REG_DIC_TARGETCPU[i] = 0;
|
|
|
|
// not needed, always read as corresponding MP11 core
|
|
|
|
}
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
// ack until it gets a spurious IRQ
|
2019-04-11 18:16:20 -03:00
|
|
|
do {
|
|
|
|
irq_s = REG_GIC_PENDING(GIC_THIS_CPU_ALIAS);
|
|
|
|
REG_GIC_IRQEND(GIC_THIS_CPU_ALIAS) = irq_s;
|
|
|
|
} while(irq_s != GIC_IRQ_SPURIOUS);
|
|
|
|
}
|
|
|
|
|
2020-08-02 11:40:18 -03:00
|
|
|
static void gicSetIrqCfg(u32 irqn) {
|
2020-07-19 11:59:52 -03:00
|
|
|
u32 smt, cfg;
|
|
|
|
|
|
|
|
smt = irqn & 15;
|
|
|
|
cfg = REG_DIC_CFGREG[irqn / 16];
|
|
|
|
cfg &= ~(3 << smt);
|
2020-08-02 11:40:18 -03:00
|
|
|
cfg |= gicGetDefaultIrqCfg(irqn) << smt;
|
2020-07-19 11:59:52 -03:00
|
|
|
REG_DIC_CFGREG[irqn / 16] = cfg;
|
|
|
|
}
|
|
|
|
|
2020-08-02 11:40:18 -03:00
|
|
|
void gicSetInterruptConfig(u32 irqn, u32 coremask, u32 prio, gicIrqHandler handler)
|
2019-04-11 18:16:20 -03:00
|
|
|
{
|
2020-07-19 11:59:52 -03:00
|
|
|
if (handler == NULL) // maybe add runtime ptr checks here too?
|
|
|
|
handler = gicDummyHandler;
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
gicIrqConfig[irqn].tgt = coremask;
|
|
|
|
gicIrqConfig[irqn].prio = prio;
|
|
|
|
gicIrqHandlers[irqn] = handler;
|
2019-04-11 18:16:20 -03:00
|
|
|
}
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
void gicClearInterruptConfig(u32 irqn)
|
2019-04-11 18:16:20 -03:00
|
|
|
{
|
2020-07-19 11:59:52 -03:00
|
|
|
memset(&gicIrqConfig[irqn], 0, sizeof(gicIrqConfig[irqn]));
|
|
|
|
gicIrqHandlers[irqn] = NULL;
|
|
|
|
}
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
void gicEnableInterrupt(u32 irqn)
|
|
|
|
{
|
|
|
|
REG_DIC_PRIORITY[irqn] = gicIrqConfig[irqn].prio;
|
|
|
|
REG_DIC_TARGETCPU[irqn] = gicIrqConfig[irqn].tgt;
|
2020-08-02 11:40:18 -03:00
|
|
|
gicSetIrqCfg(irqn);
|
2019-04-11 18:16:20 -03:00
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
REG_DIC_CLRPENDING[irqn / 32] |= BIT(irqn & 0x1F);
|
|
|
|
REG_DIC_SETENABLE[irqn / 32] |= BIT(irqn & 0x1F);
|
|
|
|
}
|
|
|
|
|
|
|
|
void gicDisableInterrupt(u32 irqn)
|
|
|
|
{
|
|
|
|
REG_DIC_CLRENABLE[irqn / 32] |= BIT(irqn & 0x1F);
|
|
|
|
REG_DIC_CLRPENDING[irqn / 32] |= BIT(irqn & 0x1F);
|
2019-04-11 18:16:20 -03:00
|
|
|
}
|
|
|
|
|
2020-07-19 11:59:52 -03:00
|
|
|
void gicTriggerSoftInterrupt(u32 softirq)
|
2019-04-11 18:16:20 -03:00
|
|
|
{
|
2020-07-19 11:59:52 -03:00
|
|
|
REG_DIC_SOFTINT = softirq;
|
2019-04-11 18:16:20 -03:00
|
|
|
}
|
2020-11-18 16:47:00 -03:00
|
|
|
|
|
|
|
static void irqEvReset(void) {
|
|
|
|
memset(&gicIrqPending, 0, sizeof(gicIrqPending));
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 irqEvTest(u32 param, u32 clear) {
|
|
|
|
u32 index, tstmask, clrmask;
|
|
|
|
|
|
|
|
if (param >= DIC_MAX_IRQ)
|
|
|
|
bkpt;
|
|
|
|
index = param / 32;
|
|
|
|
tstmask = BIT(param % 32);
|
|
|
|
clrmask = clear ? tstmask : 0;
|
|
|
|
return !!(atomic_fetch_and(&gicIrqPending[index], ~clrmask) & tstmask);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const EventInterface evIRQ = {
|
|
|
|
.reset = irqEvReset,
|
|
|
|
.test = irqEvTest
|
|
|
|
};
|
|
|
|
|
|
|
|
const EventInterface *getEventIRQ(void) {
|
|
|
|
return &evIRQ;
|
|
|
|
}
|