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:
parent
6bcc59cafa
commit
a25c4eff32
@ -63,7 +63,19 @@ static void nios2_cpu_reset(DeviceState *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#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;
|
Nios2CPU *cpu = opaque;
|
||||||
CPUNios2State *env = &cpu->env;
|
CPUNios2State *env = &cpu->env;
|
||||||
@ -87,15 +99,6 @@ static void nios2_cpu_initfn(Object *obj)
|
|||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
mmu_init(&cpu->env);
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,10 +131,18 @@ static void realize_cr_status(CPUState *cs)
|
|||||||
RO_REG(CR_EXCEPTION);
|
RO_REG(CR_EXCEPTION);
|
||||||
WR_REG(CR_BADADDR);
|
WR_REG(CR_BADADDR);
|
||||||
|
|
||||||
/* TODO: These control registers are not present with the EIC. */
|
if (cpu->eic_present) {
|
||||||
RO_FIELD(CR_STATUS, RSIE);
|
WR_FIELD(CR_STATUS, RSIE);
|
||||||
WR_REG(CR_IENABLE);
|
RO_FIELD(CR_STATUS, NMI);
|
||||||
RO_REG(CR_IPENDING);
|
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) {
|
if (cpu->mmu_present) {
|
||||||
WR_FIELD(CR_STATUS, U);
|
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);
|
Nios2CPUClass *ncc = NIOS2_CPU_GET_CLASS(dev);
|
||||||
Error *local_err = NULL;
|
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);
|
cpu_exec_realizefn(cs, &local_err);
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
@ -187,17 +206,48 @@ static void nios2_cpu_realizefn(DeviceState *dev, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#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)
|
static bool nios2_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
||||||
{
|
{
|
||||||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||||
CPUNios2State *env = &cpu->env;
|
|
||||||
|
|
||||||
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
||||||
(env->ctrl[CR_STATUS] & CR_STATUS_PIE) &&
|
if (cpu->eic_present
|
||||||
(env->ctrl[CR_IPENDING] & env->ctrl[CR_IENABLE])) {
|
? eic_take_interrupt(cpu)
|
||||||
cs->exception_index = EXCP_IRQ;
|
: iic_take_interrupt(cpu)) {
|
||||||
nios2_cpu_do_interrupt(cs);
|
cs->exception_index = EXCP_IRQ;
|
||||||
return true;
|
nios2_cpu_do_interrupt(cs);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +114,7 @@ FIELD(CR_STATUS, CRS, 10, 6)
|
|||||||
FIELD(CR_STATUS, PRS, 16, 6)
|
FIELD(CR_STATUS, PRS, 16, 6)
|
||||||
FIELD(CR_STATUS, NMI, 22, 1)
|
FIELD(CR_STATUS, NMI, 22, 1)
|
||||||
FIELD(CR_STATUS, RSIE, 23, 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_PIE R_CR_STATUS_PIE_MASK
|
||||||
#define CR_STATUS_U R_CR_STATUS_U_MASK
|
#define CR_STATUS_U R_CR_STATUS_U_MASK
|
||||||
@ -121,6 +122,7 @@ FIELD(CR_STATUS, RSIE, 23, 1)
|
|||||||
#define CR_STATUS_IH R_CR_STATUS_IH_MASK
|
#define CR_STATUS_IH R_CR_STATUS_IH_MASK
|
||||||
#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK
|
#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK
|
||||||
#define CR_STATUS_RSIE R_CR_STATUS_RSIE_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, CAUSE, 2, 5)
|
||||||
FIELD(CR_EXCEPTION, ECCFTL, 31, 1)
|
FIELD(CR_EXCEPTION, ECCFTL, 31, 1)
|
||||||
@ -234,6 +236,12 @@ struct ArchCPU {
|
|||||||
|
|
||||||
/* Bits within each control register which are reserved or readonly. */
|
/* Bits within each control register which are reserved or readonly. */
|
||||||
ControlRegState cr_state[NUM_CR_REGS];
|
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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,6 +37,10 @@ static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
|
|||||||
uint32_t old_status = env->ctrl[CR_STATUS];
|
uint32_t old_status = env->ctrl[CR_STATUS];
|
||||||
uint32_t new_status = old_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) {
|
if ((old_status & CR_STATUS_EH) == 0) {
|
||||||
int r_ea = R_EA, cr_es = CR_ESTATUS;
|
int r_ea = R_EA, cr_es = CR_ESTATUS;
|
||||||
|
|
||||||
@ -60,6 +64,14 @@ static void do_exception(Nios2CPU *cpu, uint32_t exception_addr,
|
|||||||
CR_TLBMISC_DBL);
|
CR_TLBMISC_DBL);
|
||||||
env->ctrl[CR_TLBMISC] |= tlbmisc_set;
|
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);
|
new_status &= ~(CR_STATUS_PIE | CR_STATUS_U);
|
||||||
@ -77,6 +89,39 @@ static void do_iic_irq(Nios2CPU *cpu)
|
|||||||
do_exception(cpu, cpu->exception_addr, 0, false);
|
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)
|
void nios2_cpu_do_interrupt(CPUState *cs)
|
||||||
{
|
{
|
||||||
Nios2CPU *cpu = NIOS2_CPU(cs);
|
Nios2CPU *cpu = NIOS2_CPU(cs);
|
||||||
@ -142,7 +187,11 @@ void nios2_cpu_do_interrupt(CPUState *cs)
|
|||||||
|
|
||||||
switch (cs->exception_index) {
|
switch (cs->exception_index) {
|
||||||
case EXCP_IRQ:
|
case EXCP_IRQ:
|
||||||
do_iic_irq(cpu);
|
if (cpu->eic_present) {
|
||||||
|
do_eic_irq(cpu);
|
||||||
|
} else {
|
||||||
|
do_iic_irq(cpu);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EXCP_TLB_D:
|
case EXCP_TLB_D:
|
||||||
|
Loading…
Reference in New Issue
Block a user