qemu/hw/intc/aspeed_intc.c
Jamin Lin 49da40cf5f hw/intc/aspeed: Add object type name to trace events for better debugging
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 <jamin_lin@aspeedtech.com>
Reviewed-by: Cédric Le Goater <clg@redhat.com>
Link: https://lore.kernel.org/qemu-devel/20250307035945.3698802-8-jamin_lin@aspeedtech.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
2025-03-09 14:36:53 +01:00

405 lines
11 KiB
C

/*
* ASPEED INTC Controller
*
* Copyright (C) 2024 ASPEED Technology Inc.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/intc/aspeed_intc.h"
#include "hw/irq.h"
#include "qemu/log.h"
#include "trace.h"
#include "hw/registerfields.h"
#include "qapi/error.h"
/*
* 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
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",
__func__, irq);
return;
}
trace_aspeed_intc_update_irq(name, irq, level);
qemu_set_irq(s->output_pins[irq], 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 irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on.
*/
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;
int i;
if (irq >= aic->num_ints) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
__func__, irq);
return;
}
trace_aspeed_intc_set_irq(name, irq, level);
enable = s->enable[irq];
if (!level) {
return;
}
for (i = 0; i < aic->num_lines; i++) {
if (s->orgates[irq].levels[i]) {
if (enable & BIT(i)) {
select |= BIT(i);
}
}
}
if (!select) {
return;
}
trace_aspeed_intc_select(name, select);
if (s->mask[irq] || 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[irq] |= select;
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(name, irq, s->regs[status_reg]);
aspeed_intc_update(s, irq, 1);
}
}
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;
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(name, 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(name, change, s->mask[irq]);
} else {
s->mask[irq] |= change;
trace_aspeed_intc_mask(name, 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);
const char *name = object_get_typename(OBJECT(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(name, 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(name, irq, s->regs[reg]);
aspeed_intc_update(s, irq, 1);
} else {
/* clear irq */
trace_aspeed_intc_clear_irq(name, 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);
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_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(name, offset, size, data);
switch (reg) {
case R_GICINT128_EN:
case R_GICINT129_EN:
case R_GICINT130_EN:
case R_GICINT131_EN:
case R_GICINT132_EN:
case R_GICINT133_EN:
case R_GICINT134_EN:
case R_GICINT135_EN:
case R_GICINT136_EN:
aspeed_intc_enable_handler(s, offset, data);
break;
case R_GICINT128_STATUS:
case R_GICINT129_STATUS:
case R_GICINT130_STATUS:
case R_GICINT131_STATUS:
case R_GICINT132_STATUS:
case R_GICINT133_STATUS:
case R_GICINT134_STATUS:
case R_GICINT135_STATUS:
case R_GICINT136_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,
.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);
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++) {
object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i],
TYPE_OR_IRQ);
object_property_set_int(OBJECT(&s->orgates[i]), "num-lines",
aic->num_lines, &error_abort);
}
}
static void aspeed_intc_reset(DeviceState *dev)
{
AspeedINTCState *s = ASPEED_INTC(dev);
AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
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));
}
static void aspeed_intc_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
AspeedINTCState *s = ASPEED_INTC(dev);
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);
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", aic->nr_regs << 2);
memory_region_add_subregion(&s->iomem_container, aic->reg_offset,
&s->iomem);
qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints);
for (i = 0; i < aic->num_ints; i++) {
if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) {
return;
}
sysbus_init_irq(sbd, &s->output_pins[i]);
}
}
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;
}
static const TypeInfo aspeed_intc_info = {
.name = TYPE_ASPEED_INTC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_init = aspeed_intc_instance_init,
.instance_size = sizeof(AspeedINTCState),
.class_init = aspeed_intc_class_init,
.class_size = sizeof(AspeedINTCClass),
.abstract = true,
};
static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
dc->desc = "ASPEED 2700 INTC Controller";
aic->num_lines = 32;
aic->num_ints = 9;
aic->mem_size = 0x4000;
aic->nr_regs = 0x808 >> 2;
aic->reg_offset = 0x1000;
}
static const TypeInfo aspeed_2700_intc_info = {
.name = TYPE_ASPEED_2700_INTC,
.parent = TYPE_ASPEED_INTC,
.class_init = aspeed_2700_intc_class_init,
};
static void aspeed_intc_register_types(void)
{
type_register_static(&aspeed_intc_info);
type_register_static(&aspeed_2700_intc_info);
}
type_init(aspeed_intc_register_types);