/*
* This file is part of fastboot 3DS
* Copyright (C) 2017 derrek, profi200
*
* 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 3 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/>.
*/
#include "types.h"
#include "arm11/hardware/interrupt.h"
#include "arm.h"
#include "arm11/hardware/cfg11.h"
IrqHandler irqHandlerTable[224] = {0}; // First 32 interrupts are private to each core (4 * 32).
// 96 external interrupts (total 128).
void IRQ_init(void)
{
// Disable the interrupt interface for this CPU
REG_CPU_II_CNT = 0;
if(!__getCpuId())
{
// Disable FIQs
REG_CFG11_FIQ_CNT = 1;
// Disable the global interrupt distributor
REG_GID_CNT = 0;
// Disable all 128 interrupts
REGs_GID_ENA_CLR[0] = 0xFFFFFFFFu; // Interrupts 0-15 cant be disabled
REGs_GID_ENA_CLR[1] = 0xFFFFFFFFu;
REGs_GID_ENA_CLR[2] = 0xFFFFFFFFu;
REGs_GID_ENA_CLR[3] = 0xFFFFFFFFu;
// Set all pending interrupts to inactive state
REGs_GID_PEN_CLR[0] = 0xFFFFFFFFu; // Interrupt 0-15 can't be set to inactive apparently
REGs_GID_PEN_CLR[1] = 0xFFFFFFFFu;
REGs_GID_PEN_CLR[2] = 0xFFFFFFFFu;
REGs_GID_PEN_CLR[3] = 0xFFFFFFFFu;
// Set all 128 interrupts to lowest priority
for(u32 i = 0; i < 32; i++) REGs_GID_IPRIO[i] = 0xE0E0E0E0u;
// Set all 128 interrupts to target no CPU.
// Interrupt 0-31 can't be changed
for(u32 i = 8; i < 32; i++) REGs_GID_ITARG[i] = 0;
// Set all interrupts to rising edge sensitive and 1-N software model
for(u32 i = 0; i < 8; i++) REGs_GID_ICONF[i] = 0xFFFFFFFFu;
// Enable the global interrupt distributor
REG_GID_CNT = 1;
}
else
{
REGs_GID_PEN_CLR[0] = 0xFFFFFFFFu;
for(u32 i = 0; i < 8; i++) REGs_GID_IPRIO[i] = 0xE0E0E0E0u;
REGs_GID_ICONF[0] = 0xFFFFFFFFu;
REGs_GID_ICONF[1] = 0xFFFFFFFFu;
}
// Mask no interrupt
REG_CPU_II_MASK = 0xF0;
// All priority bits are compared for pre-emption
REG_CPU_II_BIN_POI = 3;
// Enable the interrupt interface for this CPU
REG_CPU_II_CNT = 1;
// Get rid of all interrupts stuck in pending/active state
u32 tmp;
do
{
tmp = REG_CPU_II_AKN; // Aknowledge
REG_CPU_II_EOI = tmp; // End of interrupt
} while(tmp != 1023);
}
void IRQ_registerHandler(Interrupt id, u8 prio, u8 cpuMask, bool edgeTriggered, IrqHandler handler)
{
const u32 cpuId = __getCpuId();
if(!cpuMask) cpuMask = 1u<<cpuId;
const u32 oldState = enterCriticalSection();
if(handler) irqHandlerTable[(id < 32 ? 32 * cpuId + id : 96u + id)] = handler;
u32 shift = (id % 4 * 8) + 4;
u32 tmp = REGs_GID_IPRIO[id>>2] & ~(0xFu<<shift);
REGs_GID_IPRIO[id>>2] = tmp | (u32)prio<<shift;
shift = id % 4 * 8;
tmp = REGs_GID_ITARG[id>>2] & ~(0xFu<<shift);
REGs_GID_ITARG[id>>2] = tmp | (u32)cpuMask<<shift;
shift = (id % 16 * 2) + 1;
tmp = REGs_GID_ICONF[id>>4] & ~(1u<<shift);
REGs_GID_ICONF[id>>4] = tmp | (u32)edgeTriggered<<shift;
REGs_GID_ENA_SET[id>>5] = 1u<<(id % 32);
leaveCriticalSection(oldState);
}
void IRQ_unregisterHandler(Interrupt id)
{
const u32 oldState = enterCriticalSection();
REGs_GID_ENA_CLR[id>>5] = 1u<<(id % 32);
irqHandlerTable[(id < 32 ? 32 * __getCpuId() + id : 96u + id)] = (IrqHandler)NULL;
leaveCriticalSection(oldState);
}
void IRQ_enable(Interrupt id)
{
REGs_GID_ENA_SET[id>>5] = 1u<<(id % 32);
}
void IRQ_disable(Interrupt id)
{
REGs_GID_ENA_CLR[id>>5] = 1u<<(id % 32);
}
void IRQ_setPriority(Interrupt id, u8 prio)
{
const u32 oldState = enterCriticalSection();
u32 shift = (id % 4 * 8) + 4;
u32 tmp = REGs_GID_IPRIO[id>>2] & ~(0xFu<<shift);
REGs_GID_IPRIO[id>>2] = tmp | (u32)prio<<shift;
leaveCriticalSection(oldState);
}
void IRQ_softwareInterrupt(Interrupt id, u8 cpuMask)
{
REG_GID_SW_INT = (u32)cpuMask<<16 | id;
}