rt-thread/libcpu/aarch64/common/gic/gic_pl400.c

369 lines
8.7 KiB
C

/*
* Copyright 2014, General Dynamics C4 Systems
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(GD_GPL)
*/
#include "gic_pl400.h"
#include "iomap.h"
#define BIT(n) (1ul<<(n))
#define MASK(n) (BIT(n)-1ul)
/* Setters/getters helpers */
#define IRQ_REG(IRQ) ((IRQ) / 32)
#define IRQ_BIT(IRQ) BIT((IRQ) % 32)
#define IRQ_MASK MASK(10)
#define IS_IRQ_VALID(X) (((X)&IRQ_MASK) < SPECIAL_IRQ_START)
#define CPU(X) (1<<(X))
#define TARGET_CPU_ALLINT(CPU) ( \
( ((CPU)&0xff)<<0 ) |\
( ((CPU)&0xff)<<8 ) |\
( ((CPU)&0xff)<<16 ) |\
( ((CPU)&0xff)<<24 ) \
)
#define TARGET_CPU0_ALLINT TARGET_CPU_ALLINT(CPU(0))
#define IRQ_SET_ALL 0xffffffff;
/* Special IRQ's */
#define SPECIAL_IRQ_START 1020
#define IRQ_NONE 1023
/* Memory map for GIC distributor */
struct gic_dist_map {
uint32_t enable; /* 0x000 */
uint32_t ic_type; /* 0x004 */
uint32_t dist_ident; /* 0x008 */
uint32_t res1[29]; /* [0x00C, 0x080) */
uint32_t group[32]; /* [0x080, 0x100) */
uint32_t enable_set[32]; /* [0x100, 0x180) */
uint32_t enable_clr[32]; /* [0x180, 0x200) */
uint32_t pending_set[32]; /* [0x200, 0x280) */
uint32_t pending_clr[32]; /* [0x280, 0x300) */
uint32_t active[32]; /* [0x300, 0x380) */
uint32_t active_clr[32]; /* [0x380, 0x400) */
uint32_t priority[255]; /* [0x400, 0x7FC) */
uint32_t res2; /* 0x7FC */
uint32_t targets[255]; /* [0x800, 0xBFC) */
uint32_t res3; /* 0xBFC */
uint32_t config[64]; /* [0xC00, 0xD00) */
uint32_t ppi_status; /* [0xD00, 0xD04) */
uint32_t spi_status[15]; /* [0xD04, 0xD40) */
uint32_t res4[112]; /* [0xD40, 0xF00) */
uint32_t sgi_control; /* 0xF00 */
uint32_t res5[3]; /* [0xF04, 0xF10) */
uint32_t sgi_pending_clr[4]; /* [0xF10, 0xF20) */
uint32_t sgi_pending_set[4]; /* [0xF20, 0xF30) */
uint32_t res10[40]; /* [0xF30, 0xFD0) */
uint32_t periph_id[8]; /* [0xFD0, 0xFF0) */
uint32_t component_id[4]; /* [0xFF0, 0xFFF] */
};
/* Memory map for GIC cpu interface */
struct gic_cpu_iface_map {
uint32_t icontrol; /* 0x000 */
uint32_t pri_msk_c; /* 0x004 */
uint32_t bp_c; /* 0x008 */
uint32_t int_ack; /* 0x00C */
uint32_t eoi; /* 0x010 */
uint32_t run_priority; /* 0x014 */
uint32_t hi_pend; /* 0x018 */
uint32_t ns_alias_bp_c; /* 0x01C */
uint32_t ns_alias_ack; /* 0x020 GIC_PL400 only */
uint32_t ns_alias_eoi; /* 0x024 GIC_PL400 only */
uint32_t ns_alias_hi_pend; /* 0x028 GIC_PL400 only */
uint32_t res1[41]; /* [0x02C, 0x0D0) */
uint32_t active_priority[4]; /* [0x0D0, 0xC0] GIC_PL400 only */
uint32_t ns_active_priority[4]; /* [0xE0,0xF0] GIC_PL400 only */
uint32_t res4[3]; /* [0xF0, 0xFC] */
uint32_t cpu_if_ident; /* 0x0FC */
uint32_t deactive; /* [0x1000] */
};
volatile struct gic_dist_map *gic_dist =
(volatile struct gic_dist_map*)(GIC_PL400_DISTRIBUTOR_PPTR);
volatile struct gic_cpu_iface_map *gic_cpuiface =
(volatile struct gic_cpu_iface_map*)(GIC_PL400_CONTROLLER_PPTR);
/* Helpers */
static inline int
is_irq_pending(irq_t irq)
{
int word = irq / 32;
int bit = irq & 0x1f;
return !!(gic_dist->pending_set[word] & BIT(bit));
}
static inline int
is_irq_active(irq_t irq)
{
int word = irq / 32;
int bit = irq & 0x1f;
return !!(gic_dist->active[word] & BIT(bit));
}
static inline int
is_irq_enabled(irq_t irq)
{
int word = irq / 32;
int bit = irq & 0x1f;
return !!(gic_dist->enable_set[word] & BIT(bit));
}
static inline int
is_irq_edge_triggered(irq_t irq)
{
int word = irq / 16;
int bit = ((irq & 0xf) * 2);
return !!(gic_dist->config[word] & BIT(bit + 1));
}
static inline int
is_irq_1_N(irq_t irq)
{
int word = irq / 16;
int bit = ((irq & 0xf) * 2);
return !!(gic_dist->config[word] & BIT(bit + 0));
}
static inline int
is_irq_N_N(irq_t irq)
{
return !(is_irq_1_N(irq));
}
static inline void
dist_pending_clr(irq_t irq)
{
int word = irq / 32;
int bit = irq & 0x1f;
/* Using |= here is detrimental to your health */
gic_dist->pending_clr[word] = BIT(bit);
}
static inline void
dist_pending_set(irq_t irq)
{
int word = irq / 32;
int bit = irq & 0x1f;
gic_dist->pending_set[word] = BIT(bit);
}
static inline void
dist_enable_clr(irq_t irq)
{
int word = irq / 32;
int bit = irq & 0x1f;
/* Using |= here is detrimental to your health */
gic_dist->enable_clr[word] = BIT(bit);
}
static inline void
dist_enable_set(irq_t irq)
{
int word = irq / 32;
int bit = irq & 0x1f;
gic_dist->enable_set[word] = BIT(bit);
}
/**
DONT_TRANSLATE
*/
static void dist_init(void)
{
int i;
int nirqs = 32 * ((gic_dist->ic_type & 0x1f) + 1);
gic_dist->enable = 0;
#if 0
/* configure to group 0 for security */
for (i = 0; i < nirqs; i += 32) {
gic_dist->group[i / 32] = 0xffffffff;
}
#endif
for (i = 0; i < nirqs; i += 32) {
/* disable */
gic_dist->enable_clr[i / 32] = IRQ_SET_ALL;
/* clear pending */
gic_dist->pending_clr[i / 32] = IRQ_SET_ALL;
}
/* reset interrupts priority */
for (i = 32; i < nirqs; i += 4) {
gic_dist->priority[i / 4] = 0x0;
}
/*
* reset int target to cpu 0
* (Should really query which processor we're running on and use that)
*/
for (i = 0; i < nirqs; i += 4) {
gic_dist->targets[i / 4] = TARGET_CPU0_ALLINT;
}
/* level-triggered, 1-N */
for (i = 64; i < nirqs; i += 32) {
gic_dist->config[i / 32] = 0;
//gic_dist->config[i / 32] = 0x55555555;
}
/* enable the int controller */
gic_dist->enable = 1;
}
/**
DONT_TRANSLATE
*/
static void cpu_iface_init(void)
{
uint32_t i;
/* For non-Exynos4, the registers are banked per CPU, need to clear them */
gic_dist->enable_clr[0] = IRQ_SET_ALL;
gic_dist->pending_clr[0] = IRQ_SET_ALL;
gic_dist->priority[0] = 0x00;
/* put everything in group 0 */
/* clear any software generated interrupts */
for (i = 0; i < 16; i += 4) {
gic_dist->sgi_pending_clr[i / 4] = IRQ_SET_ALL;
}
gic_cpuiface->icontrol = 0;
gic_cpuiface->pri_msk_c = 0x000000f0;
gic_cpuiface->bp_c = 0x00000003;
while (((i = gic_cpuiface->int_ack) & IRQ_MASK) != IRQ_NONE) {
gic_cpuiface->eoi = i;
}
gic_cpuiface->icontrol = 0x1;
}
/**
DONT_TRANSLATE
*/
void initIRQController(void)
{
dist_init();
cpu_iface_init();
}
/*
* The only sane way to get an GIC IRQ number that can be properly
* ACKED later is through the int_ack register. Unfortunately, reading
* this register changes the interrupt state to pending so future
* reads will not return the same value For this reason, we have a
* global variable to store the IRQ number.
*/
static uint32_t active_irq = IRQ_NONE;
/**
DONT_TRANSLATE
*/
interrupt_t
getActiveIRQ(void)
{
uint32_t irq;
if (!IS_IRQ_VALID(active_irq)) {
active_irq = gic_cpuiface->int_ack;
}
if (IS_IRQ_VALID(active_irq)) {
irq = active_irq & IRQ_MASK;
} else {
irq = 1023;
}
return irq;
}
/*
* GIC has 4 states: pending->active(+pending)->inactive
* seL4 expects two states: active->inactive.
* We ignore the active state in GIC to conform
*/
/**
DONT_TRANSLATE
*/
bool_t
isIRQPending(void)
{
return IS_IRQ_VALID(gic_cpuiface->hi_pend);
}
/**
DONT_TRANSLATE
*/
void
maskInterrupt(bool_t disable, interrupt_t irq)
{
if (disable) {
dist_enable_clr(irq);
} else {
dist_enable_set(irq);
}
}
/**
DONT_TRANSLATE
*/
void
ackInterrupt(irq_t irq)
{
if (!(IS_IRQ_VALID(active_irq) && (active_irq & IRQ_MASK) == irq)) {
return;
}
if (is_irq_edge_triggered(irq)) {
dist_pending_clr(irq);
}
gic_cpuiface->eoi = active_irq;
active_irq = IRQ_NONE;
}
void
handleSpuriousIRQ(void)
{
}
void rt_hw_interrupt_mask(int vector)
{
maskInterrupt(1, vector);
}
void rt_hw_interrupt_umask(int vector)
{
maskInterrupt(0, vector);
}
int rt_hw_interrupt_get_irq(void)
{
return getActiveIRQ();
}
void rt_hw_interrupt_ack(int fiq_irq)
{
return ackInterrupt(fiq_irq);
}