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