xref: /openbmc/qemu/hw/intc/aspeed_intc.c (revision 9178ff91f3105d25fef8e595014fbdfba7d9e278)
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 REG32(GICINT192_201_EN,         0xB00)
43 REG32(GICINT192_201_STATUS,     0xB04)
44 
45 static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic,
46                                                 uint32_t reg)
47 {
48     int i;
49 
50     for (i = 0; i < aic->irq_table_count; i++) {
51         if (aic->irq_table[i].enable_reg == reg ||
52             aic->irq_table[i].status_reg == reg) {
53             return &aic->irq_table[i];
54         }
55     }
56 
57     /*
58      * Invalid reg.
59      */
60     g_assert_not_reached();
61 }
62 
63 /*
64  * Update the state of an interrupt controller pin by setting
65  * the specified output pin to the given level.
66  * The input pin index should be between 0 and the number of input pins.
67  * The output pin index should be between 0 and the number of output pins.
68  */
69 static void aspeed_intc_update(AspeedINTCState *s, int inpin_idx,
70                                int outpin_idx, int level)
71 {
72     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
73     const char *name = object_get_typename(OBJECT(s));
74 
75     assert((outpin_idx < aic->num_outpins) && (inpin_idx < aic->num_inpins));
76 
77     trace_aspeed_intc_update_irq(name, inpin_idx, outpin_idx, level);
78     qemu_set_irq(s->output_pins[outpin_idx], level);
79 }
80 
81 static void aspeed_intc_set_irq_handler(AspeedINTCState *s,
82                                         const AspeedINTCIRQ *intc_irq,
83                                         uint32_t select)
84 {
85     const char *name = object_get_typename(OBJECT(s));
86     uint32_t status_reg;
87     int outpin_idx;
88     int inpin_idx;
89 
90     status_reg = intc_irq->status_reg;
91     outpin_idx = intc_irq->outpin_idx;
92     inpin_idx = intc_irq->inpin_idx;
93 
94     if (s->mask[inpin_idx] || s->regs[status_reg]) {
95         /*
96          * a. mask is not 0 means in ISR mode
97          * sources interrupt routine are executing.
98          * b. status register value is not 0 means previous
99          * source interrupt does not be executed, yet.
100          *
101          * save source interrupt to pending variable.
102          */
103         s->pending[inpin_idx] |= select;
104         trace_aspeed_intc_pending_irq(name, inpin_idx, s->pending[inpin_idx]);
105     } else {
106         /*
107          * notify firmware which source interrupt are coming
108          * by setting status register
109          */
110         s->regs[status_reg] = select;
111         trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx,
112                                       s->regs[status_reg]);
113         aspeed_intc_update(s, inpin_idx, outpin_idx, 1);
114     }
115 }
116 
117 static void aspeed_intc_set_irq_handler_multi_outpins(AspeedINTCState *s,
118                                  const AspeedINTCIRQ *intc_irq, uint32_t select)
119 {
120     const char *name = object_get_typename(OBJECT(s));
121     uint32_t status_reg;
122     int num_outpins;
123     int outpin_idx;
124     int inpin_idx;
125     int i;
126 
127     num_outpins = intc_irq->num_outpins;
128     status_reg = intc_irq->status_reg;
129     outpin_idx = intc_irq->outpin_idx;
130     inpin_idx = intc_irq->inpin_idx;
131 
132     for (i = 0; i < num_outpins; i++) {
133         if (select & BIT(i)) {
134             if (s->mask[inpin_idx] & BIT(i) ||
135                 s->regs[status_reg] & BIT(i)) {
136                 /*
137                  * a. mask bit is not 0 means in ISR mode sources interrupt
138                  * routine are executing.
139                  * b. status bit is not 0 means previous source interrupt
140                  * does not be executed, yet.
141                  *
142                  * save source interrupt to pending bit.
143                  */
144                  s->pending[inpin_idx] |= BIT(i);
145                  trace_aspeed_intc_pending_irq(name, inpin_idx,
146                                                s->pending[inpin_idx]);
147             } else {
148                 /*
149                  * notify firmware which source interrupt are coming
150                  * by setting status bit
151                  */
152                 s->regs[status_reg] |= BIT(i);
153                 trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i,
154                                               s->regs[status_reg]);
155                 aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1);
156             }
157         }
158     }
159 }
160 
161 /*
162  * GICINT192_201 maps 1:10 to input IRQ 0 and output IRQs 0 to 9.
163  * GICINT128 to GICINT136 map 1:1 to input IRQs 1 to 9 and output
164  * IRQs 10 to 18. The value of input IRQ should be between 0 and
165  * the number of input pins.
166  */
167 static void aspeed_intc_set_irq(void *opaque, int irq, int level)
168 {
169     AspeedINTCState *s = (AspeedINTCState *)opaque;
170     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
171     const char *name = object_get_typename(OBJECT(s));
172     const AspeedINTCIRQ *intc_irq;
173     uint32_t select = 0;
174     uint32_t enable;
175     int num_outpins;
176     int inpin_idx;
177     int i;
178 
179     assert(irq < aic->num_inpins);
180 
181     intc_irq = &aic->irq_table[irq];
182     num_outpins = intc_irq->num_outpins;
183     inpin_idx = intc_irq->inpin_idx;
184     trace_aspeed_intc_set_irq(name, inpin_idx, level);
185     enable = s->enable[inpin_idx];
186 
187     if (!level) {
188         return;
189     }
190 
191     for (i = 0; i < aic->num_lines; i++) {
192         if (s->orgates[inpin_idx].levels[i]) {
193             if (enable & BIT(i)) {
194                 select |= BIT(i);
195             }
196         }
197     }
198 
199     if (!select) {
200         return;
201     }
202 
203     trace_aspeed_intc_select(name, select);
204     if (num_outpins > 1) {
205         aspeed_intc_set_irq_handler_multi_outpins(s, intc_irq, select);
206     } else {
207         aspeed_intc_set_irq_handler(s, intc_irq, select);
208     }
209 }
210 
211 static void aspeed_intc_enable_handler(AspeedINTCState *s, hwaddr offset,
212                                        uint64_t data)
213 {
214     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
215     const char *name = object_get_typename(OBJECT(s));
216     const AspeedINTCIRQ *intc_irq;
217     uint32_t reg = offset >> 2;
218     uint32_t old_enable;
219     uint32_t change;
220     int inpin_idx;
221 
222     intc_irq = aspeed_intc_get_irq(aic, reg);
223     inpin_idx = intc_irq->inpin_idx;
224 
225     assert(inpin_idx < aic->num_inpins);
226 
227     /*
228      * The enable registers are used to enable source interrupts.
229      * They also handle masking and unmasking of source interrupts
230      * during the execution of the source ISR.
231      */
232 
233     /* disable all source interrupt */
234     if (!data && !s->enable[inpin_idx]) {
235         s->regs[reg] = data;
236         return;
237     }
238 
239     old_enable = s->enable[inpin_idx];
240     s->enable[inpin_idx] |= data;
241 
242     /* enable new source interrupt */
243     if (old_enable != s->enable[inpin_idx]) {
244         trace_aspeed_intc_enable(name, s->enable[inpin_idx]);
245         s->regs[reg] = data;
246         return;
247     }
248 
249     /* mask and unmask source interrupt */
250     change = s->regs[reg] ^ data;
251     if (change & data) {
252         s->mask[inpin_idx] &= ~change;
253         trace_aspeed_intc_unmask(name, change, s->mask[inpin_idx]);
254     } else {
255         s->mask[inpin_idx] |= change;
256         trace_aspeed_intc_mask(name, change, s->mask[inpin_idx]);
257     }
258 
259     s->regs[reg] = data;
260 }
261 
262 static void aspeed_intc_status_handler(AspeedINTCState *s, hwaddr offset,
263                                        uint64_t data)
264 {
265     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
266     const char *name = object_get_typename(OBJECT(s));
267     const AspeedINTCIRQ *intc_irq;
268     uint32_t reg = offset >> 2;
269     int outpin_idx;
270     int inpin_idx;
271 
272     if (!data) {
273         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__);
274         return;
275     }
276 
277     intc_irq = aspeed_intc_get_irq(aic, reg);
278     outpin_idx = intc_irq->outpin_idx;
279     inpin_idx = intc_irq->inpin_idx;
280 
281     assert(inpin_idx < aic->num_inpins);
282 
283     /* clear status */
284     s->regs[reg] &= ~data;
285 
286     /*
287      * These status registers are used for notify sources ISR are executed.
288      * If one source ISR is executed, it will clear one bit.
289      * If it clear all bits, it means to initialize this register status
290      * rather than sources ISR are executed.
291      */
292     if (data == 0xffffffff) {
293         return;
294     }
295 
296     /* All source ISR execution are done */
297     if (!s->regs[reg]) {
298         trace_aspeed_intc_all_isr_done(name, inpin_idx);
299         if (s->pending[inpin_idx]) {
300             /*
301              * handle pending source interrupt
302              * notify firmware which source interrupt are pending
303              * by setting status register
304              */
305             s->regs[reg] = s->pending[inpin_idx];
306             s->pending[inpin_idx] = 0;
307             trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx,
308                                           s->regs[reg]);
309             aspeed_intc_update(s, inpin_idx, outpin_idx, 1);
310         } else {
311             /* clear irq */
312             trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx, 0);
313             aspeed_intc_update(s, inpin_idx, outpin_idx, 0);
314         }
315     }
316 }
317 
318 static void aspeed_intc_status_handler_multi_outpins(AspeedINTCState *s,
319                                                 hwaddr offset, uint64_t data)
320 {
321     const char *name = object_get_typename(OBJECT(s));
322     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
323     const AspeedINTCIRQ *intc_irq;
324     uint32_t reg = offset >> 2;
325     int num_outpins;
326     int outpin_idx;
327     int inpin_idx;
328     int i;
329 
330     if (!data) {
331         qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid data 0\n", __func__);
332         return;
333     }
334 
335     intc_irq = aspeed_intc_get_irq(aic, reg);
336     num_outpins = intc_irq->num_outpins;
337     outpin_idx = intc_irq->outpin_idx;
338     inpin_idx = intc_irq->inpin_idx;
339     assert(inpin_idx < aic->num_inpins);
340 
341     /* clear status */
342     s->regs[reg] &= ~data;
343 
344     /*
345      * The status registers are used for notify sources ISR are executed.
346      * If one source ISR is executed, it will clear one bit.
347      * If it clear all bits, it means to initialize this register status
348      * rather than sources ISR are executed.
349      */
350     if (data == 0xffffffff) {
351         return;
352     }
353 
354     for (i = 0; i < num_outpins; i++) {
355         /* All source ISR executions are done from a specific bit */
356         if (data & BIT(i)) {
357             trace_aspeed_intc_all_isr_done_bit(name, inpin_idx, i);
358             if (s->pending[inpin_idx] & BIT(i)) {
359                 /*
360                  * Handle pending source interrupt.
361                  * Notify firmware which source interrupt is pending
362                  * by setting the status bit.
363                  */
364                 s->regs[reg] |= BIT(i);
365                 s->pending[inpin_idx] &= ~BIT(i);
366                 trace_aspeed_intc_trigger_irq(name, inpin_idx, outpin_idx + i,
367                                               s->regs[reg]);
368                 aspeed_intc_update(s, inpin_idx, outpin_idx + i, 1);
369             } else {
370                 /* clear irq for the specific bit */
371                 trace_aspeed_intc_clear_irq(name, inpin_idx, outpin_idx + i, 0);
372                 aspeed_intc_update(s, inpin_idx, outpin_idx + i, 0);
373             }
374         }
375     }
376 }
377 
378 static uint64_t aspeed_intc_read(void *opaque, hwaddr offset, unsigned int size)
379 {
380     AspeedINTCState *s = ASPEED_INTC(opaque);
381     const char *name = object_get_typename(OBJECT(s));
382     uint32_t reg = offset >> 2;
383     uint32_t value = 0;
384 
385     value = s->regs[reg];
386     trace_aspeed_intc_read(name, offset, size, value);
387 
388     return value;
389 }
390 
391 static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data,
392                                         unsigned size)
393 {
394     AspeedINTCState *s = ASPEED_INTC(opaque);
395     const char *name = object_get_typename(OBJECT(s));
396     uint32_t reg = offset >> 2;
397 
398     trace_aspeed_intc_write(name, offset, size, data);
399 
400     switch (reg) {
401     case R_GICINT128_EN:
402     case R_GICINT129_EN:
403     case R_GICINT130_EN:
404     case R_GICINT131_EN:
405     case R_GICINT132_EN:
406     case R_GICINT133_EN:
407     case R_GICINT134_EN:
408     case R_GICINT135_EN:
409     case R_GICINT136_EN:
410     case R_GICINT192_201_EN:
411         aspeed_intc_enable_handler(s, offset, data);
412         break;
413     case R_GICINT128_STATUS:
414     case R_GICINT129_STATUS:
415     case R_GICINT130_STATUS:
416     case R_GICINT131_STATUS:
417     case R_GICINT132_STATUS:
418     case R_GICINT133_STATUS:
419     case R_GICINT134_STATUS:
420     case R_GICINT135_STATUS:
421     case R_GICINT136_STATUS:
422         aspeed_intc_status_handler(s, offset, data);
423         break;
424     case R_GICINT192_201_STATUS:
425         aspeed_intc_status_handler_multi_outpins(s, offset, data);
426         break;
427     default:
428         s->regs[reg] = data;
429         break;
430     }
431 
432     return;
433 }
434 
435 static const MemoryRegionOps aspeed_intc_ops = {
436     .read = aspeed_intc_read,
437     .write = aspeed_intc_write,
438     .endianness = DEVICE_LITTLE_ENDIAN,
439     .valid = {
440         .min_access_size = 4,
441         .max_access_size = 4,
442     }
443 };
444 
445 static void aspeed_intc_instance_init(Object *obj)
446 {
447     AspeedINTCState *s = ASPEED_INTC(obj);
448     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
449     int i;
450 
451     assert(aic->num_inpins <= ASPEED_INTC_MAX_INPINS);
452     for (i = 0; i < aic->num_inpins; i++) {
453         object_initialize_child(obj, "intc-orgates[*]", &s->orgates[i],
454                                 TYPE_OR_IRQ);
455         object_property_set_int(OBJECT(&s->orgates[i]), "num-lines",
456                                 aic->num_lines, &error_abort);
457     }
458 }
459 
460 static void aspeed_intc_reset(DeviceState *dev)
461 {
462     AspeedINTCState *s = ASPEED_INTC(dev);
463     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
464 
465     memset(s->regs, 0, aic->nr_regs << 2);
466     memset(s->enable, 0, sizeof(s->enable));
467     memset(s->mask, 0, sizeof(s->mask));
468     memset(s->pending, 0, sizeof(s->pending));
469 }
470 
471 static void aspeed_intc_realize(DeviceState *dev, Error **errp)
472 {
473     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
474     AspeedINTCState *s = ASPEED_INTC(dev);
475     AspeedINTCClass *aic = ASPEED_INTC_GET_CLASS(s);
476     int i;
477 
478     memory_region_init(&s->iomem_container, OBJECT(s),
479             TYPE_ASPEED_INTC ".container", aic->mem_size);
480 
481     sysbus_init_mmio(sbd, &s->iomem_container);
482 
483     s->regs = g_new(uint32_t, aic->nr_regs);
484     memory_region_init_io(&s->iomem, OBJECT(s), aic->reg_ops, s,
485                           TYPE_ASPEED_INTC ".regs", aic->nr_regs << 2);
486 
487     memory_region_add_subregion(&s->iomem_container, aic->reg_offset,
488                                 &s->iomem);
489 
490     qdev_init_gpio_in(dev, aspeed_intc_set_irq, aic->num_inpins);
491 
492     for (i = 0; i < aic->num_inpins; i++) {
493         if (!qdev_realize(DEVICE(&s->orgates[i]), NULL, errp)) {
494             return;
495         }
496     }
497 
498     for (i = 0; i < aic->num_outpins; i++) {
499         sysbus_init_irq(sbd, &s->output_pins[i]);
500     }
501 }
502 
503 static void aspeed_intc_unrealize(DeviceState *dev)
504 {
505     AspeedINTCState *s = ASPEED_INTC(dev);
506 
507     g_free(s->regs);
508     s->regs = NULL;
509 }
510 
511 static void aspeed_intc_class_init(ObjectClass *klass, void *data)
512 {
513     DeviceClass *dc = DEVICE_CLASS(klass);
514     AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
515 
516     dc->desc = "ASPEED INTC Controller";
517     dc->realize = aspeed_intc_realize;
518     dc->unrealize = aspeed_intc_unrealize;
519     device_class_set_legacy_reset(dc, aspeed_intc_reset);
520     dc->vmsd = NULL;
521 
522     aic->reg_ops = &aspeed_intc_ops;
523 }
524 
525 static const TypeInfo aspeed_intc_info = {
526     .name = TYPE_ASPEED_INTC,
527     .parent = TYPE_SYS_BUS_DEVICE,
528     .instance_init = aspeed_intc_instance_init,
529     .instance_size = sizeof(AspeedINTCState),
530     .class_init = aspeed_intc_class_init,
531     .class_size = sizeof(AspeedINTCClass),
532     .abstract = true,
533 };
534 
535 static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = {
536     {0, 0, 10, R_GICINT192_201_EN, R_GICINT192_201_STATUS},
537     {1, 10, 1, R_GICINT128_EN, R_GICINT128_STATUS},
538     {2, 11, 1, R_GICINT129_EN, R_GICINT129_STATUS},
539     {3, 12, 1, R_GICINT130_EN, R_GICINT130_STATUS},
540     {4, 13, 1, R_GICINT131_EN, R_GICINT131_STATUS},
541     {5, 14, 1, R_GICINT132_EN, R_GICINT132_STATUS},
542     {6, 15, 1, R_GICINT133_EN, R_GICINT133_STATUS},
543     {7, 16, 1, R_GICINT134_EN, R_GICINT134_STATUS},
544     {8, 17, 1, R_GICINT135_EN, R_GICINT135_STATUS},
545     {9, 18, 1, R_GICINT136_EN, R_GICINT136_STATUS},
546 };
547 
548 static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data)
549 {
550     DeviceClass *dc = DEVICE_CLASS(klass);
551     AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass);
552 
553     dc->desc = "ASPEED 2700 INTC Controller";
554     aic->num_lines = 32;
555     aic->num_inpins = 10;
556     aic->num_outpins = 19;
557     aic->mem_size = 0x4000;
558     aic->nr_regs = 0xB08 >> 2;
559     aic->reg_offset = 0x1000;
560     aic->irq_table = aspeed_2700_intc_irqs;
561     aic->irq_table_count = ARRAY_SIZE(aspeed_2700_intc_irqs);
562 }
563 
564 static const TypeInfo aspeed_2700_intc_info = {
565     .name = TYPE_ASPEED_2700_INTC,
566     .parent = TYPE_ASPEED_INTC,
567     .class_init = aspeed_2700_intc_class_init,
568 };
569 
570 static void aspeed_intc_register_types(void)
571 {
572     type_register_static(&aspeed_intc_info);
573     type_register_static(&aspeed_2700_intc_info);
574 }
575 
576 type_init(aspeed_intc_register_types);
577