502 lines
12 KiB
C
502 lines
12 KiB
C
/* ------------------------------------------
|
|
* Copyright (c) 2016, Synopsys, Inc. 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) Neither the name of the Synopsys, Inc., nor the names of its contributors may
|
|
* be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
|
*
|
|
* \version 2016.05
|
|
* \date 2014-07-15
|
|
* \author Wayne Ren(Wei.Ren@synopsys.com)
|
|
--------------------------------------------- */
|
|
|
|
/**
|
|
* \file
|
|
* \ingroup ARC_HAL_EXCEPTION_CPU ARC_HAL_EXCEPTION_INTERRUPT
|
|
* \brief C Implementation of exception and interrupt management
|
|
*/
|
|
#include "inc/arc/arc_exception.h"
|
|
#include "inc/arc/arc_cache.h"
|
|
|
|
//#define DBG_LESS
|
|
//#include "embARC_debug.h"
|
|
|
|
/**
|
|
* \addtogroup ARC_HAL_EXCEPTION_CPU
|
|
* @{
|
|
* \var exc_entry_table
|
|
* \brief exception entry table
|
|
*
|
|
* install exception entry table to ARC_AUX_INT_VECT_BASE in startup.
|
|
* According to ARCv2 ISA, vectors are fetched in instruction space and thus
|
|
* may be present in ICCM, Instruction Cache, or
|
|
* main memory accessed by instruction fetch logic.
|
|
* So it is put into a specific section .vector.
|
|
*
|
|
* Please note that the exc_entry_table maybe cached in ARC. Some functions is
|
|
* defined in .s files.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* \ingroup ARC_HAL_EXCEPTION_CPU
|
|
* \brief default cpu exception handler
|
|
* \param p_excinf pointer to the exception frame
|
|
*/
|
|
static void exc_handler_default(void *p_excinf)
|
|
{
|
|
// uint32_t excpt_cause_reg = 0;
|
|
// uint32_t excpt_ret_reg = 0;
|
|
// uint32_t exc_no = 0;
|
|
|
|
// excpt_cause_reg = _arc_aux_read(AUX_ECR);
|
|
// excpt_ret_reg = _arc_aux_read(AUX_ERRET);
|
|
// exc_no = (excpt_cause_reg >> 16) & 0xff;
|
|
|
|
Asm("kflag 1");
|
|
}
|
|
|
|
|
|
/**
|
|
* \ingroup ARC_HAL_EXCEPTION_INTERRUPT
|
|
* \brief default interrupt handler
|
|
* \param[in] p_excinf information for interrupt handler
|
|
*/
|
|
static void int_handler_default(void *p_excinf)
|
|
{
|
|
// uint32_t int_cause_reg = 0;
|
|
|
|
// int_cause_reg = _arc_aux_read(AUX_IRQ_CAUSE);
|
|
Asm("kflag 1");
|
|
}
|
|
|
|
__attribute__ ((aligned(1024), section(".vector")))
|
|
EXC_ENTRY exc_entry_table[NUM_EXC_ALL] = {
|
|
[0] = _arc_reset,
|
|
[1 ... NUM_EXC_CPU-1] = exc_entry_cpu,
|
|
[NUM_EXC_CPU ... NUM_EXC_ALL-1] = exc_entry_int
|
|
};
|
|
/**
|
|
* \var exc_int_handler_table
|
|
* \brief the cpu exception and interrupt exception handler table
|
|
* called in exc_entry_default and exc_entry_int
|
|
*/
|
|
EXC_HANDLER exc_int_handler_table[NUM_EXC_ALL] = {
|
|
[0 ... NUM_EXC_CPU-1] = exc_handler_default,
|
|
[NUM_EXC_CPU ... NUM_EXC_ALL-1] = int_handler_default
|
|
};
|
|
|
|
/**
|
|
* \var exc_nest_count
|
|
* \brief the counter for exc/int processing, =0 no int/exc
|
|
* >1 in int/exc processing
|
|
* @}
|
|
*/
|
|
uint32_t exc_nest_count;
|
|
|
|
typedef struct aux_irq_ctrl_field {
|
|
/* note: little endian */
|
|
uint32_t save_nr_gpr_pairs: 5; /** Indicates number of general-purpose register pairs saved, from 0 to 8/16 */
|
|
uint32_t res: 4; /** Reserved */
|
|
uint32_t save_blink: 1; /** Indicates whether to save and restore BLINK */
|
|
uint32_t save_lp_regs: 1; /** Indicates whether to save and restore loop registers (LP_COUNT, LP_START, LP_END) */
|
|
uint32_t save_u_to_u: 1; /** Indicates if user context is saved to user stack */
|
|
uint32_t res2: 1; /** Reserved */
|
|
uint32_t save_idx_regs: 1; /** Indicates whether to save and restore code-density registers (EI_BASE, JLI_BASE, LDI_BASE) */
|
|
uint32_t res3: 18; /** Reserved */
|
|
} aux_irq_ctrl_field_t;
|
|
|
|
typedef union {
|
|
aux_irq_ctrl_field_t bits;
|
|
uint32_t value;
|
|
} aux_irq_ctrl_t;
|
|
|
|
/**
|
|
* \ingroup ARC_HAL_EXCEPTION_CPU ARC_HAL_EXCEPTION_INTERRUPT
|
|
* \brief intialize the exception and interrupt handling
|
|
*/
|
|
void exc_int_init(void)
|
|
{
|
|
uint32_t i;
|
|
uint32_t status;
|
|
aux_irq_ctrl_t ictrl;
|
|
|
|
ictrl.value = 0;
|
|
|
|
#ifndef ARC_FEATURE_RF16
|
|
ictrl.bits.save_nr_gpr_pairs = 6; /* r0 to r11 (r12 saved manually) */
|
|
#else
|
|
ictrl.bits.save_nr_gpr_pairs = 3; /* r0 to r3, r10, r11 */
|
|
#endif
|
|
ictrl.bits.save_blink = 1;
|
|
ictrl.bits.save_lp_regs = 1; /* LP_COUNT, LP_START, LP_END */
|
|
ictrl.bits.save_u_to_u = 0; /* user ctxt saved on kernel stack */
|
|
ictrl.bits.save_idx_regs = 1; /* JLI, LDI, EI */
|
|
|
|
status = arc_lock_save();
|
|
for (i = NUM_EXC_CPU; i < NUM_EXC_ALL; i++) {
|
|
/* interrupt level triggered, disabled, priority is the lowest */
|
|
_arc_aux_write(AUX_IRQ_SELECT, i);
|
|
_arc_aux_write(AUX_IRQ_ENABLE, 0);
|
|
_arc_aux_write(AUX_IRQ_TRIGGER, 0);
|
|
#if defined(ARC_FEATURE_SEC_PRESENT) && (SECURESHIELD_VERSION < 2)
|
|
_arc_aux_write(AUX_IRQ_PRIORITY, (1 << AUX_IRQ_PRIORITY_BIT_S)|(INT_PRI_MAX - INT_PRI_MIN));
|
|
#else
|
|
_arc_aux_write(AUX_IRQ_PRIORITY, INT_PRI_MAX - INT_PRI_MIN);
|
|
#endif
|
|
}
|
|
_arc_aux_write(AUX_IRQ_CTRL, ictrl.value);
|
|
|
|
arc_unlock_restore(status);
|
|
|
|
/** ipm should be set after cpu unlock restore to avoid reset of the status32 value */
|
|
arc_int_ipm_set((INT_PRI_MAX - INT_PRI_MIN));
|
|
}
|
|
|
|
/**
|
|
* \ingroup ARC_HAL_EXCEPTION_CPU
|
|
* \brief install a CPU exception entry
|
|
* \param[in] excno exception number
|
|
* \param[in] entry the entry of exception to install
|
|
*/
|
|
int32_t exc_entry_install(const uint32_t excno, EXC_ENTRY entry)
|
|
{
|
|
uint32_t status;
|
|
|
|
EXC_ENTRY *table = (EXC_ENTRY *)_arc_aux_read(AUX_INT_VECT_BASE);
|
|
|
|
if (excno < NUM_EXC_ALL && entry != NULL
|
|
&& table[excno] != entry) {
|
|
status = cpu_lock_save();
|
|
/* directly write to mem, as arc gets exception handler from mem not from cache */
|
|
/* FIXME, here maybe icache is dirty, need to be invalidated */
|
|
table[excno] = entry;
|
|
|
|
if (_arc_aux_read(AUX_BCR_D_CACHE) > 0x2) {
|
|
/* dcache is available */
|
|
dcache_flush_line((uint32_t)&table[excno]);
|
|
}
|
|
|
|
if (_arc_aux_read(AUX_BCR_D_CACHE) > 0x2) {
|
|
/* icache is available */
|
|
icache_invalidate_line((uint32_t)&table[excno]);
|
|
}
|
|
cpu_unlock_restore(status);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \ingroup ARC_HAL_EXCEPTION_CPU
|
|
* \brief get the installed CPU exception entry
|
|
* \param[in] excno exception number
|
|
* \return the installed CPU exception entry
|
|
*/
|
|
EXC_ENTRY exc_entry_get(const uint32_t excno)
|
|
{
|
|
if (excno < NUM_EXC_ALL) {
|
|
return exc_entry_table[excno];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* \ingroup ARC_HAL_EXCEPTION_CPU
|
|
* \brief install an exception handler
|
|
* \param[in] excno exception number
|
|
* \param[in] handler the handler of exception to install
|
|
*/
|
|
int32_t exc_handler_install(const uint32_t excno, EXC_HANDLER handler)
|
|
{
|
|
if (excno < NUM_EXC_ALL && handler != NULL) {
|
|
exc_int_handler_table[excno] = handler;
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \ingroup ARC_HAL_EXCEPTION_CPU
|
|
* \brief get the installed exception handler
|
|
* \param[in] excno exception number
|
|
* \return the installed exception handler or NULL
|
|
*/
|
|
EXC_HANDLER exc_handler_get(const uint32_t excno)
|
|
{
|
|
if (excno < NUM_EXC_ALL) {
|
|
return exc_int_handler_table[excno];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#ifndef EMBARC_OVERRIDE_ARC_INTERRUPT_MANAGEMENT
|
|
/**
|
|
* \brief disable the specific interrupt
|
|
*
|
|
* \param[in] intno interrupt number
|
|
*/
|
|
int32_t int_disable(const uint32_t intno)
|
|
{
|
|
if (intno >= NUM_EXC_CPU && intno < NUM_EXC_ALL) {
|
|
arc_int_disable(intno);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief enable the specific int
|
|
*
|
|
* \param[in] intno interrupt number
|
|
*/
|
|
int32_t int_enable(const uint32_t intno)
|
|
{
|
|
if (intno >= NUM_EXC_CPU && intno < NUM_EXC_ALL) {
|
|
arc_int_enable(intno);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief check whether the specific int is enabled
|
|
*
|
|
* \param[in] intno interrupt number
|
|
* \return 0 disabled, 1 enabled, < 0 error
|
|
*/
|
|
int32_t int_enabled(const uint32_t intno)
|
|
{
|
|
if (intno >= NUM_EXC_CPU && intno < NUM_EXC_ALL) {
|
|
_arc_aux_write(AUX_IRQ_SELECT, intno);
|
|
return _arc_aux_read(AUX_IRQ_ENABLE);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief get the interrupt priority mask
|
|
*
|
|
* \returns interrupt priority mask, negative num
|
|
*/
|
|
int32_t int_ipm_get(void)
|
|
{
|
|
return ((int32_t)arc_int_ipm_get() + INT_PRI_MIN);
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief set the interrupt priority mask
|
|
*
|
|
* \param[in] intpri interrupt priority
|
|
*/
|
|
int32_t int_ipm_set(int32_t intpri)
|
|
{
|
|
if (intpri >= INT_PRI_MIN && intpri <= INT_PRI_MAX) {
|
|
intpri = intpri - INT_PRI_MIN;
|
|
arc_int_ipm_set(intpri);
|
|
return 0;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief get current interrupt priority mask
|
|
*
|
|
* \param[in] intno interrupt number
|
|
* \return <0 interrupt priority, 0 error
|
|
*/
|
|
int32_t int_pri_get(const uint32_t intno)
|
|
{
|
|
if (intno >= NUM_EXC_CPU && intno < NUM_EXC_ALL) {
|
|
return (int32_t)arc_int_pri_get(intno) + INT_PRI_MIN;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief set interrupt priority
|
|
*
|
|
* \param[in] intno interrupt number
|
|
* \param[in] intpri interrupt priority
|
|
* \return <0 error, 0 ok
|
|
*/
|
|
int32_t int_pri_set(const uint32_t intno, int32_t intpri)
|
|
{
|
|
uint32_t status;
|
|
|
|
if (intno >= NUM_EXC_CPU && intno < NUM_EXC_ALL) {
|
|
status = cpu_lock_save();
|
|
intpri = intpri - INT_PRI_MIN;
|
|
arc_int_pri_set(intno,(uint32_t)intpri);
|
|
cpu_unlock_restore(status);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief set interrupt secure or not secure
|
|
* This function is valid in secureshield v2
|
|
* \param[in] intno interrupt number
|
|
* \param[in] secure, 0 for normal, >0 for secure
|
|
* \return <0 error, 0 ok
|
|
*/
|
|
int32_t int_secure_set(const uint32_t intno, uint32_t secure)
|
|
{
|
|
if (intno >= NUM_EXC_CPU && intno < NUM_EXC_ALL) {
|
|
arc_int_secure_set(intno, secure);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief probe the pending status of interrupt
|
|
*
|
|
* \param[in] intno interrupt number
|
|
*
|
|
* \returns 1 pending, 0 no pending, -1 error
|
|
*/
|
|
int32_t int_probe(const uint32_t intno)
|
|
{
|
|
if (intno >= NUM_EXC_CPU && intno < NUM_EXC_ALL) {
|
|
return arc_int_probe(intno);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief trigger the interrupt in software
|
|
*
|
|
* \param[in] intno interrupt number
|
|
* \return 0 ok, -1 error
|
|
*/
|
|
int32_t int_sw_trigger(const uint32_t intno)
|
|
{
|
|
if (intno >= NUM_EXC_CPU && intno < NUM_EXC_ALL) {
|
|
arc_int_sw_trigger(intno);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \brief config the interrupt level triggered or pulse triggered
|
|
*
|
|
* \param[in] intno interrupt number
|
|
* \param[in] level, 0-level trigger, 1-pulse triggered
|
|
* \return 0 ok, -1 error
|
|
*/
|
|
int32_t int_level_config(const uint32_t intno, const uint32_t level)
|
|
{
|
|
if (intno >= NUM_EXC_CPU && intno < NUM_EXC_ALL) {
|
|
arc_int_level_config(intno, level);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief lock cpu, disable interrupts
|
|
*/
|
|
void cpu_lock(void)
|
|
{
|
|
arc_lock();
|
|
}
|
|
|
|
/**
|
|
* \brief unlock cpu, enable interrupts to happen
|
|
*/
|
|
void cpu_unlock(void)
|
|
{
|
|
arc_unlock();
|
|
}
|
|
|
|
/**
|
|
* \brief lock cpu and return status
|
|
*
|
|
* \returns cpu status
|
|
*/
|
|
uint32_t cpu_lock_save(void)
|
|
{
|
|
return arc_lock_save();
|
|
}
|
|
|
|
/**
|
|
* \brief unlock cpu with the specific status
|
|
*
|
|
* \param[in] status cpu status saved by cpu_lock_save
|
|
*/
|
|
void cpu_unlock_restore(const uint32_t status)
|
|
{
|
|
arc_unlock_restore(status);
|
|
}
|
|
|
|
/**
|
|
* \ingroup ARC_HAL_EXCEPTION_INTERRUPT
|
|
* \brief install an interrupt handler
|
|
* \param[in] intno interrupt number
|
|
* \param[in] handler interrupt handler to install
|
|
*/
|
|
int32_t int_handler_install(const uint32_t intno, INT_HANDLER handler)
|
|
{
|
|
/*!< \todo parameter check ? */
|
|
if (intno >= NUM_EXC_CPU) {
|
|
return exc_handler_install(intno, handler);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* \ingroup ARC_HAL_EXCEPTION_INTERRUPT
|
|
* \brief get the installed an interrupt handler
|
|
* \param[in] intno interrupt number
|
|
* \return the installed interrupt handler or NULL
|
|
*/
|
|
INT_HANDLER int_handler_get(const uint32_t intno)
|
|
{
|
|
if (intno >= NUM_EXC_CPU) {
|
|
return exc_handler_get(intno);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif /* EMBARC_OVERRIDE_ARC_INTERRUPT_MANAGEMENT */
|