target/xtensa: implement NMI support
When NMI is configured it is taken regardless of INTENABLE SR contents, PS.INTLEVEL or PS.EXCM. It is cleared automatically once it's taken. Add nmi_level to XtensaConfig, puth there NMI level from the overlay or XCHAL_NUM_INTLEVELS + 1 when NMI is not configured. Add NMI mask to INTENABLE SR and limit CINTLEVEL to nmi_level - 1 when determining pending IRQ level in check_interrupt(). Always take and clear pending interrupt at nmi_level in the handle_interrupt(). Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
This commit is contained in:
parent
91dc2b2d12
commit
a7d479ee51
@ -35,9 +35,13 @@ void check_interrupts(CPUXtensaState *env)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
int minlevel = xtensa_get_cintlevel(env);
|
||||
uint32_t int_set_enabled = env->sregs[INTSET] & env->sregs[INTENABLE];
|
||||
uint32_t int_set_enabled = env->sregs[INTSET] &
|
||||
(env->sregs[INTENABLE] | env->config->inttype_mask[INTTYPE_NMI]);
|
||||
int level;
|
||||
|
||||
if (minlevel >= env->config->nmi_level) {
|
||||
minlevel = env->config->nmi_level - 1;
|
||||
}
|
||||
for (level = env->config->nlevel; level > minlevel; --level) {
|
||||
if (env->config->level_mask[level] & int_set_enabled) {
|
||||
env->pending_irq_level = level;
|
||||
|
@ -433,6 +433,7 @@ struct XtensaConfig {
|
||||
uint32_t exception_vector[EXC_MAX];
|
||||
unsigned ninterrupt;
|
||||
unsigned nlevel;
|
||||
unsigned nmi_level;
|
||||
uint32_t interrupt_vector[MAX_NLEVEL + MAX_NNMI + 1];
|
||||
uint32_t level_mask[MAX_NLEVEL + MAX_NNMI + 1];
|
||||
uint32_t inttype_mask[INTTYPE_MAX];
|
||||
|
@ -132,11 +132,15 @@ void HELPER(intset)(CPUXtensaState *env, uint32_t v)
|
||||
v & env->config->inttype_mask[INTTYPE_SOFTWARE]);
|
||||
}
|
||||
|
||||
static void intclear(CPUXtensaState *env, uint32_t v)
|
||||
{
|
||||
atomic_and(&env->sregs[INTSET], ~v);
|
||||
}
|
||||
|
||||
void HELPER(intclear)(CPUXtensaState *env, uint32_t v)
|
||||
{
|
||||
atomic_and(&env->sregs[INTSET],
|
||||
~(v & (env->config->inttype_mask[INTTYPE_SOFTWARE] |
|
||||
env->config->inttype_mask[INTTYPE_EDGE])));
|
||||
intclear(env, v & (env->config->inttype_mask[INTTYPE_SOFTWARE] |
|
||||
env->config->inttype_mask[INTTYPE_EDGE]));
|
||||
}
|
||||
|
||||
static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector)
|
||||
@ -159,11 +163,11 @@ static void handle_interrupt(CPUXtensaState *env)
|
||||
{
|
||||
int level = env->pending_irq_level;
|
||||
|
||||
if (level > xtensa_get_cintlevel(env) &&
|
||||
level <= env->config->nlevel &&
|
||||
(env->config->level_mask[level] &
|
||||
env->sregs[INTSET] &
|
||||
env->sregs[INTENABLE])) {
|
||||
if ((level > xtensa_get_cintlevel(env) &&
|
||||
level <= env->config->nlevel &&
|
||||
(env->config->level_mask[level] &
|
||||
env->sregs[INTSET] & env->sregs[INTENABLE])) ||
|
||||
level == env->config->nmi_level) {
|
||||
CPUState *cs = env_cpu(env);
|
||||
|
||||
if (level > 1) {
|
||||
@ -173,6 +177,9 @@ static void handle_interrupt(CPUXtensaState *env)
|
||||
(env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM;
|
||||
env->pc = relocated_vector(env,
|
||||
env->config->interrupt_vector[level]);
|
||||
if (level == env->config->nmi_level) {
|
||||
intclear(env, env->config->inttype_mask[INTTYPE_NMI]);
|
||||
}
|
||||
} else {
|
||||
env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE;
|
||||
|
||||
|
@ -216,6 +216,9 @@
|
||||
#define XTHAL_INTTYPE_IDMA_ERR INTTYPE_IDMA_ERR
|
||||
#define XTHAL_INTTYPE_GS_ERR INTTYPE_GS_ERR
|
||||
|
||||
#ifndef XCHAL_NMILEVEL
|
||||
#define XCHAL_NMILEVEL (XCHAL_NUM_INTLEVELS + 1)
|
||||
#endif
|
||||
|
||||
#define INTERRUPT(i) { \
|
||||
.level = XCHAL_INT ## i ## _LEVEL, \
|
||||
@ -305,7 +308,8 @@
|
||||
|
||||
#define INTERRUPTS_SECTION \
|
||||
.ninterrupt = XCHAL_NUM_INTERRUPTS, \
|
||||
.nlevel = XCHAL_NUM_INTLEVELS, \
|
||||
.nlevel = XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI, \
|
||||
.nmi_level = XCHAL_NMILEVEL, \
|
||||
.interrupt_vector = INTERRUPT_VECTORS, \
|
||||
.level_mask = LEVEL_MASKS, \
|
||||
.inttype_mask = INTTYPE_MASKS, \
|
||||
|
Loading…
x
Reference in New Issue
Block a user