The behavior of the enable and status registers is almost identical between INTC(CPU Die) and INTCIO(IO Die). To reduce duplicated code, adds "aspeed_intc_enable_handler" functions to handle enable register write behavior and "aspeed_intc_status_handler" functions to handle status register write behavior.
Signed-off-by: Jamin Lin <jamin_...@aspeedtech.com> --- hw/intc/aspeed_intc.c | 190 ++++++++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 82 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 316885a27a..8f9fa97acc 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -114,6 +114,112 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) } } +static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, + uint64_t data) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + uint32_t addr = offset >> 2; + uint32_t old_enable; + uint32_t change; + uint32_t irq; + + irq = (offset & 0x0f00) >> 8; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + /* + * The enable registers are used to enable source interrupts. + * They also handle masking and unmasking of source interrupts + * during the execution of the source ISR. + */ + + /* disable all source interrupt */ + if (!data && !s->enable[irq]) { + s->regs[addr] = data; + return; + } + + old_enable = s->enable[irq]; + s->enable[irq] |= data; + + /* enable new source interrupt */ + if (old_enable != s->enable[irq]) { + trace_aspeed_intc_enable(s->enable[irq]); + s->regs[addr] = data; + return; + } + + /* mask and unmask source interrupt */ + change = s->regs[addr] ^ data; + if (change & data) { + s->mask[irq] &= ~change; + trace_aspeed_intc_unmask(change, s->mask[irq]); + } else { + s->mask[irq] |= change; + trace_aspeed_intc_mask(change, s->mask[irq]); + } + + s->regs[addr] = data; +} + +static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, + uint64_t data) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + uint32_t addr = offset >> 2; + uint32_t irq; + + if (!data) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); + return; + } + + irq = (offset & 0x0f00) >> 8; + + if (irq >= aic->num_ints) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + __func__, irq); + return; + } + + /* clear status */ + s->regs[addr] &= ~data; + + /* + * These status registers are used for notify sources ISR are executed. + * If one source ISR is executed, it will clear one bit. + * If it clear all bits, it means to initialize this register status + * rather than sources ISR are executed. + */ + if (data == 0xffffffff) { + return; + } + + /* All source ISR execution are done */ + if (!s->regs[addr]) { + trace_aspeed_intc_all_isr_done(irq); + if (s->pending[irq]) { + /* + * handle pending source interrupt + * notify firmware which source interrupt are pending + * by setting status register + */ + s->regs[addr] = s->pending[irq]; + s->pending[irq] = 0; + trace_aspeed_intc_trigger_irq(irq, s->regs[addr]); + aspeed_intc_update(s, irq, 1); + } else { + /* clear irq */ + trace_aspeed_intc_clear_irq(irq, 0); + aspeed_intc_update(s, irq, 0); + } + } +} + static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) { AspeedINTCState *s = ASPEED_INTC(opaque); @@ -140,9 +246,6 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, AspeedINTCState *s = ASPEED_INTC(opaque); AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); uint32_t addr = offset >> 2; - uint32_t old_enable; - uint32_t change; - uint32_t irq; if (offset >= aic->reg_size) { qemu_log_mask(LOG_GUEST_ERROR, @@ -163,45 +266,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, case R_GICINT134_EN: case R_GICINT135_EN: case R_GICINT136_EN: - irq = (offset & 0x0f00) >> 8; - - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", - __func__, irq); - return; - } - - /* - * These registers are used for enable sources interrupt and - * mask and unmask source interrupt while executing source ISR. - */ - - /* disable all source interrupt */ - if (!data && !s->enable[irq]) { - s->regs[addr] = data; - return; - } - - old_enable = s->enable[irq]; - s->enable[irq] |= data; - - /* enable new source interrupt */ - if (old_enable != s->enable[irq]) { - trace_aspeed_intc_enable(s->enable[irq]); - s->regs[addr] = data; - return; - } - - /* mask and unmask source interrupt */ - change = s->regs[addr] ^ data; - if (change & data) { - s->mask[irq] &= ~change; - trace_aspeed_intc_unmask(change, s->mask[irq]); - } else { - s->mask[irq] |= change; - trace_aspeed_intc_mask(change, s->mask[irq]); - } - s->regs[addr] = data; + aspeed_intc_enable_handler(s, offset, data); break; case R_GICINT128_STATUS: case R_GICINT129_STATUS: @@ -212,46 +277,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, case R_GICINT134_STATUS: case R_GICINT135_STATUS: case R_GICINT136_STATUS: - irq = (offset & 0x0f00) >> 8; - - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", - __func__, irq); - return; - } - - /* clear status */ - s->regs[addr] &= ~data; - - /* - * These status registers are used for notify sources ISR are executed. - * If one source ISR is executed, it will clear one bit. - * If it clear all bits, it means to initialize this register status - * rather than sources ISR are executed. - */ - if (data == 0xffffffff) { - return; - } - - /* All source ISR execution are done */ - if (!s->regs[addr]) { - trace_aspeed_intc_all_isr_done(irq); - if (s->pending[irq]) { - /* - * handle pending source interrupt - * notify firmware which source interrupt are pending - * by setting status register - */ - s->regs[addr] = s->pending[irq]; - s->pending[irq] = 0; - trace_aspeed_intc_trigger_irq(irq, s->regs[addr]); - aspeed_intc_update(s, irq, 1); - } else { - /* clear irq */ - trace_aspeed_intc_clear_irq(irq, 0); - aspeed_intc_update(s, irq, 0); - } - } + aspeed_intc_status_handler(s, offset, data); break; default: s->regs[addr] = data; -- 2.34.1