125 lines
3.3 KiB
C
125 lines
3.3 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)
|
|
*/
|
|
#ifndef __PLAT_MACHINE_INTERRUPT_H
|
|
#define __PLAT_MACHINE_INTERRUPT_H
|
|
|
|
#include <config.h>
|
|
#include <types.h>
|
|
#include <armv/machine.h>
|
|
|
|
#include <machine/io.h>
|
|
#include <kernel/vspace.h>
|
|
#include <arch/kernel/vspace.h>
|
|
#include <plat/machine.h>
|
|
#include <linker.h>
|
|
|
|
#define CMPER_REG(base, off) ((volatile uint32_t *)((base) + (off)))
|
|
#define CMPER_TIMER3_CLKCTRL 0x84
|
|
#define CMPER_CLKCTRL_DISABLE 0
|
|
#define CMPER_CLKCTRL_ENABLE 2
|
|
#define CMPER_CLKSEL_TIMER3 0x50c
|
|
#define CMPER_CKLSEL_MOSC 1
|
|
|
|
|
|
#define INTCPS_SYSCONFIG_SOFTRESET BIT(1)
|
|
#define INTCPS_SYSSTATUS_RESETDONE BIT(0)
|
|
#define INTCPS_CONTROL_NEWIRQAGR BIT(0)
|
|
#define INTCPS_SIR_IRQ_SPURIOUSIRQFLAG 0xffffff80
|
|
|
|
/*
|
|
* The struct below is used to discourage the compiler from generating literals
|
|
* for every single address we might access.
|
|
*/
|
|
volatile struct INTC_map {
|
|
uint32_t padding[4];
|
|
uint32_t intcps_sysconfig;
|
|
uint32_t intcps_sysstatus;
|
|
uint32_t padding2[10];
|
|
uint32_t intcps_sir_irq;
|
|
uint32_t intcps_sir_fiq;
|
|
uint32_t intcps_control;
|
|
uint32_t intcps_protection;
|
|
uint32_t intcps_idle;
|
|
uint32_t padding3[3];
|
|
uint32_t intcps_irq_priority;
|
|
uint32_t intcps_fiq_priority;
|
|
uint32_t intcps_threshold;
|
|
uint32_t padding4[5];
|
|
struct {
|
|
uint32_t intcps_itr;
|
|
uint32_t intcps_mir;
|
|
uint32_t intcps_mir_clear;
|
|
uint32_t intcps_mir_set;
|
|
uint32_t intcps_isr_set;
|
|
uint32_t intcps_isr_clear;
|
|
uint32_t intcps_pending_irq;
|
|
uint32_t intcps_pending_fiq;
|
|
} intcps_n[4];
|
|
uint32_t intcps_ilr[128];
|
|
} *intc = (volatile void*)INTC_PPTR;
|
|
|
|
|
|
static inline interrupt_t
|
|
getActiveIRQ(void)
|
|
{
|
|
uint32_t intcps_sir_irq = intc->intcps_sir_irq;
|
|
interrupt_t irq = (interrupt_t)(intcps_sir_irq & 0x7f);
|
|
|
|
if ((intcps_sir_irq & INTCPS_SIR_IRQ_SPURIOUSIRQFLAG) == 0) {
|
|
assert((irq / 32) < (sizeof intc->intcps_n / sizeof intc->intcps_n[0]));
|
|
if (intc->intcps_n[irq / 32].intcps_pending_irq & (1 << (irq & 31))) {
|
|
return irq;
|
|
}
|
|
}
|
|
return irqInvalid;
|
|
}
|
|
|
|
/* Check for pending IRQ */
|
|
static inline bool_t
|
|
isIRQPending(void)
|
|
{
|
|
return getActiveIRQ() != irqInvalid;
|
|
}
|
|
|
|
/* Enable or disable irq according to the 'disable' flag. */
|
|
static inline void
|
|
maskInterrupt(bool_t disable, interrupt_t irq)
|
|
{
|
|
if (likely(irq < maxIRQ)) {
|
|
if (disable) {
|
|
intc->intcps_n[irq / 32].intcps_mir_set = 1 << (irq & 31);
|
|
} else {
|
|
intc->intcps_n[irq / 32].intcps_mir_clear = 1 << (irq & 31);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void
|
|
ackInterrupt(irq_t irq)
|
|
{
|
|
/*
|
|
* am335x ref man, sec 6.2.2 only requires a DSB after NEWIRQAGR.
|
|
* I found that without dsb() or more code before, I get interrupts
|
|
* without the associated pending bit being set. Perhaps this
|
|
* indicates a missing barrier in code elsewhere? -TimN
|
|
*/
|
|
dsb();
|
|
intc->intcps_control = INTCPS_CONTROL_NEWIRQAGR;
|
|
dsb();
|
|
}
|
|
|
|
static inline void
|
|
handleSpuriousIRQ(void)
|
|
{
|
|
/* Reset and re-enable IRQs. */
|
|
intc->intcps_control = INTCPS_CONTROL_NEWIRQAGR;
|
|
dsb();
|
|
}
|
|
#endif /* __PLAT_MACHINE_INTERRUPT_H */
|