xref: /qemu/hw/intc/aspeed_intc.c (revision 3d6e15eafb3a3977f6659211e08d112807f20626)
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 
45d831c5fdSJamin Lin static void aspeed_intc_update(AspeedINTCState *s, int irq, int level)
46d831c5fdSJamin Lin {
47d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
48d831c5fdSJamin Lin 
49d831c5fdSJamin Lin     if (irq >= aic->num_ints) {
50d831c5fdSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
51d831c5fdSJamin Lin                       __func__, irq);
52d831c5fdSJamin Lin         return;
53d831c5fdSJamin Lin     }
54d831c5fdSJamin Lin 
55d831c5fdSJamin Lin     trace_aspeed_intc_update_irq(irq, level);
56d831c5fdSJamin Lin     qemu_set_irq(s->output_pins[irq], level);
57d831c5fdSJamin Lin }
58d831c5fdSJamin Lin 
59d831c5fdSJamin Lin /*
60d831c5fdSJamin Lin  * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804.
61d831c5fdSJamin Lin  * Utilize "address & 0x0f00" to get the irq and irq output pin index
62d831c5fdSJamin Lin  * The value of irq should be 0 to num_ints.
63d831c5fdSJamin Lin  * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on.
64d831c5fdSJamin Lin  */
65d831c5fdSJamin Lin static void aspeed_intc_set_irq(void *opaque, int irq, int level)
66d831c5fdSJamin Lin {
67d831c5fdSJamin Lin     AspeedINTCState *s = (AspeedINTCState *)opaque;
68d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
690cffaaceSJamin Lin     uint32_t status_reg = GICINT_STATUS_BASE + ((0x100 * irq) >> 2);
70d831c5fdSJamin Lin     uint32_t select = 0;
71d831c5fdSJamin Lin     uint32_t enable;
72d831c5fdSJamin Lin     int i;
73d831c5fdSJamin Lin 
74d831c5fdSJamin Lin     if (irq >= aic->num_ints) {
75d831c5fdSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
76d831c5fdSJamin Lin                       __func__, irq);
77d831c5fdSJamin Lin         return;
78d831c5fdSJamin Lin     }
79d831c5fdSJamin Lin 
80d831c5fdSJamin Lin     trace_aspeed_intc_set_irq(irq, level);
81d831c5fdSJamin Lin     enable = s->enable[irq];
82d831c5fdSJamin Lin 
83d831c5fdSJamin Lin     if (!level) {
84d831c5fdSJamin Lin         return;
85d831c5fdSJamin Lin     }
86d831c5fdSJamin Lin 
87d831c5fdSJamin Lin     for (i = 0; i < aic->num_lines; i++) {
88d831c5fdSJamin Lin         if (s->orgates[irq].levels[i]) {
89d831c5fdSJamin Lin             if (enable & BIT(i)) {
90d831c5fdSJamin Lin                 select |= BIT(i);
91d831c5fdSJamin Lin             }
92d831c5fdSJamin Lin         }
93d831c5fdSJamin Lin     }
94d831c5fdSJamin Lin 
95d831c5fdSJamin Lin     if (!select) {
96d831c5fdSJamin Lin         return;
97d831c5fdSJamin Lin     }
98d831c5fdSJamin Lin 
99d831c5fdSJamin Lin     trace_aspeed_intc_select(select);
100d831c5fdSJamin Lin 
1010cffaaceSJamin Lin     if (s->mask[irq] || s->regs[status_reg]) {
102d831c5fdSJamin Lin         /*
103d831c5fdSJamin Lin          * a. mask is not 0 means in ISR mode
104d831c5fdSJamin Lin          * sources interrupt routine are executing.
105d831c5fdSJamin Lin          * b. status register value is not 0 means previous
106d831c5fdSJamin Lin          * source interrupt does not be executed, yet.
107d831c5fdSJamin Lin          *
108d831c5fdSJamin Lin          * save source interrupt to pending variable.
109d831c5fdSJamin Lin          */
110d831c5fdSJamin Lin         s->pending[irq] |= select;
111d831c5fdSJamin Lin         trace_aspeed_intc_pending_irq(irq, s->pending[irq]);
112d831c5fdSJamin Lin     } else {
113d831c5fdSJamin Lin         /*
114d831c5fdSJamin Lin          * notify firmware which source interrupt are coming
115d831c5fdSJamin Lin          * by setting status register
116d831c5fdSJamin Lin          */
1170cffaaceSJamin Lin         s->regs[status_reg] = select;
1180cffaaceSJamin Lin         trace_aspeed_intc_trigger_irq(irq, s->regs[status_reg]);
119d831c5fdSJamin Lin         aspeed_intc_update(s, irq, 1);
120d831c5fdSJamin Lin     }
121d831c5fdSJamin Lin }
122d831c5fdSJamin Lin 
123*3d6e15eaSJamin Lin static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset,
124*3d6e15eaSJamin Lin                                        uint64_t data)
125d831c5fdSJamin Lin {
126d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
1270cffaaceSJamin Lin     uint32_t reg = offset >> 2;
128d831c5fdSJamin Lin     uint32_t old_enable;
129d831c5fdSJamin Lin     uint32_t change;
130d831c5fdSJamin Lin     uint32_t irq;
131d831c5fdSJamin Lin 
132d831c5fdSJamin Lin     irq = (offset & 0x0f00) >> 8;
133d831c5fdSJamin Lin 
134d831c5fdSJamin Lin     if (irq >= aic->num_ints) {
135d831c5fdSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
136d831c5fdSJamin Lin                       __func__, irq);
137d831c5fdSJamin Lin         return;
138d831c5fdSJamin Lin     }
139d831c5fdSJamin Lin 
140d831c5fdSJamin Lin     /*
141*3d6e15eaSJamin Lin      * The enable registers are used to enable source interrupts.
142*3d6e15eaSJamin Lin      * They also handle masking and unmasking of source interrupts
143*3d6e15eaSJamin Lin      * during the execution of the source ISR.
144d831c5fdSJamin Lin      */
145d831c5fdSJamin Lin 
146d831c5fdSJamin Lin     /* disable all source interrupt */
147d831c5fdSJamin Lin     if (!data && !s->enable[irq]) {
1480cffaaceSJamin Lin         s->regs[reg] = data;
149d831c5fdSJamin Lin         return;
150d831c5fdSJamin Lin     }
151d831c5fdSJamin Lin 
152d831c5fdSJamin Lin     old_enable = s->enable[irq];
153d831c5fdSJamin Lin     s->enable[irq] |= data;
154d831c5fdSJamin Lin 
155d831c5fdSJamin Lin     /* enable new source interrupt */
156d831c5fdSJamin Lin     if (old_enable != s->enable[irq]) {
157d831c5fdSJamin Lin         trace_aspeed_intc_enable(s->enable[irq]);
1580cffaaceSJamin Lin         s->regs[reg] = data;
159d831c5fdSJamin Lin         return;
160d831c5fdSJamin Lin     }
161d831c5fdSJamin Lin 
162d831c5fdSJamin Lin     /* mask and unmask source interrupt */
1630cffaaceSJamin Lin     change = s->regs[reg] ^ data;
164d831c5fdSJamin Lin     if (change & data) {
165d831c5fdSJamin Lin         s->mask[irq] &= ~change;
166d831c5fdSJamin Lin         trace_aspeed_intc_unmask(change, s->mask[irq]);
167d831c5fdSJamin Lin     } else {
168d831c5fdSJamin Lin         s->mask[irq] |= change;
169d831c5fdSJamin Lin         trace_aspeed_intc_mask(change, s->mask[irq]);
170d831c5fdSJamin Lin     }
171*3d6e15eaSJamin Lin 
1720cffaaceSJamin Lin     s->regs[reg] = data;
173*3d6e15eaSJamin Lin }
174*3d6e15eaSJamin Lin 
175*3d6e15eaSJamin Lin static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset,
176*3d6e15eaSJamin Lin                                        uint64_t data)
177*3d6e15eaSJamin Lin {
178*3d6e15eaSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
179*3d6e15eaSJamin Lin     uint32_t reg = offset >> 2;
180*3d6e15eaSJamin Lin     uint32_t irq;
181*3d6e15eaSJamin Lin 
182*3d6e15eaSJamin Lin     if (!data) {
183*3d6e15eaSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__);
184*3d6e15eaSJamin Lin         return;
185*3d6e15eaSJamin Lin     }
186*3d6e15eaSJamin Lin 
187d831c5fdSJamin Lin     irq = (offset & 0x0f00) >> 8;
188d831c5fdSJamin Lin 
189d831c5fdSJamin Lin     if (irq >= aic->num_ints) {
190d831c5fdSJamin Lin         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid interrupt number: %d\n",
191d831c5fdSJamin Lin                       __func__, irq);
192d831c5fdSJamin Lin         return;
193d831c5fdSJamin Lin     }
194d831c5fdSJamin Lin 
195d831c5fdSJamin Lin     /* clear status */
1960cffaaceSJamin Lin     s->regs[reg] &= ~data;
197d831c5fdSJamin Lin 
198d831c5fdSJamin Lin     /*
199d831c5fdSJamin Lin      * These status registers are used for notify sources ISR are executed.
200d831c5fdSJamin Lin      * If one source ISR is executed, it will clear one bit.
201d831c5fdSJamin Lin      * If it clear all bits, it means to initialize this register status
202d831c5fdSJamin Lin      * rather than sources ISR are executed.
203d831c5fdSJamin Lin      */
204d831c5fdSJamin Lin     if (data == 0xffffffff) {
205d831c5fdSJamin Lin         return;
206d831c5fdSJamin Lin     }
207d831c5fdSJamin Lin 
208d831c5fdSJamin Lin     /* All source ISR execution are done */
2090cffaaceSJamin Lin     if (!s->regs[reg]) {
210d831c5fdSJamin Lin         trace_aspeed_intc_all_isr_done(irq);
211d831c5fdSJamin Lin         if (s->pending[irq]) {
212d831c5fdSJamin Lin             /*
213d831c5fdSJamin Lin              * handle pending source interrupt
214d831c5fdSJamin Lin              * notify firmware which source interrupt are pending
215d831c5fdSJamin Lin              * by setting status register
216d831c5fdSJamin Lin              */
2170cffaaceSJamin Lin             s->regs[reg] = s->pending[irq];
218d831c5fdSJamin Lin             s->pending[irq] = 0;
2190cffaaceSJamin Lin             trace_aspeed_intc_trigger_irq(irq, s->regs[reg]);
220d831c5fdSJamin Lin             aspeed_intc_update(s, irq, 1);
221d831c5fdSJamin Lin         } else {
222d831c5fdSJamin Lin             /* clear irq */
223d831c5fdSJamin Lin             trace_aspeed_intc_clear_irq(irq, 0);
224d831c5fdSJamin Lin             aspeed_intc_update(s, irq, 0);
225d831c5fdSJamin Lin         }
226d831c5fdSJamin Lin     }
227*3d6e15eaSJamin Lin }
228*3d6e15eaSJamin Lin 
229*3d6e15eaSJamin Lin static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size)
230*3d6e15eaSJamin Lin {
231*3d6e15eaSJamin Lin     AspeedINTCState *s = ASPEED_INTC(opaque);
232*3d6e15eaSJamin Lin     uint32_t reg = offset >> 2;
233*3d6e15eaSJamin Lin     uint32_t value = 0;
234*3d6e15eaSJamin Lin 
235*3d6e15eaSJamin Lin     value = s->regs[reg];
236*3d6e15eaSJamin Lin     trace_aspeed_intc_read(offset, size, value);
237*3d6e15eaSJamin Lin 
238*3d6e15eaSJamin Lin     return value;
239*3d6e15eaSJamin Lin }
240*3d6e15eaSJamin Lin 
241*3d6e15eaSJamin Lin static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data,
242*3d6e15eaSJamin Lin                                         unsigned size)
243*3d6e15eaSJamin Lin {
244*3d6e15eaSJamin Lin     AspeedINTCState *s = ASPEED_INTC(opaque);
245*3d6e15eaSJamin Lin     uint32_t reg = offset >> 2;
246*3d6e15eaSJamin Lin 
247*3d6e15eaSJamin Lin     trace_aspeed_intc_write(offset, size, data);
248*3d6e15eaSJamin Lin 
249*3d6e15eaSJamin Lin     switch (reg) {
250*3d6e15eaSJamin Lin     case R_GICINT128_EN:
251*3d6e15eaSJamin Lin     case R_GICINT129_EN:
252*3d6e15eaSJamin Lin     case R_GICINT130_EN:
253*3d6e15eaSJamin Lin     case R_GICINT131_EN:
254*3d6e15eaSJamin Lin     case R_GICINT132_EN:
255*3d6e15eaSJamin Lin     case R_GICINT133_EN:
256*3d6e15eaSJamin Lin     case R_GICINT134_EN:
257*3d6e15eaSJamin Lin     case R_GICINT135_EN:
258*3d6e15eaSJamin Lin     case R_GICINT136_EN:
259*3d6e15eaSJamin Lin         aspeed_intc_enable_handler(s, offset, data);
260*3d6e15eaSJamin Lin         break;
261*3d6e15eaSJamin Lin     case R_GICINT128_STATUS:
262*3d6e15eaSJamin Lin     case R_GICINT129_STATUS:
263*3d6e15eaSJamin Lin     case R_GICINT130_STATUS:
264*3d6e15eaSJamin Lin     case R_GICINT131_STATUS:
265*3d6e15eaSJamin Lin     case R_GICINT132_STATUS:
266*3d6e15eaSJamin Lin     case R_GICINT133_STATUS:
267*3d6e15eaSJamin Lin     case R_GICINT134_STATUS:
268*3d6e15eaSJamin Lin     case R_GICINT135_STATUS:
269*3d6e15eaSJamin Lin     case R_GICINT136_STATUS:
270*3d6e15eaSJamin Lin         aspeed_intc_status_handler(s, offset, data);
271d831c5fdSJamin Lin         break;
272d831c5fdSJamin Lin     default:
2730cffaaceSJamin Lin         s->regs[reg] = data;
274d831c5fdSJamin Lin         break;
275d831c5fdSJamin Lin     }
276d831c5fdSJamin Lin 
277d831c5fdSJamin Lin     return;
278d831c5fdSJamin Lin }
279d831c5fdSJamin Lin 
280d831c5fdSJamin Lin static const MemoryRegionOps aspeed_intc_ops = {
281d831c5fdSJamin Lin     .read = aspeed_intc_read,
282d831c5fdSJamin Lin     .write = aspeed_intc_write,
283d831c5fdSJamin Lin     .endianness = DEVICE_LITTLE_ENDIAN,
284d831c5fdSJamin Lin     .valid = {
285d831c5fdSJamin Lin         .min_access_size = 4,
286d831c5fdSJamin Lin         .max_access_size = 4,
287d831c5fdSJamin Lin     }
288d831c5fdSJamin Lin };
289d831c5fdSJamin Lin 
290d831c5fdSJamin Lin static void aspeed_intc_instance_init(Object *obj)
291d831c5fdSJamin Lin {
292d831c5fdSJamin Lin     AspeedINTCState *s = ASPEED_INTC(obj);
293d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
294d831c5fdSJamin Lin     int i;
295d831c5fdSJamin Lin 
296d831c5fdSJamin Lin     assert(aic->num_ints <= ASPEED_INTC_NR_INTS);
297d831c5fdSJamin Lin     for (i = 0; i < aic->num_ints; i++) {
298d831c5fdSJamin Lin         object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i],
299d831c5fdSJamin Lin                                 TYPE_OR_IRQ);
300d831c5fdSJamin Lin         object_property_set_int(OBJECT(&s->orgates[i]), "num-lines",
301d831c5fdSJamin Lin                                 aic->num_lines, &error_abort);
302d831c5fdSJamin Lin     }
303d831c5fdSJamin Lin }
304d831c5fdSJamin Lin 
305d831c5fdSJamin Lin static void aspeed_intc_reset(DeviceState *dev)
306d831c5fdSJamin Lin {
307d831c5fdSJamin Lin     AspeedINTCState *s = ASPEED_INTC(dev);
308b008465dSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
309d831c5fdSJamin Lin 
310b008465dSJamin Lin     memset(s->regs, 0, aic->nr_regs << 2);
311d831c5fdSJamin Lin     memset(s->enable, 0, sizeof(s->enable));
312d831c5fdSJamin Lin     memset(s->mask, 0, sizeof(s->mask));
313d831c5fdSJamin Lin     memset(s->pending, 0, sizeof(s->pending));
314d831c5fdSJamin Lin }
315d831c5fdSJamin Lin 
316d831c5fdSJamin Lin static void aspeed_intc_realize(DeviceState *dev, Error **errp)
317d831c5fdSJamin Lin {
318d831c5fdSJamin Lin     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
319d831c5fdSJamin Lin     AspeedINTCState *s = ASPEED_INTC(dev);
320d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
321d831c5fdSJamin Lin     int i;
322d831c5fdSJamin Lin 
323c5728c34SJamin Lin     memory_region_init(&s->iomem_container, OBJECT(s),
324c5728c34SJamin Lin             TYPE_ASPEED_INTC ".container", aic->mem_size);
325c5728c34SJamin Lin 
326c5728c34SJamin Lin     sysbus_init_mmio(sbd, &s->iomem_container);
327c5728c34SJamin Lin 
328b008465dSJamin Lin     s->regs = g_new(uint32_t, aic->nr_regs);
329d831c5fdSJamin Lin     memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_intc_ops, s,
330b008465dSJamin Lin                           TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2);
331d831c5fdSJamin Lin 
3327ffee511SJamin Lin     memory_region_add_subregion(&s->iomem_container, aic->reg_offset,
3337ffee511SJamin Lin                                 &s->iomem);
334c5728c34SJamin Lin 
335d831c5fdSJamin Lin     qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_ints);
336d831c5fdSJamin Lin 
337d831c5fdSJamin Lin     for (i = 0; i < aic->num_ints; i++) {
338d831c5fdSJamin Lin         if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) {
339d831c5fdSJamin Lin             return;
340d831c5fdSJamin Lin         }
341d831c5fdSJamin Lin         sysbus_init_irq(sbd, &s->output_pins[i]);
342d831c5fdSJamin Lin     }
343d831c5fdSJamin Lin }
344d831c5fdSJamin Lin 
345563afea0SJamin Lin static void aspeed_intc_unrealize(DeviceState *dev)
346563afea0SJamin Lin {
347563afea0SJamin Lin     AspeedINTCState *s = ASPEED_INTC(dev);
348563afea0SJamin Lin 
349563afea0SJamin Lin     g_free(s->regs);
350563afea0SJamin Lin     s->regs = NULL;
351563afea0SJamin Lin }
352563afea0SJamin Lin 
353d831c5fdSJamin Lin static void aspeed_intc_class_init(ObjectClass *klass, void *data)
354d831c5fdSJamin Lin {
355d831c5fdSJamin Lin     DeviceClass *dc = DEVICE_CLASS(klass);
356d831c5fdSJamin Lin 
357d831c5fdSJamin Lin     dc->desc = "ASPEED INTC Controller";
358d831c5fdSJamin Lin     dc->realize = aspeed_intc_realize;
359563afea0SJamin Lin     dc->unrealize = aspeed_intc_unrealize;
360e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, aspeed_intc_reset);
361d831c5fdSJamin Lin     dc->vmsd = NULL;
362d831c5fdSJamin Lin }
363d831c5fdSJamin Lin 
364d831c5fdSJamin Lin static const TypeInfo aspeed_intc_info = {
365d831c5fdSJamin Lin     .name = TYPE_ASPEED_INTC,
366d831c5fdSJamin Lin     .parent = TYPE_SYS_BUS_DEVICE,
367d831c5fdSJamin Lin     .instance_init = aspeed_intc_instance_init,
368d831c5fdSJamin Lin     .instance_size = sizeof(AspeedINTCState),
369d831c5fdSJamin Lin     .class_init = aspeed_intc_class_init,
370d831c5fdSJamin Lin     .class_size = sizeof(AspeedINTCClass),
371d831c5fdSJamin Lin     .abstract = true,
372d831c5fdSJamin Lin };
373d831c5fdSJamin Lin 
374d831c5fdSJamin Lin static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data)
375d831c5fdSJamin Lin {
376d831c5fdSJamin Lin     DeviceClass *dc = DEVICE_CLASS(klass);
377d831c5fdSJamin Lin     AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
378d831c5fdSJamin Lin 
379d831c5fdSJamin Lin     dc->desc = "ASPEED 2700 INTC Controller";
380d831c5fdSJamin Lin     aic->num_lines = 32;
381d831c5fdSJamin Lin     aic->num_ints = 9;
382c5728c34SJamin Lin     aic->mem_size = 0x4000;
3837ffee511SJamin Lin     aic->nr_regs = 0x808 >> 2;
3847ffee511SJamin Lin     aic->reg_offset = 0x1000;
385d831c5fdSJamin Lin }
386d831c5fdSJamin Lin 
387d831c5fdSJamin Lin static const TypeInfo aspeed_2700_intc_info = {
388d831c5fdSJamin Lin     .name = TYPE_ASPEED_2700_INTC,
389d831c5fdSJamin Lin     .parent = TYPE_ASPEED_INTC,
390d831c5fdSJamin Lin     .class_init = aspeed_2700_intc_class_init,
391d831c5fdSJamin Lin };
392d831c5fdSJamin Lin 
393d831c5fdSJamin Lin static void aspeed_intc_register_types(void)
394d831c5fdSJamin Lin {
395d831c5fdSJamin Lin     type_register_static(&aspeed_intc_info);
396d831c5fdSJamin Lin     type_register_static(&aspeed_2700_intc_info);
397d831c5fdSJamin Lin }
398d831c5fdSJamin Lin 
399d831c5fdSJamin Lin type_init(aspeed_intc_register_types);
400