diff options
author | Richard Henderson <richard.henderson@linaro.org> | 2022-04-21 08:17:27 -0700 |
---|---|---|
committer | Richard Henderson <richard.henderson@linaro.org> | 2022-04-26 08:17:05 -0700 |
commit | a25c4eff32ba6192cff648ccaf0316bd829c80af (patch) | |
tree | 3d7c78f1735eb3c9437f7846b91f7701f6b439c6 /target/nios2/cpu.c | |
parent | 6bcc59cafa271b634009bc92c01c866602ea4c53 (diff) |
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>
Diffstat (limited to 'target/nios2/cpu.c')
-rw-r--r-- | target/nios2/cpu.c | 92 |
1 files changed, 71 insertions, 21 deletions
diff --git a/target/nios2/cpu.c b/target/nios2/cpu.c index d043c02fcd..19b2409974 100644 --- a/target/nios2/cpu.c +++ b/target/nios2/cpu.c @@ -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,18 +206,49 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp) } #ifndef CONFIG_USER_ONLY -static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +static bool eic_take_interrupt(Nios2CPU *cpu) { - Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; + const uint32_t status = env->ctrl[CR_STATUS]; - 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); + 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); + + 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; } #endif /* !CONFIG_USER_ONLY */ |