From c5728c3488b936e1cef5d4d83d26df853f8fac6b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:10 +0800 Subject: hw/intc/aspeed: Support setting different memory size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the AST2700 datasheet, the INTC(CPU DIE) controller has 16KB (0x4000) of register space, and the INTCIO (I/O DIE) controller has 1KB (0x400) of register space. Introduced a new class attribute "mem_size" to set different memory sizes for the INTC models in AST2700. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 126b711..033b574 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -302,10 +302,16 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); int i; + memory_region_init(&s->iomem_container, OBJECT(s), + TYPE_ASPEED_INTC ".container", aic->mem_size); + + sysbus_init_mmio(sbd, &s->iomem_container); + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, TYPE_ASPEED_INTC ".regs", ASPEED_INTC_NR_REGS << 2); - sysbus_init_mmio(sbd, &s->iomem); + memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); + qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints); for (i = 0; i < aic->num_ints; i++) { @@ -344,6 +350,7 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2700 INTC Controller"; aic->num_lines = 32; aic->num_ints = 9; + aic->mem_size = 0x4000; } static const TypeInfo aspeed_2700_intc_info = { -- cgit v1.1 From 0cffaace0565b68a354c67f147bf8f0f438726e1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:11 +0800 Subject: hw/intc/aspeed: Rename status_addr and addr to status_reg and reg for clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the variables "status_addr" to "status_reg" and "addr" to "reg" because they are used as register index. This change makes the code more appropriate and improves readability. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 033b574..465f41e 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -60,7 +60,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) { AspeedINTCState *s = (AspeedINTCState *)opaque; AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); - uint32_t status_addr = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); + uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); uint32_t select = 0; uint32_t enable; int i; @@ -92,7 +92,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) trace_aspeed_intc_select(select); - if (s->mask[irq] || s->regs[status_addr]) { + if (s->mask[irq] || s->regs[status_reg]) { /* * a. mask is not 0 means in ISR mode * sources interrupt routine are executing. @@ -108,8 +108,8 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) * notify firmware which source interrupt are coming * by setting status register */ - s->regs[status_addr] = select; - trace_aspeed_intc_trigger_irq(irq, s->regs[status_addr]); + s->regs[status_reg] = select; + trace_aspeed_intc_trigger_irq(irq, s->regs[status_reg]); aspeed_intc_update(s, irq, 1); } } @@ -117,17 +117,17 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) { AspeedINTCState *s = ASPEED_INTC(opaque); - uint32_t addr = offset >> 2; + uint32_t reg = offset >> 2; uint32_t value = 0; - if (addr >= ASPEED_INTC_NR_REGS) { + if (reg >= ASPEED_INTC_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", __func__, offset); return 0; } - value = s->regs[addr]; + value = s->regs[reg]; trace_aspeed_intc_read(offset, size, value); return value; @@ -138,12 +138,12 @@ 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 reg = offset >> 2; uint32_t old_enable; uint32_t change; uint32_t irq; - if (addr >= ASPEED_INTC_NR_REGS) { + if (reg >= ASPEED_INTC_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", __func__, offset); @@ -152,7 +152,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, trace_aspeed_intc_write(offset, size, data); - switch (addr) { + switch (reg) { case R_GICINT128_EN: case R_GICINT129_EN: case R_GICINT130_EN: @@ -177,7 +177,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, /* disable all source interrupt */ if (!data && !s->enable[irq]) { - s->regs[addr] = data; + s->regs[reg] = data; return; } @@ -187,12 +187,12 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, /* enable new source interrupt */ if (old_enable != s->enable[irq]) { trace_aspeed_intc_enable(s->enable[irq]); - s->regs[addr] = data; + s->regs[reg] = data; return; } /* mask and unmask source interrupt */ - change = s->regs[addr] ^ data; + change = s->regs[reg] ^ data; if (change & data) { s->mask[irq] &= ~change; trace_aspeed_intc_unmask(change, s->mask[irq]); @@ -200,7 +200,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, s->mask[irq] |= change; trace_aspeed_intc_mask(change, s->mask[irq]); } - s->regs[addr] = data; + s->regs[reg] = data; break; case R_GICINT128_STATUS: case R_GICINT129_STATUS: @@ -220,7 +220,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, } /* clear status */ - s->regs[addr] &= ~data; + s->regs[reg] &= ~data; /* * These status registers are used for notify sources ISR are executed. @@ -233,7 +233,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, } /* All source ISR execution are done */ - if (!s->regs[addr]) { + if (!s->regs[reg]) { trace_aspeed_intc_all_isr_done(irq); if (s->pending[irq]) { /* @@ -241,9 +241,9 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, * notify firmware which source interrupt are pending * by setting status register */ - s->regs[addr] = s->pending[irq]; + s->regs[reg] = s->pending[irq]; s->pending[irq] = 0; - trace_aspeed_intc_trigger_irq(irq, s->regs[addr]); + trace_aspeed_intc_trigger_irq(irq, s->regs[reg]); aspeed_intc_update(s, irq, 1); } else { /* clear irq */ @@ -253,7 +253,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, } break; default: - s->regs[addr] = data; + s->regs[reg] = data; break; } -- cgit v1.1 From 563afea0aebd15eac74b89467204f4b76b2ee6fa Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:12 +0800 Subject: hw/intc/aspeed: Introduce dynamic allocation for regs array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the size of the "regs" array is 0x2000, which is too large. To save code size and avoid mapping large unused gaps, will update it to only map the useful set of registers. This update will support multiple sub-regions with different sizes. To address the redundant size issue, replace the static "regs" array with a dynamically allocated "regs" memory. Introduce a new "aspeed_intc_unrealize" function to free the allocated "regs" memory. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 465f41e..5589015 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -289,7 +289,7 @@ static void aspeed_intc_reset(DeviceState *dev) { AspeedINTCState *s = ASPEED_INTC(dev); - memset(s->regs, 0, sizeof(s->regs)); + memset(s->regs, 0, ASPEED_INTC_NR_REGS << 2); memset(s->enable, 0, sizeof(s->enable)); memset(s->mask, 0, sizeof(s->mask)); memset(s->pending, 0, sizeof(s->pending)); @@ -307,6 +307,7 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem_container); + s->regs = g_new(uint32_t, ASPEED_INTC_NR_REGS); memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, TYPE_ASPEED_INTC ".regs", ASPEED_INTC_NR_REGS << 2); @@ -322,12 +323,21 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) } } +static void aspeed_intc_unrealize(DeviceState *dev) +{ + AspeedINTCState *s = ASPEED_INTC(dev); + + g_free(s->regs); + s->regs = NULL; +} + static void aspeed_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "ASPEED INTC Controller"; dc->realize = aspeed_intc_realize; + dc->unrealize = aspeed_intc_unrealize; device_class_set_legacy_reset(dc, aspeed_intc_reset); dc->vmsd = NULL; } -- cgit v1.1 From b008465d655ff3ff314fe1ef81031293b582ebaf Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:13 +0800 Subject: hw/intc/aspeed: Support setting different register size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the size of the regs array is 0x2000, which is too large. So far, it only use GICINT128 - GICINT134, and the offsets from 0 to 0x1000 are unused. To save code size, introduce a new class attribute "reg_size" to set the different register sizes for the INTC models in AST2700 and add a regs sub-region in the memory container. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 5589015..134922e 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -120,13 +120,6 @@ static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) uint32_t reg = offset >> 2; uint32_t value = 0; - if (reg >= ASPEED_INTC_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", - __func__, offset); - return 0; - } - value = s->regs[reg]; trace_aspeed_intc_read(offset, size, value); @@ -143,13 +136,6 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, uint32_t change; uint32_t irq; - if (reg >= ASPEED_INTC_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", - __func__, offset); - return; - } - trace_aspeed_intc_write(offset, size, data); switch (reg) { @@ -288,8 +274,9 @@ static void aspeed_intc_instance_init(Object *obj) static void aspeed_intc_reset(DeviceState *dev) { AspeedINTCState *s = ASPEED_INTC(dev); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); - memset(s->regs, 0, ASPEED_INTC_NR_REGS << 2); + memset(s->regs, 0, aic->nr_regs << 2); memset(s->enable, 0, sizeof(s->enable)); memset(s->mask, 0, sizeof(s->mask)); memset(s->pending, 0, sizeof(s->pending)); @@ -307,9 +294,9 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem_container); - s->regs = g_new(uint32_t, ASPEED_INTC_NR_REGS); + s->regs = g_new(uint32_t, aic->nr_regs); memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, - TYPE_ASPEED_INTC ".regs", ASPEED_INTC_NR_REGS << 2); + TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2); memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); @@ -361,6 +348,7 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) aic->num_lines = 32; aic->num_ints = 9; aic->mem_size = 0x4000; + aic->nr_regs = 0x2000 >> 2; } static const TypeInfo aspeed_2700_intc_info = { -- cgit v1.1 From 7ffee511fcf1487e016ae1d11c5e191557a8b804 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:14 +0800 Subject: hw/intc/aspeed: Reduce regs array size by adding a register sub-region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the size of the "regs" array is 0x2000, which is too large. So far, it only uses "GICINT128 to `GICINT134", and the offsets from 0 to 0x1000 are unused. To save code size and avoid mapping large unused gaps, update to only map the useful set of registers: INTC register [0x1000 – 0x1804] Update "reg_size" to 0x808. Introduce a new class attribute "reg_offset" to set the start offset of a "INTC" sub-region. Set the "reg_offset" to 0x1000 for INTC registers. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 134922e..d684b4b 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -14,25 +14,31 @@ #include "hw/registerfields.h" #include "qapi/error.h" -/* INTC Registers */ -REG32(GICINT128_EN, 0x1000) -REG32(GICINT128_STATUS, 0x1004) -REG32(GICINT129_EN, 0x1100) -REG32(GICINT129_STATUS, 0x1104) -REG32(GICINT130_EN, 0x1200) -REG32(GICINT130_STATUS, 0x1204) -REG32(GICINT131_EN, 0x1300) -REG32(GICINT131_STATUS, 0x1304) -REG32(GICINT132_EN, 0x1400) -REG32(GICINT132_STATUS, 0x1404) -REG32(GICINT133_EN, 0x1500) -REG32(GICINT133_STATUS, 0x1504) -REG32(GICINT134_EN, 0x1600) -REG32(GICINT134_STATUS, 0x1604) -REG32(GICINT135_EN, 0x1700) -REG32(GICINT135_STATUS, 0x1704) -REG32(GICINT136_EN, 0x1800) -REG32(GICINT136_STATUS, 0x1804) +/* + * INTC Registers + * + * values below are offset by - 0x1000 from datasheet + * because its memory region is start at 0x1000 + * + */ +REG32(GICINT128_EN, 0x000) +REG32(GICINT128_STATUS, 0x004) +REG32(GICINT129_EN, 0x100) +REG32(GICINT129_STATUS, 0x104) +REG32(GICINT130_EN, 0x200) +REG32(GICINT130_STATUS, 0x204) +REG32(GICINT131_EN, 0x300) +REG32(GICINT131_STATUS, 0x304) +REG32(GICINT132_EN, 0x400) +REG32(GICINT132_STATUS, 0x404) +REG32(GICINT133_EN, 0x500) +REG32(GICINT133_STATUS, 0x504) +REG32(GICINT134_EN, 0x600) +REG32(GICINT134_STATUS, 0x604) +REG32(GICINT135_EN, 0x700) +REG32(GICINT135_STATUS, 0x704) +REG32(GICINT136_EN, 0x800) +REG32(GICINT136_STATUS, 0x804) #define GICINT_STATUS_BASE R_GICINT128_STATUS @@ -298,7 +304,8 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2); - memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); + memory_region_add_subregion(&s->iomem_container, aic->reg_offset, + &s->iomem); qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints); @@ -348,7 +355,8 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) aic->num_lines = 32; aic->num_ints = 9; aic->mem_size = 0x4000; - aic->nr_regs = 0x2000 >> 2; + aic->nr_regs = 0x808 >> 2; + aic->reg_offset = 0x1000; } static const TypeInfo aspeed_2700_intc_info = { -- cgit v1.1 From 3d6e15eafb3a3977f6659211e08d112807f20626 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:15 +0800 Subject: hw/intc/aspeed: Introduce helper functions for enable and status registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 191 ++++++++++++++++++++++++++++---------------------- 1 file changed, 108 insertions(+), 83 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index d684b4b..b58a7ee 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -120,6 +120,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 reg = 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[reg] = 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[reg] = data; + return; + } + + /* mask and unmask source interrupt */ + change = s->regs[reg] ^ 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[reg] = data; +} + +static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, + uint64_t data) +{ + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + uint32_t reg = 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[reg] &= ~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[reg]) { + 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[reg] = s->pending[irq]; + s->pending[irq] = 0; + trace_aspeed_intc_trigger_irq(irq, s->regs[reg]); + 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); @@ -136,11 +242,7 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, unsigned size) { AspeedINTCState *s = ASPEED_INTC(opaque); - AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); uint32_t reg = offset >> 2; - uint32_t old_enable; - uint32_t change; - uint32_t irq; trace_aspeed_intc_write(offset, size, data); @@ -154,45 +256,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[reg] = 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[reg] = data; - return; - } - - /* mask and unmask source interrupt */ - change = s->regs[reg] ^ 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[reg] = data; + aspeed_intc_enable_handler(s, offset, data); break; case R_GICINT128_STATUS: case R_GICINT129_STATUS: @@ -203,46 +267,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[reg] &= ~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[reg]) { - 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[reg] = s->pending[irq]; - s->pending[irq] = 0; - trace_aspeed_intc_trigger_irq(irq, s->regs[reg]); - 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[reg] = data; -- cgit v1.1 From 49da40cf5ffca090f6918ba882db8fb9536792a7 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:16 +0800 Subject: hw/intc/aspeed: Add object type name to trace events for better debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, these trace events only refer to INTC. To simplify the INTC model, both INTC(CPU Die) and INTCIO(IO Die) will share the same helper functions. However, it is difficult to recognize whether these trace events are comes from INTC or INTCIO. To make these trace events more readable, adds object type name to the INTC trace events. Update trace events to include the "name" field for better identification. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 32 +++++++++++++++++++------------- hw/intc/trace-events | 24 ++++++++++++------------ 2 files changed, 31 insertions(+), 25 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index b58a7ee..d06e697 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -45,6 +45,7 @@ REG32(GICINT136_STATUS, 0x804) static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); if (irq >= aic->num_ints) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", @@ -52,7 +53,7 @@ static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) return; } - trace_aspeed_intc_update_irq(irq, level); + trace_aspeed_intc_update_irq(name, irq, level); qemu_set_irq(s->output_pins[irq], level); } @@ -66,6 +67,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) { AspeedINTCState *s = (AspeedINTCState *)opaque; AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); uint32_t select = 0; uint32_t enable; @@ -77,7 +79,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) return; } - trace_aspeed_intc_set_irq(irq, level); + trace_aspeed_intc_set_irq(name, irq, level); enable = s->enable[irq]; if (!level) { @@ -96,7 +98,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) return; } - trace_aspeed_intc_select(select); + trace_aspeed_intc_select(name, select); if (s->mask[irq] || s->regs[status_reg]) { /* @@ -108,14 +110,14 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) * save source interrupt to pending variable. */ s->pending[irq] |= select; - trace_aspeed_intc_pending_irq(irq, s->pending[irq]); + trace_aspeed_intc_pending_irq(name, irq, s->pending[irq]); } else { /* * notify firmware which source interrupt are coming * by setting status register */ s->regs[status_reg] = select; - trace_aspeed_intc_trigger_irq(irq, s->regs[status_reg]); + trace_aspeed_intc_trigger_irq(name, irq, s->regs[status_reg]); aspeed_intc_update(s, irq, 1); } } @@ -124,6 +126,7 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, uint64_t data) { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; uint32_t old_enable; uint32_t change; @@ -154,7 +157,7 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, /* enable new source interrupt */ if (old_enable != s->enable[irq]) { - trace_aspeed_intc_enable(s->enable[irq]); + trace_aspeed_intc_enable(name, s->enable[irq]); s->regs[reg] = data; return; } @@ -163,10 +166,10 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, change = s->regs[reg] ^ data; if (change & data) { s->mask[irq] &= ~change; - trace_aspeed_intc_unmask(change, s->mask[irq]); + trace_aspeed_intc_unmask(name, change, s->mask[irq]); } else { s->mask[irq] |= change; - trace_aspeed_intc_mask(change, s->mask[irq]); + trace_aspeed_intc_mask(name, change, s->mask[irq]); } s->regs[reg] = data; @@ -176,6 +179,7 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, uint64_t data) { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; uint32_t irq; @@ -207,7 +211,7 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, /* All source ISR execution are done */ if (!s->regs[reg]) { - trace_aspeed_intc_all_isr_done(irq); + trace_aspeed_intc_all_isr_done(name, irq); if (s->pending[irq]) { /* * handle pending source interrupt @@ -216,11 +220,11 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, */ s->regs[reg] = s->pending[irq]; s->pending[irq] = 0; - trace_aspeed_intc_trigger_irq(irq, s->regs[reg]); + trace_aspeed_intc_trigger_irq(name, irq, s->regs[reg]); aspeed_intc_update(s, irq, 1); } else { /* clear irq */ - trace_aspeed_intc_clear_irq(irq, 0); + trace_aspeed_intc_clear_irq(name, irq, 0); aspeed_intc_update(s, irq, 0); } } @@ -229,11 +233,12 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) { AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; uint32_t value = 0; value = s->regs[reg]; - trace_aspeed_intc_read(offset, size, value); + trace_aspeed_intc_read(name, offset, size, value); return value; } @@ -242,9 +247,10 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, unsigned size) { AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; - trace_aspeed_intc_write(offset, size, data); + trace_aspeed_intc_write(name, offset, size, data); switch (reg) { case R_GICINT128_EN: diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 3dcf147..e9ca347 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -80,18 +80,18 @@ aspeed_vic_update_irq(int flags) "Raising IRQ: %d" aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 # aspeed_intc.c -aspeed_intc_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 -aspeed_intc_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 -aspeed_intc_set_irq(int irq, int level) "Set IRQ %d: %d" -aspeed_intc_clear_irq(int irq, int level) "Clear IRQ %d: %d" -aspeed_intc_update_irq(int irq, int level) "Update IRQ: %d: %d" -aspeed_intc_pending_irq(int irq, uint32_t value) "Pending IRQ: %d: 0x%x" -aspeed_intc_trigger_irq(int irq, uint32_t value) "Trigger IRQ: %d: 0x%x" -aspeed_intc_all_isr_done(int irq) "All source ISR execution are done: %d" -aspeed_intc_enable(uint32_t value) "Enable: 0x%x" -aspeed_intc_select(uint32_t value) "Select: 0x%x" -aspeed_intc_mask(uint32_t change, uint32_t value) "Mask: 0x%x: 0x%x" -aspeed_intc_unmask(uint32_t change, uint32_t value) "UnMask: 0x%x: 0x%x" +aspeed_intc_read(const char *s, uint64_t offset, unsigned size, uint32_t value) "%s: From 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_write(const char *s, uint64_t offset, unsigned size, uint32_t data) "%s: To 0x%" PRIx64 " of size %u: 0x%" PRIx32 +aspeed_intc_set_irq(const char *s, int irq, int level) "%s: Set IRQ %d: %d" +aspeed_intc_clear_irq(const char *s, int irq, int level) "%s: Clear IRQ %d: %d" +aspeed_intc_update_irq(const char *s, int irq, int level) "%s: Update IRQ: %d: %d" +aspeed_intc_pending_irq(const char *s, int irq, uint32_t value) "%s: Pending IRQ: %d: 0x%x" +aspeed_intc_trigger_irq(const char *s, int irq, uint32_t value) "%s: Trigger IRQ: %d: 0x%x" +aspeed_intc_all_isr_done(const char *s, int irq) "%s: All source ISR execution are done: %d" +aspeed_intc_enable(const char *s, uint32_t value) "%s: Enable: 0x%x" +aspeed_intc_select(const char *s, uint32_t value) "%s: Select: 0x%x" +aspeed_intc_mask(const char *s, uint32_t change, uint32_t value) "%s: Mask: 0x%x: 0x%x" +aspeed_intc_unmask(const char *s, uint32_t change, uint32_t value) "%s: UnMask: 0x%x: 0x%x" # arm_gic.c gic_enable_irq(int irq) "irq %d enabled" -- cgit v1.1 From 28194d5d15b92f0b3f6628236f93001c3fdd0d39 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:19 +0800 Subject: hw/intc/aspeed: Support different memory region ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous implementation set the "aspeed_intc_ops" struct, containing read and write callbacks, to be used when I/O is performed on the INTC region. Both "aspeed_intc_read" and "aspeed_intc_write" callback functions were used for INTC (CPU Die). To support the INTCIO (IO Die) model, introduces a new "reg_ops" class attribute. This allows setting different memory region operations to support different INTC models. Will introduce "aspeed_intcio_read" and "aspeed_intcio_write" callback functions are used for INTCIO. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-11-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index d06e697..d8ee6e1 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -332,7 +332,7 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem_container); s->regs = g_new(uint32_t, aic->nr_regs); - memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, + memory_region_init_io(&s->iomem, OBJECT(s), aic->reg_ops, s, TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2); memory_region_add_subregion(&s->iomem_container, aic->reg_offset, @@ -359,12 +359,15 @@ static void aspeed_intc_unrealize(DeviceState *dev) static void aspeed_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); dc->desc = "ASPEED INTC Controller"; dc->realize = aspeed_intc_realize; dc->unrealize = aspeed_intc_unrealize; device_class_set_legacy_reset(dc, aspeed_intc_reset); dc->vmsd = NULL; + + aic->reg_ops = &aspeed_intc_ops; } static const TypeInfo aspeed_intc_info = { -- cgit v1.1 From 63f3618f9be0f28ff36cd4b5685877715b97e669 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:20 +0800 Subject: hw/intc/aspeed: Rename num_ints to num_inpins for clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support AST2700 A1, some registers of the INTC(CPU Die) support one input pin to multiple output pins. Renamed "num_ints" to "num_inpins" in the INTC controller code for better clarity and consistency in naming conventions. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-12-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index d8ee6e1..217fda6 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -47,8 +47,9 @@ static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + if (irq >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid input pin index: %d\n", __func__, irq); return; } @@ -60,7 +61,7 @@ static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) /* * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804. * Utilize "address & 0x0f00" to get the irq and irq output pin index - * The value of irq should be 0 to num_ints. + * The value of irq should be 0 to num_inpins. * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on. */ static void aspeed_intc_set_irq(void *opaque, int irq, int level) @@ -73,8 +74,8 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) uint32_t enable; int i; - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + if (irq >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", __func__, irq); return; } @@ -134,8 +135,9 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, irq = (offset & 0x0f00) >> 8; - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + if (irq >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid input pin index: %d\n", __func__, irq); return; } @@ -190,8 +192,9 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, irq = (offset & 0x0f00) >> 8; - if (irq >= aic->num_ints) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", + if (irq >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid input pin index: %d\n", __func__, irq); return; } @@ -299,8 +302,8 @@ static void aspeed_intc_instance_init(Object *obj) AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); int i; - assert(aic->num_ints <= ASPEED_INTC_NR_INTS); - for (i = 0; i < aic->num_ints; i++) { + assert(aic->num_inpins <= ASPEED_INTC_MAX_INPINS); + for (i = 0; i < aic->num_inpins; i++) { object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i], TYPE_OR_IRQ); object_property_set_int(OBJECT(&s->orgates[i]), "num-lines", @@ -338,9 +341,9 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->iomem_container, aic->reg_offset, &s->iomem); - qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints); + qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_inpins); - for (i = 0; i < aic->num_ints; i++) { + for (i = 0; i < aic->num_inpins; i++) { if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) { return; } @@ -387,7 +390,7 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2700 INTC Controller"; aic->num_lines = 32; - aic->num_ints = 9; + aic->num_inpins = 9; aic->mem_size = 0x4000; aic->nr_regs = 0x808 >> 2; aic->reg_offset = 0x1000; -- cgit v1.1 From 35c909cd80d4095690bb1c98c263b01d9617de65 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:21 +0800 Subject: hw/intc/aspeed: Add support for multiple output pins in INTC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added support for multiple output pins in the INTC controller to accommodate the AST2700 A1. Introduced "num_outpins" to represent the number of output pins. Updated the IRQ handling logic to initialize and connect output pins separately from input pins. Modified the "aspeed_soc_ast2700_realize" function to connect source orgates to INTC and INTC to GIC128 - GIC136. Updated the "aspeed_intc_realize" function to initialize output pins. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-13-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 217fda6..6f37afc 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -347,6 +347,9 @@ static void aspeed_intc_realize(DeviceState *dev, Error **errp) if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) { return; } + } + + for (i = 0; i < aic->num_outpins; i++) { sysbus_init_irq(sbd, &s->output_pins[i]); } } @@ -391,6 +394,7 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2700 INTC Controller"; aic->num_lines = 32; aic->num_inpins = 9; + aic->num_outpins = 9; aic->mem_size = 0x4000; aic->nr_regs = 0x808 >> 2; aic->reg_offset = 0x1000; -- cgit v1.1 From c6c5e63d46add459732d8d8d3b84bd5d26dff0ad Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:22 +0800 Subject: hw/intc/aspeed: Refactor INTC to support separate input and output pin indices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactors the INTC to distinguish between input and output pin indices, improving interrupt handling clarity and accuracy. Updated the functions to handle both input and output pin indices. Added detailed logging for input and output pin indices in trace events. These changes ensure that the INTC controller can handle multiple input and output pins, improving support for the AST2700 A1. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-14-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 97 ++++++++++++++++++++++++++++++++------------------- hw/intc/trace-events | 12 +++---- 2 files changed, 67 insertions(+), 42 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 6f37afc..1cbee0e 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -42,20 +42,32 @@ REG32(GICINT136_STATUS, 0x804) #define GICINT_STATUS_BASE R_GICINT128_STATUS -static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) +/* + * Update the state of an interrupt controller pin by setting + * the specified output pin to the given level. + * The input pin index should be between 0 and the number of input pins. + * The output pin index should be between 0 and the number of output pins. + */ +static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx, + int outpin_idx, int level) { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); - if (irq >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Invalid input pin index: %d\n", - __func__, irq); + if (inpin_idx >= aic->num_inpins) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", + __func__, inpin_idx); return; } - trace_aspeed_intc_update_irq(name, irq, level); - qemu_set_irq(s->output_pins[irq], level); + if (outpin_idx >= aic->num_outpins) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid output pin index: %d\n", + __func__, outpin_idx); + return; + } + + trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level); + qemu_set_irq(s->output_pins[outpin_idx], level); } /* @@ -72,23 +84,28 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); uint32_t select = 0; uint32_t enable; + int outpin_idx; + int inpin_idx; int i; + outpin_idx = irq; + inpin_idx = irq; + if (irq >= aic->num_inpins) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", __func__, irq); return; } - trace_aspeed_intc_set_irq(name, irq, level); - enable = s->enable[irq]; + trace_aspeed_intc_set_irq(name, inpin_idx, level); + enable = s->enable[inpin_idx]; if (!level) { return; } for (i = 0; i < aic->num_lines; i++) { - if (s->orgates[irq].levels[i]) { + if (s->orgates[inpin_idx].levels[i]) { if (enable & BIT(i)) { select |= BIT(i); } @@ -101,7 +118,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) trace_aspeed_intc_select(name, select); - if (s->mask[irq] || s->regs[status_reg]) { + if (s->mask[inpin_idx] || s->regs[status_reg]) { /* * a. mask is not 0 means in ISR mode * sources interrupt routine are executing. @@ -110,16 +127,17 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) * * save source interrupt to pending variable. */ - s->pending[irq] |= select; - trace_aspeed_intc_pending_irq(name, irq, s->pending[irq]); + s->pending[inpin_idx] |= select; + trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]); } else { /* * notify firmware which source interrupt are coming * by setting status register */ s->regs[status_reg] = select; - trace_aspeed_intc_trigger_irq(name, irq, s->regs[status_reg]); - aspeed_intc_update(s, irq, 1); + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, + s->regs[status_reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx, 1); } } @@ -131,14 +149,16 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, uint32_t reg = offset >> 2; uint32_t old_enable; uint32_t change; + int inpin_idx; uint32_t irq; irq = (offset & 0x0f00) >> 8; + inpin_idx = irq; - if (irq >= aic->num_inpins) { + if (inpin_idx >= aic->num_inpins) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", - __func__, irq); + __func__, inpin_idx); return; } @@ -149,17 +169,17 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, */ /* disable all source interrupt */ - if (!data && !s->enable[irq]) { + if (!data && !s->enable[inpin_idx]) { s->regs[reg] = data; return; } - old_enable = s->enable[irq]; - s->enable[irq] |= data; + old_enable = s->enable[inpin_idx]; + s->enable[inpin_idx] |= data; /* enable new source interrupt */ - if (old_enable != s->enable[irq]) { - trace_aspeed_intc_enable(name, s->enable[irq]); + if (old_enable != s->enable[inpin_idx]) { + trace_aspeed_intc_enable(name, s->enable[inpin_idx]); s->regs[reg] = data; return; } @@ -167,11 +187,11 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, /* mask and unmask source interrupt */ change = s->regs[reg] ^ data; if (change & data) { - s->mask[irq] &= ~change; - trace_aspeed_intc_unmask(name, change, s->mask[irq]); + s->mask[inpin_idx] &= ~change; + trace_aspeed_intc_unmask(name, change, s->mask[inpin_idx]); } else { - s->mask[irq] |= change; - trace_aspeed_intc_mask(name, change, s->mask[irq]); + s->mask[inpin_idx] |= change; + trace_aspeed_intc_mask(name, change, s->mask[inpin_idx]); } s->regs[reg] = data; @@ -183,6 +203,8 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); uint32_t reg = offset >> 2; + int outpin_idx; + int inpin_idx; uint32_t irq; if (!data) { @@ -191,11 +213,13 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, } irq = (offset & 0x0f00) >> 8; + outpin_idx = irq; + inpin_idx = irq; - if (irq >= aic->num_inpins) { + if (inpin_idx >= aic->num_inpins) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", - __func__, irq); + __func__, inpin_idx); return; } @@ -214,21 +238,22 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, /* All source ISR execution are done */ if (!s->regs[reg]) { - trace_aspeed_intc_all_isr_done(name, irq); - if (s->pending[irq]) { + trace_aspeed_intc_all_isr_done(name, inpin_idx); + if (s->pending[inpin_idx]) { /* * handle pending source interrupt * notify firmware which source interrupt are pending * by setting status register */ - s->regs[reg] = s->pending[irq]; - s->pending[irq] = 0; - trace_aspeed_intc_trigger_irq(name, irq, s->regs[reg]); - aspeed_intc_update(s, irq, 1); + s->regs[reg] = s->pending[inpin_idx]; + s->pending[inpin_idx] = 0; + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, + s->regs[reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx, 1); } else { /* clear irq */ - trace_aspeed_intc_clear_irq(name, irq, 0); - aspeed_intc_update(s, irq, 0); + trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx, 0); + aspeed_intc_update(s, inpin_idx, outpin_idx, 0); } } } diff --git a/hw/intc/trace-events b/hw/intc/trace-events index e9ca347..e97eea8 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -82,12 +82,12 @@ aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 # aspeed_intc.c aspeed_intc_read(const char *s, uint64_t offset, unsigned size, uint32_t value) "%s: From 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_intc_write(const char *s, uint64_t offset, unsigned size, uint32_t data) "%s: To 0x%" PRIx64 " of size %u: 0x%" PRIx32 -aspeed_intc_set_irq(const char *s, int irq, int level) "%s: Set IRQ %d: %d" -aspeed_intc_clear_irq(const char *s, int irq, int level) "%s: Clear IRQ %d: %d" -aspeed_intc_update_irq(const char *s, int irq, int level) "%s: Update IRQ: %d: %d" -aspeed_intc_pending_irq(const char *s, int irq, uint32_t value) "%s: Pending IRQ: %d: 0x%x" -aspeed_intc_trigger_irq(const char *s, int irq, uint32_t value) "%s: Trigger IRQ: %d: 0x%x" -aspeed_intc_all_isr_done(const char *s, int irq) "%s: All source ISR execution are done: %d" +aspeed_intc_set_irq(const char *s, int inpin_idx, int level) "%s: Set IRQ %d: %d" +aspeed_intc_clear_irq(const char *s, int inpin_idx, int outpin_idx, int level) "%s: Clear IRQ %d-%d: %d" +aspeed_intc_update_irq(const char *s, int inpin_idx, int outpin_idx, int level) "%s: Update IRQ: %d-%d: %d" +aspeed_intc_pending_irq(const char *s, int inpin_idx, uint32_t value) "%s: Pending IRQ: %d: 0x%x" +aspeed_intc_trigger_irq(const char *s, int inpin_idx, int outpin_idx, uint32_t value) "%s: Trigger IRQ: %d-%d: 0x%x" +aspeed_intc_all_isr_done(const char *s, int inpin_idx) "%s: All source ISR execution are done: %d" aspeed_intc_enable(const char *s, uint32_t value) "%s: Enable: 0x%x" aspeed_intc_select(const char *s, uint32_t value) "%s: Select: 0x%x" aspeed_intc_mask(const char *s, uint32_t change, uint32_t value) "%s: Mask: 0x%x: 0x%x" -- cgit v1.1 From ab24c6a2df8e6c8055b6f1dfe80697320b327c50 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:23 +0800 Subject: hw/intc/aspeed: Introduce AspeedINTCIRQ structure to save the irq index and register address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The INTC controller supports GICINT128 to GICINT136, mapping 1:1 to input and output IRQs 0 to 8. Previously, the formula "address & 0x0f00" was used to derive the IRQ index numbers. However, the INTC controller also supports GICINT192_201, mapping 1 input IRQ pin to 10 output IRQ pins. The pin numbers for input and output are different. It is difficult to use a formula to determine the index number of INTC model supported input and output IRQs. To simplify and improve readability, introduces the AspeedINTCIRQ structure to save the input/output IRQ index and its enable/status register address. Introduce the "aspeed_2700_intc_irqs" table to store IRQ information for INTC. Introduce the "aspeed_intc_get_irq" function to retrieve the input/output IRQ pin index from the provided status/enable register address. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-15-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 87 ++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 39 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 1cbee0e..be24516 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -40,7 +40,23 @@ REG32(GICINT135_STATUS, 0x704) REG32(GICINT136_EN, 0x800) REG32(GICINT136_STATUS, 0x804) -#define GICINT_STATUS_BASE R_GICINT128_STATUS +static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, + uint32_t reg) +{ + int i; + + for (i = 0; i < aic->irq_table_count; i++) { + if (aic->irq_table[i].enable_reg == reg || + aic->irq_table[i].status_reg == reg) { + return &aic->irq_table[i]; + } + } + + /* + * Invalid reg. + */ + g_assert_not_reached(); +} /* * Update the state of an interrupt controller pin by setting @@ -54,17 +70,7 @@ static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx, AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); - if (inpin_idx >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", - __func__, inpin_idx); - return; - } - - if (outpin_idx >= aic->num_outpins) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid output pin index: %d\n", - __func__, outpin_idx); - return; - } + assert((outpin_idx < aic->num_outpins) && (inpin_idx < aic->num_inpins)); trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level); qemu_set_irq(s->output_pins[outpin_idx], level); @@ -81,21 +87,20 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) AspeedINTCState *s = (AspeedINTCState *)opaque; AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); - uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); + const AspeedINTCIRQ *intc_irq; + uint32_t status_reg; uint32_t select = 0; uint32_t enable; int outpin_idx; int inpin_idx; int i; - outpin_idx = irq; - inpin_idx = irq; + assert(irq < aic->num_inpins); - if (irq >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n", - __func__, irq); - return; - } + intc_irq = &aic->irq_table[irq]; + status_reg = intc_irq->status_reg; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; trace_aspeed_intc_set_irq(name, inpin_idx, level); enable = s->enable[inpin_idx]; @@ -146,21 +151,16 @@ static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); + const AspeedINTCIRQ *intc_irq; uint32_t reg = offset >> 2; uint32_t old_enable; uint32_t change; int inpin_idx; - uint32_t irq; - irq = (offset & 0x0f00) >> 8; - inpin_idx = irq; + intc_irq = aspeed_intc_get_irq(aic, reg); + inpin_idx = intc_irq->inpin_idx; - if (inpin_idx >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Invalid input pin index: %d\n", - __func__, inpin_idx); - return; - } + assert(inpin_idx < aic->num_inpins); /* * The enable registers are used to enable source interrupts. @@ -202,26 +202,21 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, { AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); + const AspeedINTCIRQ *intc_irq; uint32_t reg = offset >> 2; int outpin_idx; int inpin_idx; - uint32_t irq; if (!data) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); return; } - irq = (offset & 0x0f00) >> 8; - outpin_idx = irq; - inpin_idx = irq; + intc_irq = aspeed_intc_get_irq(aic, reg); + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; - if (inpin_idx >= aic->num_inpins) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Invalid input pin index: %d\n", - __func__, inpin_idx); - return; - } + assert(inpin_idx < aic->num_inpins); /* clear status */ s->regs[reg] &= ~data; @@ -411,6 +406,18 @@ static const TypeInfo aspeed_intc_info = { .abstract = true, }; +static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 1, R_GICINT128_EN, R_GICINT128_STATUS}, + {1, 1, 1, R_GICINT129_EN, R_GICINT129_STATUS}, + {2, 2, 1, R_GICINT130_EN, R_GICINT130_STATUS}, + {3, 3, 1, R_GICINT131_EN, R_GICINT131_STATUS}, + {4, 4, 1, R_GICINT132_EN, R_GICINT132_STATUS}, + {5, 5, 1, R_GICINT133_EN, R_GICINT133_STATUS}, + {6, 6, 1, R_GICINT134_EN, R_GICINT134_STATUS}, + {7, 7, 1, R_GICINT135_EN, R_GICINT135_STATUS}, + {8, 8, 1, R_GICINT136_EN, R_GICINT136_STATUS}, +}; + static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -423,6 +430,8 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) aic->mem_size = 0x4000; aic->nr_regs = 0x808 >> 2; aic->reg_offset = 0x1000; + aic->irq_table = aspeed_2700_intc_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs); } static const TypeInfo aspeed_2700_intc_info = { -- cgit v1.1 From 5824e8bf6beb226aa5dee94d4e92b671e9b082f1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:24 +0800 Subject: hw/intc/aspeed: Introduce IRQ handler function to reduce code duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The behavior of the INTC set IRQ is almost identical between INTC and INTCIO. To reduce duplicated code, introduce the "aspeed_intc_set_irq_handler" function to handle both INTC and INTCIO IRQ behavior. No functional change. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-16-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 70 ++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 31 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index be24516..3aa97ad 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -76,11 +76,45 @@ static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx, qemu_set_irq(s->output_pins[outpin_idx], level); } +static void aspeed_intc_set_irq_handler(AspeedINTCState *s, + const AspeedINTCIRQ *intc_irq, + uint32_t select) +{ + const char *name = object_get_typename(OBJECT(s)); + uint32_t status_reg; + int outpin_idx; + int inpin_idx; + + status_reg = intc_irq->status_reg; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; + + if (s->mask[inpin_idx] || s->regs[status_reg]) { + /* + * a. mask is not 0 means in ISR mode + * sources interrupt routine are executing. + * b. status register value is not 0 means previous + * source interrupt does not be executed, yet. + * + * save source interrupt to pending variable. + */ + s->pending[inpin_idx] |= select; + trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]); + } else { + /* + * notify firmware which source interrupt are coming + * by setting status register + */ + s->regs[status_reg] = select; + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, + s->regs[status_reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx, 1); + } +} + /* - * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804. - * Utilize "address & 0x0f00" to get the irq and irq output pin index - * The value of irq should be 0 to num_inpins. - * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on. + * GICINT128 to GICINT136 map 1:1 to input and output IRQs 0 to 8. + * The value of input IRQ should be between 0 and the number of inputs. */ static void aspeed_intc_set_irq(void *opaque, int irq, int level) { @@ -88,20 +122,15 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); const char *name = object_get_typename(OBJECT(s)); const AspeedINTCIRQ *intc_irq; - uint32_t status_reg; uint32_t select = 0; uint32_t enable; - int outpin_idx; int inpin_idx; int i; assert(irq < aic->num_inpins); intc_irq = &aic->irq_table[irq]; - status_reg = intc_irq->status_reg; - outpin_idx = intc_irq->outpin_idx; inpin_idx = intc_irq->inpin_idx; - trace_aspeed_intc_set_irq(name, inpin_idx, level); enable = s->enable[inpin_idx]; @@ -122,28 +151,7 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) } trace_aspeed_intc_select(name, select); - - if (s->mask[inpin_idx] || s->regs[status_reg]) { - /* - * a. mask is not 0 means in ISR mode - * sources interrupt routine are executing. - * b. status register value is not 0 means previous - * source interrupt does not be executed, yet. - * - * save source interrupt to pending variable. - */ - s->pending[inpin_idx] |= select; - trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]); - } else { - /* - * notify firmware which source interrupt are coming - * by setting status register - */ - s->regs[status_reg] = select; - trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, - s->regs[status_reg]); - aspeed_intc_update(s, inpin_idx, outpin_idx, 1); - } + aspeed_intc_set_irq_handler(s, intc_irq, select); } static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, -- cgit v1.1 From 9178ff91f3105d25fef8e595014fbdfba7d9e278 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:25 +0800 Subject: hw/intc/aspeed: Add Support for Multi-Output IRQ Handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This update introduces support for handling multi-output IRQs in the AST2700 interrupt controller (INTC), specifically for GICINT192_201. GICINT192_201 maps 1:10 to input IRQ 0 and output IRQs 0 to 9. Each status bit corresponds to a specific IRQ. Implemented "aspeed_intc_set_irq_handler_multi_outpins" to handle IRQs with multiple output pins. Introduced "aspeed_intc_status_handler_multi_outpins" for managing status registers associated with multi-output IRQs. Added new IRQ definitions for GICINT192_201 in INTC. Adjusted the IRQ array to accommodate 10 input pins and 19 output pins, aligning with the new GICINT192_201 mappings. |------------------------------| | INTC | |inpin[0:0]--------->outpin[0] | |inpin[0:1]--------->outpin[1] | |inpin[0:2]--------->outpin[2] | |inpin[0:3]--------->outpin[3] | orgates[0]-------> |inpin[0:4]--------->outpin[4] | |inpin[0:5]--------->outpin[5] | |inpin[0:6]--------->outpin[6] | |inpin[0:7]--------->outpin[7] | |inpin[0:8]--------->outpin[8] | |inpin[0:9]--------->outpin[9] | | | orgates[1]------> |inpin[1]----------->outpin[10]| orgates[2]------> |inpin[2]----------->outpin[11]| orgates[3]------> |inpin[3]----------->outpin[12]| orgates[4]------> |inpin[4]----------->outpin[13]| orgates[5]------> |inpin[5]----------->outpin[14]| orgates[6]------> |inpin[6]----------->outpin[15]| orgates[7]------> |inpin[7]----------->outpin[16]| orgates[8]------> |inpin[8]----------->outpin[17]| orgates[9]------> |inpin[9]----------->outpin[18]| |------------------------------| Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-17-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 149 +++++++++++++++++++++++++++++++++++++++++++++----- hw/intc/trace-events | 1 + 2 files changed, 135 insertions(+), 15 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 3aa97ad..f2ca923 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -39,6 +39,8 @@ REG32(GICINT135_EN, 0x700) REG32(GICINT135_STATUS, 0x704) REG32(GICINT136_EN, 0x800) REG32(GICINT136_STATUS, 0x804) +REG32(GICINT192_201_EN, 0xB00) +REG32(GICINT192_201_STATUS, 0xB04) static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, uint32_t reg) @@ -112,9 +114,55 @@ static void aspeed_intc_set_irq_handler(AspeedINTCState *s, } } +static void aspeed_intc_set_irq_handler_multi_outpins(AspeedINTCState *s, + const AspeedINTCIRQ *intc_irq, uint32_t select) +{ + const char *name = object_get_typename(OBJECT(s)); + uint32_t status_reg; + int num_outpins; + int outpin_idx; + int inpin_idx; + int i; + + num_outpins = intc_irq->num_outpins; + status_reg = intc_irq->status_reg; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; + + for (i = 0; i < num_outpins; i++) { + if (select & BIT(i)) { + if (s->mask[inpin_idx] & BIT(i) || + s->regs[status_reg] & BIT(i)) { + /* + * a. mask bit is not 0 means in ISR mode sources interrupt + * routine are executing. + * b. status bit is not 0 means previous source interrupt + * does not be executed, yet. + * + * save source interrupt to pending bit. + */ + s->pending[inpin_idx] |= BIT(i); + trace_aspeed_intc_pending_irq(name, inpin_idx, + s->pending[inpin_idx]); + } else { + /* + * notify firmware which source interrupt are coming + * by setting status bit + */ + s->regs[status_reg] |= BIT(i); + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i, + s->regs[status_reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1); + } + } + } +} + /* - * GICINT128 to GICINT136 map 1:1 to input and output IRQs 0 to 8. - * The value of input IRQ should be between 0 and the number of inputs. + * GICINT192_201 maps 1:10 to input IRQ 0 and output IRQs 0 to 9. + * GICINT128 to GICINT136 map 1:1 to input IRQs 1 to 9 and output + * IRQs 10 to 18. The value of input IRQ should be between 0 and + * the number of input pins. */ static void aspeed_intc_set_irq(void *opaque, int irq, int level) { @@ -124,12 +172,14 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) const AspeedINTCIRQ *intc_irq; uint32_t select = 0; uint32_t enable; + int num_outpins; int inpin_idx; int i; assert(irq < aic->num_inpins); intc_irq = &aic->irq_table[irq]; + num_outpins = intc_irq->num_outpins; inpin_idx = intc_irq->inpin_idx; trace_aspeed_intc_set_irq(name, inpin_idx, level); enable = s->enable[inpin_idx]; @@ -151,7 +201,11 @@ static void aspeed_intc_set_irq(void *opaque, int irq, int level) } trace_aspeed_intc_select(name, select); - aspeed_intc_set_irq_handler(s, intc_irq, select); + if (num_outpins > 1) { + aspeed_intc_set_irq_handler_multi_outpins(s, intc_irq, select); + } else { + aspeed_intc_set_irq_handler(s, intc_irq, select); + } } static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, @@ -261,6 +315,66 @@ static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, } } +static void aspeed_intc_status_handler_multi_outpins(AspeedINTCState *s, + hwaddr offset, uint64_t data) +{ + const char *name = object_get_typename(OBJECT(s)); + AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); + const AspeedINTCIRQ *intc_irq; + uint32_t reg = offset >> 2; + int num_outpins; + int outpin_idx; + int inpin_idx; + int i; + + if (!data) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); + return; + } + + intc_irq = aspeed_intc_get_irq(aic, reg); + num_outpins = intc_irq->num_outpins; + outpin_idx = intc_irq->outpin_idx; + inpin_idx = intc_irq->inpin_idx; + assert(inpin_idx < aic->num_inpins); + + /* clear status */ + s->regs[reg] &= ~data; + + /* + * The 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; + } + + for (i = 0; i < num_outpins; i++) { + /* All source ISR executions are done from a specific bit */ + if (data & BIT(i)) { + trace_aspeed_intc_all_isr_done_bit(name, inpin_idx, i); + if (s->pending[inpin_idx] & BIT(i)) { + /* + * Handle pending source interrupt. + * Notify firmware which source interrupt is pending + * by setting the status bit. + */ + s->regs[reg] |= BIT(i); + s->pending[inpin_idx] &= ~BIT(i); + trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i, + s->regs[reg]); + aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1); + } else { + /* clear irq for the specific bit */ + trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx + i, 0); + aspeed_intc_update(s, inpin_idx, outpin_idx + i, 0); + } + } + } +} + static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) { AspeedINTCState *s = ASPEED_INTC(opaque); @@ -293,6 +407,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: + case R_GICINT192_201_EN: aspeed_intc_enable_handler(s, offset, data); break; case R_GICINT128_STATUS: @@ -306,6 +421,9 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, case R_GICINT136_STATUS: aspeed_intc_status_handler(s, offset, data); break; + case R_GICINT192_201_STATUS: + aspeed_intc_status_handler_multi_outpins(s, offset, data); + break; default: s->regs[reg] = data; break; @@ -415,15 +533,16 @@ static const TypeInfo aspeed_intc_info = { }; static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = { - {0, 0, 1, R_GICINT128_EN, R_GICINT128_STATUS}, - {1, 1, 1, R_GICINT129_EN, R_GICINT129_STATUS}, - {2, 2, 1, R_GICINT130_EN, R_GICINT130_STATUS}, - {3, 3, 1, R_GICINT131_EN, R_GICINT131_STATUS}, - {4, 4, 1, R_GICINT132_EN, R_GICINT132_STATUS}, - {5, 5, 1, R_GICINT133_EN, R_GICINT133_STATUS}, - {6, 6, 1, R_GICINT134_EN, R_GICINT134_STATUS}, - {7, 7, 1, R_GICINT135_EN, R_GICINT135_STATUS}, - {8, 8, 1, R_GICINT136_EN, R_GICINT136_STATUS}, + {0, 0, 10, R_GICINT192_201_EN, R_GICINT192_201_STATUS}, + {1, 10, 1, R_GICINT128_EN, R_GICINT128_STATUS}, + {2, 11, 1, R_GICINT129_EN, R_GICINT129_STATUS}, + {3, 12, 1, R_GICINT130_EN, R_GICINT130_STATUS}, + {4, 13, 1, R_GICINT131_EN, R_GICINT131_STATUS}, + {5, 14, 1, R_GICINT132_EN, R_GICINT132_STATUS}, + {6, 15, 1, R_GICINT133_EN, R_GICINT133_STATUS}, + {7, 16, 1, R_GICINT134_EN, R_GICINT134_STATUS}, + {8, 17, 1, R_GICINT135_EN, R_GICINT135_STATUS}, + {9, 18, 1, R_GICINT136_EN, R_GICINT136_STATUS}, }; static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) @@ -433,10 +552,10 @@ static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) dc->desc = "ASPEED 2700 INTC Controller"; aic->num_lines = 32; - aic->num_inpins = 9; - aic->num_outpins = 9; + aic->num_inpins = 10; + aic->num_outpins = 19; aic->mem_size = 0x4000; - aic->nr_regs = 0x808 >> 2; + aic->nr_regs = 0xB08 >> 2; aic->reg_offset = 0x1000; aic->irq_table = aspeed_2700_intc_irqs; aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs); diff --git a/hw/intc/trace-events b/hw/intc/trace-events index e97eea8..913197a 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -92,6 +92,7 @@ aspeed_intc_enable(const char *s, uint32_t value) "%s: Enable: 0x%x" aspeed_intc_select(const char *s, uint32_t value) "%s: Select: 0x%x" aspeed_intc_mask(const char *s, uint32_t change, uint32_t value) "%s: Mask: 0x%x: 0x%x" aspeed_intc_unmask(const char *s, uint32_t change, uint32_t value) "%s: UnMask: 0x%x: 0x%x" +aspeed_intc_all_isr_done_bit(const char *s, int inpin_idx, int bit) "%s: All source ISR execution are done from specific bit: %d-%d" # arm_gic.c gic_enable_irq(int irq) "irq %d enabled" -- cgit v1.1 From 38ba38d87df3421ee0b28f9dfaf393456861a8e0 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Fri, 7 Mar 2025 11:59:26 +0800 Subject: hw/intc/aspeed: Add Support for AST2700 INTCIO Controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new ast2700 INTCIO class to support AST2700 INTCIO. Added new register definitions for INTCIO, including enable and status registers for IRQs GICINT192 through GICINT197. Created a dedicated IRQ array for INTCIO, supporting six input pins and six output pins, aligning with the newly defined registers. Implemented "aspeed_intcio_read" and "aspeed_intcio_write" to handle INTCIO-specific register access. To GICINT196 | ETH1 |-----------| |--------------------------| -------->|0 | | INTCIO | ETH2 | 4| orgates[0]------>|inpin[0]-------->outpin[0]| -------->|1 5| orgates[1]------>|inpin[1]-------->outpin[1]| ETH3 | 6| orgates[2]------>|inpin[2]-------->outpin[2]| -------->|2 19| orgates[3]------>|inpin[3]-------->outpin[3]| UART0 | 20|-->orgates[4]------>|inpin[4]-------->outpin[4]| -------->|7 21| orgates[5]------>|inpin[5]-------->outpin[5]| UART1 | 22| |--------------------------| -------->|8 23| UART2 | 24| -------->|9 25| UART3 | 26| ---------|10 27| UART5 | 28| -------->|11 29| UART6 | | -------->|12 30| UART7 | 31| -------->|13 | UART8 | OR[0:31] | -------->|14 | UART9 | | -------->|15 | UART10 | | -------->|16 | UART11 | | -------->|17 | UART12 | | -------->|18 | |-----------| Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-18-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) (limited to 'hw/intc') diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index f2ca923..3fd4170 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -42,6 +42,26 @@ REG32(GICINT136_STATUS, 0x804) REG32(GICINT192_201_EN, 0xB00) REG32(GICINT192_201_STATUS, 0xB04) +/* + * INTCIO Registers + * + * values below are offset by - 0x100 from datasheet + * because its memory region is start at 0x100 + * + */ +REG32(GICINT192_EN, 0x00) +REG32(GICINT192_STATUS, 0x04) +REG32(GICINT193_EN, 0x10) +REG32(GICINT193_STATUS, 0x14) +REG32(GICINT194_EN, 0x20) +REG32(GICINT194_STATUS, 0x24) +REG32(GICINT195_EN, 0x30) +REG32(GICINT195_STATUS, 0x34) +REG32(GICINT196_EN, 0x40) +REG32(GICINT196_STATUS, 0x44) +REG32(GICINT197_EN, 0x50) +REG32(GICINT197_STATUS, 0x54) + static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, uint32_t reg) { @@ -432,6 +452,55 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, return; } +static uint64_t aspeed_intcio_read(void *opaque, hwaddr offset, + unsigned int size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + uint32_t value = 0; + + value = s->regs[reg]; + trace_aspeed_intc_read(name, offset, size, value); + + return value; +} + +static void aspeed_intcio_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + case R_GICINT192_EN: + case R_GICINT193_EN: + case R_GICINT194_EN: + case R_GICINT195_EN: + case R_GICINT196_EN: + case R_GICINT197_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_GICINT192_STATUS: + case R_GICINT193_STATUS: + case R_GICINT194_STATUS: + case R_GICINT195_STATUS: + case R_GICINT196_STATUS: + case R_GICINT197_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } + + return; +} + + static const MemoryRegionOps aspeed_intc_ops = { .read = aspeed_intc_read, .write = aspeed_intc_write, @@ -442,6 +511,16 @@ static const MemoryRegionOps aspeed_intc_ops = { } }; +static const MemoryRegionOps aspeed_intcio_ops = { + .read = aspeed_intcio_read, + .write = aspeed_intcio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + static void aspeed_intc_instance_init(Object *obj) { AspeedINTCState *s = ASPEED_INTC(obj); @@ -567,10 +646,43 @@ static const TypeInfo aspeed_2700_intc_info = { .class_init = aspeed_2700_intc_class_init, }; +static AspeedINTCIRQ aspeed_2700_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 1, R_GICINT192_EN, R_GICINT192_STATUS}, + {1, 1, 1, R_GICINT193_EN, R_GICINT193_STATUS}, + {2, 2, 1, R_GICINT194_EN, R_GICINT194_STATUS}, + {3, 3, 1, R_GICINT195_EN, R_GICINT195_STATUS}, + {4, 4, 1, R_GICINT196_EN, R_GICINT196_STATUS}, + {5, 5, 1, R_GICINT197_EN, R_GICINT197_STATUS}, +}; + +static void aspeed_2700_intcio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 INTC IO Controller"; + aic->num_lines = 32; + aic->num_inpins = 6; + aic->num_outpins = 6; + aic->mem_size = 0x400; + aic->nr_regs = 0x58 >> 2; + aic->reg_offset = 0x100; + aic->reg_ops = &aspeed_intcio_ops; + aic->irq_table = aspeed_2700_intcio_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intcio_irqs); +} + +static const TypeInfo aspeed_2700_intcio_info = { + .name = TYPE_ASPEED_2700_INTCIO, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700_intcio_class_init, +}; + static void aspeed_intc_register_types(void) { type_register_static(&aspeed_intc_info); type_register_static(&aspeed_2700_intc_info); + type_register_static(&aspeed_2700_intcio_info); } type_init(aspeed_intc_register_types); -- cgit v1.1