xref: /openbmc/qemu/hw/intc/aspeed_intc.c (revision ab24c6a2df8e6c8055b6f1dfe80697320b327c50)
1 /*
2  * ASPEED INTC Controller
3  *
4  * Copyright (C) 2024 ASPEED Technology Inc.
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "qemu/osdep.h"
10 #include "hw/intc/aspeed_intc.h"
11 #include "hw/irq.h"
12 #include "qemu/log.h"
13 #include "trace.h"
14 #include "hw/registerfields.h"
15 #include "qapi/error.h"
16 
17 /*
18  * INTC Registers
19  *
20  * values below are offset by - 0x1000 from datasheet
21  * because its memory region is start at 0x1000
22  *
23  */
24 REG32(GICINT128_EN,         0x000)
25 REG32(GICINT128_STATUS,     0x004)
26 REG32(GICINT129_EN,         0x100)
27 REG32(GICINT129_STATUS,     0x104)
28 REG32(GICINT130_EN,         0x200)
29 REG32(GICINT130_STATUS,     0x204)
30 REG32(GICINT131_EN,         0x300)
31 REG32(GICINT131_STATUS,     0x304)
32 REG32(GICINT132_EN,         0x400)
33 REG32(GICINT132_STATUS,     0x404)
34 REG32(GICINT133_EN,         0x500)
35 REG32(GICINT133_STATUS,     0x504)
36 REG32(GICINT134_EN,         0x600)
37 REG32(GICINT134_STATUS,     0x604)
38 REG32(GICINT135_EN,         0x700)
39 REG32(GICINT135_STATUS,     0x704)
40 REG32(GICINT136_EN,         0x800)
41 REG32(GICINT136_STATUS,     0x804)
42 
43 static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic,
44                                                 uint32_t reg)
45 {
46     int i;
47 
48     for (i = 0; i < aic->irq_table_count; i++) {
49         if (aic->irq_table[i].enable_reg == reg ||
50             aic->irq_table[i].status_reg == reg) {
51             return &aic->irq_table[i];
52         }
53     }
54 
55     /*
56      * Invalid reg.
57      */
58     g_assert_not_reached();
59 }
60 
61 /*
62  * Update the state of an interrupt controller pin by setting
63  * the specified output pin to the given level.
64  * The input pin index should be between 0 and the number of input pins.
65  * The output pin index should be between 0 and the number of output pins.
66  */
67 static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx,
68                                int outpin_idx, int level)
69 {
70     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
71     const char *name = object_get_typename(OBJECT(s));
72 
73     assert((outpin_idx < aic->num_outpins) && (inpin_idx < aic->num_inpins));
74 
75     trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level);
76     qemu_set_irq(s->output_pins[outpin_idx], level);
77 }
78 
79 /*
80  * The address of GICINT128 to GICINT136 are from 0x1000 to 0x1804.
81  * Utilize "address & 0x0f00" to get the irq and irq output pin index
82  * The value of irq should be 0 to num_inpins.
83  * The irq 0 indicates GICINT128, irq 1 indicates GICINT129 and so on.
84  */
85 static void aspeed_intc_set_irq(void *opaque, int irq, int level)
86 {
87     AspeedINTCState *s = (AspeedINTCState *)opaque;
88     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
89     const char *name = object_get_typename(OBJECT(s));
90     const AspeedINTCIRQ *intc_irq;
91     uint32_t status_reg;
92     uint32_t select = 0;
93     uint32_t enable;
94     int outpin_idx;
95     int inpin_idx;
96     int i;
97 
98     assert(irq < aic->num_inpins);
99 
100     intc_irq = &aic->irq_table[irq];
101     status_reg = intc_irq->status_reg;
102     outpin_idx = intc_irq->outpin_idx;
103     inpin_idx = intc_irq->inpin_idx;
104 
105     trace_aspeed_intc_set_irq(name, inpin_idx, level);
106     enable = s->enable[inpin_idx];
107 
108     if (!level) {
109         return;
110     }
111 
112     for (i = 0; i < aic->num_lines; i++) {
113         if (s->orgates[inpin_idx].levels[i]) {
114             if (enable & BIT(i)) {
115                 select |= BIT(i);
116             }
117         }
118     }
119 
120     if (!select) {
121         return;
122     }
123 
124     trace_aspeed_intc_select(name, select);
125 
126     if (s->mask[inpin_idx] || s->regs[status_reg]) {
127         /*
128          * a. mask is not 0 means in ISR mode
129          * sources interrupt routine are executing.
130          * b. status register value is not 0 means previous
131          * source interrupt does not be executed, yet.
132          *
133          * save source interrupt to pending variable.
134          */
135         s->pending[inpin_idx] |= select;
136         trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]);
137     } else {
138         /*
139          * notify firmware which source interrupt are coming
140          * by setting status register
141          */
142         s->regs[status_reg] = select;
143         trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx,
144                                       s->regs[status_reg]);
145         aspeed_intc_update(s, inpin_idx, outpin_idx, 1);
146     }
147 }
148 
149 static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset,
150                                        uint64_t data)
151 {
152     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
153     const char *name = object_get_typename(OBJECT(s));
154     const AspeedINTCIRQ *intc_irq;
155     uint32_t reg = offset >> 2;
156     uint32_t old_enable;
157     uint32_t change;
158     int inpin_idx;
159 
160     intc_irq = aspeed_intc_get_irq(aic, reg);
161     inpin_idx = intc_irq->inpin_idx;
162 
163     assert(inpin_idx < aic->num_inpins);
164 
165     /*
166      * The enable registers are used to enable source interrupts.
167      * They also handle masking and unmasking of source interrupts
168      * during the execution of the source ISR.
169      */
170 
171     /* disable all source interrupt */
172     if (!data && !s->enable[inpin_idx]) {
173         s->regs[reg] = data;
174         return;
175     }
176 
177     old_enable = s->enable[inpin_idx];
178     s->enable[inpin_idx] |= data;
179 
180     /* enable new source interrupt */
181     if (old_enable != s->enable[inpin_idx]) {
182         trace_aspeed_intc_enable(name, s->enable[inpin_idx]);
183         s->regs[reg] = data;
184         return;
185     }
186 
187     /* mask and unmask source interrupt */
188     change = s->regs[reg] ^ data;
189     if (change & data) {
190         s->mask[inpin_idx] &= ~change;
191         trace_aspeed_intc_unmask(name, change, s->mask[inpin_idx]);
192     } else {
193         s->mask[inpin_idx] |= change;
194         trace_aspeed_intc_mask(name, change, s->mask[inpin_idx]);
195     }
196 
197     s->regs[reg] = data;
198 }
199 
200 static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset,
201                                        uint64_t data)
202 {
203     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
204     const char *name = object_get_typename(OBJECT(s));
205     const AspeedINTCIRQ *intc_irq;
206     uint32_t reg = offset >> 2;
207     int outpin_idx;
208     int inpin_idx;
209 
210     if (!data) {
211         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__);
212         return;
213     }
214 
215     intc_irq = aspeed_intc_get_irq(aic, reg);
216     outpin_idx = intc_irq->outpin_idx;
217     inpin_idx = intc_irq->inpin_idx;
218 
219     assert(inpin_idx < aic->num_inpins);
220 
221     /* clear status */
222     s->regs[reg] &= ~data;
223 
224     /*
225      * These status registers are used for notify sources ISR are executed.
226      * If one source ISR is executed, it will clear one bit.
227      * If it clear all bits, it means to initialize this register status
228      * rather than sources ISR are executed.
229      */
230     if (data == 0xffffffff) {
231         return;
232     }
233 
234     /* All source ISR execution are done */
235     if (!s->regs[reg]) {
236         trace_aspeed_intc_all_isr_done(name, inpin_idx);
237         if (s->pending[inpin_idx]) {
238             /*
239              * handle pending source interrupt
240              * notify firmware which source interrupt are pending
241              * by setting status register
242              */
243             s->regs[reg] = s->pending[inpin_idx];
244             s->pending[inpin_idx] = 0;
245             trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx,
246                                           s->regs[reg]);
247             aspeed_intc_update(s, inpin_idx, outpin_idx, 1);
248         } else {
249             /* clear irq */
250             trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx, 0);
251             aspeed_intc_update(s, inpin_idx, outpin_idx, 0);
252         }
253     }
254 }
255 
256 static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size)
257 {
258     AspeedINTCState *s = ASPEED_INTC(opaque);
259     const char *name = object_get_typename(OBJECT(s));
260     uint32_t reg = offset >> 2;
261     uint32_t value = 0;
262 
263     value = s->regs[reg];
264     trace_aspeed_intc_read(name, offset, size, value);
265 
266     return value;
267 }
268 
269 static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data,
270                                         unsigned size)
271 {
272     AspeedINTCState *s = ASPEED_INTC(opaque);
273     const char *name = object_get_typename(OBJECT(s));
274     uint32_t reg = offset >> 2;
275 
276     trace_aspeed_intc_write(name, offset, size, data);
277 
278     switch (reg) {
279     case R_GICINT128_EN:
280     case R_GICINT129_EN:
281     case R_GICINT130_EN:
282     case R_GICINT131_EN:
283     case R_GICINT132_EN:
284     case R_GICINT133_EN:
285     case R_GICINT134_EN:
286     case R_GICINT135_EN:
287     case R_GICINT136_EN:
288         aspeed_intc_enable_handler(s, offset, data);
289         break;
290     case R_GICINT128_STATUS:
291     case R_GICINT129_STATUS:
292     case R_GICINT130_STATUS:
293     case R_GICINT131_STATUS:
294     case R_GICINT132_STATUS:
295     case R_GICINT133_STATUS:
296     case R_GICINT134_STATUS:
297     case R_GICINT135_STATUS:
298     case R_GICINT136_STATUS:
299         aspeed_intc_status_handler(s, offset, data);
300         break;
301     default:
302         s->regs[reg] = data;
303         break;
304     }
305 
306     return;
307 }
308 
309 static const MemoryRegionOps aspeed_intc_ops = {
310     .read = aspeed_intc_read,
311     .write = aspeed_intc_write,
312     .endianness = DEVICE_LITTLE_ENDIAN,
313     .valid = {
314         .min_access_size = 4,
315         .max_access_size = 4,
316     }
317 };
318 
319 static void aspeed_intc_instance_init(Object *obj)
320 {
321     AspeedINTCState *s = ASPEED_INTC(obj);
322     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
323     int i;
324 
325     assert(aic->num_inpins <= ASPEED_INTC_MAX_INPINS);
326     for (i = 0; i < aic->num_inpins; i++) {
327         object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i],
328                                 TYPE_OR_IRQ);
329         object_property_set_int(OBJECT(&s->orgates[i]), "num-lines",
330                                 aic->num_lines, &error_abort);
331     }
332 }
333 
334 static void aspeed_intc_reset(DeviceState *dev)
335 {
336     AspeedINTCState *s = ASPEED_INTC(dev);
337     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
338 
339     memset(s->regs, 0, aic->nr_regs << 2);
340     memset(s->enable, 0, sizeof(s->enable));
341     memset(s->mask, 0, sizeof(s->mask));
342     memset(s->pending, 0, sizeof(s->pending));
343 }
344 
345 static void aspeed_intc_realize(DeviceState *dev, Error **errp)
346 {
347     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
348     AspeedINTCState *s = ASPEED_INTC(dev);
349     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
350     int i;
351 
352     memory_region_init(&s->iomem_container, OBJECT(s),
353             TYPE_ASPEED_INTC ".container", aic->mem_size);
354 
355     sysbus_init_mmio(sbd, &s->iomem_container);
356 
357     s->regs = g_new(uint32_t, aic->nr_regs);
358     memory_region_init_io(&s->iomem, OBJECT(s), aic->reg_ops, s,
359                           TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2);
360 
361     memory_region_add_subregion(&s->iomem_container, aic->reg_offset,
362                                 &s->iomem);
363 
364     qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_inpins);
365 
366     for (i = 0; i < aic->num_inpins; i++) {
367         if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) {
368             return;
369         }
370     }
371 
372     for (i = 0; i < aic->num_outpins; i++) {
373         sysbus_init_irq(sbd, &s->output_pins[i]);
374     }
375 }
376 
377 static void aspeed_intc_unrealize(DeviceState *dev)
378 {
379     AspeedINTCState *s = ASPEED_INTC(dev);
380 
381     g_free(s->regs);
382     s->regs = NULL;
383 }
384 
385 static void aspeed_intc_class_init(ObjectClass *klass, void *data)
386 {
387     DeviceClass *dc = DEVICE_CLASS(klass);
388     AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
389 
390     dc->desc = "ASPEED INTC Controller";
391     dc->realize = aspeed_intc_realize;
392     dc->unrealize = aspeed_intc_unrealize;
393     device_class_set_legacy_reset(dc, aspeed_intc_reset);
394     dc->vmsd = NULL;
395 
396     aic->reg_ops = &aspeed_intc_ops;
397 }
398 
399 static const TypeInfo aspeed_intc_info = {
400     .name = TYPE_ASPEED_INTC,
401     .parent = TYPE_SYS_BUS_DEVICE,
402     .instance_init = aspeed_intc_instance_init,
403     .instance_size = sizeof(AspeedINTCState),
404     .class_init = aspeed_intc_class_init,
405     .class_size = sizeof(AspeedINTCClass),
406     .abstract = true,
407 };
408 
409 static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = {
410     {0, 0, 1, R_GICINT128_EN, R_GICINT128_STATUS},
411     {1, 1, 1, R_GICINT129_EN, R_GICINT129_STATUS},
412     {2, 2, 1, R_GICINT130_EN, R_GICINT130_STATUS},
413     {3, 3, 1, R_GICINT131_EN, R_GICINT131_STATUS},
414     {4, 4, 1, R_GICINT132_EN, R_GICINT132_STATUS},
415     {5, 5, 1, R_GICINT133_EN, R_GICINT133_STATUS},
416     {6, 6, 1, R_GICINT134_EN, R_GICINT134_STATUS},
417     {7, 7, 1, R_GICINT135_EN, R_GICINT135_STATUS},
418     {8, 8, 1, R_GICINT136_EN, R_GICINT136_STATUS},
419 };
420 
421 static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data)
422 {
423     DeviceClass *dc = DEVICE_CLASS(klass);
424     AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
425 
426     dc->desc = "ASPEED 2700 INTC Controller";
427     aic->num_lines = 32;
428     aic->num_inpins = 9;
429     aic->num_outpins = 9;
430     aic->mem_size = 0x4000;
431     aic->nr_regs = 0x808 >> 2;
432     aic->reg_offset = 0x1000;
433     aic->irq_table = aspeed_2700_intc_irqs;
434     aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs);
435 }
436 
437 static const TypeInfo aspeed_2700_intc_info = {
438     .name = TYPE_ASPEED_2700_INTC,
439     .parent = TYPE_ASPEED_INTC,
440     .class_init = aspeed_2700_intc_class_init,
441 };
442 
443 static void aspeed_intc_register_types(void)
444 {
445     type_register_static(&aspeed_intc_info);
446     type_register_static(&aspeed_2700_intc_info);
447 }
448 
449 type_init(aspeed_intc_register_types);
450