target/nios2: Implement EIC interrupt processing

This is the cpu side of the operation.  Register one irq line,
called EIC.  Split out the rather different processing to a
separate function.

Delay initialization of gpio irqs until realize.  We need to
provide a window after init in which the board can set eic_present.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20220421151735.31996-57-richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2022-04-21 08:17:27 -07:00
parent 6bcc59cafa
commit a25c4eff32
3 changed files with 129 additions and 22 deletions

View file

@ -63,7 +63,19 @@ static void nios2_cpu_reset(DeviceState *dev)
}
#ifndef CONFIG_USER_ONLY
static void nios2_cpu_set_irq(void *opaque, int irq, int level)
static void eic_set_irq(void *opaque, int irq, int level)
{
Nios2CPU *cpu = opaque;
CPUState *cs = CPU(cpu);
if (level) {
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
} else {
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
}
}
static void iic_set_irq(void *opaque, int irq, int level)
{
Nios2CPU *cpu = opaque;
CPUNios2State *env = &cpu->env;
@ -87,15 +99,6 @@ static void nios2_cpu_initfn(Object *obj)
#if !defined(CONFIG_USER_ONLY)
mmu_init(&cpu->env);
/*
* These interrupt lines model the IIC (internal interrupt
* controller). QEMU does not currently support the EIC
* (external interrupt controller) -- if we did it would be
* a separate device in hw/intc with a custom interface to
* the CPU, and boards using it would not wire up these IRQ lines.
*/
qdev_init_gpio_in_named(DEVICE(cpu), nios2_cpu_set_irq, "IRQ", 32);
#endif
}
@ -128,10 +131,18 @@ static void realize_cr_status(CPUState *cs)
RO_REG(CR_EXCEPTION);
WR_REG(CR_BADADDR);
/* TODO: These control registers are not present with the EIC. */
RO_FIELD(CR_STATUS, RSIE);
WR_REG(CR_IENABLE);
RO_REG(CR_IPENDING);
if (cpu->eic_present) {
WR_FIELD(CR_STATUS, RSIE);
RO_FIELD(CR_STATUS, NMI);
WR_FIELD(CR_STATUS, PRS);
RO_FIELD(CR_STATUS, CRS);
WR_FIELD(CR_STATUS, IL);
WR_FIELD(CR_STATUS, IH);
} else {
RO_FIELD(CR_STATUS, RSIE);
WR_REG(CR_IENABLE);
RO_REG(CR_IPENDING);
}
if (cpu->mmu_present) {
WR_FIELD(CR_STATUS, U);
@ -170,6 +181,14 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev);
Error *local_err = NULL;
#ifndef CONFIG_USER_ONLY
if (cpu->eic_present) {
qdev_init_gpio_in_named(DEVICE(cpu), eic_set_irq, "EIC", 1);
} else {
qdev_init_gpio_in_named(DEVICE(cpu), iic_set_irq, "IRQ", 32);
}
#endif
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
@ -187,17 +206,48 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
}
#ifndef CONFIG_USER_ONLY
static bool eic_take_interrupt(Nios2CPU *cpu)
{
CPUNios2State *env = &cpu->env;
const uint32_t status = env->ctrl[CR_STATUS];
if (cpu->rnmi) {
return !(status & CR_STATUS_NMI);
}
if (!(status & CR_STATUS_PIE)) {
return false;
}
if (cpu->ril <= FIELD_EX32(status, CR_STATUS, IL)) {
return false;
}
if (cpu->rrs != FIELD_EX32(status, CR_STATUS, CRS)) {
return true;
}
return status & CR_STATUS_RSIE;
}
static bool iic_take_interrupt(Nios2CPU *cpu)
{
CPUNios2State *env = &cpu->env;
if (!(env->ctrl[CR_STATUS] & CR_STATUS_PIE)) {
return false;
}
return env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE];
}
static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
Nios2CPU *cpu = NIOS2_CPU(cs);
CPUNios2State *env = &cpu->env;
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
(env->ctrl[CR_STATUS] & CR_STATUS_PIE) &&
(env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE])) {
cs->exception_index = EXCP_IRQ;
nios2_cpu_do_interrupt(cs);
return true;
if (interrupt_request & CPU_INTERRUPT_HARD) {
if (cpu->eic_present
? eic_take_interrupt(cpu)
: iic_take_interrupt(cpu)) {
cs->exception_index = EXCP_IRQ;
nios2_cpu_do_interrupt(cs);
return true;
}
}
return false;
}