1d831c5fdSJamin Lin /* 2d831c5fdSJamin Lin * ASPEED INTC Controller 3d831c5fdSJamin Lin * 4d831c5fdSJamin Lin * Copyright (C) 2024 ASPEED Technology Inc. 5d831c5fdSJamin Lin * 6d831c5fdSJamin Lin * SPDX-License-Identifier: GPL-2.0-or-later 7d831c5fdSJamin Lin */ 8d831c5fdSJamin Lin 9d831c5fdSJamin Lin #include "qemu/osdep.h" 10d831c5fdSJamin Lin #include "hw/intc/aspeed_intc.h" 11d831c5fdSJamin Lin #include "hw/irq.h" 12d831c5fdSJamin Lin #include "qemu/log.h" 13d831c5fdSJamin Lin #include "trace.h" 14d831c5fdSJamin Lin #include "hw/registerfields.h" 15d831c5fdSJamin Lin #include "qapi/error.h" 16d831c5fdSJamin Lin 17d831c5fdSJamin Lin /* INTC Registers */ 18d831c5fdSJamin Lin REG32(GICINT128_EN, 0x1000) 19d831c5fdSJamin Lin REG32(GICINT128_STATUS, 0x1004) 20d831c5fdSJamin Lin REG32(GICINT129_EN, 0x1100) 21d831c5fdSJamin Lin REG32(GICINT129_STATUS, 0x1104) 22d831c5fdSJamin Lin REG32(GICINT130_EN, 0x1200) 23d831c5fdSJamin Lin REG32(GICINT130_STATUS, 0x1204) 24d831c5fdSJamin Lin REG32(GICINT131_EN, 0x1300) 25d831c5fdSJamin Lin REG32(GICINT131_STATUS, 0x1304) 26d831c5fdSJamin Lin REG32(GICINT132_EN, 0x1400) 27d831c5fdSJamin Lin REG32(GICINT132_STATUS, 0x1404) 28d831c5fdSJamin Lin REG32(GICINT133_EN, 0x1500) 29d831c5fdSJamin Lin REG32(GICINT133_STATUS, 0x1504) 30d831c5fdSJamin Lin REG32(GICINT134_EN, 0x1600) 31d831c5fdSJamin Lin REG32(GICINT134_STATUS, 0x1604) 32d831c5fdSJamin Lin REG32(GICINT135_EN, 0x1700) 33d831c5fdSJamin Lin REG32(GICINT135_STATUS, 0x1704) 34d831c5fdSJamin Lin REG32(GICINT136_EN, 0x1800) 35d831c5fdSJamin Lin REG32(GICINT136_STATUS, 0x1804) 36d831c5fdSJamin Lin 37d831c5fdSJamin Lin #define GICINT_STATUS_BASE R_GICINT128_STATUS 38d831c5fdSJamin Lin 39d831c5fdSJamin Lin static void aspeed_intc_update(AspeedINTCState *s, int irq, int level) 40d831c5fdSJamin Lin { 41d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 42d831c5fdSJamin Lin 43d831c5fdSJamin Lin if (irq >= aic->num_ints) { 44d831c5fdSJamin Lin qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", 45d831c5fdSJamin Lin __func__, irq); 46d831c5fdSJamin Lin return; 47d831c5fdSJamin Lin } 48d831c5fdSJamin Lin 49d831c5fdSJamin Lin trace_aspeed_intc_update_irq(irq, level); 50d831c5fdSJamin Lin qemu_set_irq(s->output_pins[irq], level); 51d831c5fdSJamin Lin } 52d831c5fdSJamin Lin 53d831c5fdSJamin Lin /* 54d831c5fdSJamin Lin * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804. 55d831c5fdSJamin Lin * Utilize "address & 0x0f00" to get the irq and irq output pin index 56d831c5fdSJamin Lin * The value of irq should be 0 to num_ints. 57d831c5fdSJamin Lin * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on. 58d831c5fdSJamin Lin */ 59d831c5fdSJamin Lin static void aspeed_intc_set_irq(void *opaque, int irq, int level) 60d831c5fdSJamin Lin { 61d831c5fdSJamin Lin AspeedINTCState *s = (AspeedINTCState *)opaque; 62d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 630cffaaceSJamin Lin uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2); 64d831c5fdSJamin Lin uint32_t select = 0; 65d831c5fdSJamin Lin uint32_t enable; 66d831c5fdSJamin Lin int i; 67d831c5fdSJamin Lin 68d831c5fdSJamin Lin if (irq >= aic->num_ints) { 69d831c5fdSJamin Lin qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", 70d831c5fdSJamin Lin __func__, irq); 71d831c5fdSJamin Lin return; 72d831c5fdSJamin Lin } 73d831c5fdSJamin Lin 74d831c5fdSJamin Lin trace_aspeed_intc_set_irq(irq, level); 75d831c5fdSJamin Lin enable = s->enable[irq]; 76d831c5fdSJamin Lin 77d831c5fdSJamin Lin if (!level) { 78d831c5fdSJamin Lin return; 79d831c5fdSJamin Lin } 80d831c5fdSJamin Lin 81d831c5fdSJamin Lin for (i = 0; i < aic->num_lines; i++) { 82d831c5fdSJamin Lin if (s->orgates[irq].levels[i]) { 83d831c5fdSJamin Lin if (enable & BIT(i)) { 84d831c5fdSJamin Lin select |= BIT(i); 85d831c5fdSJamin Lin } 86d831c5fdSJamin Lin } 87d831c5fdSJamin Lin } 88d831c5fdSJamin Lin 89d831c5fdSJamin Lin if (!select) { 90d831c5fdSJamin Lin return; 91d831c5fdSJamin Lin } 92d831c5fdSJamin Lin 93d831c5fdSJamin Lin trace_aspeed_intc_select(select); 94d831c5fdSJamin Lin 950cffaaceSJamin Lin if (s->mask[irq] || s->regs[status_reg]) { 96d831c5fdSJamin Lin /* 97d831c5fdSJamin Lin * a. mask is not 0 means in ISR mode 98d831c5fdSJamin Lin * sources interrupt routine are executing. 99d831c5fdSJamin Lin * b. status register value is not 0 means previous 100d831c5fdSJamin Lin * source interrupt does not be executed, yet. 101d831c5fdSJamin Lin * 102d831c5fdSJamin Lin * save source interrupt to pending variable. 103d831c5fdSJamin Lin */ 104d831c5fdSJamin Lin s->pending[irq] |= select; 105d831c5fdSJamin Lin trace_aspeed_intc_pending_irq(irq, s->pending[irq]); 106d831c5fdSJamin Lin } else { 107d831c5fdSJamin Lin /* 108d831c5fdSJamin Lin * notify firmware which source interrupt are coming 109d831c5fdSJamin Lin * by setting status register 110d831c5fdSJamin Lin */ 1110cffaaceSJamin Lin s->regs[status_reg] = select; 1120cffaaceSJamin Lin trace_aspeed_intc_trigger_irq(irq, s->regs[status_reg]); 113d831c5fdSJamin Lin aspeed_intc_update(s, irq, 1); 114d831c5fdSJamin Lin } 115d831c5fdSJamin Lin } 116d831c5fdSJamin Lin 117d831c5fdSJamin Lin static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) 118d831c5fdSJamin Lin { 119d831c5fdSJamin Lin AspeedINTCState *s = ASPEED_INTC(opaque); 1200cffaaceSJamin Lin uint32_t reg = offset >> 2; 121d831c5fdSJamin Lin uint32_t value = 0; 122d831c5fdSJamin Lin 1230cffaaceSJamin Lin if (reg >= ASPEED_INTC_NR_REGS) { 124d831c5fdSJamin Lin qemu_log_mask(LOG_GUEST_ERROR, 125d831c5fdSJamin Lin "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", 126d831c5fdSJamin Lin __func__, offset); 127d831c5fdSJamin Lin return 0; 128d831c5fdSJamin Lin } 129d831c5fdSJamin Lin 1300cffaaceSJamin Lin value = s->regs[reg]; 131d831c5fdSJamin Lin trace_aspeed_intc_read(offset, size, value); 132d831c5fdSJamin Lin 133d831c5fdSJamin Lin return value; 134d831c5fdSJamin Lin } 135d831c5fdSJamin Lin 136d831c5fdSJamin Lin static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, 137d831c5fdSJamin Lin unsigned size) 138d831c5fdSJamin Lin { 139d831c5fdSJamin Lin AspeedINTCState *s = ASPEED_INTC(opaque); 140d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 1410cffaaceSJamin Lin uint32_t reg = offset >> 2; 142d831c5fdSJamin Lin uint32_t old_enable; 143d831c5fdSJamin Lin uint32_t change; 144d831c5fdSJamin Lin uint32_t irq; 145d831c5fdSJamin Lin 1460cffaaceSJamin Lin if (reg >= ASPEED_INTC_NR_REGS) { 147d831c5fdSJamin Lin qemu_log_mask(LOG_GUEST_ERROR, 148d831c5fdSJamin Lin "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", 149d831c5fdSJamin Lin __func__, offset); 150d831c5fdSJamin Lin return; 151d831c5fdSJamin Lin } 152d831c5fdSJamin Lin 153d831c5fdSJamin Lin trace_aspeed_intc_write(offset, size, data); 154d831c5fdSJamin Lin 1550cffaaceSJamin Lin switch (reg) { 156d831c5fdSJamin Lin case R_GICINT128_EN: 157d831c5fdSJamin Lin case R_GICINT129_EN: 158d831c5fdSJamin Lin case R_GICINT130_EN: 159d831c5fdSJamin Lin case R_GICINT131_EN: 160d831c5fdSJamin Lin case R_GICINT132_EN: 161d831c5fdSJamin Lin case R_GICINT133_EN: 162d831c5fdSJamin Lin case R_GICINT134_EN: 163d831c5fdSJamin Lin case R_GICINT135_EN: 164d831c5fdSJamin Lin case R_GICINT136_EN: 165d831c5fdSJamin Lin irq = (offset & 0x0f00) >> 8; 166d831c5fdSJamin Lin 167d831c5fdSJamin Lin if (irq >= aic->num_ints) { 168d831c5fdSJamin Lin qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", 169d831c5fdSJamin Lin __func__, irq); 170d831c5fdSJamin Lin return; 171d831c5fdSJamin Lin } 172d831c5fdSJamin Lin 173d831c5fdSJamin Lin /* 174d831c5fdSJamin Lin * These registers are used for enable sources interrupt and 175d831c5fdSJamin Lin * mask and unmask source interrupt while executing source ISR. 176d831c5fdSJamin Lin */ 177d831c5fdSJamin Lin 178d831c5fdSJamin Lin /* disable all source interrupt */ 179d831c5fdSJamin Lin if (!data && !s->enable[irq]) { 1800cffaaceSJamin Lin s->regs[reg] = data; 181d831c5fdSJamin Lin return; 182d831c5fdSJamin Lin } 183d831c5fdSJamin Lin 184d831c5fdSJamin Lin old_enable = s->enable[irq]; 185d831c5fdSJamin Lin s->enable[irq] |= data; 186d831c5fdSJamin Lin 187d831c5fdSJamin Lin /* enable new source interrupt */ 188d831c5fdSJamin Lin if (old_enable != s->enable[irq]) { 189d831c5fdSJamin Lin trace_aspeed_intc_enable(s->enable[irq]); 1900cffaaceSJamin Lin s->regs[reg] = data; 191d831c5fdSJamin Lin return; 192d831c5fdSJamin Lin } 193d831c5fdSJamin Lin 194d831c5fdSJamin Lin /* mask and unmask source interrupt */ 1950cffaaceSJamin Lin change = s->regs[reg] ^ data; 196d831c5fdSJamin Lin if (change & data) { 197d831c5fdSJamin Lin s->mask[irq] &= ~change; 198d831c5fdSJamin Lin trace_aspeed_intc_unmask(change, s->mask[irq]); 199d831c5fdSJamin Lin } else { 200d831c5fdSJamin Lin s->mask[irq] |= change; 201d831c5fdSJamin Lin trace_aspeed_intc_mask(change, s->mask[irq]); 202d831c5fdSJamin Lin } 2030cffaaceSJamin Lin s->regs[reg] = data; 204d831c5fdSJamin Lin break; 205d831c5fdSJamin Lin case R_GICINT128_STATUS: 206d831c5fdSJamin Lin case R_GICINT129_STATUS: 207d831c5fdSJamin Lin case R_GICINT130_STATUS: 208d831c5fdSJamin Lin case R_GICINT131_STATUS: 209d831c5fdSJamin Lin case R_GICINT132_STATUS: 210d831c5fdSJamin Lin case R_GICINT133_STATUS: 211d831c5fdSJamin Lin case R_GICINT134_STATUS: 212d831c5fdSJamin Lin case R_GICINT135_STATUS: 213d831c5fdSJamin Lin case R_GICINT136_STATUS: 214d831c5fdSJamin Lin irq = (offset & 0x0f00) >> 8; 215d831c5fdSJamin Lin 216d831c5fdSJamin Lin if (irq >= aic->num_ints) { 217d831c5fdSJamin Lin qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n", 218d831c5fdSJamin Lin __func__, irq); 219d831c5fdSJamin Lin return; 220d831c5fdSJamin Lin } 221d831c5fdSJamin Lin 222d831c5fdSJamin Lin /* clear status */ 2230cffaaceSJamin Lin s->regs[reg] &= ~data; 224d831c5fdSJamin Lin 225d831c5fdSJamin Lin /* 226d831c5fdSJamin Lin * These status registers are used for notify sources ISR are executed. 227d831c5fdSJamin Lin * If one source ISR is executed, it will clear one bit. 228d831c5fdSJamin Lin * If it clear all bits, it means to initialize this register status 229d831c5fdSJamin Lin * rather than sources ISR are executed. 230d831c5fdSJamin Lin */ 231d831c5fdSJamin Lin if (data == 0xffffffff) { 232d831c5fdSJamin Lin return; 233d831c5fdSJamin Lin } 234d831c5fdSJamin Lin 235d831c5fdSJamin Lin /* All source ISR execution are done */ 2360cffaaceSJamin Lin if (!s->regs[reg]) { 237d831c5fdSJamin Lin trace_aspeed_intc_all_isr_done(irq); 238d831c5fdSJamin Lin if (s->pending[irq]) { 239d831c5fdSJamin Lin /* 240d831c5fdSJamin Lin * handle pending source interrupt 241d831c5fdSJamin Lin * notify firmware which source interrupt are pending 242d831c5fdSJamin Lin * by setting status register 243d831c5fdSJamin Lin */ 2440cffaaceSJamin Lin s->regs[reg] = s->pending[irq]; 245d831c5fdSJamin Lin s->pending[irq] = 0; 2460cffaaceSJamin Lin trace_aspeed_intc_trigger_irq(irq, s->regs[reg]); 247d831c5fdSJamin Lin aspeed_intc_update(s, irq, 1); 248d831c5fdSJamin Lin } else { 249d831c5fdSJamin Lin /* clear irq */ 250d831c5fdSJamin Lin trace_aspeed_intc_clear_irq(irq, 0); 251d831c5fdSJamin Lin aspeed_intc_update(s, irq, 0); 252d831c5fdSJamin Lin } 253d831c5fdSJamin Lin } 254d831c5fdSJamin Lin break; 255d831c5fdSJamin Lin default: 2560cffaaceSJamin Lin s->regs[reg] = data; 257d831c5fdSJamin Lin break; 258d831c5fdSJamin Lin } 259d831c5fdSJamin Lin 260d831c5fdSJamin Lin return; 261d831c5fdSJamin Lin } 262d831c5fdSJamin Lin 263d831c5fdSJamin Lin static const MemoryRegionOps aspeed_intc_ops = { 264d831c5fdSJamin Lin .read = aspeed_intc_read, 265d831c5fdSJamin Lin .write = aspeed_intc_write, 266d831c5fdSJamin Lin .endianness = DEVICE_LITTLE_ENDIAN, 267d831c5fdSJamin Lin .valid = { 268d831c5fdSJamin Lin .min_access_size = 4, 269d831c5fdSJamin Lin .max_access_size = 4, 270d831c5fdSJamin Lin } 271d831c5fdSJamin Lin }; 272d831c5fdSJamin Lin 273d831c5fdSJamin Lin static void aspeed_intc_instance_init(Object *obj) 274d831c5fdSJamin Lin { 275d831c5fdSJamin Lin AspeedINTCState *s = ASPEED_INTC(obj); 276d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 277d831c5fdSJamin Lin int i; 278d831c5fdSJamin Lin 279d831c5fdSJamin Lin assert(aic->num_ints <= ASPEED_INTC_NR_INTS); 280d831c5fdSJamin Lin for (i = 0; i < aic->num_ints; i++) { 281d831c5fdSJamin Lin object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i], 282d831c5fdSJamin Lin TYPE_OR_IRQ); 283d831c5fdSJamin Lin object_property_set_int(OBJECT(&s->orgates[i]), "num-lines", 284d831c5fdSJamin Lin aic->num_lines, &error_abort); 285d831c5fdSJamin Lin } 286d831c5fdSJamin Lin } 287d831c5fdSJamin Lin 288d831c5fdSJamin Lin static void aspeed_intc_reset(DeviceState *dev) 289d831c5fdSJamin Lin { 290d831c5fdSJamin Lin AspeedINTCState *s = ASPEED_INTC(dev); 291d831c5fdSJamin Lin 292*563afea0SJamin Lin memset(s->regs, 0, ASPEED_INTC_NR_REGS << 2); 293d831c5fdSJamin Lin memset(s->enable, 0, sizeof(s->enable)); 294d831c5fdSJamin Lin memset(s->mask, 0, sizeof(s->mask)); 295d831c5fdSJamin Lin memset(s->pending, 0, sizeof(s->pending)); 296d831c5fdSJamin Lin } 297d831c5fdSJamin Lin 298d831c5fdSJamin Lin static void aspeed_intc_realize(DeviceState *dev, Error **errp) 299d831c5fdSJamin Lin { 300d831c5fdSJamin Lin SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 301d831c5fdSJamin Lin AspeedINTCState *s = ASPEED_INTC(dev); 302d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 303d831c5fdSJamin Lin int i; 304d831c5fdSJamin Lin 305c5728c34SJamin Lin memory_region_init(&s->iomem_container, OBJECT(s), 306c5728c34SJamin Lin TYPE_ASPEED_INTC ".container", aic->mem_size); 307c5728c34SJamin Lin 308c5728c34SJamin Lin sysbus_init_mmio(sbd, &s->iomem_container); 309c5728c34SJamin Lin 310*563afea0SJamin Lin s->regs = g_new(uint32_t, ASPEED_INTC_NR_REGS); 311d831c5fdSJamin Lin memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s, 312d831c5fdSJamin Lin TYPE_ASPEED_INTC ".regs", ASPEED_INTC_NR_REGS << 2); 313d831c5fdSJamin Lin 314c5728c34SJamin Lin memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem); 315c5728c34SJamin Lin 316d831c5fdSJamin Lin qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints); 317d831c5fdSJamin Lin 318d831c5fdSJamin Lin for (i = 0; i < aic->num_ints; i++) { 319d831c5fdSJamin Lin if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) { 320d831c5fdSJamin Lin return; 321d831c5fdSJamin Lin } 322d831c5fdSJamin Lin sysbus_init_irq(sbd, &s->output_pins[i]); 323d831c5fdSJamin Lin } 324d831c5fdSJamin Lin } 325d831c5fdSJamin Lin 326*563afea0SJamin Lin static void aspeed_intc_unrealize(DeviceState *dev) 327*563afea0SJamin Lin { 328*563afea0SJamin Lin AspeedINTCState *s = ASPEED_INTC(dev); 329*563afea0SJamin Lin 330*563afea0SJamin Lin g_free(s->regs); 331*563afea0SJamin Lin s->regs = NULL; 332*563afea0SJamin Lin } 333*563afea0SJamin Lin 334d831c5fdSJamin Lin static void aspeed_intc_class_init(ObjectClass *klass, void *data) 335d831c5fdSJamin Lin { 336d831c5fdSJamin Lin DeviceClass *dc = DEVICE_CLASS(klass); 337d831c5fdSJamin Lin 338d831c5fdSJamin Lin dc->desc = "ASPEED INTC Controller"; 339d831c5fdSJamin Lin dc->realize = aspeed_intc_realize; 340*563afea0SJamin Lin dc->unrealize = aspeed_intc_unrealize; 341e3d08143SPeter Maydell device_class_set_legacy_reset(dc, aspeed_intc_reset); 342d831c5fdSJamin Lin dc->vmsd = NULL; 343d831c5fdSJamin Lin } 344d831c5fdSJamin Lin 345d831c5fdSJamin Lin static const TypeInfo aspeed_intc_info = { 346d831c5fdSJamin Lin .name = TYPE_ASPEED_INTC, 347d831c5fdSJamin Lin .parent = TYPE_SYS_BUS_DEVICE, 348d831c5fdSJamin Lin .instance_init = aspeed_intc_instance_init, 349d831c5fdSJamin Lin .instance_size = sizeof(AspeedINTCState), 350d831c5fdSJamin Lin .class_init = aspeed_intc_class_init, 351d831c5fdSJamin Lin .class_size = sizeof(AspeedINTCClass), 352d831c5fdSJamin Lin .abstract = true, 353d831c5fdSJamin Lin }; 354d831c5fdSJamin Lin 355d831c5fdSJamin Lin static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) 356d831c5fdSJamin Lin { 357d831c5fdSJamin Lin DeviceClass *dc = DEVICE_CLASS(klass); 358d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); 359d831c5fdSJamin Lin 360d831c5fdSJamin Lin dc->desc = "ASPEED 2700 INTC Controller"; 361d831c5fdSJamin Lin aic->num_lines = 32; 362d831c5fdSJamin Lin aic->num_ints = 9; 363c5728c34SJamin Lin aic->mem_size = 0x4000; 364d831c5fdSJamin Lin } 365d831c5fdSJamin Lin 366d831c5fdSJamin Lin static const TypeInfo aspeed_2700_intc_info = { 367d831c5fdSJamin Lin .name = TYPE_ASPEED_2700_INTC, 368d831c5fdSJamin Lin .parent = TYPE_ASPEED_INTC, 369d831c5fdSJamin Lin .class_init = aspeed_2700_intc_class_init, 370d831c5fdSJamin Lin }; 371d831c5fdSJamin Lin 372d831c5fdSJamin Lin static void aspeed_intc_register_types(void) 373d831c5fdSJamin Lin { 374d831c5fdSJamin Lin type_register_static(&aspeed_intc_info); 375d831c5fdSJamin Lin type_register_static(&aspeed_2700_intc_info); 376d831c5fdSJamin Lin } 377d831c5fdSJamin Lin 378d831c5fdSJamin Lin type_init(aspeed_intc_register_types); 379