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 177ffee511SJamin Lin /* 187ffee511SJamin Lin * INTC Registers 197ffee511SJamin Lin * 207ffee511SJamin Lin * values below are offset by - 0x1000 from datasheet 217ffee511SJamin Lin * because its memory region is start at 0x1000 227ffee511SJamin Lin * 237ffee511SJamin Lin */ 247ffee511SJamin Lin REG32(GICINT128_EN, 0x000) 257ffee511SJamin Lin REG32(GICINT128_STATUS, 0x004) 267ffee511SJamin Lin REG32(GICINT129_EN, 0x100) 277ffee511SJamin Lin REG32(GICINT129_STATUS, 0x104) 287ffee511SJamin Lin REG32(GICINT130_EN, 0x200) 297ffee511SJamin Lin REG32(GICINT130_STATUS, 0x204) 307ffee511SJamin Lin REG32(GICINT131_EN, 0x300) 317ffee511SJamin Lin REG32(GICINT131_STATUS, 0x304) 327ffee511SJamin Lin REG32(GICINT132_EN, 0x400) 337ffee511SJamin Lin REG32(GICINT132_STATUS, 0x404) 347ffee511SJamin Lin REG32(GICINT133_EN, 0x500) 357ffee511SJamin Lin REG32(GICINT133_STATUS, 0x504) 367ffee511SJamin Lin REG32(GICINT134_EN, 0x600) 377ffee511SJamin Lin REG32(GICINT134_STATUS, 0x604) 387ffee511SJamin Lin REG32(GICINT135_EN, 0x700) 397ffee511SJamin Lin REG32(GICINT135_STATUS, 0x704) 407ffee511SJamin Lin REG32(GICINT136_EN, 0x800) 417ffee511SJamin Lin REG32(GICINT136_STATUS, 0x804) 429178ff91SJamin Lin REG32(GICINT192_201_EN, 0xB00) 439178ff91SJamin Lin REG32(GICINT192_201_STATUS, 0xB04) 44d831c5fdSJamin Lin 4538ba38d8SJamin Lin /* 4638ba38d8SJamin Lin * INTCIO Registers 4738ba38d8SJamin Lin * 4838ba38d8SJamin Lin * values below are offset by - 0x100 from datasheet 4938ba38d8SJamin Lin * because its memory region is start at 0x100 5038ba38d8SJamin Lin * 5138ba38d8SJamin Lin */ 5238ba38d8SJamin Lin REG32(GICINT192_EN, 0x00) 5338ba38d8SJamin Lin REG32(GICINT192_STATUS, 0x04) 5438ba38d8SJamin Lin REG32(GICINT193_EN, 0x10) 5538ba38d8SJamin Lin REG32(GICINT193_STATUS, 0x14) 5638ba38d8SJamin Lin REG32(GICINT194_EN, 0x20) 5738ba38d8SJamin Lin REG32(GICINT194_STATUS, 0x24) 5838ba38d8SJamin Lin REG32(GICINT195_EN, 0x30) 5938ba38d8SJamin Lin REG32(GICINT195_STATUS, 0x34) 6038ba38d8SJamin Lin REG32(GICINT196_EN, 0x40) 6138ba38d8SJamin Lin REG32(GICINT196_STATUS, 0x44) 6238ba38d8SJamin Lin REG32(GICINT197_EN, 0x50) 6338ba38d8SJamin Lin REG32(GICINT197_STATUS, 0x54) 6438ba38d8SJamin Lin 65ab24c6a2SJamin Lin static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, 66ab24c6a2SJamin Lin uint32_t reg) 67ab24c6a2SJamin Lin { 68ab24c6a2SJamin Lin int i; 69ab24c6a2SJamin Lin 70ab24c6a2SJamin Lin for (i = 0; i < aic->irq_table_count; i++) { 71ab24c6a2SJamin Lin if (aic->irq_table[i].enable_reg == reg || 72ab24c6a2SJamin Lin aic->irq_table[i].status_reg == reg) { 73ab24c6a2SJamin Lin return &aic->irq_table[i]; 74ab24c6a2SJamin Lin } 75ab24c6a2SJamin Lin } 76ab24c6a2SJamin Lin 77ab24c6a2SJamin Lin /* 78ab24c6a2SJamin Lin * Invalid reg. 79ab24c6a2SJamin Lin */ 80ab24c6a2SJamin Lin g_assert_not_reached(); 81ab24c6a2SJamin Lin } 82d831c5fdSJamin Lin 83c6c5e63dSJamin Lin /* 84c6c5e63dSJamin Lin * Update the state of an interrupt controller pin by setting 85c6c5e63dSJamin Lin * the specified output pin to the given level. 86c6c5e63dSJamin Lin * The input pin index should be between 0 and the number of input pins. 87c6c5e63dSJamin Lin * The output pin index should be between 0 and the number of output pins. 88c6c5e63dSJamin Lin */ 89c6c5e63dSJamin Lin static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx, 90c6c5e63dSJamin Lin int outpin_idx, int level) 91d831c5fdSJamin Lin { 92d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 9349da40cfSJamin Lin const char *name = object_get_typename(OBJECT(s)); 94d831c5fdSJamin Lin 95ab24c6a2SJamin Lin assert((outpin_idx < aic->num_outpins) && (inpin_idx < aic->num_inpins)); 96c6c5e63dSJamin Lin 97c6c5e63dSJamin Lin trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level); 98c6c5e63dSJamin Lin qemu_set_irq(s->output_pins[outpin_idx], level); 99d831c5fdSJamin Lin } 100d831c5fdSJamin Lin 1015824e8bfSJamin Lin static void aspeed_intc_set_irq_handler(AspeedINTCState *s, 1025824e8bfSJamin Lin const AspeedINTCIRQ *intc_irq, 1035824e8bfSJamin Lin uint32_t select) 104d831c5fdSJamin Lin { 10549da40cfSJamin Lin const char *name = object_get_typename(OBJECT(s)); 106ab24c6a2SJamin Lin uint32_t status_reg; 107c6c5e63dSJamin Lin int outpin_idx; 108c6c5e63dSJamin Lin int inpin_idx; 109d831c5fdSJamin Lin 110ab24c6a2SJamin Lin status_reg = intc_irq->status_reg; 111ab24c6a2SJamin Lin outpin_idx = intc_irq->outpin_idx; 112ab24c6a2SJamin Lin inpin_idx = intc_irq->inpin_idx; 113d831c5fdSJamin Lin 114*7b8cbe51SSteven Lee if ((s->mask[inpin_idx] & select) || (s->regs[status_reg] & select)) { 115d831c5fdSJamin Lin /* 116d831c5fdSJamin Lin * a. mask is not 0 means in ISR mode 117d831c5fdSJamin Lin * sources interrupt routine are executing. 118d831c5fdSJamin Lin * b. status register value is not 0 means previous 119d831c5fdSJamin Lin * source interrupt does not be executed, yet. 120d831c5fdSJamin Lin * 121d831c5fdSJamin Lin * save source interrupt to pending variable. 122d831c5fdSJamin Lin */ 123c6c5e63dSJamin Lin s->pending[inpin_idx] |= select; 124c6c5e63dSJamin Lin trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]); 125d831c5fdSJamin Lin } else { 126d831c5fdSJamin Lin /* 127d831c5fdSJamin Lin * notify firmware which source interrupt are coming 128d831c5fdSJamin Lin * by setting status register 129d831c5fdSJamin Lin */ 1300cffaaceSJamin Lin s->regs[status_reg] = select; 131c6c5e63dSJamin Lin trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, 132c6c5e63dSJamin Lin s->regs[status_reg]); 133c6c5e63dSJamin Lin aspeed_intc_update(s, inpin_idx, outpin_idx, 1); 134d831c5fdSJamin Lin } 135d831c5fdSJamin Lin } 136d831c5fdSJamin Lin 1379178ff91SJamin Lin static void aspeed_intc_set_irq_handler_multi_outpins(AspeedINTCState *s, 1389178ff91SJamin Lin const AspeedINTCIRQ *intc_irq, uint32_t select) 1399178ff91SJamin Lin { 1409178ff91SJamin Lin const char *name = object_get_typename(OBJECT(s)); 1419178ff91SJamin Lin uint32_t status_reg; 1429178ff91SJamin Lin int num_outpins; 1439178ff91SJamin Lin int outpin_idx; 1449178ff91SJamin Lin int inpin_idx; 1459178ff91SJamin Lin int i; 1469178ff91SJamin Lin 1479178ff91SJamin Lin num_outpins = intc_irq->num_outpins; 1489178ff91SJamin Lin status_reg = intc_irq->status_reg; 1499178ff91SJamin Lin outpin_idx = intc_irq->outpin_idx; 1509178ff91SJamin Lin inpin_idx = intc_irq->inpin_idx; 1519178ff91SJamin Lin 1529178ff91SJamin Lin for (i = 0; i < num_outpins; i++) { 1539178ff91SJamin Lin if (select & BIT(i)) { 1549178ff91SJamin Lin if (s->mask[inpin_idx] & BIT(i) || 1559178ff91SJamin Lin s->regs[status_reg] & BIT(i)) { 1565824e8bfSJamin Lin /* 1579178ff91SJamin Lin * a. mask bit is not 0 means in ISR mode sources interrupt 1589178ff91SJamin Lin * routine are executing. 1599178ff91SJamin Lin * b. status bit is not 0 means previous source interrupt 1609178ff91SJamin Lin * does not be executed, yet. 1619178ff91SJamin Lin * 1629178ff91SJamin Lin * save source interrupt to pending bit. 1639178ff91SJamin Lin */ 1649178ff91SJamin Lin s->pending[inpin_idx] |= BIT(i); 1659178ff91SJamin Lin trace_aspeed_intc_pending_irq(name, inpin_idx, 1669178ff91SJamin Lin s->pending[inpin_idx]); 1679178ff91SJamin Lin } else { 1689178ff91SJamin Lin /* 1699178ff91SJamin Lin * notify firmware which source interrupt are coming 1709178ff91SJamin Lin * by setting status bit 1719178ff91SJamin Lin */ 1729178ff91SJamin Lin s->regs[status_reg] |= BIT(i); 1739178ff91SJamin Lin trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i, 1749178ff91SJamin Lin s->regs[status_reg]); 1759178ff91SJamin Lin aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1); 1769178ff91SJamin Lin } 1779178ff91SJamin Lin } 1789178ff91SJamin Lin } 1799178ff91SJamin Lin } 1809178ff91SJamin Lin 1819178ff91SJamin Lin /* 1829178ff91SJamin Lin * GICINT192_201 maps 1:10 to input IRQ 0 and output IRQs 0 to 9. 1839178ff91SJamin Lin * GICINT128 to GICINT136 map 1:1 to input IRQs 1 to 9 and output 1849178ff91SJamin Lin * IRQs 10 to 18. The value of input IRQ should be between 0 and 1859178ff91SJamin Lin * the number of input pins. 1865824e8bfSJamin Lin */ 1875824e8bfSJamin Lin static void aspeed_intc_set_irq(void *opaque, int irq, int level) 1885824e8bfSJamin Lin { 1895824e8bfSJamin Lin AspeedINTCState *s = (AspeedINTCState *)opaque; 1905824e8bfSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 1915824e8bfSJamin Lin const char *name = object_get_typename(OBJECT(s)); 1925824e8bfSJamin Lin const AspeedINTCIRQ *intc_irq; 1935824e8bfSJamin Lin uint32_t select = 0; 1945824e8bfSJamin Lin uint32_t enable; 1959178ff91SJamin Lin int num_outpins; 1965824e8bfSJamin Lin int inpin_idx; 1975824e8bfSJamin Lin int i; 1985824e8bfSJamin Lin 1995824e8bfSJamin Lin assert(irq < aic->num_inpins); 2005824e8bfSJamin Lin 2015824e8bfSJamin Lin intc_irq = &aic->irq_table[irq]; 2029178ff91SJamin Lin num_outpins = intc_irq->num_outpins; 2035824e8bfSJamin Lin inpin_idx = intc_irq->inpin_idx; 2045824e8bfSJamin Lin trace_aspeed_intc_set_irq(name, inpin_idx, level); 2055824e8bfSJamin Lin enable = s->enable[inpin_idx]; 2065824e8bfSJamin Lin 2075824e8bfSJamin Lin if (!level) { 2085824e8bfSJamin Lin return; 2095824e8bfSJamin Lin } 2105824e8bfSJamin Lin 2115824e8bfSJamin Lin for (i = 0; i < aic->num_lines; i++) { 2125824e8bfSJamin Lin if (s->orgates[inpin_idx].levels[i]) { 2135824e8bfSJamin Lin if (enable & BIT(i)) { 2145824e8bfSJamin Lin select |= BIT(i); 2155824e8bfSJamin Lin } 2165824e8bfSJamin Lin } 2175824e8bfSJamin Lin } 2185824e8bfSJamin Lin 2195824e8bfSJamin Lin if (!select) { 2205824e8bfSJamin Lin return; 2215824e8bfSJamin Lin } 2225824e8bfSJamin Lin 2235824e8bfSJamin Lin trace_aspeed_intc_select(name, select); 2249178ff91SJamin Lin if (num_outpins > 1) { 2259178ff91SJamin Lin aspeed_intc_set_irq_handler_multi_outpins(s, intc_irq, select); 2269178ff91SJamin Lin } else { 2275824e8bfSJamin Lin aspeed_intc_set_irq_handler(s, intc_irq, select); 2285824e8bfSJamin Lin } 2299178ff91SJamin Lin } 2305824e8bfSJamin Lin 2313d6e15eaSJamin Lin static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset, 2323d6e15eaSJamin Lin uint64_t data) 233d831c5fdSJamin Lin { 234d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 23549da40cfSJamin Lin const char *name = object_get_typename(OBJECT(s)); 236ab24c6a2SJamin Lin const AspeedINTCIRQ *intc_irq; 2370cffaaceSJamin Lin uint32_t reg = offset >> 2; 238d831c5fdSJamin Lin uint32_t old_enable; 239d831c5fdSJamin Lin uint32_t change; 240c6c5e63dSJamin Lin int inpin_idx; 241d831c5fdSJamin Lin 242ab24c6a2SJamin Lin intc_irq = aspeed_intc_get_irq(aic, reg); 243ab24c6a2SJamin Lin inpin_idx = intc_irq->inpin_idx; 244d831c5fdSJamin Lin 245ab24c6a2SJamin Lin assert(inpin_idx < aic->num_inpins); 246d831c5fdSJamin Lin 247d831c5fdSJamin Lin /* 2483d6e15eaSJamin Lin * The enable registers are used to enable source interrupts. 2493d6e15eaSJamin Lin * They also handle masking and unmasking of source interrupts 2503d6e15eaSJamin Lin * during the execution of the source ISR. 251d831c5fdSJamin Lin */ 252d831c5fdSJamin Lin 253d831c5fdSJamin Lin /* disable all source interrupt */ 254c6c5e63dSJamin Lin if (!data && !s->enable[inpin_idx]) { 2550cffaaceSJamin Lin s->regs[reg] = data; 256d831c5fdSJamin Lin return; 257d831c5fdSJamin Lin } 258d831c5fdSJamin Lin 259c6c5e63dSJamin Lin old_enable = s->enable[inpin_idx]; 260c6c5e63dSJamin Lin s->enable[inpin_idx] |= data; 261d831c5fdSJamin Lin 262d831c5fdSJamin Lin /* enable new source interrupt */ 263c6c5e63dSJamin Lin if (old_enable != s->enable[inpin_idx]) { 264c6c5e63dSJamin Lin trace_aspeed_intc_enable(name, s->enable[inpin_idx]); 2650cffaaceSJamin Lin s->regs[reg] = data; 266d831c5fdSJamin Lin return; 267d831c5fdSJamin Lin } 268d831c5fdSJamin Lin 269d831c5fdSJamin Lin /* mask and unmask source interrupt */ 2700cffaaceSJamin Lin change = s->regs[reg] ^ data; 271d831c5fdSJamin Lin if (change & data) { 272c6c5e63dSJamin Lin s->mask[inpin_idx] &= ~change; 273c6c5e63dSJamin Lin trace_aspeed_intc_unmask(name, change, s->mask[inpin_idx]); 274d831c5fdSJamin Lin } else { 275c6c5e63dSJamin Lin s->mask[inpin_idx] |= change; 276c6c5e63dSJamin Lin trace_aspeed_intc_mask(name, change, s->mask[inpin_idx]); 277d831c5fdSJamin Lin } 2783d6e15eaSJamin Lin 2790cffaaceSJamin Lin s->regs[reg] = data; 2803d6e15eaSJamin Lin } 2813d6e15eaSJamin Lin 2823d6e15eaSJamin Lin static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset, 2833d6e15eaSJamin Lin uint64_t data) 2843d6e15eaSJamin Lin { 2853d6e15eaSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 28649da40cfSJamin Lin const char *name = object_get_typename(OBJECT(s)); 287ab24c6a2SJamin Lin const AspeedINTCIRQ *intc_irq; 2883d6e15eaSJamin Lin uint32_t reg = offset >> 2; 289c6c5e63dSJamin Lin int outpin_idx; 290c6c5e63dSJamin Lin int inpin_idx; 2913d6e15eaSJamin Lin 2923d6e15eaSJamin Lin if (!data) { 2933d6e15eaSJamin Lin qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); 2943d6e15eaSJamin Lin return; 2953d6e15eaSJamin Lin } 2963d6e15eaSJamin Lin 297ab24c6a2SJamin Lin intc_irq = aspeed_intc_get_irq(aic, reg); 298ab24c6a2SJamin Lin outpin_idx = intc_irq->outpin_idx; 299ab24c6a2SJamin Lin inpin_idx = intc_irq->inpin_idx; 300d831c5fdSJamin Lin 301ab24c6a2SJamin Lin assert(inpin_idx < aic->num_inpins); 302d831c5fdSJamin Lin 303d831c5fdSJamin Lin /* clear status */ 3040cffaaceSJamin Lin s->regs[reg] &= ~data; 305d831c5fdSJamin Lin 306d831c5fdSJamin Lin /* 307d831c5fdSJamin Lin * These status registers are used for notify sources ISR are executed. 308d831c5fdSJamin Lin * If one source ISR is executed, it will clear one bit. 309d831c5fdSJamin Lin * If it clear all bits, it means to initialize this register status 310d831c5fdSJamin Lin * rather than sources ISR are executed. 311d831c5fdSJamin Lin */ 312d831c5fdSJamin Lin if (data == 0xffffffff) { 313d831c5fdSJamin Lin return; 314d831c5fdSJamin Lin } 315d831c5fdSJamin Lin 316d831c5fdSJamin Lin /* All source ISR execution are done */ 3170cffaaceSJamin Lin if (!s->regs[reg]) { 318c6c5e63dSJamin Lin trace_aspeed_intc_all_isr_done(name, inpin_idx); 319c6c5e63dSJamin Lin if (s->pending[inpin_idx]) { 320d831c5fdSJamin Lin /* 321d831c5fdSJamin Lin * handle pending source interrupt 322d831c5fdSJamin Lin * notify firmware which source interrupt are pending 323d831c5fdSJamin Lin * by setting status register 324d831c5fdSJamin Lin */ 325c6c5e63dSJamin Lin s->regs[reg] = s->pending[inpin_idx]; 326c6c5e63dSJamin Lin s->pending[inpin_idx] = 0; 327c6c5e63dSJamin Lin trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx, 328c6c5e63dSJamin Lin s->regs[reg]); 329c6c5e63dSJamin Lin aspeed_intc_update(s, inpin_idx, outpin_idx, 1); 330d831c5fdSJamin Lin } else { 331d831c5fdSJamin Lin /* clear irq */ 332c6c5e63dSJamin Lin trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx, 0); 333c6c5e63dSJamin Lin aspeed_intc_update(s, inpin_idx, outpin_idx, 0); 334d831c5fdSJamin Lin } 335d831c5fdSJamin Lin } 3363d6e15eaSJamin Lin } 3373d6e15eaSJamin Lin 3389178ff91SJamin Lin static void aspeed_intc_status_handler_multi_outpins(AspeedINTCState *s, 3399178ff91SJamin Lin hwaddr offset, uint64_t data) 3409178ff91SJamin Lin { 3419178ff91SJamin Lin const char *name = object_get_typename(OBJECT(s)); 3429178ff91SJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 3439178ff91SJamin Lin const AspeedINTCIRQ *intc_irq; 3449178ff91SJamin Lin uint32_t reg = offset >> 2; 3459178ff91SJamin Lin int num_outpins; 3469178ff91SJamin Lin int outpin_idx; 3479178ff91SJamin Lin int inpin_idx; 3489178ff91SJamin Lin int i; 3499178ff91SJamin Lin 3509178ff91SJamin Lin if (!data) { 3519178ff91SJamin Lin qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__); 3529178ff91SJamin Lin return; 3539178ff91SJamin Lin } 3549178ff91SJamin Lin 3559178ff91SJamin Lin intc_irq = aspeed_intc_get_irq(aic, reg); 3569178ff91SJamin Lin num_outpins = intc_irq->num_outpins; 3579178ff91SJamin Lin outpin_idx = intc_irq->outpin_idx; 3589178ff91SJamin Lin inpin_idx = intc_irq->inpin_idx; 3599178ff91SJamin Lin assert(inpin_idx < aic->num_inpins); 3609178ff91SJamin Lin 3619178ff91SJamin Lin /* clear status */ 3629178ff91SJamin Lin s->regs[reg] &= ~data; 3639178ff91SJamin Lin 3649178ff91SJamin Lin /* 3659178ff91SJamin Lin * The status registers are used for notify sources ISR are executed. 3669178ff91SJamin Lin * If one source ISR is executed, it will clear one bit. 3679178ff91SJamin Lin * If it clear all bits, it means to initialize this register status 3689178ff91SJamin Lin * rather than sources ISR are executed. 3699178ff91SJamin Lin */ 3709178ff91SJamin Lin if (data == 0xffffffff) { 3719178ff91SJamin Lin return; 3729178ff91SJamin Lin } 3739178ff91SJamin Lin 3749178ff91SJamin Lin for (i = 0; i < num_outpins; i++) { 3759178ff91SJamin Lin /* All source ISR executions are done from a specific bit */ 3769178ff91SJamin Lin if (data & BIT(i)) { 3779178ff91SJamin Lin trace_aspeed_intc_all_isr_done_bit(name, inpin_idx, i); 3789178ff91SJamin Lin if (s->pending[inpin_idx] & BIT(i)) { 3799178ff91SJamin Lin /* 3809178ff91SJamin Lin * Handle pending source interrupt. 3819178ff91SJamin Lin * Notify firmware which source interrupt is pending 3829178ff91SJamin Lin * by setting the status bit. 3839178ff91SJamin Lin */ 3849178ff91SJamin Lin s->regs[reg] |= BIT(i); 3859178ff91SJamin Lin s->pending[inpin_idx] &= ~BIT(i); 3869178ff91SJamin Lin trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i, 3879178ff91SJamin Lin s->regs[reg]); 3889178ff91SJamin Lin aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1); 3899178ff91SJamin Lin } else { 3909178ff91SJamin Lin /* clear irq for the specific bit */ 3919178ff91SJamin Lin trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx + i, 0); 3929178ff91SJamin Lin aspeed_intc_update(s, inpin_idx, outpin_idx + i, 0); 3939178ff91SJamin Lin } 3949178ff91SJamin Lin } 3959178ff91SJamin Lin } 3969178ff91SJamin Lin } 3979178ff91SJamin Lin 3983d6e15eaSJamin Lin static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size) 3993d6e15eaSJamin Lin { 4003d6e15eaSJamin Lin AspeedINTCState *s = ASPEED_INTC(opaque); 40149da40cfSJamin Lin const char *name = object_get_typename(OBJECT(s)); 4023d6e15eaSJamin Lin uint32_t reg = offset >> 2; 4033d6e15eaSJamin Lin uint32_t value = 0; 4043d6e15eaSJamin Lin 4053d6e15eaSJamin Lin value = s->regs[reg]; 40649da40cfSJamin Lin trace_aspeed_intc_read(name, offset, size, value); 4073d6e15eaSJamin Lin 4083d6e15eaSJamin Lin return value; 4093d6e15eaSJamin Lin } 4103d6e15eaSJamin Lin 4113d6e15eaSJamin Lin static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, 4123d6e15eaSJamin Lin unsigned size) 4133d6e15eaSJamin Lin { 4143d6e15eaSJamin Lin AspeedINTCState *s = ASPEED_INTC(opaque); 41549da40cfSJamin Lin const char *name = object_get_typename(OBJECT(s)); 4163d6e15eaSJamin Lin uint32_t reg = offset >> 2; 4173d6e15eaSJamin Lin 41849da40cfSJamin Lin trace_aspeed_intc_write(name, offset, size, data); 4193d6e15eaSJamin Lin 4203d6e15eaSJamin Lin switch (reg) { 4213d6e15eaSJamin Lin case R_GICINT128_EN: 4223d6e15eaSJamin Lin case R_GICINT129_EN: 4233d6e15eaSJamin Lin case R_GICINT130_EN: 4243d6e15eaSJamin Lin case R_GICINT131_EN: 4253d6e15eaSJamin Lin case R_GICINT132_EN: 4263d6e15eaSJamin Lin case R_GICINT133_EN: 4273d6e15eaSJamin Lin case R_GICINT134_EN: 4283d6e15eaSJamin Lin case R_GICINT135_EN: 4293d6e15eaSJamin Lin case R_GICINT136_EN: 4309178ff91SJamin Lin case R_GICINT192_201_EN: 4313d6e15eaSJamin Lin aspeed_intc_enable_handler(s, offset, data); 4323d6e15eaSJamin Lin break; 4333d6e15eaSJamin Lin case R_GICINT128_STATUS: 4343d6e15eaSJamin Lin case R_GICINT129_STATUS: 4353d6e15eaSJamin Lin case R_GICINT130_STATUS: 4363d6e15eaSJamin Lin case R_GICINT131_STATUS: 4373d6e15eaSJamin Lin case R_GICINT132_STATUS: 4383d6e15eaSJamin Lin case R_GICINT133_STATUS: 4393d6e15eaSJamin Lin case R_GICINT134_STATUS: 4403d6e15eaSJamin Lin case R_GICINT135_STATUS: 4413d6e15eaSJamin Lin case R_GICINT136_STATUS: 4423d6e15eaSJamin Lin aspeed_intc_status_handler(s, offset, data); 443d831c5fdSJamin Lin break; 4449178ff91SJamin Lin case R_GICINT192_201_STATUS: 4459178ff91SJamin Lin aspeed_intc_status_handler_multi_outpins(s, offset, data); 4469178ff91SJamin Lin break; 447d831c5fdSJamin Lin default: 4480cffaaceSJamin Lin s->regs[reg] = data; 449d831c5fdSJamin Lin break; 450d831c5fdSJamin Lin } 451d831c5fdSJamin Lin 452d831c5fdSJamin Lin return; 453d831c5fdSJamin Lin } 454d831c5fdSJamin Lin 45538ba38d8SJamin Lin static uint64_t aspeed_intcio_read(void *opaque, hwaddr offset, 45638ba38d8SJamin Lin unsigned int size) 45738ba38d8SJamin Lin { 45838ba38d8SJamin Lin AspeedINTCState *s = ASPEED_INTC(opaque); 45938ba38d8SJamin Lin const char *name = object_get_typename(OBJECT(s)); 46038ba38d8SJamin Lin uint32_t reg = offset >> 2; 46138ba38d8SJamin Lin uint32_t value = 0; 46238ba38d8SJamin Lin 46338ba38d8SJamin Lin value = s->regs[reg]; 46438ba38d8SJamin Lin trace_aspeed_intc_read(name, offset, size, value); 46538ba38d8SJamin Lin 46638ba38d8SJamin Lin return value; 46738ba38d8SJamin Lin } 46838ba38d8SJamin Lin 46938ba38d8SJamin Lin static void aspeed_intcio_write(void *opaque, hwaddr offset, uint64_t data, 47038ba38d8SJamin Lin unsigned size) 47138ba38d8SJamin Lin { 47238ba38d8SJamin Lin AspeedINTCState *s = ASPEED_INTC(opaque); 47338ba38d8SJamin Lin const char *name = object_get_typename(OBJECT(s)); 47438ba38d8SJamin Lin uint32_t reg = offset >> 2; 47538ba38d8SJamin Lin 47638ba38d8SJamin Lin trace_aspeed_intc_write(name, offset, size, data); 47738ba38d8SJamin Lin 47838ba38d8SJamin Lin switch (reg) { 47938ba38d8SJamin Lin case R_GICINT192_EN: 48038ba38d8SJamin Lin case R_GICINT193_EN: 48138ba38d8SJamin Lin case R_GICINT194_EN: 48238ba38d8SJamin Lin case R_GICINT195_EN: 48338ba38d8SJamin Lin case R_GICINT196_EN: 48438ba38d8SJamin Lin case R_GICINT197_EN: 48538ba38d8SJamin Lin aspeed_intc_enable_handler(s, offset, data); 48638ba38d8SJamin Lin break; 48738ba38d8SJamin Lin case R_GICINT192_STATUS: 48838ba38d8SJamin Lin case R_GICINT193_STATUS: 48938ba38d8SJamin Lin case R_GICINT194_STATUS: 49038ba38d8SJamin Lin case R_GICINT195_STATUS: 49138ba38d8SJamin Lin case R_GICINT196_STATUS: 49238ba38d8SJamin Lin case R_GICINT197_STATUS: 49338ba38d8SJamin Lin aspeed_intc_status_handler(s, offset, data); 49438ba38d8SJamin Lin break; 49538ba38d8SJamin Lin default: 49638ba38d8SJamin Lin s->regs[reg] = data; 49738ba38d8SJamin Lin break; 49838ba38d8SJamin Lin } 49938ba38d8SJamin Lin 50038ba38d8SJamin Lin return; 50138ba38d8SJamin Lin } 50238ba38d8SJamin Lin 50338ba38d8SJamin Lin 504d831c5fdSJamin Lin static const MemoryRegionOps aspeed_intc_ops = { 505d831c5fdSJamin Lin .read = aspeed_intc_read, 506d831c5fdSJamin Lin .write = aspeed_intc_write, 507d831c5fdSJamin Lin .endianness = DEVICE_LITTLE_ENDIAN, 508d831c5fdSJamin Lin .valid = { 509d831c5fdSJamin Lin .min_access_size = 4, 510d831c5fdSJamin Lin .max_access_size = 4, 511d831c5fdSJamin Lin } 512d831c5fdSJamin Lin }; 513d831c5fdSJamin Lin 51438ba38d8SJamin Lin static const MemoryRegionOps aspeed_intcio_ops = { 51538ba38d8SJamin Lin .read = aspeed_intcio_read, 51638ba38d8SJamin Lin .write = aspeed_intcio_write, 51738ba38d8SJamin Lin .endianness = DEVICE_LITTLE_ENDIAN, 51838ba38d8SJamin Lin .valid = { 51938ba38d8SJamin Lin .min_access_size = 4, 52038ba38d8SJamin Lin .max_access_size = 4, 52138ba38d8SJamin Lin } 52238ba38d8SJamin Lin }; 52338ba38d8SJamin Lin 524d831c5fdSJamin Lin static void aspeed_intc_instance_init(Object *obj) 525d831c5fdSJamin Lin { 526d831c5fdSJamin Lin AspeedINTCState *s = ASPEED_INTC(obj); 527d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 528d831c5fdSJamin Lin int i; 529d831c5fdSJamin Lin 53063f3618fSJamin Lin assert(aic->num_inpins <= ASPEED_INTC_MAX_INPINS); 53163f3618fSJamin Lin for (i = 0; i < aic->num_inpins; i++) { 532d831c5fdSJamin Lin object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i], 533d831c5fdSJamin Lin TYPE_OR_IRQ); 534d831c5fdSJamin Lin object_property_set_int(OBJECT(&s->orgates[i]), "num-lines", 535d831c5fdSJamin Lin aic->num_lines, &error_abort); 536d831c5fdSJamin Lin } 537d831c5fdSJamin Lin } 538d831c5fdSJamin Lin 539d831c5fdSJamin Lin static void aspeed_intc_reset(DeviceState *dev) 540d831c5fdSJamin Lin { 541d831c5fdSJamin Lin AspeedINTCState *s = ASPEED_INTC(dev); 542b008465dSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 543d831c5fdSJamin Lin 544b008465dSJamin Lin memset(s->regs, 0, aic->nr_regs << 2); 545d831c5fdSJamin Lin memset(s->enable, 0, sizeof(s->enable)); 546d831c5fdSJamin Lin memset(s->mask, 0, sizeof(s->mask)); 547d831c5fdSJamin Lin memset(s->pending, 0, sizeof(s->pending)); 548d831c5fdSJamin Lin } 549d831c5fdSJamin Lin 550d831c5fdSJamin Lin static void aspeed_intc_realize(DeviceState *dev, Error **errp) 551d831c5fdSJamin Lin { 552d831c5fdSJamin Lin SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 553d831c5fdSJamin Lin AspeedINTCState *s = ASPEED_INTC(dev); 554d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s); 555d831c5fdSJamin Lin int i; 556d831c5fdSJamin Lin 557c5728c34SJamin Lin memory_region_init(&s->iomem_container, OBJECT(s), 558c5728c34SJamin Lin TYPE_ASPEED_INTC ".container", aic->mem_size); 559c5728c34SJamin Lin 560c5728c34SJamin Lin sysbus_init_mmio(sbd, &s->iomem_container); 561c5728c34SJamin Lin 562b008465dSJamin Lin s->regs = g_new(uint32_t, aic->nr_regs); 56328194d5dSJamin Lin memory_region_init_io(&s->iomem, OBJECT(s), aic->reg_ops, s, 564b008465dSJamin Lin TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2); 565d831c5fdSJamin Lin 5667ffee511SJamin Lin memory_region_add_subregion(&s->iomem_container, aic->reg_offset, 5677ffee511SJamin Lin &s->iomem); 568c5728c34SJamin Lin 56963f3618fSJamin Lin qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_inpins); 570d831c5fdSJamin Lin 57163f3618fSJamin Lin for (i = 0; i < aic->num_inpins; i++) { 572d831c5fdSJamin Lin if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) { 573d831c5fdSJamin Lin return; 574d831c5fdSJamin Lin } 57535c909cdSJamin Lin } 57635c909cdSJamin Lin 57735c909cdSJamin Lin for (i = 0; i < aic->num_outpins; i++) { 578d831c5fdSJamin Lin sysbus_init_irq(sbd, &s->output_pins[i]); 579d831c5fdSJamin Lin } 580d831c5fdSJamin Lin } 581d831c5fdSJamin Lin 582563afea0SJamin Lin static void aspeed_intc_unrealize(DeviceState *dev) 583563afea0SJamin Lin { 584563afea0SJamin Lin AspeedINTCState *s = ASPEED_INTC(dev); 585563afea0SJamin Lin 586563afea0SJamin Lin g_free(s->regs); 587563afea0SJamin Lin s->regs = NULL; 588563afea0SJamin Lin } 589563afea0SJamin Lin 590d831c5fdSJamin Lin static void aspeed_intc_class_init(ObjectClass *klass, void *data) 591d831c5fdSJamin Lin { 592d831c5fdSJamin Lin DeviceClass *dc = DEVICE_CLASS(klass); 59328194d5dSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); 594d831c5fdSJamin Lin 595d831c5fdSJamin Lin dc->desc = "ASPEED INTC Controller"; 596d831c5fdSJamin Lin dc->realize = aspeed_intc_realize; 597563afea0SJamin Lin dc->unrealize = aspeed_intc_unrealize; 598e3d08143SPeter Maydell device_class_set_legacy_reset(dc, aspeed_intc_reset); 599d831c5fdSJamin Lin dc->vmsd = NULL; 60028194d5dSJamin Lin 60128194d5dSJamin Lin aic->reg_ops = &aspeed_intc_ops; 602d831c5fdSJamin Lin } 603d831c5fdSJamin Lin 604d831c5fdSJamin Lin static const TypeInfo aspeed_intc_info = { 605d831c5fdSJamin Lin .name = TYPE_ASPEED_INTC, 606d831c5fdSJamin Lin .parent = TYPE_SYS_BUS_DEVICE, 607d831c5fdSJamin Lin .instance_init = aspeed_intc_instance_init, 608d831c5fdSJamin Lin .instance_size = sizeof(AspeedINTCState), 609d831c5fdSJamin Lin .class_init = aspeed_intc_class_init, 610d831c5fdSJamin Lin .class_size = sizeof(AspeedINTCClass), 611d831c5fdSJamin Lin .abstract = true, 612d831c5fdSJamin Lin }; 613d831c5fdSJamin Lin 614ab24c6a2SJamin Lin static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = { 6159178ff91SJamin Lin {0, 0, 10, R_GICINT192_201_EN, R_GICINT192_201_STATUS}, 6169178ff91SJamin Lin {1, 10, 1, R_GICINT128_EN, R_GICINT128_STATUS}, 6179178ff91SJamin Lin {2, 11, 1, R_GICINT129_EN, R_GICINT129_STATUS}, 6189178ff91SJamin Lin {3, 12, 1, R_GICINT130_EN, R_GICINT130_STATUS}, 6199178ff91SJamin Lin {4, 13, 1, R_GICINT131_EN, R_GICINT131_STATUS}, 6209178ff91SJamin Lin {5, 14, 1, R_GICINT132_EN, R_GICINT132_STATUS}, 6219178ff91SJamin Lin {6, 15, 1, R_GICINT133_EN, R_GICINT133_STATUS}, 6229178ff91SJamin Lin {7, 16, 1, R_GICINT134_EN, R_GICINT134_STATUS}, 6239178ff91SJamin Lin {8, 17, 1, R_GICINT135_EN, R_GICINT135_STATUS}, 6249178ff91SJamin Lin {9, 18, 1, R_GICINT136_EN, R_GICINT136_STATUS}, 625ab24c6a2SJamin Lin }; 626ab24c6a2SJamin Lin 627d831c5fdSJamin Lin static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) 628d831c5fdSJamin Lin { 629d831c5fdSJamin Lin DeviceClass *dc = DEVICE_CLASS(klass); 630d831c5fdSJamin Lin AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); 631d831c5fdSJamin Lin 632d831c5fdSJamin Lin dc->desc = "ASPEED 2700 INTC Controller"; 633d831c5fdSJamin Lin aic->num_lines = 32; 6349178ff91SJamin Lin aic->num_inpins = 10; 6359178ff91SJamin Lin aic->num_outpins = 19; 636c5728c34SJamin Lin aic->mem_size = 0x4000; 6379178ff91SJamin Lin aic->nr_regs = 0xB08 >> 2; 6387ffee511SJamin Lin aic->reg_offset = 0x1000; 639ab24c6a2SJamin Lin aic->irq_table = aspeed_2700_intc_irqs; 640ab24c6a2SJamin Lin aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs); 641d831c5fdSJamin Lin } 642d831c5fdSJamin Lin 643d831c5fdSJamin Lin static const TypeInfo aspeed_2700_intc_info = { 644d831c5fdSJamin Lin .name = TYPE_ASPEED_2700_INTC, 645d831c5fdSJamin Lin .parent = TYPE_ASPEED_INTC, 646d831c5fdSJamin Lin .class_init = aspeed_2700_intc_class_init, 647d831c5fdSJamin Lin }; 648d831c5fdSJamin Lin 64938ba38d8SJamin Lin static AspeedINTCIRQ aspeed_2700_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { 65038ba38d8SJamin Lin {0, 0, 1, R_GICINT192_EN, R_GICINT192_STATUS}, 65138ba38d8SJamin Lin {1, 1, 1, R_GICINT193_EN, R_GICINT193_STATUS}, 65238ba38d8SJamin Lin {2, 2, 1, R_GICINT194_EN, R_GICINT194_STATUS}, 65338ba38d8SJamin Lin {3, 3, 1, R_GICINT195_EN, R_GICINT195_STATUS}, 65438ba38d8SJamin Lin {4, 4, 1, R_GICINT196_EN, R_GICINT196_STATUS}, 65538ba38d8SJamin Lin {5, 5, 1, R_GICINT197_EN, R_GICINT197_STATUS}, 65638ba38d8SJamin Lin }; 65738ba38d8SJamin Lin 65838ba38d8SJamin Lin static void aspeed_2700_intcio_class_init(ObjectClass *klass, void *data) 65938ba38d8SJamin Lin { 66038ba38d8SJamin Lin DeviceClass *dc = DEVICE_CLASS(klass); 66138ba38d8SJamin Lin AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); 66238ba38d8SJamin Lin 66338ba38d8SJamin Lin dc->desc = "ASPEED 2700 INTC IO Controller"; 66438ba38d8SJamin Lin aic->num_lines = 32; 66538ba38d8SJamin Lin aic->num_inpins = 6; 66638ba38d8SJamin Lin aic->num_outpins = 6; 66738ba38d8SJamin Lin aic->mem_size = 0x400; 66838ba38d8SJamin Lin aic->nr_regs = 0x58 >> 2; 66938ba38d8SJamin Lin aic->reg_offset = 0x100; 67038ba38d8SJamin Lin aic->reg_ops = &aspeed_intcio_ops; 67138ba38d8SJamin Lin aic->irq_table = aspeed_2700_intcio_irqs; 67238ba38d8SJamin Lin aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intcio_irqs); 67338ba38d8SJamin Lin } 67438ba38d8SJamin Lin 67538ba38d8SJamin Lin static const TypeInfo aspeed_2700_intcio_info = { 67638ba38d8SJamin Lin .name = TYPE_ASPEED_2700_INTCIO, 67738ba38d8SJamin Lin .parent = TYPE_ASPEED_INTC, 67838ba38d8SJamin Lin .class_init = aspeed_2700_intcio_class_init, 67938ba38d8SJamin Lin }; 68038ba38d8SJamin Lin 681d831c5fdSJamin Lin static void aspeed_intc_register_types(void) 682d831c5fdSJamin Lin { 683d831c5fdSJamin Lin type_register_static(&aspeed_intc_info); 684d831c5fdSJamin Lin type_register_static(&aspeed_2700_intc_info); 68538ba38d8SJamin Lin type_register_static(&aspeed_2700_intcio_info); 686d831c5fdSJamin Lin } 687d831c5fdSJamin Lin 688d831c5fdSJamin Lin type_init(aspeed_intc_register_types); 689