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