xref: /qemu/hw/intc/grlib_irqmp.c (revision a0e93dd8)
1 /*
2  * QEMU GRLIB IRQMP Emulator
3  *
4  * (Extended interrupt not supported)
5  *
6  * SPDX-License-Identifier: MIT
7  *
8  * Copyright (c) 2010-2024 AdaCore
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26  * THE SOFTWARE.
27  */
28 
29 #include "qemu/osdep.h"
30 #include "hw/irq.h"
31 #include "hw/sysbus.h"
32 
33 #include "hw/qdev-properties.h"
34 #include "hw/intc/grlib_irqmp.h"
35 
36 #include "trace.h"
37 #include "qapi/error.h"
38 #include "qemu/module.h"
39 #include "qom/object.h"
40 
41 #define IRQMP_MAX_CPU 16
42 #define IRQMP_REG_SIZE 256      /* Size of memory mapped registers */
43 
44 /* Memory mapped register offsets */
45 #define LEVEL_OFFSET     0x00
46 #define PENDING_OFFSET   0x04
47 #define FORCE0_OFFSET    0x08
48 #define CLEAR_OFFSET     0x0C
49 #define MP_STATUS_OFFSET 0x10
50 #define BROADCAST_OFFSET 0x14
51 #define MASK_OFFSET      0x40
52 #define FORCE_OFFSET     0x80
53 #define EXTENDED_OFFSET  0xC0
54 
55 /* Multiprocessor Status Register  */
56 #define MP_STATUS_CPU_STATUS_MASK ((1 << IRQMP_MAX_CPU)-2)
57 #define MP_STATUS_NCPU_SHIFT      28
58 
59 #define MAX_PILS 16
60 
61 OBJECT_DECLARE_SIMPLE_TYPE(IRQMP, GRLIB_IRQMP)
62 
63 typedef struct IRQMPState IRQMPState;
64 
65 struct IRQMP {
66     SysBusDevice parent_obj;
67 
68     MemoryRegion iomem;
69 
70     unsigned int ncpus;
71     IRQMPState *state;
72     qemu_irq start_signal[IRQMP_MAX_CPU];
73     qemu_irq irq[IRQMP_MAX_CPU];
74 };
75 
76 struct IRQMPState {
77     uint32_t level;
78     uint32_t pending;
79     uint32_t clear;
80     uint32_t mpstatus;
81     uint32_t broadcast;
82 
83     uint32_t mask[IRQMP_MAX_CPU];
84     uint32_t force[IRQMP_MAX_CPU];
85     uint32_t extended[IRQMP_MAX_CPU];
86 
87     IRQMP    *parent;
88 };
89 
90 static void grlib_irqmp_check_irqs(IRQMPState *state)
91 {
92     int i;
93 
94     assert(state != NULL);
95     assert(state->parent != NULL);
96 
97     for (i = 0; i < state->parent->ncpus; i++) {
98         uint32_t pend = (state->pending | state->force[i]) & state->mask[i];
99         uint32_t level0 = pend & ~state->level;
100         uint32_t level1 = pend &  state->level;
101 
102         trace_grlib_irqmp_check_irqs(state->pending, state->force[i],
103                                      state->mask[i], level1, level0);
104 
105         /* Trigger level1 interrupt first and level0 if there is no level1 */
106         qemu_set_irq(state->parent->irq[i], level1 ?: level0);
107     }
108 }
109 
110 static void grlib_irqmp_ack_mask(IRQMPState *state, unsigned int cpu,
111                                  uint32_t mask)
112 {
113     /* Clear registers */
114     state->pending  &= ~mask;
115     state->force[cpu] &= ~mask;
116 
117     grlib_irqmp_check_irqs(state);
118 }
119 
120 void grlib_irqmp_ack(DeviceState *dev, unsigned int cpu, int intno)
121 {
122     IRQMP        *irqmp = GRLIB_IRQMP(dev);
123     IRQMPState   *state;
124     uint32_t      mask;
125 
126     state = irqmp->state;
127     assert(state != NULL);
128 
129     intno &= 15;
130     mask = 1 << intno;
131 
132     trace_grlib_irqmp_ack(intno);
133 
134     grlib_irqmp_ack_mask(state, cpu, mask);
135 }
136 
137 static void grlib_irqmp_set_irq(void *opaque, int irq, int level)
138 {
139     IRQMP      *irqmp = GRLIB_IRQMP(opaque);
140     IRQMPState *s;
141     int         i = 0;
142 
143     s = irqmp->state;
144     assert(s         != NULL);
145     assert(s->parent != NULL);
146 
147 
148     if (level) {
149         trace_grlib_irqmp_set_irq(irq);
150 
151         if (s->broadcast & 1 << irq) {
152             /* Broadcasted IRQ */
153             for (i = 0; i < IRQMP_MAX_CPU; i++) {
154                 s->force[i] |= 1 << irq;
155             }
156         } else {
157             s->pending |= 1 << irq;
158         }
159         grlib_irqmp_check_irqs(s);
160     }
161 }
162 
163 static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr,
164                                  unsigned size)
165 {
166     IRQMP      *irqmp = opaque;
167     IRQMPState *state;
168 
169     assert(irqmp != NULL);
170     state = irqmp->state;
171     assert(state != NULL);
172 
173     addr &= 0xff;
174 
175     /* global registers */
176     switch (addr) {
177     case LEVEL_OFFSET:
178         return state->level;
179 
180     case PENDING_OFFSET:
181         return state->pending;
182 
183     case FORCE0_OFFSET:
184         /* This register is an "alias" for the force register of CPU 0 */
185         return state->force[0];
186 
187     case CLEAR_OFFSET:
188         /* Always read as 0 */
189         return 0;
190 
191     case MP_STATUS_OFFSET:
192         return state->mpstatus;
193 
194     case BROADCAST_OFFSET:
195         return state->broadcast;
196 
197     default:
198         break;
199     }
200 
201     /* mask registers */
202     if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
203         int cpu = (addr - MASK_OFFSET) / 4;
204         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
205 
206         return state->mask[cpu];
207     }
208 
209     /* force registers */
210     if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
211         int cpu = (addr - FORCE_OFFSET) / 4;
212         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
213 
214         return state->force[cpu];
215     }
216 
217     /* extended (not supported) */
218     if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
219         int cpu = (addr - EXTENDED_OFFSET) / 4;
220         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
221 
222         return state->extended[cpu];
223     }
224 
225     trace_grlib_irqmp_readl_unknown(addr);
226     return 0;
227 }
228 
229 static void grlib_irqmp_write(void *opaque, hwaddr addr,
230                               uint64_t value, unsigned size)
231 {
232     IRQMP *irqmp = opaque;
233     IRQMPState *state;
234     int i;
235 
236     assert(irqmp != NULL);
237     state = irqmp->state;
238     assert(state != NULL);
239 
240     addr &= 0xff;
241 
242     /* global registers */
243     switch (addr) {
244     case LEVEL_OFFSET:
245         value &= 0xFFFF << 1; /* clean up the value */
246         state->level = value;
247         return;
248 
249     case PENDING_OFFSET:
250         /* Read Only */
251         return;
252 
253     case FORCE0_OFFSET:
254         /* This register is an "alias" for the force register of CPU 0 */
255 
256         value &= 0xFFFE; /* clean up the value */
257         state->force[0] = value;
258         grlib_irqmp_check_irqs(irqmp->state);
259         return;
260 
261     case CLEAR_OFFSET:
262         value &= ~1; /* clean up the value */
263         for (i = 0; i < irqmp->ncpus; i++) {
264             grlib_irqmp_ack_mask(state, i, value);
265         }
266         return;
267 
268     case MP_STATUS_OFFSET:
269         /*
270          * Writing and reading operations are reversed for the CPU status.
271          * Writing "1" will start the CPU, but reading "1" means that the CPU
272          * is power-down.
273          */
274         value &= MP_STATUS_CPU_STATUS_MASK;
275         for (i = 0; i < irqmp->ncpus; i++) {
276             if ((value >> i) & 1) {
277                 qemu_set_irq(irqmp->start_signal[i], 1);
278                 state->mpstatus &= ~(1 << i);
279             }
280         }
281         return;
282 
283     case BROADCAST_OFFSET:
284         value &= 0xFFFE; /* clean up the value */
285         state->broadcast = value;
286         return;
287 
288     default:
289         break;
290     }
291 
292     /* mask registers */
293     if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
294         int cpu = (addr - MASK_OFFSET) / 4;
295         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
296 
297         value &= ~1; /* clean up the value */
298         state->mask[cpu] = value;
299         grlib_irqmp_check_irqs(irqmp->state);
300         return;
301     }
302 
303     /* force registers */
304     if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
305         int cpu = (addr - FORCE_OFFSET) / 4;
306         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
307 
308         uint32_t force = value & 0xFFFE;
309         uint32_t clear = (value >> 16) & 0xFFFE;
310         uint32_t old   = state->force[cpu];
311 
312         state->force[cpu] = (old | force) & ~clear;
313         grlib_irqmp_check_irqs(irqmp->state);
314         return;
315     }
316 
317     /* extended (not supported) */
318     if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
319         int cpu = (addr - EXTENDED_OFFSET) / 4;
320         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
321 
322         value &= 0xF; /* clean up the value */
323         state->extended[cpu] = value;
324         return;
325     }
326 
327     trace_grlib_irqmp_writel_unknown(addr, value);
328 }
329 
330 static const MemoryRegionOps grlib_irqmp_ops = {
331     .read = grlib_irqmp_read,
332     .write = grlib_irqmp_write,
333     .endianness = DEVICE_NATIVE_ENDIAN,
334     .valid = {
335         .min_access_size = 4,
336         .max_access_size = 4,
337     },
338 };
339 
340 static void grlib_irqmp_reset(DeviceState *d)
341 {
342     IRQMP *irqmp = GRLIB_IRQMP(d);
343     assert(irqmp->state != NULL);
344 
345     memset(irqmp->state, 0, sizeof *irqmp->state);
346     irqmp->state->parent = irqmp;
347     irqmp->state->mpstatus = ((irqmp->ncpus - 1) << MP_STATUS_NCPU_SHIFT) |
348         ((1 << irqmp->ncpus) - 2);
349 }
350 
351 static void grlib_irqmp_realize(DeviceState *dev, Error **errp)
352 {
353     IRQMP *irqmp = GRLIB_IRQMP(dev);
354 
355     if ((!irqmp->ncpus) || (irqmp->ncpus > IRQMP_MAX_CPU)) {
356         error_setg(errp, "Invalid ncpus properties: "
357                    "%u, must be 0 < ncpus =< %u.", irqmp->ncpus,
358                    IRQMP_MAX_CPU);
359     }
360 
361     qdev_init_gpio_in(dev, grlib_irqmp_set_irq, MAX_PILS);
362 
363     /*
364      * Transitionning from 0 to 1 starts the CPUs. The opposite can't
365      * happen.
366      */
367     qdev_init_gpio_out_named(dev, irqmp->start_signal, "grlib-start-cpu",
368                              IRQMP_MAX_CPU);
369     qdev_init_gpio_out_named(dev, irqmp->irq, "grlib-irq", irqmp->ncpus);
370     memory_region_init_io(&irqmp->iomem, OBJECT(dev), &grlib_irqmp_ops, irqmp,
371                           "irqmp", IRQMP_REG_SIZE);
372 
373     irqmp->state = g_malloc0(sizeof *irqmp->state);
374 
375     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &irqmp->iomem);
376 }
377 
378 static Property grlib_irqmp_properties[] = {
379     DEFINE_PROP_UINT32("ncpus", IRQMP, ncpus, 1),
380     DEFINE_PROP_END_OF_LIST(),
381 };
382 
383 static void grlib_irqmp_class_init(ObjectClass *klass, void *data)
384 {
385     DeviceClass *dc = DEVICE_CLASS(klass);
386 
387     dc->realize = grlib_irqmp_realize;
388     dc->reset = grlib_irqmp_reset;
389     device_class_set_props(dc, grlib_irqmp_properties);
390 }
391 
392 static const TypeInfo grlib_irqmp_info = {
393     .name          = TYPE_GRLIB_IRQMP,
394     .parent        = TYPE_SYS_BUS_DEVICE,
395     .instance_size = sizeof(IRQMP),
396     .class_init    = grlib_irqmp_class_init,
397 };
398 
399 static void grlib_irqmp_register_types(void)
400 {
401     type_register_static(&grlib_irqmp_info);
402 }
403 
404 type_init(grlib_irqmp_register_types)
405