Skip to content

Commit

Permalink
target/nios2: Implement EIC interrupt processing
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
rth7680 committed Apr 26, 2022
1 parent 6bcc59c commit a25c4ef
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 22 deletions.
92 changes: 71 additions & 21 deletions target/nios2/cpu.c
Expand Up @@ -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;
Expand All @@ -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
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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 */
Expand Down
8 changes: 8 additions & 0 deletions target/nios2/cpu.h
Expand Up @@ -114,13 +114,15 @@ 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) /* only in sstatus */

#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK
#define CR_STATUS_U R_CR_STATUS_U_MASK
#define CR_STATUS_EH R_CR_STATUS_EH_MASK
#define CR_STATUS_IH R_CR_STATUS_IH_MASK
#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK
#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK
#define CR_STATUS_SRS R_CR_STATUS_SRS_MASK

FIELD(CR_EXCEPTION, CAUSE, 2, 5)
FIELD(CR_EXCEPTION, ECCFTL, 31, 1)
Expand Down Expand Up @@ -234,6 +236,12 @@ struct ArchCPU {

/* 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 */
};


Expand Down
51 changes: 50 additions & 1 deletion target/nios2/helper.c
Expand Up @@ -37,6 +37,10 @@ static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
uint32_t old_status = env->ctrl[CR_STATUS];
uint32_t new_status = old_status;

/* With shadow regs, exceptions are always taken into CRS 0. */
new_status &= ~R_CR_STATUS_CRS_MASK;
env->regs = env->shadow_regs[0];

if ((old_status & CR_STATUS_EH) == 0) {
int r_ea = R_EA, cr_es = CR_ESTATUS;

Expand All @@ -60,6 +64,14 @@ static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
CR_TLBMISC_DBL);
env->ctrl[CR_TLBMISC] |= tlbmisc_set;
}

/*
* With shadow regs, and EH == 0, PRS is set from CRS.
* At least, so says Table 3-9, and some other text,
* though Table 3-38 says otherwise.
*/
new_status = FIELD_DP32(new_status, CR_STATUS, PRS,
FIELD_EX32(old_status, CR_STATUS, CRS));
}

new_status &= ~(CR_STATUS_PIE | CR_STATUS_U);
Expand All @@ -77,6 +89,39 @@ static void do_iic_irq(Nios2CPU *cpu)
do_exception(cpu, cpu->exception_addr, 0, false);
}

static void do_eic_irq(Nios2CPU *cpu)
{
CPUNios2State *env = &cpu->env;
uint32_t old_status = env->ctrl[CR_STATUS];
uint32_t new_status = old_status;
uint32_t old_rs = FIELD_EX32(old_status, CR_STATUS, CRS);
uint32_t new_rs = cpu->rrs;

new_status = FIELD_DP32(new_status, CR_STATUS, CRS, new_rs);
new_status = FIELD_DP32(new_status, CR_STATUS, IL, cpu->ril);
new_status = FIELD_DP32(new_status, CR_STATUS, NMI, cpu->rnmi);
new_status &= ~(CR_STATUS_RSIE | CR_STATUS_U);
new_status |= CR_STATUS_IH;

if (!(new_status & CR_STATUS_EH)) {
new_status = FIELD_DP32(new_status, CR_STATUS, PRS, old_rs);
if (new_rs == 0) {
env->ctrl[CR_ESTATUS] = old_status;
} else {
if (new_rs != old_rs) {
old_status |= CR_STATUS_SRS;
}
env->shadow_regs[new_rs][R_SSTATUS] = old_status;
}
env->shadow_regs[new_rs][R_EA] = env->pc + 4;
}

env->ctrl[CR_STATUS] = new_status;
nios2_update_crs(env);

env->pc = cpu->rha;
}

void nios2_cpu_do_interrupt(CPUState *cs)
{
Nios2CPU *cpu = NIOS2_CPU(cs);
Expand Down Expand Up @@ -142,7 +187,11 @@ void nios2_cpu_do_interrupt(CPUState *cs)

switch (cs->exception_index) {
case EXCP_IRQ:
do_iic_irq(cpu);
if (cpu->eic_present) {
do_eic_irq(cpu);
} else {
do_iic_irq(cpu);
}
break;

case EXCP_TLB_D:
Expand Down

0 comments on commit a25c4ef

Please sign in to comment.