aboutsummaryrefslogtreecommitdiff
path: root/target/nios2/cpu.c
diff options
context:
space:
mode:
authorRichard Henderson <richard.henderson@linaro.org>2022-04-21 08:17:27 -0700
committerRichard Henderson <richard.henderson@linaro.org>2022-04-26 08:17:05 -0700
commita25c4eff32ba6192cff648ccaf0316bd829c80af (patch)
tree3d7c78f1735eb3c9437f7846b91f7701f6b439c6 /target/nios2/cpu.c
parent6bcc59cafa271b634009bc92c01c866602ea4c53 (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.c92
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 */