/* ------------------------------------------ | |
* 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; | |
//dbg_printf(DBG_LESS_INFO, "default cpu exception handler\r\n"); | |
//dbg_printf(DBG_LESS_INFO, "exc_no:%d, last sp:0x%08x, ecr:0x%08x, eret:0x%08x\r\n", | |
//exc_no, (uint32_t)p_excinf, excpt_cause_reg, excpt_ret_reg); | |
#if SECURESHIELD_VERSION == 2 | |
while (1); | |
#else | |
Asm("kflag 1"); | |
#endif | |
} | |
/** | |
* \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); | |
//dbg_printf(DBG_LESS_INFO, "default interrupt handler\r\n"); | |
//dbg_printf(DBG_LESS_INFO, "last sp:0x%08x, icause:0x%08x\r\n", (uint32_t)p_excinf, int_cause_reg); | |
#if SECURESHIELD_VERSION == 2 | |
while (1); | |
#else | |
Asm("kflag 1"); | |
#endif | |
} | |
__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 */ |