Atmosphere/exosphere/interrupt.c
2018-02-24 07:06:29 -08:00

98 lines
3.1 KiB
C

#include <stdint.h>
#include <stdbool.h>
#include "utils.h"
#include "interrupt.h"
/* Global of registered handlers. */
struct {
unsigned int id;
void (*handler)(void);
} g_registered_interrupts[MAX_REGISTERED_INTERRUPTS] = { {0, NULL}, {0, NULL}, {0, NULL}, {0, NULL} };
/* Prototypes for internal (private) functions. */
unsigned int get_interrupt_id(void);
/* Initializes the GIC. TODO: This must be called during wakeup. */
void intr_initialize_gic(void) {
/* Setup interrupts 0-0x1F as nonsecure with highest non-secure priority. */
GICD_IGROUPR[0] = 0xFFFFFFFF;
for (unsigned int i = 0; i < 0x20; i++) {
GICD_IPRIORITYR[i] = GIC_PRI_HIGHEST;
}
/* Setup the GICC. */
GICC_CTLR = 0x1D9;
GICC_PMR = GIC_PRI_HIGHEST;
GICC_BPR = 7;
}
/* Sets an interrupt's group in the GICD. */
void intr_set_group(unsigned int id, int group) {
GICD_IGROUPR[id >> 5] = (GICD_IGROUPR[id >> 5] & (~(1 << (id & 0x1F)))) | ((group & 1) << (id & 0x1F));
}
/* Sets an interrupt id as pending in the GICD. */
void intr_set_pending(unsigned int id) {
GICD_ISPENDR[id >> 5] = 1 << (id & 0x1F);
}
/* Sets an interrupt's priority in the GICD. */
void intr_set_priority(unsigned int id, uint8_t priority) {
GICD_IPRIORITYR[id] = priority;
}
/* Sets an interrupt's target CPU mask in the GICD. */
void intr_set_cpu_mask(unsigned int id, uint8_t mask) {
GICD_ITARGETSR[id] = mask;
}
/* Sets an interrupt's edge/level bits in the GICD. */
void intr_set_edge_level(unsigned int id, int edge_level) {
GICD_ICFGR[id >> 4] = (GICD_ICFGR[id >> 4] & (~(3 << ((id & 0xF) << 1))) | (((edge_level & 1) << 1) << ((id & 0xF) << 1));
}
/* Sets an interrupt's enabled status in the GICD. */
void intr_set_enabled(unsigned int id, int enabled) {
GICD_ISENABLER[id >> 5] = (enabled & 1) << (id & 0x1F);
}
/* To be called by FIQ handler. */
void handle_registered_interrupt(void) {
unsigned int interrupt_id = get_interrupt_id();
if (interrupt_id <= 0xDF) {
bool found_handler = false;
for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) {
if (g_registered_interrupts[i].id == interrupt_id) {
found_handler = true;
g_registered_interrupts[i].handler();
/* Mark that interrupt is done. */
GICC_EOIR = interrupt_id;
break;
}
}
/* We must have found a handler, or something went wrong. */
if (!found_handler) {
generic_panic();
}
}
}
/* Registers an interrupt into the global. */
void intr_register_handler(unsigned int id, void (*handler)(void)) {
bool registered_handler = false;
for (unsigned int i = 0; i < MAX_REGISTERED_INTERRUPTS; i++) {
if (g_registered_interrupts[i].id == 0) {
g_registered_interrupts[i].handler = handler;
g_registered_interrupts[i].id = id;
registered_handler = true;
break;
}
}
/* Failure to register is an error condition. */
if (!registered_handler) {
generic_panic();
}
}