blob: b2ba5dc69189ab227135d13088807d81e6ba7117 [file] [log] [blame]
/* ------------------------------------------
* 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 */