@@ -115,6 +115,7 @@ FIELD(CR_STATUS, CRS, 10, 6)
FIELD(CR_STATUS, PRS, 16, 6)
FIELD(CR_STATUS, NMI, 22, 1)
FIELD(CR_STATUS, RSIE, 23, 1)
+FIELD(CR_STATUS, SRS, 31, 1)
#define CR_STATUS_PIE (1u << R_CR_STATUS_PIE_SHIFT)
#define CR_STATUS_U (1u << R_CR_STATUS_U_SHIFT)
@@ -122,6 +123,7 @@ FIELD(CR_STATUS, RSIE, 23, 1)
#define CR_STATUS_IH (1u << R_CR_STATUS_IH_SHIFT)
#define CR_STATUS_NMI (1u << R_CR_STATUS_NMI_SHIFT)
#define CR_STATUS_RSIE (1u << R_CR_STATUS_RSIE_SHIFT)
+#define CR_STATUS_SRS (1u << R_CR_STATUS_SRS_SHIFT)
FIELD(CR_EXCEPTION, CAUSE, 2, 5)
FIELD(CR_EXCEPTION, ECCFTL, 31, 1)
@@ -252,6 +254,12 @@ struct Nios2CPU {
/* Bits within each control register which are reserved or readonly. */
ControlRegState cr_state[NUM_CR_REGS];
+
+ /* External Interrupt Controller Interface */
+ uint32_t rha; /* Requested handler address */
+ uint32_t ril; /* Requested interrupt level */
+ uint32_t rrs; /* Requested register set */
+ bool rnmi; /* Requested nonmaskable interrupt */
};
@@ -67,7 +67,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;
@@ -149,15 +161,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
}
@@ -173,6 +176,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);
@@ -189,17 +200,47 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
}
#ifndef CONFIG_USER_ONLY
+static bool eic_take_interrupt(Nios2CPU *cpu)
+{
+ CPUNios2State *env = &cpu->env;
+
+ if (cpu->rnmi) {
+ return !(env->status & CR_STATUS_NMI);
+ }
+ if (!(env->status & CR_STATUS_PIE)) {
+ return false;
+ }
+ if (cpu->ril <= FIELD_EX32(env->status, CR_STATUS, IL)) {
+ return false;
+ }
+ if (cpu->rrs != FIELD_EX32(env->status, CR_STATUS, CRS)) {
+ return true;
+ }
+ return env->status & CR_STATUS_RSIE;
+}
+
+static bool iic_take_interrupt(Nios2CPU *cpu)
+{
+ CPUNios2State *env = &cpu->env;
+
+ if (!(env->status & CR_STATUS_PIE)) {
+ return false;
+ }
+ return env->ipending & env->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->status & CR_STATUS_PIE) &&
- (env->ipending & env->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;
}
@@ -49,6 +49,36 @@ void nios2_cpu_record_sigsegv(CPUState *cs, vaddr addr,
#else /* !CONFIG_USER_ONLY */
+static void eic_do_interrupt(Nios2CPU *cpu)
+{
+ CPUNios2State *env = &cpu->env;
+ uint32_t old_status = env->status;
+ uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS);
+ uint32_t new_rs = cpu->rrs;
+
+ env->status = FIELD_DP32(env->status, CR_STATUS, CRS, new_rs);
+ env->status = FIELD_DP32(env->status, CR_STATUS, IL, cpu->ril);
+ env->status = FIELD_DP32(env->status, CR_STATUS, NMI, cpu->rnmi);
+ env->status &= ~(CR_STATUS_RSIE | CR_STATUS_U);
+ env->status |= CR_STATUS_IH;
+ nios2_update_crs(env);
+
+ if (!(env->status & CR_STATUS_EH)) {
+ env->status = FIELD_DP32(env->status, CR_STATUS, PRS, old_rs);
+ if (new_rs == 0) {
+ env->estatus = old_status;
+ } else {
+ if (new_rs != old_rs) {
+ old_status |= CR_STATUS_SRS;
+ }
+ env->crs[R_SSTATUS] = old_status;
+ }
+ env->crs[R_EA] = env->pc + 4;
+ }
+
+ env->pc = cpu->rha;
+}
+
void nios2_cpu_do_interrupt(CPUState *cs)
{
Nios2CPU *cpu = NIOS2_CPU(cs);
@@ -60,6 +90,10 @@ void nios2_cpu_do_interrupt(CPUState *cs)
switch (cs->exception_index) {
case EXCP_IRQ:
qemu_log_mask(CPU_LOG_INT, "interrupt at pc=%x\n", env->pc);
+ if (cpu->eic_present) {
+ eic_do_interrupt(cpu);
+ return;
+ }
break;
case EXCP_TLBD:
@@ -113,6 +147,9 @@ void nios2_cpu_do_interrupt(CPUState *cs)
* Finish Internal Interrupt or Noninterrupt Exception.
*/
+ env->status &= ~R_CR_STATUS_CRS_MASK;
+ nios2_update_crs(env);
+
if (!(env->status & CR_STATUS_EH)) {
env->ctrl[cr_estatus] = env->status;
env->crs[r_ea] = env->pc + 4;
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> --- target/nios2/cpu.h | 8 +++++ target/nios2/cpu.c | 75 +++++++++++++++++++++++++++++++++---------- target/nios2/helper.c | 37 +++++++++++++++++++++ 3 files changed, 103 insertions(+), 17 deletions(-)