blob: c16000752681c92425e1d31a8ca923e7d88f6403 [file] [log] [blame]
#include "dlog.h"
#include "io.h"
#include "irq.h"
#define GICD_CTLR (0x00)
#define GICD_TYPER (0x04)
#define GICD_ISENABLER (0x100)
#define GICD_ICENABLER (0x180)
#define GICD_ICPENDR (0x280)
#define GICD_ICACTIVER (0x380)
#define GICD_IPRIORITYR (0x400)
#define GICD_ITARGETSR (0x800)
#define GICD_ICFGR (0xc00)
#define GICC_CTLR (0x000)
#define GICC_PMR (0x004)
#define GICC_IAR (0x00c)
#define GICC_EOIR (0x010)
struct irq_handle {
uint32_t iar;
};
/*
* Dismisses an irq that was signaled and is being processed.
*/
void irq_dismiss(struct irq_handle *h)
{
io_write(GICC_BASE + GICC_EOIR, h->iar);
}
/*
* Configures the given irq number before it can be enabled.
*/
void arch_irq_config(uint32_t num, enum irq_trigger t, enum irq_polarity p)
{
uint32_t v = io_read(GICD_BASE + GICD_ICFGR + (num / 16) * 4);
if (t == irq_trigger_level)
v &= ~(2u << ((num % 16) * 2));
else
v |= 2u << ((num % 16) * 2);
io_write(GICD_BASE + GICD_ICFGR + (num / 16) * 4, v);
}
/*
* Enables the given irq number such that interrupts will be signaled when its
* interrupt line is asserted. A caller must first configure the irq before
* enabling it.
*/
void irq_enable(uint32_t num)
{
io_write(GICD_BASE + GICD_ISENABLER + (num / 32) * 4,
(1u << (num % 32)));
}
/*
* Handles an interrupt signaled when the CPU was in a lower level (EL0 or EL1),
* it is called directly from the exception handler.
*
* The return value indicates whether a new vcpu should be scheduled.
*/
bool irq_handle_lower(void)
{
struct irq_handle h = {
.iar = io_read(GICC_BASE + GICC_IAR),
};
dlog("irq: %u\n", h.iar & 0x3ff);
return irq_handle(h.iar & 0x3ff, &h);
}
/*
* Initializes the GICv2 for use as the interrupt controller.
*/
void arch_irq_init_percpu(void)
{
uint32_t i;
uint32_t max = 32 * (1 + (io_read(GICD_BASE + GICD_TYPER) & 0x1f));
/* Disable all irqs, clear pending & active states. */
for (i = 0; i < (max + 31) / 32; i++) {
io_write(GICD_BASE + GICD_ICENABLER + i * 4, 0xffffffff);
io_write(GICD_BASE + GICD_ICACTIVER + i * 4, 0xffffffff);
io_write(GICD_BASE + GICD_ICPENDR + i * 4, 0xffffffff);
}
/* Set the priority to zero, and cpu target to cpu 0 by default. */
for (i = 0; i < (max + 3) / 4; i++) {
io_write(GICD_BASE + GICD_IPRIORITYR + i * 4, 0);
io_write(GICD_BASE + GICD_ITARGETSR + i * 4, 0x01010101);
}
/* Allow all irq levels to interrupt the current CPU. */
io_write(GICC_BASE + GICC_PMR, 0xff);
/* Enable distributor and CPU interfaces. */
io_write(GICD_BASE + GICD_CTLR, 1);
io_write(GICC_BASE + GICC_CTLR, 1);
}