xref: /qemu/hw/intc/aspeed_intc.c (revision 7b8cbe5162e69ad629c5326bf3c158b81857955d)
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