xref: /qemu/hw/intc/aspeed_intc.c (revision 5824e8bf6beb226aa5dee94d4e92b671e9b082f1)
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)
42d831c5fdSJamin Lin 
43ab24c6a2SJamin Lin static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic,
44ab24c6a2SJamin Lin                                                 uint32_t reg)
45ab24c6a2SJamin Lin {
46ab24c6a2SJamin Lin     int i;
47ab24c6a2SJamin Lin 
48ab24c6a2SJamin Lin     for (i = 0; i < aic->irq_table_count; i++) {
49ab24c6a2SJamin Lin         if (aic->irq_table[i].enable_reg == reg ||
50ab24c6a2SJamin Lin             aic->irq_table[i].status_reg == reg) {
51ab24c6a2SJamin Lin             return &aic->irq_table[i];
52ab24c6a2SJamin Lin         }
53ab24c6a2SJamin Lin     }
54ab24c6a2SJamin Lin 
55ab24c6a2SJamin Lin     /*
56ab24c6a2SJamin Lin      * Invalid reg.
57ab24c6a2SJamin Lin      */
58ab24c6a2SJamin Lin     g_assert_not_reached();
59ab24c6a2SJamin Lin }
60d831c5fdSJamin Lin 
61c6c5e63dSJamin Lin /*
62c6c5e63dSJamin Lin  * Update the state of an interrupt controller pin by setting
63c6c5e63dSJamin Lin  * the specified output pin to the given level.
64c6c5e63dSJamin Lin  * The input pin index should be between 0 and the number of input pins.
65c6c5e63dSJamin Lin  * The output pin index should be between 0 and the number of output pins.
66c6c5e63dSJamin Lin  */
67c6c5e63dSJamin Lin static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx,
68c6c5e63dSJamin Lin                                int outpin_idx, int level)
69d831c5fdSJamin Lin {
70d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
7149da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
72d831c5fdSJamin Lin 
73ab24c6a2SJamin Lin     assert((outpin_idx < aic->num_outpins) && (inpin_idx < aic->num_inpins));
74c6c5e63dSJamin Lin 
75c6c5e63dSJamin Lin     trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level);
76c6c5e63dSJamin Lin     qemu_set_irq(s->output_pins[outpin_idx], level);
77d831c5fdSJamin Lin }
78d831c5fdSJamin Lin 
79*5824e8bfSJamin Lin static void aspeed_intc_set_irq_handler(AspeedINTCState *s,
80*5824e8bfSJamin Lin                                         const AspeedINTCIRQ *intc_irq,
81*5824e8bfSJamin Lin                                         uint32_t select)
82d831c5fdSJamin Lin {
8349da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
84ab24c6a2SJamin Lin     uint32_t status_reg;
85c6c5e63dSJamin Lin     int outpin_idx;
86c6c5e63dSJamin Lin     int inpin_idx;
87d831c5fdSJamin Lin 
88ab24c6a2SJamin Lin     status_reg = intc_irq->status_reg;
89ab24c6a2SJamin Lin     outpin_idx = intc_irq->outpin_idx;
90ab24c6a2SJamin Lin     inpin_idx = intc_irq->inpin_idx;
91d831c5fdSJamin Lin 
92c6c5e63dSJamin Lin     if (s->mask[inpin_idx] || s->regs[status_reg]) {
93d831c5fdSJamin Lin         /*
94d831c5fdSJamin Lin          * a. mask is not 0 means in ISR mode
95d831c5fdSJamin Lin          * sources interrupt routine are executing.
96d831c5fdSJamin Lin          * b. status register value is not 0 means previous
97d831c5fdSJamin Lin          * source interrupt does not be executed, yet.
98d831c5fdSJamin Lin          *
99d831c5fdSJamin Lin          * save source interrupt to pending variable.
100d831c5fdSJamin Lin          */
101c6c5e63dSJamin Lin         s->pending[inpin_idx] |= select;
102c6c5e63dSJamin Lin         trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]);
103d831c5fdSJamin Lin     } else {
104d831c5fdSJamin Lin         /*
105d831c5fdSJamin Lin          * notify firmware which source interrupt are coming
106d831c5fdSJamin Lin          * by setting status register
107d831c5fdSJamin Lin          */
1080cffaaceSJamin Lin         s->regs[status_reg] = select;
109c6c5e63dSJamin Lin         trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx,
110c6c5e63dSJamin Lin                                       s->regs[status_reg]);
111c6c5e63dSJamin Lin         aspeed_intc_update(s, inpin_idx, outpin_idx, 1);
112d831c5fdSJamin Lin     }
113d831c5fdSJamin Lin }
114d831c5fdSJamin Lin 
115*5824e8bfSJamin Lin /*
116*5824e8bfSJamin Lin  * GICINT128 to GICINT136 map 1:1 to input and output IRQs 0 to 8.
117*5824e8bfSJamin Lin  * The value of input IRQ should be between 0 and the number of inputs.
118*5824e8bfSJamin Lin  */
119*5824e8bfSJamin Lin static void aspeed_intc_set_irq(void *opaque, int irq, int level)
120*5824e8bfSJamin Lin {
121*5824e8bfSJamin Lin     AspeedINTCState *s = (AspeedINTCState *)opaque;
122*5824e8bfSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
123*5824e8bfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
124*5824e8bfSJamin Lin     const AspeedINTCIRQ *intc_irq;
125*5824e8bfSJamin Lin     uint32_t select = 0;
126*5824e8bfSJamin Lin     uint32_t enable;
127*5824e8bfSJamin Lin     int inpin_idx;
128*5824e8bfSJamin Lin     int i;
129*5824e8bfSJamin Lin 
130*5824e8bfSJamin Lin     assert(irq < aic->num_inpins);
131*5824e8bfSJamin Lin 
132*5824e8bfSJamin Lin     intc_irq = &aic->irq_table[irq];
133*5824e8bfSJamin Lin     inpin_idx = intc_irq->inpin_idx;
134*5824e8bfSJamin Lin     trace_aspeed_intc_set_irq(name, inpin_idx, level);
135*5824e8bfSJamin Lin     enable = s->enable[inpin_idx];
136*5824e8bfSJamin Lin 
137*5824e8bfSJamin Lin     if (!level) {
138*5824e8bfSJamin Lin         return;
139*5824e8bfSJamin Lin     }
140*5824e8bfSJamin Lin 
141*5824e8bfSJamin Lin     for (i = 0; i < aic->num_lines; i++) {
142*5824e8bfSJamin Lin         if (s->orgates[inpin_idx].levels[i]) {
143*5824e8bfSJamin Lin             if (enable & BIT(i)) {
144*5824e8bfSJamin Lin                 select |= BIT(i);
145*5824e8bfSJamin Lin             }
146*5824e8bfSJamin Lin         }
147*5824e8bfSJamin Lin     }
148*5824e8bfSJamin Lin 
149*5824e8bfSJamin Lin     if (!select) {
150*5824e8bfSJamin Lin         return;
151*5824e8bfSJamin Lin     }
152*5824e8bfSJamin Lin 
153*5824e8bfSJamin Lin     trace_aspeed_intc_select(name, select);
154*5824e8bfSJamin Lin     aspeed_intc_set_irq_handler(s, intc_irq, select);
155*5824e8bfSJamin Lin }
156*5824e8bfSJamin Lin 
1573d6e15eaSJamin Lin static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset,
1583d6e15eaSJamin Lin                                        uint64_t data)
159d831c5fdSJamin Lin {
160d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
16149da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
162ab24c6a2SJamin Lin     const AspeedINTCIRQ *intc_irq;
1630cffaaceSJamin Lin     uint32_t reg = offset >> 2;
164d831c5fdSJamin Lin     uint32_t old_enable;
165d831c5fdSJamin Lin     uint32_t change;
166c6c5e63dSJamin Lin     int inpin_idx;
167d831c5fdSJamin Lin 
168ab24c6a2SJamin Lin     intc_irq = aspeed_intc_get_irq(aic, reg);
169ab24c6a2SJamin Lin     inpin_idx = intc_irq->inpin_idx;
170d831c5fdSJamin Lin 
171ab24c6a2SJamin Lin     assert(inpin_idx < aic->num_inpins);
172d831c5fdSJamin Lin 
173d831c5fdSJamin Lin     /*
1743d6e15eaSJamin Lin      * The enable registers are used to enable source interrupts.
1753d6e15eaSJamin Lin      * They also handle masking and unmasking of source interrupts
1763d6e15eaSJamin Lin      * during the execution of the source ISR.
177d831c5fdSJamin Lin      */
178d831c5fdSJamin Lin 
179d831c5fdSJamin Lin     /* disable all source interrupt */
180c6c5e63dSJamin Lin     if (!data && !s->enable[inpin_idx]) {
1810cffaaceSJamin Lin         s->regs[reg] = data;
182d831c5fdSJamin Lin         return;
183d831c5fdSJamin Lin     }
184d831c5fdSJamin Lin 
185c6c5e63dSJamin Lin     old_enable = s->enable[inpin_idx];
186c6c5e63dSJamin Lin     s->enable[inpin_idx] |= data;
187d831c5fdSJamin Lin 
188d831c5fdSJamin Lin     /* enable new source interrupt */
189c6c5e63dSJamin Lin     if (old_enable != s->enable[inpin_idx]) {
190c6c5e63dSJamin Lin         trace_aspeed_intc_enable(name, s->enable[inpin_idx]);
1910cffaaceSJamin Lin         s->regs[reg] = data;
192d831c5fdSJamin Lin         return;
193d831c5fdSJamin Lin     }
194d831c5fdSJamin Lin 
195d831c5fdSJamin Lin     /* mask and unmask source interrupt */
1960cffaaceSJamin Lin     change = s->regs[reg] ^ data;
197d831c5fdSJamin Lin     if (change & data) {
198c6c5e63dSJamin Lin         s->mask[inpin_idx] &= ~change;
199c6c5e63dSJamin Lin         trace_aspeed_intc_unmask(name, change, s->mask[inpin_idx]);
200d831c5fdSJamin Lin     } else {
201c6c5e63dSJamin Lin         s->mask[inpin_idx] |= change;
202c6c5e63dSJamin Lin         trace_aspeed_intc_mask(name, change, s->mask[inpin_idx]);
203d831c5fdSJamin Lin     }
2043d6e15eaSJamin Lin 
2050cffaaceSJamin Lin     s->regs[reg] = data;
2063d6e15eaSJamin Lin }
2073d6e15eaSJamin Lin 
2083d6e15eaSJamin Lin static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset,
2093d6e15eaSJamin Lin                                        uint64_t data)
2103d6e15eaSJamin Lin {
2113d6e15eaSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
21249da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
213ab24c6a2SJamin Lin     const AspeedINTCIRQ *intc_irq;
2143d6e15eaSJamin Lin     uint32_t reg = offset >> 2;
215c6c5e63dSJamin Lin     int outpin_idx;
216c6c5e63dSJamin Lin     int inpin_idx;
2173d6e15eaSJamin Lin 
2183d6e15eaSJamin Lin     if (!data) {
2193d6e15eaSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__);
2203d6e15eaSJamin Lin         return;
2213d6e15eaSJamin Lin     }
2223d6e15eaSJamin Lin 
223ab24c6a2SJamin Lin     intc_irq = aspeed_intc_get_irq(aic, reg);
224ab24c6a2SJamin Lin     outpin_idx = intc_irq->outpin_idx;
225ab24c6a2SJamin Lin     inpin_idx = intc_irq->inpin_idx;
226d831c5fdSJamin Lin 
227ab24c6a2SJamin Lin     assert(inpin_idx < aic->num_inpins);
228d831c5fdSJamin Lin 
229d831c5fdSJamin Lin     /* clear status */
2300cffaaceSJamin Lin     s->regs[reg] &= ~data;
231d831c5fdSJamin Lin 
232d831c5fdSJamin Lin     /*
233d831c5fdSJamin Lin      * These status registers are used for notify sources ISR are executed.
234d831c5fdSJamin Lin      * If one source ISR is executed, it will clear one bit.
235d831c5fdSJamin Lin      * If it clear all bits, it means to initialize this register status
236d831c5fdSJamin Lin      * rather than sources ISR are executed.
237d831c5fdSJamin Lin      */
238d831c5fdSJamin Lin     if (data == 0xffffffff) {
239d831c5fdSJamin Lin         return;
240d831c5fdSJamin Lin     }
241d831c5fdSJamin Lin 
242d831c5fdSJamin Lin     /* All source ISR execution are done */
2430cffaaceSJamin Lin     if (!s->regs[reg]) {
244c6c5e63dSJamin Lin         trace_aspeed_intc_all_isr_done(name, inpin_idx);
245c6c5e63dSJamin Lin         if (s->pending[inpin_idx]) {
246d831c5fdSJamin Lin             /*
247d831c5fdSJamin Lin              * handle pending source interrupt
248d831c5fdSJamin Lin              * notify firmware which source interrupt are pending
249d831c5fdSJamin Lin              * by setting status register
250d831c5fdSJamin Lin              */
251c6c5e63dSJamin Lin             s->regs[reg] = s->pending[inpin_idx];
252c6c5e63dSJamin Lin             s->pending[inpin_idx] = 0;
253c6c5e63dSJamin Lin             trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx,
254c6c5e63dSJamin Lin                                           s->regs[reg]);
255c6c5e63dSJamin Lin             aspeed_intc_update(s, inpin_idx, outpin_idx, 1);
256d831c5fdSJamin Lin         } else {
257d831c5fdSJamin Lin             /* clear irq */
258c6c5e63dSJamin Lin             trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx, 0);
259c6c5e63dSJamin Lin             aspeed_intc_update(s, inpin_idx, outpin_idx, 0);
260d831c5fdSJamin Lin         }
261d831c5fdSJamin Lin     }
2623d6e15eaSJamin Lin }
2633d6e15eaSJamin Lin 
2643d6e15eaSJamin Lin static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size)
2653d6e15eaSJamin Lin {
2663d6e15eaSJamin Lin     AspeedINTCState *s = ASPEED_INTC(opaque);
26749da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
2683d6e15eaSJamin Lin     uint32_t reg = offset >> 2;
2693d6e15eaSJamin Lin     uint32_t value = 0;
2703d6e15eaSJamin Lin 
2713d6e15eaSJamin Lin     value = s->regs[reg];
27249da40cfSJamin Lin     trace_aspeed_intc_read(name, offset, size, value);
2733d6e15eaSJamin Lin 
2743d6e15eaSJamin Lin     return value;
2753d6e15eaSJamin Lin }
2763d6e15eaSJamin Lin 
2773d6e15eaSJamin Lin static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data,
2783d6e15eaSJamin Lin                                         unsigned size)
2793d6e15eaSJamin Lin {
2803d6e15eaSJamin Lin     AspeedINTCState *s = ASPEED_INTC(opaque);
28149da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
2823d6e15eaSJamin Lin     uint32_t reg = offset >> 2;
2833d6e15eaSJamin Lin 
28449da40cfSJamin Lin     trace_aspeed_intc_write(name, offset, size, data);
2853d6e15eaSJamin Lin 
2863d6e15eaSJamin Lin     switch (reg) {
2873d6e15eaSJamin Lin     case R_GICINT128_EN:
2883d6e15eaSJamin Lin     case R_GICINT129_EN:
2893d6e15eaSJamin Lin     case R_GICINT130_EN:
2903d6e15eaSJamin Lin     case R_GICINT131_EN:
2913d6e15eaSJamin Lin     case R_GICINT132_EN:
2923d6e15eaSJamin Lin     case R_GICINT133_EN:
2933d6e15eaSJamin Lin     case R_GICINT134_EN:
2943d6e15eaSJamin Lin     case R_GICINT135_EN:
2953d6e15eaSJamin Lin     case R_GICINT136_EN:
2963d6e15eaSJamin Lin         aspeed_intc_enable_handler(s, offset, data);
2973d6e15eaSJamin Lin         break;
2983d6e15eaSJamin Lin     case R_GICINT128_STATUS:
2993d6e15eaSJamin Lin     case R_GICINT129_STATUS:
3003d6e15eaSJamin Lin     case R_GICINT130_STATUS:
3013d6e15eaSJamin Lin     case R_GICINT131_STATUS:
3023d6e15eaSJamin Lin     case R_GICINT132_STATUS:
3033d6e15eaSJamin Lin     case R_GICINT133_STATUS:
3043d6e15eaSJamin Lin     case R_GICINT134_STATUS:
3053d6e15eaSJamin Lin     case R_GICINT135_STATUS:
3063d6e15eaSJamin Lin     case R_GICINT136_STATUS:
3073d6e15eaSJamin Lin         aspeed_intc_status_handler(s, offset, data);
308d831c5fdSJamin Lin         break;
309d831c5fdSJamin Lin     default:
3100cffaaceSJamin Lin         s->regs[reg] = data;
311d831c5fdSJamin Lin         break;
312d831c5fdSJamin Lin     }
313d831c5fdSJamin Lin 
314d831c5fdSJamin Lin     return;
315d831c5fdSJamin Lin }
316d831c5fdSJamin Lin 
317d831c5fdSJamin Lin static const MemoryRegionOps aspeed_intc_ops = {
318d831c5fdSJamin Lin     .read = aspeed_intc_read,
319d831c5fdSJamin Lin     .write = aspeed_intc_write,
320d831c5fdSJamin Lin     .endianness = DEVICE_LITTLE_ENDIAN,
321d831c5fdSJamin Lin     .valid = {
322d831c5fdSJamin Lin         .min_access_size = 4,
323d831c5fdSJamin Lin         .max_access_size = 4,
324d831c5fdSJamin Lin     }
325d831c5fdSJamin Lin };
326d831c5fdSJamin Lin 
327d831c5fdSJamin Lin static void aspeed_intc_instance_init(Object *obj)
328d831c5fdSJamin Lin {
329d831c5fdSJamin Lin     AspeedINTCState *s = ASPEED_INTC(obj);
330d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
331d831c5fdSJamin Lin     int i;
332d831c5fdSJamin Lin 
33363f3618fSJamin Lin     assert(aic->num_inpins <= ASPEED_INTC_MAX_INPINS);
33463f3618fSJamin Lin     for (i = 0; i < aic->num_inpins; i++) {
335d831c5fdSJamin Lin         object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i],
336d831c5fdSJamin Lin                                 TYPE_OR_IRQ);
337d831c5fdSJamin Lin         object_property_set_int(OBJECT(&s->orgates[i]), "num-lines",
338d831c5fdSJamin Lin                                 aic->num_lines, &error_abort);
339d831c5fdSJamin Lin     }
340d831c5fdSJamin Lin }
341d831c5fdSJamin Lin 
342d831c5fdSJamin Lin static void aspeed_intc_reset(DeviceState *dev)
343d831c5fdSJamin Lin {
344d831c5fdSJamin Lin     AspeedINTCState *s = ASPEED_INTC(dev);
345b008465dSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
346d831c5fdSJamin Lin 
347b008465dSJamin Lin     memset(s->regs, 0, aic->nr_regs << 2);
348d831c5fdSJamin Lin     memset(s->enable, 0, sizeof(s->enable));
349d831c5fdSJamin Lin     memset(s->mask, 0, sizeof(s->mask));
350d831c5fdSJamin Lin     memset(s->pending, 0, sizeof(s->pending));
351d831c5fdSJamin Lin }
352d831c5fdSJamin Lin 
353d831c5fdSJamin Lin static void aspeed_intc_realize(DeviceState *dev, Error **errp)
354d831c5fdSJamin Lin {
355d831c5fdSJamin Lin     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
356d831c5fdSJamin Lin     AspeedINTCState *s = ASPEED_INTC(dev);
357d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
358d831c5fdSJamin Lin     int i;
359d831c5fdSJamin Lin 
360c5728c34SJamin Lin     memory_region_init(&s->iomem_container, OBJECT(s),
361c5728c34SJamin Lin             TYPE_ASPEED_INTC ".container", aic->mem_size);
362c5728c34SJamin Lin 
363c5728c34SJamin Lin     sysbus_init_mmio(sbd, &s->iomem_container);
364c5728c34SJamin Lin 
365b008465dSJamin Lin     s->regs = g_new(uint32_t, aic->nr_regs);
36628194d5dSJamin Lin     memory_region_init_io(&s->iomem, OBJECT(s), aic->reg_ops, s,
367b008465dSJamin Lin                           TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2);
368d831c5fdSJamin Lin 
3697ffee511SJamin Lin     memory_region_add_subregion(&s->iomem_container, aic->reg_offset,
3707ffee511SJamin Lin                                 &s->iomem);
371c5728c34SJamin Lin 
37263f3618fSJamin Lin     qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_inpins);
373d831c5fdSJamin Lin 
37463f3618fSJamin Lin     for (i = 0; i < aic->num_inpins; i++) {
375d831c5fdSJamin Lin         if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) {
376d831c5fdSJamin Lin             return;
377d831c5fdSJamin Lin         }
37835c909cdSJamin Lin     }
37935c909cdSJamin Lin 
38035c909cdSJamin Lin     for (i = 0; i < aic->num_outpins; i++) {
381d831c5fdSJamin Lin         sysbus_init_irq(sbd, &s->output_pins[i]);
382d831c5fdSJamin Lin     }
383d831c5fdSJamin Lin }
384d831c5fdSJamin Lin 
385563afea0SJamin Lin static void aspeed_intc_unrealize(DeviceState *dev)
386563afea0SJamin Lin {
387563afea0SJamin Lin     AspeedINTCState *s = ASPEED_INTC(dev);
388563afea0SJamin Lin 
389563afea0SJamin Lin     g_free(s->regs);
390563afea0SJamin Lin     s->regs = NULL;
391563afea0SJamin Lin }
392563afea0SJamin Lin 
393d831c5fdSJamin Lin static void aspeed_intc_class_init(ObjectClass *klass, void *data)
394d831c5fdSJamin Lin {
395d831c5fdSJamin Lin     DeviceClass *dc = DEVICE_CLASS(klass);
39628194d5dSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
397d831c5fdSJamin Lin 
398d831c5fdSJamin Lin     dc->desc = "ASPEED INTC Controller";
399d831c5fdSJamin Lin     dc->realize = aspeed_intc_realize;
400563afea0SJamin Lin     dc->unrealize = aspeed_intc_unrealize;
401e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, aspeed_intc_reset);
402d831c5fdSJamin Lin     dc->vmsd = NULL;
40328194d5dSJamin Lin 
40428194d5dSJamin Lin     aic->reg_ops = &aspeed_intc_ops;
405d831c5fdSJamin Lin }
406d831c5fdSJamin Lin 
407d831c5fdSJamin Lin static const TypeInfo aspeed_intc_info = {
408d831c5fdSJamin Lin     .name = TYPE_ASPEED_INTC,
409d831c5fdSJamin Lin     .parent = TYPE_SYS_BUS_DEVICE,
410d831c5fdSJamin Lin     .instance_init = aspeed_intc_instance_init,
411d831c5fdSJamin Lin     .instance_size = sizeof(AspeedINTCState),
412d831c5fdSJamin Lin     .class_init = aspeed_intc_class_init,
413d831c5fdSJamin Lin     .class_size = sizeof(AspeedINTCClass),
414d831c5fdSJamin Lin     .abstract = true,
415d831c5fdSJamin Lin };
416d831c5fdSJamin Lin 
417ab24c6a2SJamin Lin static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = {
418ab24c6a2SJamin Lin     {0, 0, 1, R_GICINT128_EN, R_GICINT128_STATUS},
419ab24c6a2SJamin Lin     {1, 1, 1, R_GICINT129_EN, R_GICINT129_STATUS},
420ab24c6a2SJamin Lin     {2, 2, 1, R_GICINT130_EN, R_GICINT130_STATUS},
421ab24c6a2SJamin Lin     {3, 3, 1, R_GICINT131_EN, R_GICINT131_STATUS},
422ab24c6a2SJamin Lin     {4, 4, 1, R_GICINT132_EN, R_GICINT132_STATUS},
423ab24c6a2SJamin Lin     {5, 5, 1, R_GICINT133_EN, R_GICINT133_STATUS},
424ab24c6a2SJamin Lin     {6, 6, 1, R_GICINT134_EN, R_GICINT134_STATUS},
425ab24c6a2SJamin Lin     {7, 7, 1, R_GICINT135_EN, R_GICINT135_STATUS},
426ab24c6a2SJamin Lin     {8, 8, 1, R_GICINT136_EN, R_GICINT136_STATUS},
427ab24c6a2SJamin Lin };
428ab24c6a2SJamin Lin 
429d831c5fdSJamin Lin static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data)
430d831c5fdSJamin Lin {
431d831c5fdSJamin Lin     DeviceClass *dc = DEVICE_CLASS(klass);
432d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
433d831c5fdSJamin Lin 
434d831c5fdSJamin Lin     dc->desc = "ASPEED 2700 INTC Controller";
435d831c5fdSJamin Lin     aic->num_lines = 32;
43663f3618fSJamin Lin     aic->num_inpins = 9;
43735c909cdSJamin Lin     aic->num_outpins = 9;
438c5728c34SJamin Lin     aic->mem_size = 0x4000;
4397ffee511SJamin Lin     aic->nr_regs = 0x808 >> 2;
4407ffee511SJamin Lin     aic->reg_offset = 0x1000;
441ab24c6a2SJamin Lin     aic->irq_table = aspeed_2700_intc_irqs;
442ab24c6a2SJamin Lin     aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs);
443d831c5fdSJamin Lin }
444d831c5fdSJamin Lin 
445d831c5fdSJamin Lin static const TypeInfo aspeed_2700_intc_info = {
446d831c5fdSJamin Lin     .name = TYPE_ASPEED_2700_INTC,
447d831c5fdSJamin Lin     .parent = TYPE_ASPEED_INTC,
448d831c5fdSJamin Lin     .class_init = aspeed_2700_intc_class_init,
449d831c5fdSJamin Lin };
450d831c5fdSJamin Lin 
451d831c5fdSJamin Lin static void aspeed_intc_register_types(void)
452d831c5fdSJamin Lin {
453d831c5fdSJamin Lin     type_register_static(&aspeed_intc_info);
454d831c5fdSJamin Lin     type_register_static(&aspeed_2700_intc_info);
455d831c5fdSJamin Lin }
456d831c5fdSJamin Lin 
457d831c5fdSJamin Lin type_init(aspeed_intc_register_types);
458