xref: /openbmc/qemu/hw/intc/bcm2836_control.c (revision 135b03cb)
1 /*
2  * Rasperry Pi 2 emulation ARM control logic module.
3  * Copyright (c) 2015, Microsoft
4  * Written by Andrew Baumann
5  *
6  * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade
7  * This code is licensed under the GNU GPLv2 and later.
8  *
9  * At present, only implements interrupt routing, and mailboxes (i.e.,
10  * not PMU interrupt, or AXI counters).
11  *
12  * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
13  *
14  * Ref:
15  * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
16  */
17 
18 #include "qemu/osdep.h"
19 #include "hw/intc/bcm2836_control.h"
20 #include "hw/irq.h"
21 #include "migration/vmstate.h"
22 #include "qemu/log.h"
23 #include "qemu/module.h"
24 
25 #define REG_GPU_ROUTE           0x0c
26 #define REG_LOCALTIMERROUTING   0x24
27 #define REG_LOCALTIMERCONTROL   0x34
28 #define REG_LOCALTIMERACK       0x38
29 #define REG_TIMERCONTROL        0x40
30 #define REG_MBOXCONTROL         0x50
31 #define REG_IRQSRC              0x60
32 #define REG_FIQSRC              0x70
33 #define REG_MBOX0_WR            0x80
34 #define REG_MBOX0_RDCLR         0xc0
35 #define REG_LIMIT              0x100
36 
37 #define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
38 #define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
39 
40 #define IRQ_CNTPSIRQ    0
41 #define IRQ_CNTPNSIRQ   1
42 #define IRQ_CNTHPIRQ    2
43 #define IRQ_CNTVIRQ     3
44 #define IRQ_MAILBOX0    4
45 #define IRQ_MAILBOX1    5
46 #define IRQ_MAILBOX2    6
47 #define IRQ_MAILBOX3    7
48 #define IRQ_GPU         8
49 #define IRQ_PMU         9
50 #define IRQ_AXI         10
51 #define IRQ_TIMER       11
52 #define IRQ_MAX         IRQ_TIMER
53 
54 #define LOCALTIMER_FREQ      38400000
55 #define LOCALTIMER_INTFLAG   (1 << 31)
56 #define LOCALTIMER_RELOAD    (1 << 30)
57 #define LOCALTIMER_INTENABLE (1 << 29)
58 #define LOCALTIMER_ENABLE    (1 << 28)
59 #define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
60 
61 static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
62                           uint32_t controlreg, uint8_t controlidx)
63 {
64     if (FIQ_BIT(controlreg, controlidx)) {
65         /* deliver a FIQ */
66         s->fiqsrc[core] |= (uint32_t)1 << irq;
67     } else if (IRQ_BIT(controlreg, controlidx)) {
68         /* deliver an IRQ */
69         s->irqsrc[core] |= (uint32_t)1 << irq;
70     } else {
71         /* the interrupt is masked */
72     }
73 }
74 
75 /* Update interrupts.  */
76 static void bcm2836_control_update(BCM2836ControlState *s)
77 {
78     int i, j;
79 
80     /* reset pending IRQs/FIQs */
81     for (i = 0; i < BCM2836_NCORES; i++) {
82         s->irqsrc[i] = s->fiqsrc[i] = 0;
83     }
84 
85     /* apply routing logic, update status regs */
86     if (s->gpu_irq) {
87         assert(s->route_gpu_irq < BCM2836_NCORES);
88         s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU;
89     }
90 
91     if (s->gpu_fiq) {
92         assert(s->route_gpu_fiq < BCM2836_NCORES);
93         s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
94     }
95 
96     /*
97      * handle the control module 'local timer' interrupt for one of the
98      * cores' IRQ/FIQ;  this is distinct from the per-CPU timer
99      * interrupts handled below.
100      */
101     if ((s->local_timer_control & LOCALTIMER_INTENABLE) &&
102         (s->local_timer_control & LOCALTIMER_INTFLAG)) {
103         if (s->route_localtimer & 4) {
104             s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
105         } else {
106             s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
107         }
108     }
109 
110     for (i = 0; i < BCM2836_NCORES; i++) {
111         /* handle local timer interrupts for this core */
112         if (s->timerirqs[i]) {
113             assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */
114             for (j = 0; j <= IRQ_CNTVIRQ; j++) {
115                 if ((s->timerirqs[i] & (1 << j)) != 0) {
116                     /* local interrupt j is set */
117                     deliver_local(s, i, j, s->timercontrol[i], j);
118                 }
119             }
120         }
121 
122         /* handle mailboxes for this core */
123         for (j = 0; j < BCM2836_MBPERCORE; j++) {
124             if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) {
125                 /* mailbox j is set */
126                 deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j);
127             }
128         }
129     }
130 
131     /* call set_irq appropriately for each output */
132     for (i = 0; i < BCM2836_NCORES; i++) {
133         qemu_set_irq(s->irq[i], s->irqsrc[i] != 0);
134         qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0);
135     }
136 }
137 
138 static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq,
139                                           int level)
140 {
141     BCM2836ControlState *s = opaque;
142 
143     assert(core >= 0 && core < BCM2836_NCORES);
144     assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ);
145 
146     s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level);
147 
148     bcm2836_control_update(s);
149 }
150 
151 /* XXX: the following wrapper functions are a kludgy workaround,
152  * needed because I can't seem to pass useful information in the "irq"
153  * parameter when using named interrupts. Feel free to clean this up!
154  */
155 
156 static void bcm2836_control_set_local_irq0(void *opaque, int core, int level)
157 {
158     bcm2836_control_set_local_irq(opaque, core, 0, level);
159 }
160 
161 static void bcm2836_control_set_local_irq1(void *opaque, int core, int level)
162 {
163     bcm2836_control_set_local_irq(opaque, core, 1, level);
164 }
165 
166 static void bcm2836_control_set_local_irq2(void *opaque, int core, int level)
167 {
168     bcm2836_control_set_local_irq(opaque, core, 2, level);
169 }
170 
171 static void bcm2836_control_set_local_irq3(void *opaque, int core, int level)
172 {
173     bcm2836_control_set_local_irq(opaque, core, 3, level);
174 }
175 
176 static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level)
177 {
178     BCM2836ControlState *s = opaque;
179 
180     s->gpu_irq = level;
181 
182     bcm2836_control_update(s);
183 }
184 
185 static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level)
186 {
187     BCM2836ControlState *s = opaque;
188 
189     s->gpu_fiq = level;
190 
191     bcm2836_control_update(s);
192 }
193 
194 static void bcm2836_control_local_timer_set_next(void *opaque)
195 {
196     BCM2836ControlState *s = opaque;
197     uint64_t next_event;
198 
199     assert(LOCALTIMER_VALUE(s->local_timer_control) > 0);
200 
201     next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
202         muldiv64(LOCALTIMER_VALUE(s->local_timer_control),
203             NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);
204     timer_mod(&s->timer, next_event);
205 }
206 
207 static void bcm2836_control_local_timer_tick(void *opaque)
208 {
209     BCM2836ControlState *s = opaque;
210 
211     bcm2836_control_local_timer_set_next(s);
212 
213     s->local_timer_control |= LOCALTIMER_INTFLAG;
214     bcm2836_control_update(s);
215 }
216 
217 static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
218 {
219     BCM2836ControlState *s = opaque;
220 
221     s->local_timer_control = val;
222     if (val & LOCALTIMER_ENABLE) {
223         bcm2836_control_local_timer_set_next(s);
224     } else {
225         timer_del(&s->timer);
226     }
227 }
228 
229 static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
230 {
231     BCM2836ControlState *s = opaque;
232 
233     if (val & LOCALTIMER_INTFLAG) {
234         s->local_timer_control &= ~LOCALTIMER_INTFLAG;
235     }
236     if ((val & LOCALTIMER_RELOAD) &&
237         (s->local_timer_control & LOCALTIMER_ENABLE)) {
238             bcm2836_control_local_timer_set_next(s);
239     }
240 }
241 
242 static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size)
243 {
244     BCM2836ControlState *s = opaque;
245 
246     if (offset == REG_GPU_ROUTE) {
247         assert(s->route_gpu_fiq < BCM2836_NCORES
248                && s->route_gpu_irq < BCM2836_NCORES);
249         return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
250     } else if (offset == REG_LOCALTIMERROUTING) {
251         return s->route_localtimer;
252     } else if (offset == REG_LOCALTIMERCONTROL) {
253         return s->local_timer_control;
254     } else if (offset == REG_LOCALTIMERACK) {
255         return 0;
256     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
257         return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
258     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
259         return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2];
260     } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) {
261         return s->irqsrc[(offset - REG_IRQSRC) >> 2];
262     } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) {
263         return s->fiqsrc[(offset - REG_FIQSRC) >> 2];
264     } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
265         return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2];
266     } else {
267         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
268                       __func__, offset);
269         return 0;
270     }
271 }
272 
273 static void bcm2836_control_write(void *opaque, hwaddr offset,
274                                   uint64_t val, unsigned size)
275 {
276     BCM2836ControlState *s = opaque;
277 
278     if (offset == REG_GPU_ROUTE) {
279         s->route_gpu_irq = val & 0x3;
280         s->route_gpu_fiq = (val >> 2) & 0x3;
281     } else if (offset == REG_LOCALTIMERROUTING) {
282         s->route_localtimer = val & 7;
283     } else if (offset == REG_LOCALTIMERCONTROL) {
284         bcm2836_control_local_timer_control(s, val);
285     } else if (offset == REG_LOCALTIMERACK) {
286         bcm2836_control_local_timer_ack(s, val);
287     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
288         s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
289     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
290         s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff;
291     } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) {
292         s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val;
293     } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
294         s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val;
295     } else {
296         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
297                       __func__, offset);
298         return;
299     }
300 
301     bcm2836_control_update(s);
302 }
303 
304 static const MemoryRegionOps bcm2836_control_ops = {
305     .read = bcm2836_control_read,
306     .write = bcm2836_control_write,
307     .endianness = DEVICE_NATIVE_ENDIAN,
308     .valid.min_access_size = 4,
309     .valid.max_access_size = 4,
310 };
311 
312 static void bcm2836_control_reset(DeviceState *d)
313 {
314     BCM2836ControlState *s = BCM2836_CONTROL(d);
315     int i;
316 
317     s->route_gpu_irq = s->route_gpu_fiq = 0;
318 
319     timer_del(&s->timer);
320     s->route_localtimer = 0;
321     s->local_timer_control = 0;
322 
323     for (i = 0; i < BCM2836_NCORES; i++) {
324         s->timercontrol[i] = 0;
325         s->mailboxcontrol[i] = 0;
326     }
327 
328     for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) {
329         s->mailboxes[i] = 0;
330     }
331 }
332 
333 static void bcm2836_control_init(Object *obj)
334 {
335     BCM2836ControlState *s = BCM2836_CONTROL(obj);
336     DeviceState *dev = DEVICE(obj);
337 
338     memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s,
339                           TYPE_BCM2836_CONTROL, REG_LIMIT);
340     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
341 
342     /* inputs from each CPU core */
343     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq",
344                             BCM2836_NCORES);
345     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq",
346                             BCM2836_NCORES);
347     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq",
348                             BCM2836_NCORES);
349     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq",
350                             BCM2836_NCORES);
351 
352     /* IRQ and FIQ inputs from upstream bcm2835 controller */
353     qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1);
354     qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1);
355 
356     /* outputs to CPU cores */
357     qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
358     qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
359 
360     /* create a qemu virtual timer */
361     timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL,
362                   bcm2836_control_local_timer_tick, s);
363 }
364 
365 static const VMStateDescription vmstate_bcm2836_control = {
366     .name = TYPE_BCM2836_CONTROL,
367     .version_id = 2,
368     .minimum_version_id = 1,
369     .fields = (VMStateField[]) {
370         VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
371                              BCM2836_NCORES * BCM2836_MBPERCORE),
372         VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState),
373         VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState),
374         VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES),
375         VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
376                              BCM2836_NCORES),
377         VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
378         VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
379         VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),
380         VMSTATE_END_OF_LIST()
381     }
382 };
383 
384 static void bcm2836_control_class_init(ObjectClass *klass, void *data)
385 {
386     DeviceClass *dc = DEVICE_CLASS(klass);
387 
388     dc->reset = bcm2836_control_reset;
389     dc->vmsd = &vmstate_bcm2836_control;
390 }
391 
392 static TypeInfo bcm2836_control_info = {
393     .name          = TYPE_BCM2836_CONTROL,
394     .parent        = TYPE_SYS_BUS_DEVICE,
395     .instance_size = sizeof(BCM2836ControlState),
396     .class_init    = bcm2836_control_class_init,
397     .instance_init = bcm2836_control_init,
398 };
399 
400 static void bcm2836_control_register_types(void)
401 {
402     type_register_static(&bcm2836_control_info);
403 }
404 
405 type_init(bcm2836_control_register_types)
406