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