xref: /openbmc/qemu/hw/intc/grlib_irqmp.c (revision f432962e)
1 /*
2  * QEMU GRLIB IRQMP Emulator
3  *
4  * (Multiprocessor and 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 #define MAX_PILS 16
56 
57 OBJECT_DECLARE_SIMPLE_TYPE(IRQMP, GRLIB_IRQMP)
58 
59 typedef struct IRQMPState IRQMPState;
60 
61 struct IRQMP {
62     SysBusDevice parent_obj;
63 
64     MemoryRegion iomem;
65 
66     IRQMPState *state;
67     qemu_irq irq;
68 };
69 
70 struct IRQMPState {
71     uint32_t level;
72     uint32_t pending;
73     uint32_t clear;
74     uint32_t broadcast;
75 
76     uint32_t mask[IRQMP_MAX_CPU];
77     uint32_t force[IRQMP_MAX_CPU];
78     uint32_t extended[IRQMP_MAX_CPU];
79 
80     IRQMP    *parent;
81 };
82 
83 static void grlib_irqmp_check_irqs(IRQMPState *state)
84 {
85     uint32_t      pend   = 0;
86     uint32_t      level0 = 0;
87     uint32_t      level1 = 0;
88 
89     assert(state != NULL);
90     assert(state->parent != NULL);
91 
92     /* IRQ for CPU 0 (no SMP support) */
93     pend = (state->pending | state->force[0])
94         & state->mask[0];
95 
96     level0 = pend & ~state->level;
97     level1 = pend &  state->level;
98 
99     trace_grlib_irqmp_check_irqs(state->pending, state->force[0],
100                                  state->mask[0], level1, level0);
101 
102     /* Trigger level1 interrupt first and level0 if there is no level1 */
103     qemu_set_irq(state->parent->irq, level1 ?: level0);
104 }
105 
106 static void grlib_irqmp_ack_mask(IRQMPState *state, uint32_t mask)
107 {
108     /* Clear registers */
109     state->pending  &= ~mask;
110     state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */
111 
112     grlib_irqmp_check_irqs(state);
113 }
114 
115 void grlib_irqmp_ack(DeviceState *dev, int intno)
116 {
117     IRQMP        *irqmp = GRLIB_IRQMP(dev);
118     IRQMPState   *state;
119     uint32_t      mask;
120 
121     state = irqmp->state;
122     assert(state != NULL);
123 
124     intno &= 15;
125     mask = 1 << intno;
126 
127     trace_grlib_irqmp_ack(intno);
128 
129     grlib_irqmp_ack_mask(state, mask);
130 }
131 
132 static void grlib_irqmp_set_irq(void *opaque, int irq, int level)
133 {
134     IRQMP      *irqmp = GRLIB_IRQMP(opaque);
135     IRQMPState *s;
136     int         i = 0;
137 
138     s = irqmp->state;
139     assert(s         != NULL);
140     assert(s->parent != NULL);
141 
142 
143     if (level) {
144         trace_grlib_irqmp_set_irq(irq);
145 
146         if (s->broadcast & 1 << irq) {
147             /* Broadcasted IRQ */
148             for (i = 0; i < IRQMP_MAX_CPU; i++) {
149                 s->force[i] |= 1 << irq;
150             }
151         } else {
152             s->pending |= 1 << irq;
153         }
154         grlib_irqmp_check_irqs(s);
155 
156     }
157 }
158 
159 static uint64_t grlib_irqmp_read(void *opaque, hwaddr addr,
160                                  unsigned size)
161 {
162     IRQMP      *irqmp = opaque;
163     IRQMPState *state;
164 
165     assert(irqmp != NULL);
166     state = irqmp->state;
167     assert(state != NULL);
168 
169     addr &= 0xff;
170 
171     /* global registers */
172     switch (addr) {
173     case LEVEL_OFFSET:
174         return state->level;
175 
176     case PENDING_OFFSET:
177         return state->pending;
178 
179     case FORCE0_OFFSET:
180         /* This register is an "alias" for the force register of CPU 0 */
181         return state->force[0];
182 
183     case CLEAR_OFFSET:
184     case MP_STATUS_OFFSET:
185         /* Always read as 0 */
186         return 0;
187 
188     case BROADCAST_OFFSET:
189         return state->broadcast;
190 
191     default:
192         break;
193     }
194 
195     /* mask registers */
196     if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
197         int cpu = (addr - MASK_OFFSET) / 4;
198         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
199 
200         return state->mask[cpu];
201     }
202 
203     /* force registers */
204     if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
205         int cpu = (addr - FORCE_OFFSET) / 4;
206         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
207 
208         return state->force[cpu];
209     }
210 
211     /* extended (not supported) */
212     if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
213         int cpu = (addr - EXTENDED_OFFSET) / 4;
214         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
215 
216         return state->extended[cpu];
217     }
218 
219     trace_grlib_irqmp_readl_unknown(addr);
220     return 0;
221 }
222 
223 static void grlib_irqmp_write(void *opaque, hwaddr addr,
224                               uint64_t value, unsigned size)
225 {
226     IRQMP      *irqmp = opaque;
227     IRQMPState *state;
228 
229     assert(irqmp != NULL);
230     state = irqmp->state;
231     assert(state != NULL);
232 
233     addr &= 0xff;
234 
235     /* global registers */
236     switch (addr) {
237     case LEVEL_OFFSET:
238         value &= 0xFFFF << 1; /* clean up the value */
239         state->level = value;
240         return;
241 
242     case PENDING_OFFSET:
243         /* Read Only */
244         return;
245 
246     case FORCE0_OFFSET:
247         /* This register is an "alias" for the force register of CPU 0 */
248 
249         value &= 0xFFFE; /* clean up the value */
250         state->force[0] = value;
251         grlib_irqmp_check_irqs(irqmp->state);
252         return;
253 
254     case CLEAR_OFFSET:
255         value &= ~1; /* clean up the value */
256         grlib_irqmp_ack_mask(state, value);
257         return;
258 
259     case MP_STATUS_OFFSET:
260         /* Read Only (no SMP support) */
261         return;
262 
263     case BROADCAST_OFFSET:
264         value &= 0xFFFE; /* clean up the value */
265         state->broadcast = value;
266         return;
267 
268     default:
269         break;
270     }
271 
272     /* mask registers */
273     if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) {
274         int cpu = (addr - MASK_OFFSET) / 4;
275         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
276 
277         value &= ~1; /* clean up the value */
278         state->mask[cpu] = value;
279         grlib_irqmp_check_irqs(irqmp->state);
280         return;
281     }
282 
283     /* force registers */
284     if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) {
285         int cpu = (addr - FORCE_OFFSET) / 4;
286         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
287 
288         uint32_t force = value & 0xFFFE;
289         uint32_t clear = (value >> 16) & 0xFFFE;
290         uint32_t old   = state->force[cpu];
291 
292         state->force[cpu] = (old | force) & ~clear;
293         grlib_irqmp_check_irqs(irqmp->state);
294         return;
295     }
296 
297     /* extended (not supported) */
298     if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) {
299         int cpu = (addr - EXTENDED_OFFSET) / 4;
300         assert(cpu >= 0 && cpu < IRQMP_MAX_CPU);
301 
302         value &= 0xF; /* clean up the value */
303         state->extended[cpu] = value;
304         return;
305     }
306 
307     trace_grlib_irqmp_writel_unknown(addr, value);
308 }
309 
310 static const MemoryRegionOps grlib_irqmp_ops = {
311     .read = grlib_irqmp_read,
312     .write = grlib_irqmp_write,
313     .endianness = DEVICE_NATIVE_ENDIAN,
314     .valid = {
315         .min_access_size = 4,
316         .max_access_size = 4,
317     },
318 };
319 
320 static void grlib_irqmp_reset(DeviceState *d)
321 {
322     IRQMP *irqmp = GRLIB_IRQMP(d);
323     assert(irqmp->state != NULL);
324 
325     memset(irqmp->state, 0, sizeof *irqmp->state);
326     irqmp->state->parent = irqmp;
327 }
328 
329 static void grlib_irqmp_init(Object *obj)
330 {
331     IRQMP *irqmp = GRLIB_IRQMP(obj);
332     SysBusDevice *dev = SYS_BUS_DEVICE(obj);
333 
334     qdev_init_gpio_in(DEVICE(obj), grlib_irqmp_set_irq, MAX_PILS);
335     qdev_init_gpio_out_named(DEVICE(obj), &irqmp->irq, "grlib-irq", 1);
336     memory_region_init_io(&irqmp->iomem, obj, &grlib_irqmp_ops, irqmp,
337                           "irqmp", IRQMP_REG_SIZE);
338 
339     irqmp->state = g_malloc0(sizeof *irqmp->state);
340 
341     sysbus_init_mmio(dev, &irqmp->iomem);
342 }
343 
344 static void grlib_irqmp_class_init(ObjectClass *klass, void *data)
345 {
346     DeviceClass *dc = DEVICE_CLASS(klass);
347 
348     dc->reset = grlib_irqmp_reset;
349 }
350 
351 static const TypeInfo grlib_irqmp_info = {
352     .name          = TYPE_GRLIB_IRQMP,
353     .parent        = TYPE_SYS_BUS_DEVICE,
354     .instance_size = sizeof(IRQMP),
355     .instance_init = grlib_irqmp_init,
356     .class_init    = grlib_irqmp_class_init,
357 };
358 
359 static void grlib_irqmp_register_types(void)
360 {
361     type_register_static(&grlib_irqmp_info);
362 }
363 
364 type_init(grlib_irqmp_register_types)
365