xref: /qemu/hw/intc/aspeed_intc.c (revision c6c5e63d46add459732d8d8d3b84bd5d26dff0ad)
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 
43d831c5fdSJamin Lin #define GICINT_STATUS_BASE     R_GICINT128_STATUS
44d831c5fdSJamin Lin 
45*c6c5e63dSJamin Lin /*
46*c6c5e63dSJamin Lin  * Update the state of an interrupt controller pin by setting
47*c6c5e63dSJamin Lin  * the specified output pin to the given level.
48*c6c5e63dSJamin Lin  * The input pin index should be between 0 and the number of input pins.
49*c6c5e63dSJamin Lin  * The output pin index should be between 0 and the number of output pins.
50*c6c5e63dSJamin Lin  */
51*c6c5e63dSJamin Lin static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx,
52*c6c5e63dSJamin Lin                                int outpin_idx, int level)
53d831c5fdSJamin Lin {
54d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
5549da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
56d831c5fdSJamin Lin 
57*c6c5e63dSJamin Lin     if (inpin_idx >= aic->num_inpins) {
58*c6c5e63dSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n",
59*c6c5e63dSJamin Lin                       __func__, inpin_idx);
60d831c5fdSJamin Lin         return;
61d831c5fdSJamin Lin     }
62d831c5fdSJamin Lin 
63*c6c5e63dSJamin Lin     if (outpin_idx >= aic->num_outpins) {
64*c6c5e63dSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid output pin index: %d\n",
65*c6c5e63dSJamin Lin                       __func__, outpin_idx);
66*c6c5e63dSJamin Lin         return;
67*c6c5e63dSJamin Lin     }
68*c6c5e63dSJamin Lin 
69*c6c5e63dSJamin Lin     trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level);
70*c6c5e63dSJamin Lin     qemu_set_irq(s->output_pins[outpin_idx], level);
71d831c5fdSJamin Lin }
72d831c5fdSJamin Lin 
73d831c5fdSJamin Lin /*
74d831c5fdSJamin Lin  * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804.
75d831c5fdSJamin Lin  * Utilize "address & 0x0f00" to get the irq and irq output pin index
7663f3618fSJamin Lin  * The value of irq should be 0 to num_inpins.
77d831c5fdSJamin Lin  * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on.
78d831c5fdSJamin Lin  */
79d831c5fdSJamin Lin static void aspeed_intc_set_irq(void *opaque, int irq, int level)
80d831c5fdSJamin Lin {
81d831c5fdSJamin Lin     AspeedINTCState *s = (AspeedINTCState *)opaque;
82d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
8349da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
840cffaaceSJamin Lin     uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2);
85d831c5fdSJamin Lin     uint32_t select = 0;
86d831c5fdSJamin Lin     uint32_t enable;
87*c6c5e63dSJamin Lin     int outpin_idx;
88*c6c5e63dSJamin Lin     int inpin_idx;
89d831c5fdSJamin Lin     int i;
90d831c5fdSJamin Lin 
91*c6c5e63dSJamin Lin     outpin_idx = irq;
92*c6c5e63dSJamin Lin     inpin_idx = irq;
93*c6c5e63dSJamin Lin 
9463f3618fSJamin Lin     if (irq >= aic->num_inpins) {
9563f3618fSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid input pin index: %d\n",
96d831c5fdSJamin Lin                       __func__, irq);
97d831c5fdSJamin Lin         return;
98d831c5fdSJamin Lin     }
99d831c5fdSJamin Lin 
100*c6c5e63dSJamin Lin     trace_aspeed_intc_set_irq(name, inpin_idx, level);
101*c6c5e63dSJamin Lin     enable = s->enable[inpin_idx];
102d831c5fdSJamin Lin 
103d831c5fdSJamin Lin     if (!level) {
104d831c5fdSJamin Lin         return;
105d831c5fdSJamin Lin     }
106d831c5fdSJamin Lin 
107d831c5fdSJamin Lin     for (i = 0; i < aic->num_lines; i++) {
108*c6c5e63dSJamin Lin         if (s->orgates[inpin_idx].levels[i]) {
109d831c5fdSJamin Lin             if (enable & BIT(i)) {
110d831c5fdSJamin Lin                 select |= BIT(i);
111d831c5fdSJamin Lin             }
112d831c5fdSJamin Lin         }
113d831c5fdSJamin Lin     }
114d831c5fdSJamin Lin 
115d831c5fdSJamin Lin     if (!select) {
116d831c5fdSJamin Lin         return;
117d831c5fdSJamin Lin     }
118d831c5fdSJamin Lin 
11949da40cfSJamin Lin     trace_aspeed_intc_select(name, select);
120d831c5fdSJamin Lin 
121*c6c5e63dSJamin Lin     if (s->mask[inpin_idx] || s->regs[status_reg]) {
122d831c5fdSJamin Lin         /*
123d831c5fdSJamin Lin          * a. mask is not 0 means in ISR mode
124d831c5fdSJamin Lin          * sources interrupt routine are executing.
125d831c5fdSJamin Lin          * b. status register value is not 0 means previous
126d831c5fdSJamin Lin          * source interrupt does not be executed, yet.
127d831c5fdSJamin Lin          *
128d831c5fdSJamin Lin          * save source interrupt to pending variable.
129d831c5fdSJamin Lin          */
130*c6c5e63dSJamin Lin         s->pending[inpin_idx] |= select;
131*c6c5e63dSJamin Lin         trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]);
132d831c5fdSJamin Lin     } else {
133d831c5fdSJamin Lin         /*
134d831c5fdSJamin Lin          * notify firmware which source interrupt are coming
135d831c5fdSJamin Lin          * by setting status register
136d831c5fdSJamin Lin          */
1370cffaaceSJamin Lin         s->regs[status_reg] = select;
138*c6c5e63dSJamin Lin         trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx,
139*c6c5e63dSJamin Lin                                       s->regs[status_reg]);
140*c6c5e63dSJamin Lin         aspeed_intc_update(s, inpin_idx, outpin_idx, 1);
141d831c5fdSJamin Lin     }
142d831c5fdSJamin Lin }
143d831c5fdSJamin Lin 
1443d6e15eaSJamin Lin static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset,
1453d6e15eaSJamin Lin                                        uint64_t data)
146d831c5fdSJamin Lin {
147d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
14849da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
1490cffaaceSJamin Lin     uint32_t reg = offset >> 2;
150d831c5fdSJamin Lin     uint32_t old_enable;
151d831c5fdSJamin Lin     uint32_t change;
152*c6c5e63dSJamin Lin     int inpin_idx;
153d831c5fdSJamin Lin     uint32_t irq;
154d831c5fdSJamin Lin 
155d831c5fdSJamin Lin     irq = (offset & 0x0f00) >> 8;
156*c6c5e63dSJamin Lin     inpin_idx = irq;
157d831c5fdSJamin Lin 
158*c6c5e63dSJamin Lin     if (inpin_idx >= aic->num_inpins) {
15963f3618fSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR,
16063f3618fSJamin Lin                       "%s: Invalid input pin index: %d\n",
161*c6c5e63dSJamin Lin                       __func__, inpin_idx);
162d831c5fdSJamin Lin         return;
163d831c5fdSJamin Lin     }
164d831c5fdSJamin Lin 
165d831c5fdSJamin Lin     /*
1663d6e15eaSJamin Lin      * The enable registers are used to enable source interrupts.
1673d6e15eaSJamin Lin      * They also handle masking and unmasking of source interrupts
1683d6e15eaSJamin Lin      * during the execution of the source ISR.
169d831c5fdSJamin Lin      */
170d831c5fdSJamin Lin 
171d831c5fdSJamin Lin     /* disable all source interrupt */
172*c6c5e63dSJamin Lin     if (!data && !s->enable[inpin_idx]) {
1730cffaaceSJamin Lin         s->regs[reg] = data;
174d831c5fdSJamin Lin         return;
175d831c5fdSJamin Lin     }
176d831c5fdSJamin Lin 
177*c6c5e63dSJamin Lin     old_enable = s->enable[inpin_idx];
178*c6c5e63dSJamin Lin     s->enable[inpin_idx] |= data;
179d831c5fdSJamin Lin 
180d831c5fdSJamin Lin     /* enable new source interrupt */
181*c6c5e63dSJamin Lin     if (old_enable != s->enable[inpin_idx]) {
182*c6c5e63dSJamin Lin         trace_aspeed_intc_enable(name, s->enable[inpin_idx]);
1830cffaaceSJamin Lin         s->regs[reg] = data;
184d831c5fdSJamin Lin         return;
185d831c5fdSJamin Lin     }
186d831c5fdSJamin Lin 
187d831c5fdSJamin Lin     /* mask and unmask source interrupt */
1880cffaaceSJamin Lin     change = s->regs[reg] ^ data;
189d831c5fdSJamin Lin     if (change & data) {
190*c6c5e63dSJamin Lin         s->mask[inpin_idx] &= ~change;
191*c6c5e63dSJamin Lin         trace_aspeed_intc_unmask(name, change, s->mask[inpin_idx]);
192d831c5fdSJamin Lin     } else {
193*c6c5e63dSJamin Lin         s->mask[inpin_idx] |= change;
194*c6c5e63dSJamin Lin         trace_aspeed_intc_mask(name, change, s->mask[inpin_idx]);
195d831c5fdSJamin Lin     }
1963d6e15eaSJamin Lin 
1970cffaaceSJamin Lin     s->regs[reg] = data;
1983d6e15eaSJamin Lin }
1993d6e15eaSJamin Lin 
2003d6e15eaSJamin Lin static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset,
2013d6e15eaSJamin Lin                                        uint64_t data)
2023d6e15eaSJamin Lin {
2033d6e15eaSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
20449da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
2053d6e15eaSJamin Lin     uint32_t reg = offset >> 2;
206*c6c5e63dSJamin Lin     int outpin_idx;
207*c6c5e63dSJamin Lin     int inpin_idx;
2083d6e15eaSJamin Lin     uint32_t irq;
2093d6e15eaSJamin Lin 
2103d6e15eaSJamin Lin     if (!data) {
2113d6e15eaSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__);
2123d6e15eaSJamin Lin         return;
2133d6e15eaSJamin Lin     }
2143d6e15eaSJamin Lin 
215d831c5fdSJamin Lin     irq = (offset & 0x0f00) >> 8;
216*c6c5e63dSJamin Lin     outpin_idx = irq;
217*c6c5e63dSJamin Lin     inpin_idx = irq;
218d831c5fdSJamin Lin 
219*c6c5e63dSJamin Lin     if (inpin_idx >= aic->num_inpins) {
22063f3618fSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR,
22163f3618fSJamin Lin                       "%s: Invalid input pin index: %d\n",
222*c6c5e63dSJamin Lin                       __func__, inpin_idx);
223d831c5fdSJamin Lin         return;
224d831c5fdSJamin Lin     }
225d831c5fdSJamin Lin 
226d831c5fdSJamin Lin     /* clear status */
2270cffaaceSJamin Lin     s->regs[reg] &= ~data;
228d831c5fdSJamin Lin 
229d831c5fdSJamin Lin     /*
230d831c5fdSJamin Lin      * These status registers are used for notify sources ISR are executed.
231d831c5fdSJamin Lin      * If one source ISR is executed, it will clear one bit.
232d831c5fdSJamin Lin      * If it clear all bits, it means to initialize this register status
233d831c5fdSJamin Lin      * rather than sources ISR are executed.
234d831c5fdSJamin Lin      */
235d831c5fdSJamin Lin     if (data == 0xffffffff) {
236d831c5fdSJamin Lin         return;
237d831c5fdSJamin Lin     }
238d831c5fdSJamin Lin 
239d831c5fdSJamin Lin     /* All source ISR execution are done */
2400cffaaceSJamin Lin     if (!s->regs[reg]) {
241*c6c5e63dSJamin Lin         trace_aspeed_intc_all_isr_done(name, inpin_idx);
242*c6c5e63dSJamin Lin         if (s->pending[inpin_idx]) {
243d831c5fdSJamin Lin             /*
244d831c5fdSJamin Lin              * handle pending source interrupt
245d831c5fdSJamin Lin              * notify firmware which source interrupt are pending
246d831c5fdSJamin Lin              * by setting status register
247d831c5fdSJamin Lin              */
248*c6c5e63dSJamin Lin             s->regs[reg] = s->pending[inpin_idx];
249*c6c5e63dSJamin Lin             s->pending[inpin_idx] = 0;
250*c6c5e63dSJamin Lin             trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx,
251*c6c5e63dSJamin Lin                                           s->regs[reg]);
252*c6c5e63dSJamin Lin             aspeed_intc_update(s, inpin_idx, outpin_idx, 1);
253d831c5fdSJamin Lin         } else {
254d831c5fdSJamin Lin             /* clear irq */
255*c6c5e63dSJamin Lin             trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx, 0);
256*c6c5e63dSJamin Lin             aspeed_intc_update(s, inpin_idx, outpin_idx, 0);
257d831c5fdSJamin Lin         }
258d831c5fdSJamin Lin     }
2593d6e15eaSJamin Lin }
2603d6e15eaSJamin Lin 
2613d6e15eaSJamin Lin static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size)
2623d6e15eaSJamin Lin {
2633d6e15eaSJamin Lin     AspeedINTCState *s = ASPEED_INTC(opaque);
26449da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
2653d6e15eaSJamin Lin     uint32_t reg = offset >> 2;
2663d6e15eaSJamin Lin     uint32_t value = 0;
2673d6e15eaSJamin Lin 
2683d6e15eaSJamin Lin     value = s->regs[reg];
26949da40cfSJamin Lin     trace_aspeed_intc_read(name, offset, size, value);
2703d6e15eaSJamin Lin 
2713d6e15eaSJamin Lin     return value;
2723d6e15eaSJamin Lin }
2733d6e15eaSJamin Lin 
2743d6e15eaSJamin Lin static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data,
2753d6e15eaSJamin Lin                                         unsigned size)
2763d6e15eaSJamin Lin {
2773d6e15eaSJamin Lin     AspeedINTCState *s = ASPEED_INTC(opaque);
27849da40cfSJamin Lin     const char *name = object_get_typename(OBJECT(s));
2793d6e15eaSJamin Lin     uint32_t reg = offset >> 2;
2803d6e15eaSJamin Lin 
28149da40cfSJamin Lin     trace_aspeed_intc_write(name, offset, size, data);
2823d6e15eaSJamin Lin 
2833d6e15eaSJamin Lin     switch (reg) {
2843d6e15eaSJamin Lin     case R_GICINT128_EN:
2853d6e15eaSJamin Lin     case R_GICINT129_EN:
2863d6e15eaSJamin Lin     case R_GICINT130_EN:
2873d6e15eaSJamin Lin     case R_GICINT131_EN:
2883d6e15eaSJamin Lin     case R_GICINT132_EN:
2893d6e15eaSJamin Lin     case R_GICINT133_EN:
2903d6e15eaSJamin Lin     case R_GICINT134_EN:
2913d6e15eaSJamin Lin     case R_GICINT135_EN:
2923d6e15eaSJamin Lin     case R_GICINT136_EN:
2933d6e15eaSJamin Lin         aspeed_intc_enable_handler(s, offset, data);
2943d6e15eaSJamin Lin         break;
2953d6e15eaSJamin Lin     case R_GICINT128_STATUS:
2963d6e15eaSJamin Lin     case R_GICINT129_STATUS:
2973d6e15eaSJamin Lin     case R_GICINT130_STATUS:
2983d6e15eaSJamin Lin     case R_GICINT131_STATUS:
2993d6e15eaSJamin Lin     case R_GICINT132_STATUS:
3003d6e15eaSJamin Lin     case R_GICINT133_STATUS:
3013d6e15eaSJamin Lin     case R_GICINT134_STATUS:
3023d6e15eaSJamin Lin     case R_GICINT135_STATUS:
3033d6e15eaSJamin Lin     case R_GICINT136_STATUS:
3043d6e15eaSJamin Lin         aspeed_intc_status_handler(s, offset, data);
305d831c5fdSJamin Lin         break;
306d831c5fdSJamin Lin     default:
3070cffaaceSJamin Lin         s->regs[reg] = data;
308d831c5fdSJamin Lin         break;
309d831c5fdSJamin Lin     }
310d831c5fdSJamin Lin 
311d831c5fdSJamin Lin     return;
312d831c5fdSJamin Lin }
313d831c5fdSJamin Lin 
314d831c5fdSJamin Lin static const MemoryRegionOps aspeed_intc_ops = {
315d831c5fdSJamin Lin     .read = aspeed_intc_read,
316d831c5fdSJamin Lin     .write = aspeed_intc_write,
317d831c5fdSJamin Lin     .endianness = DEVICE_LITTLE_ENDIAN,
318d831c5fdSJamin Lin     .valid = {
319d831c5fdSJamin Lin         .min_access_size = 4,
320d831c5fdSJamin Lin         .max_access_size = 4,
321d831c5fdSJamin Lin     }
322d831c5fdSJamin Lin };
323d831c5fdSJamin Lin 
324d831c5fdSJamin Lin static void aspeed_intc_instance_init(Object *obj)
325d831c5fdSJamin Lin {
326d831c5fdSJamin Lin     AspeedINTCState *s = ASPEED_INTC(obj);
327d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
328d831c5fdSJamin Lin     int i;
329d831c5fdSJamin Lin 
33063f3618fSJamin Lin     assert(aic->num_inpins <= ASPEED_INTC_MAX_INPINS);
33163f3618fSJamin Lin     for (i = 0; i < aic->num_inpins; i++) {
332d831c5fdSJamin Lin         object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i],
333d831c5fdSJamin Lin                                 TYPE_OR_IRQ);
334d831c5fdSJamin Lin         object_property_set_int(OBJECT(&s->orgates[i]), "num-lines",
335d831c5fdSJamin Lin                                 aic->num_lines, &error_abort);
336d831c5fdSJamin Lin     }
337d831c5fdSJamin Lin }
338d831c5fdSJamin Lin 
339d831c5fdSJamin Lin static void aspeed_intc_reset(DeviceState *dev)
340d831c5fdSJamin Lin {
341d831c5fdSJamin Lin     AspeedINTCState *s = ASPEED_INTC(dev);
342b008465dSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
343d831c5fdSJamin Lin 
344b008465dSJamin Lin     memset(s->regs, 0, aic->nr_regs << 2);
345d831c5fdSJamin Lin     memset(s->enable, 0, sizeof(s->enable));
346d831c5fdSJamin Lin     memset(s->mask, 0, sizeof(s->mask));
347d831c5fdSJamin Lin     memset(s->pending, 0, sizeof(s->pending));
348d831c5fdSJamin Lin }
349d831c5fdSJamin Lin 
350d831c5fdSJamin Lin static void aspeed_intc_realize(DeviceState *dev, Error **errp)
351d831c5fdSJamin Lin {
352d831c5fdSJamin Lin     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
353d831c5fdSJamin Lin     AspeedINTCState *s = ASPEED_INTC(dev);
354d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
355d831c5fdSJamin Lin     int i;
356d831c5fdSJamin Lin 
357c5728c34SJamin Lin     memory_region_init(&s->iomem_container, OBJECT(s),
358c5728c34SJamin Lin             TYPE_ASPEED_INTC ".container", aic->mem_size);
359c5728c34SJamin Lin 
360c5728c34SJamin Lin     sysbus_init_mmio(sbd, &s->iomem_container);
361c5728c34SJamin Lin 
362b008465dSJamin Lin     s->regs = g_new(uint32_t, aic->nr_regs);
36328194d5dSJamin Lin     memory_region_init_io(&s->iomem, OBJECT(s), aic->reg_ops, s,
364b008465dSJamin Lin                           TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2);
365d831c5fdSJamin Lin 
3667ffee511SJamin Lin     memory_region_add_subregion(&s->iomem_container, aic->reg_offset,
3677ffee511SJamin Lin                                 &s->iomem);
368c5728c34SJamin Lin 
36963f3618fSJamin Lin     qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_inpins);
370d831c5fdSJamin Lin 
37163f3618fSJamin Lin     for (i = 0; i < aic->num_inpins; i++) {
372d831c5fdSJamin Lin         if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) {
373d831c5fdSJamin Lin             return;
374d831c5fdSJamin Lin         }
37535c909cdSJamin Lin     }
37635c909cdSJamin Lin 
37735c909cdSJamin Lin     for (i = 0; i < aic->num_outpins; i++) {
378d831c5fdSJamin Lin         sysbus_init_irq(sbd, &s->output_pins[i]);
379d831c5fdSJamin Lin     }
380d831c5fdSJamin Lin }
381d831c5fdSJamin Lin 
382563afea0SJamin Lin static void aspeed_intc_unrealize(DeviceState *dev)
383563afea0SJamin Lin {
384563afea0SJamin Lin     AspeedINTCState *s = ASPEED_INTC(dev);
385563afea0SJamin Lin 
386563afea0SJamin Lin     g_free(s->regs);
387563afea0SJamin Lin     s->regs = NULL;
388563afea0SJamin Lin }
389563afea0SJamin Lin 
390d831c5fdSJamin Lin static void aspeed_intc_class_init(ObjectClass *klass, void *data)
391d831c5fdSJamin Lin {
392d831c5fdSJamin Lin     DeviceClass *dc = DEVICE_CLASS(klass);
39328194d5dSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
394d831c5fdSJamin Lin 
395d831c5fdSJamin Lin     dc->desc = "ASPEED INTC Controller";
396d831c5fdSJamin Lin     dc->realize = aspeed_intc_realize;
397563afea0SJamin Lin     dc->unrealize = aspeed_intc_unrealize;
398e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, aspeed_intc_reset);
399d831c5fdSJamin Lin     dc->vmsd = NULL;
40028194d5dSJamin Lin 
40128194d5dSJamin Lin     aic->reg_ops = &aspeed_intc_ops;
402d831c5fdSJamin Lin }
403d831c5fdSJamin Lin 
404d831c5fdSJamin Lin static const TypeInfo aspeed_intc_info = {
405d831c5fdSJamin Lin     .name = TYPE_ASPEED_INTC,
406d831c5fdSJamin Lin     .parent = TYPE_SYS_BUS_DEVICE,
407d831c5fdSJamin Lin     .instance_init = aspeed_intc_instance_init,
408d831c5fdSJamin Lin     .instance_size = sizeof(AspeedINTCState),
409d831c5fdSJamin Lin     .class_init = aspeed_intc_class_init,
410d831c5fdSJamin Lin     .class_size = sizeof(AspeedINTCClass),
411d831c5fdSJamin Lin     .abstract = true,
412d831c5fdSJamin Lin };
413d831c5fdSJamin Lin 
414d831c5fdSJamin Lin static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data)
415d831c5fdSJamin Lin {
416d831c5fdSJamin Lin     DeviceClass *dc = DEVICE_CLASS(klass);
417d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
418d831c5fdSJamin Lin 
419d831c5fdSJamin Lin     dc->desc = "ASPEED 2700 INTC Controller";
420d831c5fdSJamin Lin     aic->num_lines = 32;
42163f3618fSJamin Lin     aic->num_inpins = 9;
42235c909cdSJamin Lin     aic->num_outpins = 9;
423c5728c34SJamin Lin     aic->mem_size = 0x4000;
4247ffee511SJamin Lin     aic->nr_regs = 0x808 >> 2;
4257ffee511SJamin Lin     aic->reg_offset = 0x1000;
426d831c5fdSJamin Lin }
427d831c5fdSJamin Lin 
428d831c5fdSJamin Lin static const TypeInfo aspeed_2700_intc_info = {
429d831c5fdSJamin Lin     .name = TYPE_ASPEED_2700_INTC,
430d831c5fdSJamin Lin     .parent = TYPE_ASPEED_INTC,
431d831c5fdSJamin Lin     .class_init = aspeed_2700_intc_class_init,
432d831c5fdSJamin Lin };
433d831c5fdSJamin Lin 
434d831c5fdSJamin Lin static void aspeed_intc_register_types(void)
435d831c5fdSJamin Lin {
436d831c5fdSJamin Lin     type_register_static(&aspeed_intc_info);
437d831c5fdSJamin Lin     type_register_static(&aspeed_2700_intc_info);
438d831c5fdSJamin Lin }
439d831c5fdSJamin Lin 
440d831c5fdSJamin Lin type_init(aspeed_intc_register_types);
441