blob: a998f97fb586ecebe424e100051e26f885df5b78 [file] [log] [blame]
/******************************************************************************
*
* alt_interrupt.c - API for the Altera SoC FPGA interrupts
*
******************************************************************************/
/******************************************************************************
*
* Copyright 2013 Altera Corporation. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
******************************************************************************/
#include "alt_interrupt.h"
#include "socal/socal.h"
#include "hwlib.h"
/////
// NOTE: To enable debugging output, delete the next line and uncomment the
// line after.
#define dprintf(...)
// #define dprintf(fmt, ...) printf(fmt, __VA_ARGS__)
/////
// Interrupt attribute(s) flags
typedef enum INT_FLAG_e
{
INT_FLAG_IMPLEMENTED = 0x01
}
INT_FLAG_t;
static uint8_t alt_int_flag[ALT_INT_PROVISION_INT_COUNT];
/////
// IRQ Stack
#if ALT_INT_PROVISION_STACK_SUPPORT
static char __attribute__ ((aligned (16))) alt_int_stack_irq_block[ALT_INT_PROVISION_CPU_COUNT][ALT_INT_PROVISION_STACK_SIZE];
#ifdef __ARMCC_VERSION
// ARMCC specific helper function to fixup the IRQ stack.
// This is implemented in alt_interrupt_armcc.s.
extern void alt_int_fixup_irq_stack(uint32_t stack_irq);
#else
static void alt_int_fixup_irq_stack(uint32_t stack_irq)
{
// 1. Save the current SP
// 2. Switch to the IRQ context
// 3. Point SP to the stack IRQ block provided
// 4. Switch back to SYS context
// 5. Restore the saved "current" SP
// Mode_USR 0x10
// Mode_FIQ 0x11
// Mode_IRQ 0x12
// Mode_SVC 0x13
// Mode_MON 0x16
// Mode_ABT 0x17
// Mode_UNDEF 0x1B
// Mode_SYS 0x1F
// I_Bit 0x80 @ when I bit is set, IRQ is disabled
// F_Bit 0x40 @ when F bit is set, FIQ is disabled
// NS_BIT 0x1 @ when NS bit is set, core in non-secure
// r4 being used for stack_sys.
// Consider adding "lr" to the list of mangled registers. This way GCC will push/pop {lr} for us.
__asm(
"push {lr}\n"
"mov r4, sp\n"
"msr CPSR_c, #(0x12 | 0x80 | 0x40)\n"
"mov sp, %0\n"
"msr CPSR_c, #(0x1F | 0x80 | 0x40)\n"
"mov sp, r4\n"
"pop {lr}\n"
: : "r" (stack_irq) : "sp", "r4"
);
}
#endif
#endif // #if ALT_INT_PROVISION_STACK_SUPPORT
/////
// Interrupt dispatch information
// See Cortex-A9 MPCore TRM, section 1.3.
// SGI (16) + PPI (16) + SPI count (224) = total number of interrupts.
typedef struct INT_DISPATCH_s
{
alt_int_callback_t callback;
void * context;
}
INT_DISPATCH_t;
static INT_DISPATCH_t alt_int_dispatch[ALT_INT_PROVISION_INT_COUNT];
/////
// Distributer interface base address
static uint32_t alt_int_base_dist;
// CPU interface base address
static uint32_t alt_int_base_cpu;
/////
// Number of CPU(s) in system
static uint32_t alt_int_count_cpu;
// Number of interrupts in system, rounded up to nearest 32
static uint32_t alt_int_count_int;
/////
inline static uint32_t get_periphbase(void)
{
uint32_t periphbase;
// Read the Peripheral Base Address.
// See: Cortex-A9 TRM, section 4.3.24.
#ifdef __ARMCC_VERSION
__asm("MRC p15, 4, periphbase, c15, c0, 0");
#else
__asm("MRC p15, 4, %0, c15, c0, 0" : "=r" (periphbase));
#endif
return periphbase;
}
#if ALT_INT_PROVISION_VECTOR_SUPPORT
inline static void set_sctlr_vbit(bool value)
{
// See ARMv7, section B4.1.130.
uint32_t sctlr;
#ifdef __ARMCC_VERSION
__asm("MRC p15, 0, sctlr, c1, c0, 0");
#else
__asm("MRC p15, 0, %0, c1, c0, 0" : "=r" (sctlr));
#endif
if (value)
{
sctlr |= 1 << 13;
}
else
{
sctlr &= ~(1 << 13);
}
#ifdef __ARMCC_VERSION
__asm("MCR p15, 0, sctlr, c1, c0, 0");
#else
__asm("MCR p15, 0, %0, c1, c0, 0" : : "r" (sctlr));
#endif
}
#endif
inline static uint32_t get_current_cpu_num(void)
{
uint32_t affinity;
// Use the MPIDR. This is only available at PL1 or higher.
// See ARMv7, section B4.1.106.
#ifdef __ARMCC_VERSION
__asm("MRC p15, 0, affinity, c0, c0, 5");
#else
__asm ("MRC p15, 0, %0, c0, c0, 5" : "=r" (affinity));
#endif
return affinity & 0xFF;
}
/////
ALT_STATUS_CODE alt_int_global_init()
{
// Cache the distributor and CPU base addresses
// See: Cortex-A9 MPCore TRM, section 1.5.
{
uint32_t periphbase = get_periphbase();
alt_int_base_dist = periphbase + 0x1000;
alt_int_base_cpu = periphbase + 0x0100;
}
// Discover CPU and interrupt count
// See GIC 1.0, section 4.3.2.
{
uint32_t icdictr = alt_read_word(alt_int_base_dist + 0x4);
alt_int_count_cpu = ((icdictr >> 5) & 0x7) + 1;
alt_int_count_int = ((icdictr & 0x1F) + 1) << 5;
}
// Initialize the callback and context array
// Initialize interrupt flags array
for (int i = 0; i < ALT_INT_PROVISION_INT_COUNT; ++i)
{
alt_int_dispatch[i].callback = 0;
alt_int_dispatch[i].context = 0;
alt_int_flag[i] = 0;
}
// Discover all interrupts that are implemented in hardware.
// See GIC 1.0, section 3.1.2.
for (int i = 0; i < (ALT_INT_PROVISION_INT_COUNT / 32); ++i)
{
// Set the whole bank to be enabled.
alt_write_word(alt_int_base_dist + 0x100 + i * sizeof(uint32_t), 0xffffffff); // icdisern
// Read it back to see which bits stick.
uint32_t value = alt_read_word(alt_int_base_dist + 0x100 + i * sizeof(uint32_t)); // icdisern
for (int j = 0; j < 32; ++j)
{
if (((1 << j) & value) != 0)
{
alt_int_flag[i * 32 + j] |= INT_FLAG_IMPLEMENTED;
}
}
// Clear the whole bank to be disabled.
alt_write_word(alt_int_base_dist + 0x180 + i * sizeof(uint32_t), 0xffffffff); // icdicern
}
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_global_uninit()
{
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_global_enable()
{
// See GIC 1.0, section 4.3.1.
// See Cortex-A9 MPCore TRM, section 3.3.1.
alt_setbits_word(alt_int_base_dist + 0x0, 0x1); // icddcr
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_global_disable()
{
// See GIC 1.0, section 4.3.1.
// See Cortex-A9 MPCore TRM, section 3.3.1.
alt_clrbits_word(alt_int_base_dist + 0x0, 0x1); // icddcr
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_global_enable_ns()
{
// See Cortex-A9 MPCore TRM, section 3.3.1.
alt_setbits_word(alt_int_base_dist + 0x0, 0x2); // icddcr
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_global_disable_ns()
{
// See Cortex-A9 MPCore TRM, section 3.3.1.
alt_clrbits_word(alt_int_base_dist + 0x0, 0x2);
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_global_enable_all()
{
// See Cortex-A9 MPCore TRM, section 3.3.1.
alt_setbits_word(alt_int_base_dist + 0x0, 0x3); // icddcr
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_global_disable_all()
{
// See Cortex-A9 MPCore TRM, section 3.3.1.
alt_clrbits_word(alt_int_base_dist + 0x0, 0x3); // icddcr
return ALT_E_SUCCESS;
}
/////
ALT_STATUS_CODE alt_int_dist_secure_enable(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.4.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
return ALT_E_BAD_ARG;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
alt_clrbits_word(alt_int_base_dist + 0x80 + regoffset * sizeof(uint32_t), 1 << regbitshift); // icdisrn
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_secure_disable(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.4.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
return ALT_E_BAD_ARG;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
alt_setbits_word(alt_int_base_dist + 0x80 + regoffset * sizeof(uint32_t), 1 << regbitshift); // icdisrn
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_is_secure(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.4.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
// Because interrupts are by default after reset secure, return the
// default security state.
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
// Because interrupts are by default after reset secure, return the
// default security state.
return ALT_E_TRUE;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
uint32_t icdisrn = alt_read_word(alt_int_base_dist + 0x80 + regoffset * sizeof(uint32_t));
if ((icdisrn & (1 << regbitshift)) == 0)
{
return ALT_E_TRUE;
}
else
{
return ALT_E_FALSE;
}
}
}
ALT_STATUS_CODE alt_int_dist_enable(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.5.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
return ALT_E_BAD_ARG;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
alt_write_word(alt_int_base_dist + 0x100 + regoffset * sizeof(uint32_t), 1 << regbitshift); // icdisern
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_disable(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.6.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
return ALT_E_BAD_ARG;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
alt_write_word(alt_int_base_dist + 0x180 + regoffset * sizeof(uint32_t), 1 << regbitshift); // icdicern
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_is_enabled(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.5.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
// Interrupts on the GIC is disabled by default, so report disabled.
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
// Interrupts on the GIC is disabled by default, so report disabled.
return ALT_E_FALSE;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
uint32_t icdisern = alt_read_word(alt_int_base_dist + 0x100 + regoffset * sizeof(uint32_t));
if ((icdisern & (1 << regbitshift)) != 0)
{
return ALT_E_TRUE;
}
else
{
return ALT_E_FALSE;
}
}
}
ALT_STATUS_CODE alt_int_dist_pending_set(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.7.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
return ALT_E_BAD_ARG;
}
else if ((uint32_t)int_id < 16)
{
return ALT_E_BAD_ARG;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
alt_write_word(alt_int_base_dist + 0x200 + regoffset * sizeof(uint32_t), 1 << regbitshift); // icdisprn
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_pending_clear(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.8.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
return ALT_E_BAD_ARG;
}
else if ((uint32_t)int_id < 16)
{
return ALT_E_BAD_ARG;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
alt_write_word(alt_int_base_dist + 0x280 + regoffset * sizeof(uint32_t), 1 << regbitshift); // icdicprn
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_is_pending(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.7.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
// Interrupts on the GIC is not pending by default, so report false.
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
// Interrupts on the GIC is not pending by default, so report false.
return ALT_E_FALSE;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
uint32_t icdisprn = alt_read_word(alt_int_base_dist + 0x200 + regoffset * sizeof(uint32_t));
if ((icdisprn & (1 << regbitshift)) != 0)
{
return ALT_E_TRUE;
}
else
{
return ALT_E_FALSE;
}
}
}
ALT_STATUS_CODE alt_int_dist_is_active(ALT_INT_INTERRUPT_t int_id)
{
// See GIC 1.0, section 4.3.9.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
// Interrupts on the GIC is not active by default, so report false.
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
// Interrupts on the GIC is not active by default, so report false.
return ALT_E_FALSE;
}
else
{
uint32_t regoffset = int_id >> 5;
uint32_t regbitshift = int_id & 0x1F;
uint32_t icdabrn = alt_read_word(alt_int_base_dist + 0x300 + regoffset * sizeof(uint32_t));
if ((icdabrn & (1 << regbitshift)) != 0)
{
return ALT_E_TRUE;
}
else
{
return ALT_E_FALSE;
}
}
}
ALT_STATUS_CODE alt_int_dist_priority_get(ALT_INT_INTERRUPT_t int_id,
uint32_t * priority)
{
// See GIC 1.0, section 4.3.10.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
// Interrupts on the GIC have a default priority of 0.
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
// Interrupts on the GIC have a default priority of 0.
*priority = 0;
return ALT_E_SUCCESS;
}
else
{
uint32_t regoffset = int_id;
uint8_t icdiprn = alt_read_byte(alt_int_base_dist + 0x400 + regoffset * sizeof(uint8_t));
*priority = icdiprn;
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_priority_set(ALT_INT_INTERRUPT_t int_id,
uint32_t priority)
{
// See GIC 1.0, section 4.3.10.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
return ALT_E_BAD_ARG;
}
else if (priority < 256)
{
uint32_t regoffset = int_id;
alt_write_byte(alt_int_base_dist + 0x400 + regoffset * sizeof(uint8_t), (uint8_t)priority); // icdiprn
return ALT_E_SUCCESS;
}
else
{
return ALT_E_BAD_ARG;
}
}
ALT_STATUS_CODE alt_int_dist_target_get(ALT_INT_INTERRUPT_t int_id,
alt_int_cpu_target_t * target)
{
// See GIC 1.0, section 4.3.11.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
*target = 0;
return ALT_E_SUCCESS;
}
else
{
uint32_t regoffset = int_id;
uint8_t icdiptr = alt_read_byte(alt_int_base_dist + 0x800 + regoffset * sizeof(uint8_t));
*target = icdiptr;
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_target_set(ALT_INT_INTERRUPT_t int_id,
alt_int_cpu_target_t target)
{
// See GIC 1.0, section 4.3.11.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
return ALT_E_BAD_ARG;
}
else if (target >= (1 << alt_int_count_cpu))
{
if (target == (1 << get_current_cpu_num()))
{
return ALT_E_SUCCESS;
}
return ALT_E_BAD_ARG;
}
else if (int_id < 32)
{
return ALT_E_BAD_ARG;
}
else
{
uint32_t regoffset = int_id;
alt_write_byte(alt_int_base_dist + 0x800 + regoffset * sizeof(uint8_t), target); // icdiptr
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_trigger_get(ALT_INT_INTERRUPT_t int_id,
ALT_INT_TRIGGER_t * trigger)
{
// See GIC 1.0, section 4.3.12.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
*trigger = ALT_INT_TRIGGER_NA;
return ALT_E_SUCCESS;
}
else if (int_id < 16)
{
*trigger = ALT_INT_TRIGGER_SOFTWARE;
return ALT_E_SUCCESS;
}
else
{
uint32_t regoffset = int_id >> 4;
uint32_t regbitshift = ((int_id & 0x0F) * 2) + 1;
uint32_t icdicfrn = alt_read_word(alt_int_base_dist + 0xC00 + regoffset * sizeof(uint32_t));
if ((icdicfrn & (1 << regbitshift)) == 0)
{
*trigger = ALT_INT_TRIGGER_LEVEL;
}
else
{
*trigger = ALT_INT_TRIGGER_EDGE;
}
return ALT_E_SUCCESS;
}
}
ALT_STATUS_CODE alt_int_dist_trigger_set(ALT_INT_INTERRUPT_t int_id,
ALT_INT_TRIGGER_t trigger_type)
{
// See GIC 1.0, section 4.3.12.
if ((uint32_t)int_id >= ALT_INT_PROVISION_INT_COUNT)
{
return ALT_E_BAD_ARG;
}
else if ((alt_int_flag[int_id] & INT_FLAG_IMPLEMENTED) == 0)
{
return ALT_E_BAD_ARG;
}
else if (int_id < 16)
{
if ( (trigger_type == ALT_INT_TRIGGER_AUTODETECT)
|| (trigger_type == ALT_INT_TRIGGER_SOFTWARE))
{
return ALT_E_SUCCESS;
}
else
{
return ALT_E_BAD_ARG;
}
}
else
{
uint32_t regoffset = int_id >> 4;
uint32_t regbitshift = ((int_id & 0x0F) * 2) + 1;
if (trigger_type == ALT_INT_TRIGGER_AUTODETECT)
{
if (int_id <= 32) { trigger_type = ALT_INT_TRIGGER_EDGE; } // PPI
else if (int_id <= 40) { trigger_type = ALT_INT_TRIGGER_EDGE; } // CPU0_PARITYFAIL
else if (int_id <= 47) { trigger_type = ALT_INT_TRIGGER_LEVEL; } // CPU0_DEFLAGS
else if (int_id <= 56) { trigger_type = ALT_INT_TRIGGER_EDGE; } // CPU1_PARITYFAIL
else if (int_id <= 63) { trigger_type = ALT_INT_TRIGGER_LEVEL; } // CPU1_DEFLAGS
else if (int_id <= 66) { trigger_type = ALT_INT_TRIGGER_EDGE; } // SCU
else if (int_id <= 69) { trigger_type = ALT_INT_TRIGGER_EDGE; } // L2_ECC
else if (int_id <= 70) { trigger_type = ALT_INT_TRIGGER_LEVEL; } // L2 (other)
else if (int_id <= 71) { trigger_type = ALT_INT_TRIGGER_LEVEL; } // DDR
else if (int_id <= 135) { /* do nothing */ } // FPGA, !!!
else { trigger_type = ALT_INT_TRIGGER_LEVEL; } // everything else
}
switch (trigger_type)
{
case ALT_INT_TRIGGER_LEVEL:
alt_clrbits_word(alt_int_base_dist + 0xC00 + regoffset * sizeof(uint32_t), 1 << regbitshift); // icdicfrn
break;
case ALT_INT_TRIGGER_EDGE:
alt_setbits_word(alt_int_base_dist + 0xC00 + regoffset * sizeof(uint32_t), 1 << regbitshift); // icdicfrn
break;
default:
return ALT_E_BAD_ARG;
}
return ALT_E_SUCCESS;
}
}
/////
ALT_STATUS_CODE alt_int_sgi_trigger(ALT_INT_INTERRUPT_t int_id,
ALT_INT_SGI_TARGET_t target_filter,
alt_int_cpu_target_t target_list,
bool secure_only)
{
// See GIC 1.0, section 4.3.13.
if (target_list >= (1 << alt_int_count_cpu))
{
return ALT_E_BAD_ARG;
}
else if ((uint32_t)int_id < 16)
{
uint32_t filterbits;
uint32_t sattmask = 0;
switch (target_filter)
{
case ALT_INT_SGI_TARGET_LIST:
filterbits = 0x0 << 24;
break;
case ALT_INT_SGI_TARGET_ALL_EXCL_SENDER:
filterbits = 0x1 << 24;
break;
case ALT_INT_SGI_TARGET_SENDER_ONLY:
filterbits = 0x2 << 24;
break;
default:
return ALT_E_BAD_ARG;
}
if (!secure_only)
{
sattmask = 1 << 15;
}
alt_write_word(alt_int_base_dist + 0xF00, int_id | sattmask | (target_list << 16) | filterbits); // icdsgir
return ALT_E_SUCCESS;
}
else
{
return ALT_E_BAD_ARG;
}
}
/////
ALT_STATUS_CODE alt_int_cpu_init()
{
uint32_t cpu_num = get_current_cpu_num();
if (cpu_num >= ALT_INT_PROVISION_CPU_COUNT)
{
return ALT_E_ERROR;
}
// Setup the IRQ stack
#if ALT_INT_PROVISION_STACK_SUPPORT
// The ARM stack lowers in address as it is being used. 16 is the alignment
// of the block.
uint32_t stack_irq = (uint32_t) &alt_int_stack_irq_block[cpu_num][sizeof(alt_int_stack_irq_block[0]) - 16];
alt_int_fixup_irq_stack(stack_irq);
#endif // #if ALT_INT_PROVISION_STACK_SUPPORT
// Setup the Vector Interrupt Table
#if ALT_INT_PROVISION_VECTOR_SUPPORT
// Set the vector interrupt table (VBAR) to use __cs3_interrupt_vector and
// set SCTLR.V to always be 0.
// For SCTLR.V information, See ARMv7, section B4.1.130.
// For VBAR information, See ARMv7, section B4.1.156.
set_sctlr_vbit(false);
#ifdef __ARMCC_VERSION
extern char alt_interrupt_vector;
uint32_t vector_table = (uint32_t)&alt_interrupt_vector;
__asm("MCR p15, 0, vector_table, c12, c0, 0");
#else
extern char __cs3_interrupt_vector;
uint32_t vector_table = (uint32_t)&__cs3_interrupt_vector;
__asm("MCR p15, 0, %0, c12, c0, 0" : : "r" (vector_table));
#endif
#endif // #if ALT_INT_PROVISION_VECTOR_SUPPORT
// Setup the priority mask and binary point defaults.
// This will allow all interrupts to have sufficient priority to be
// forwarded to the CPUs.
ALT_STATUS_CODE status;
status = alt_int_cpu_priority_mask_set(0xff);
if (status != ALT_E_SUCCESS)
{
return status;
}
status = alt_int_cpu_binary_point_set(0);
if (status != ALT_E_SUCCESS)
{
return status;
}
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_cpu_uninit()
{
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_cpu_enable()
{
// See GIC 1.0, section 4.4.1.
alt_setbits_word(alt_int_base_cpu + 0x0, 0x1); // iccicr
// Unmask IRQ interrupts from current processor.
#ifdef __ARMCC_VERSION
__enable_irq();
#else
__asm("CPSIE i");
#endif
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_cpu_disable()
{
// See GIC 1.0, section 4.4.1.
alt_clrbits_word(alt_int_base_cpu + 0x0, 0x1); // iccicr
// Mask IRQ interrupts from current processor.
#ifdef __ARMCC_VERSION
__disable_irq();
#else
__asm("CPSID i");
#endif
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_cpu_enable_ns()
{
// See GIC 1.0, section 4.4.1.
alt_setbits_word(alt_int_base_cpu + 0x0, 0x2); // iccicr
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_cpu_disable_ns()
{
// See GIC 1.0, section 4.4.1.
alt_clrbits_word(alt_int_base_cpu + 0x0, 0x2); // iccicr
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_cpu_enable_all()
{
// See GIC 1.0, section 4.4.1.
alt_setbits_word(alt_int_base_cpu + 0x0, 0x3); // iccicr
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_cpu_disable_all()
{
// See GIC 1.0, section 4.4.1.
alt_clrbits_word(alt_int_base_cpu + 0x0, 0x3); // iccicr
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_cpu_config_get(bool* use_secure_binary_point,
bool* use_FIQ_for_secure_interrupts,
bool* allow_secure_ack_all_interrupts)
{
// See GIC 1.0, section 4.4.1.
uint32_t iccicr = alt_read_word(alt_int_base_cpu + 0x0);
if (use_secure_binary_point)
{
*use_secure_binary_point = (iccicr & (1 << 4)) != 0;
}
if (use_FIQ_for_secure_interrupts)
{
*use_FIQ_for_secure_interrupts = (iccicr & (1 << 3)) != 0;
}
if (allow_secure_ack_all_interrupts)
{
*allow_secure_ack_all_interrupts = (iccicr & (1 << 2)) != 0;
}
return ALT_E_SUCCESS;
}
ALT_STATUS_CODE alt_int_cpu_config_set(bool use_secure_binary_point,
bool use_FIQ_for_secure_interrupts,
bool allow_secure_ack_all_interrupts)
{
// See GIC 1.0, section 4.4.1.
uint32_t iccicr = alt_read_word(alt_int_base_cpu + 0x0);
if (use_secure_binary_point)
{
iccicr |= 1 << 4;
}
else
{
iccicr &= ~(1 << 4);
}
if (use_FIQ_for_secure_interrupts)
{
iccicr |= 1 << 3;
}
else
{
iccicr &= ~(1 << 3);
}
if (allow_secure_ack_all_interrupts)
{
iccicr |= 1 << 2;
}
else
{
iccicr &= ~(1 << 2);
}
alt_write_word(alt_int_base_cpu + 0x0, iccicr);
return ALT_E_SUCCESS;
}
uint32_t alt_int_cpu_priority_mask_get()
{
// See GIC 1.0, section 4.4.2.
uint32_t iccpmr = alt_read_word(alt_int_base_cpu + 0x4);
return iccpmr;
}
ALT_STATUS_CODE alt_int_cpu_priority_mask_set(uint32_t priority_mask)
{
// See GIC 1.0, section 4.4.2.
if (priority_mask < 256)
{
alt_write_word(alt_int_base_cpu + 0x4, priority_mask); // iccpmr
return ALT_E_SUCCESS;
}
else
{
return ALT_E_BAD_ARG;
}
}
uint32_t alt_int_cpu_binary_point_get()
{
// See GIC 1.0, section 4.4.3.
uint32_t iccbpr = alt_read_word(alt_int_base_cpu + 0x8);
return iccbpr;
}
ALT_STATUS_CODE alt_int_cpu_binary_point_set(uint32_t binary_point)
{
// See GIC 1.0, section 4.4.3.
if (binary_point < 8)
{
alt_write_word(alt_int_base_cpu + 0x8, binary_point); // iccbpr
return ALT_E_SUCCESS;
}
else
{
return ALT_E_BAD_ARG;
}
}
uint32_t alt_int_cpu_binary_point_get_ns()
{
// See GIC 1.0, section 4.4.7.
uint32_t iccabpr = alt_read_word(alt_int_base_cpu + 0x1C);
return iccabpr;
}
ALT_STATUS_CODE alt_int_cpu_binary_point_set_ns(uint32_t binary_point)
{
// See GIC 1.0, section 4.4.7.
if (binary_point < 8)
{
alt_write_word(alt_int_base_cpu + 0x1C, binary_point); // iccabpr
return ALT_E_SUCCESS;
}
else
{
return ALT_E_BAD_ARG;
}
}
/////
ALT_STATUS_CODE alt_int_isr_register(ALT_INT_INTERRUPT_t int_id,
alt_int_callback_t callback,
void * context)
{
if ((uint32_t)int_id < ALT_INT_PROVISION_INT_COUNT)
{
alt_int_dispatch[int_id].callback = callback;
alt_int_dispatch[int_id].context = context;
return ALT_E_SUCCESS;
}
else
{
return ALT_E_BAD_ARG;
}
}
ALT_STATUS_CODE alt_int_isr_unregister(ALT_INT_INTERRUPT_t int_id)
{
if ((uint32_t)int_id < ALT_INT_PROVISION_INT_COUNT)
{
alt_int_dispatch[int_id].callback = 0;
alt_int_dispatch[int_id].context = 0;
return ALT_E_SUCCESS;
}
else
{
return ALT_E_BAD_ARG;
}
}
/////
uint32_t alt_int_util_cpu_count(void)
{
return alt_int_count_cpu;
}
uint32_t alt_int_util_int_count(void)
{
return alt_int_count_int;
}
alt_int_cpu_target_t alt_int_util_cpu_current(void)
{
return 1 << get_current_cpu_num();
}
/////
#if ALT_INT_PROVISION_VECTOR_SUPPORT
#ifdef __ARMCC_VERSION
void __irq alt_int_handler_irq(void)
#else
void __attribute__ ((interrupt)) __cs3_isr_irq(void)
#endif
#else // #if ALT_INT_PROVISION_VECTOR_SUPPORT
void alt_int_handler_irq(void)
#endif // #if ALT_INT_PROVISION_VECTOR_SUPPORT
{
// See GIC 1.0, sections 4.4.4, 4.4.5.
uint32_t icciar = alt_read_word(alt_int_base_cpu + 0xC);
uint32_t ackintid = ALT_INT_ICCIAR_ACKINTID_GET(icciar);
if (ackintid < ALT_INT_PROVISION_INT_COUNT)
{
if (alt_int_dispatch[ackintid].callback)
{
alt_int_dispatch[ackintid].callback(icciar, alt_int_dispatch[ackintid].context);
}
}
else
{
// Report error.
dprintf("INT[ISR]: Unhandled interrupt ID = 0x%lx.\n", ackintid);
}
alt_write_word(alt_int_base_cpu + 0x10, icciar); // icceoir
}