xref: /openbmc/qemu/hw/intc/bcm2836_control.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
1cc28296dSAndrew Baumann /*
2cc28296dSAndrew Baumann  * Rasperry Pi 2 emulation ARM control logic module.
3cc28296dSAndrew Baumann  * Copyright (c) 2015, Microsoft
4cc28296dSAndrew Baumann  * Written by Andrew Baumann
5cc28296dSAndrew Baumann  *
6cc28296dSAndrew Baumann  * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade
7cc28296dSAndrew Baumann  * This code is licensed under the GNU GPLv2 and later.
8cc28296dSAndrew Baumann  *
9cc28296dSAndrew Baumann  * At present, only implements interrupt routing, and mailboxes (i.e.,
1067d80321SZoltán Baldaszti  * not PMU interrupt, or AXI counters).
1167d80321SZoltán Baldaszti  *
1267d80321SZoltán Baldaszti  * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
13cc28296dSAndrew Baumann  *
14cc28296dSAndrew Baumann  * Ref:
15cc28296dSAndrew Baumann  * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
16cc28296dSAndrew Baumann  */
17cc28296dSAndrew Baumann 
18c964b660SPeter Maydell #include "qemu/osdep.h"
19cc28296dSAndrew Baumann #include "hw/intc/bcm2836_control.h"
2003dd024fSPaolo Bonzini #include "qemu/log.h"
21*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
22cc28296dSAndrew Baumann 
23cc28296dSAndrew Baumann #define REG_GPU_ROUTE           0x0c
2467d80321SZoltán Baldaszti #define REG_LOCALTIMERROUTING   0x24
2567d80321SZoltán Baldaszti #define REG_LOCALTIMERCONTROL   0x34
2667d80321SZoltán Baldaszti #define REG_LOCALTIMERACK       0x38
27cc28296dSAndrew Baumann #define REG_TIMERCONTROL        0x40
28cc28296dSAndrew Baumann #define REG_MBOXCONTROL         0x50
29cc28296dSAndrew Baumann #define REG_IRQSRC              0x60
30cc28296dSAndrew Baumann #define REG_FIQSRC              0x70
31cc28296dSAndrew Baumann #define REG_MBOX0_WR            0x80
32cc28296dSAndrew Baumann #define REG_MBOX0_RDCLR         0xc0
33cc28296dSAndrew Baumann #define REG_LIMIT              0x100
34cc28296dSAndrew Baumann 
35cc28296dSAndrew Baumann #define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
36cc28296dSAndrew Baumann #define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
37cc28296dSAndrew Baumann 
38cc28296dSAndrew Baumann #define IRQ_CNTPSIRQ    0
39cc28296dSAndrew Baumann #define IRQ_CNTPNSIRQ   1
40cc28296dSAndrew Baumann #define IRQ_CNTHPIRQ    2
41cc28296dSAndrew Baumann #define IRQ_CNTVIRQ     3
42cc28296dSAndrew Baumann #define IRQ_MAILBOX0    4
43cc28296dSAndrew Baumann #define IRQ_MAILBOX1    5
44cc28296dSAndrew Baumann #define IRQ_MAILBOX2    6
45cc28296dSAndrew Baumann #define IRQ_MAILBOX3    7
46cc28296dSAndrew Baumann #define IRQ_GPU         8
47cc28296dSAndrew Baumann #define IRQ_PMU         9
48cc28296dSAndrew Baumann #define IRQ_AXI         10
49cc28296dSAndrew Baumann #define IRQ_TIMER       11
50cc28296dSAndrew Baumann #define IRQ_MAX         IRQ_TIMER
51cc28296dSAndrew Baumann 
5267d80321SZoltán Baldaszti #define LOCALTIMER_FREQ      38400000
5367d80321SZoltán Baldaszti #define LOCALTIMER_INTFLAG   (1 << 31)
5467d80321SZoltán Baldaszti #define LOCALTIMER_RELOAD    (1 << 30)
5567d80321SZoltán Baldaszti #define LOCALTIMER_INTENABLE (1 << 29)
5667d80321SZoltán Baldaszti #define LOCALTIMER_ENABLE    (1 << 28)
5767d80321SZoltán Baldaszti #define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
5867d80321SZoltán Baldaszti 
59cc28296dSAndrew Baumann static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
60cc28296dSAndrew Baumann                           uint32_t controlreg, uint8_t controlidx)
61cc28296dSAndrew Baumann {
62cc28296dSAndrew Baumann     if (FIQ_BIT(controlreg, controlidx)) {
63cc28296dSAndrew Baumann         /* deliver a FIQ */
64cc28296dSAndrew Baumann         s->fiqsrc[core] |= (uint32_t)1 << irq;
65cc28296dSAndrew Baumann     } else if (IRQ_BIT(controlreg, controlidx)) {
66cc28296dSAndrew Baumann         /* deliver an IRQ */
67cc28296dSAndrew Baumann         s->irqsrc[core] |= (uint32_t)1 << irq;
68cc28296dSAndrew Baumann     } else {
69cc28296dSAndrew Baumann         /* the interrupt is masked */
70cc28296dSAndrew Baumann     }
71cc28296dSAndrew Baumann }
72cc28296dSAndrew Baumann 
73cc28296dSAndrew Baumann /* Update interrupts.  */
74cc28296dSAndrew Baumann static void bcm2836_control_update(BCM2836ControlState *s)
75cc28296dSAndrew Baumann {
76cc28296dSAndrew Baumann     int i, j;
77cc28296dSAndrew Baumann 
78cc28296dSAndrew Baumann     /* reset pending IRQs/FIQs */
79cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES; i++) {
80cc28296dSAndrew Baumann         s->irqsrc[i] = s->fiqsrc[i] = 0;
81cc28296dSAndrew Baumann     }
82cc28296dSAndrew Baumann 
83cc28296dSAndrew Baumann     /* apply routing logic, update status regs */
84cc28296dSAndrew Baumann     if (s->gpu_irq) {
85cc28296dSAndrew Baumann         assert(s->route_gpu_irq < BCM2836_NCORES);
86cc28296dSAndrew Baumann         s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU;
87cc28296dSAndrew Baumann     }
88cc28296dSAndrew Baumann 
89cc28296dSAndrew Baumann     if (s->gpu_fiq) {
90cc28296dSAndrew Baumann         assert(s->route_gpu_fiq < BCM2836_NCORES);
91cc28296dSAndrew Baumann         s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
92cc28296dSAndrew Baumann     }
93cc28296dSAndrew Baumann 
9467d80321SZoltán Baldaszti     /*
9567d80321SZoltán Baldaszti      * handle the control module 'local timer' interrupt for one of the
9667d80321SZoltán Baldaszti      * cores' IRQ/FIQ;  this is distinct from the per-CPU timer
9767d80321SZoltán Baldaszti      * interrupts handled below.
9867d80321SZoltán Baldaszti      */
9967d80321SZoltán Baldaszti     if ((s->local_timer_control & LOCALTIMER_INTENABLE) &&
10067d80321SZoltán Baldaszti         (s->local_timer_control & LOCALTIMER_INTFLAG)) {
10167d80321SZoltán Baldaszti         if (s->route_localtimer & 4) {
10267d80321SZoltán Baldaszti             s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
10367d80321SZoltán Baldaszti         } else {
10467d80321SZoltán Baldaszti             s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
10567d80321SZoltán Baldaszti         }
10667d80321SZoltán Baldaszti     }
10767d80321SZoltán Baldaszti 
108cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES; i++) {
109cc28296dSAndrew Baumann         /* handle local timer interrupts for this core */
110cc28296dSAndrew Baumann         if (s->timerirqs[i]) {
111cc28296dSAndrew Baumann             assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */
112cc28296dSAndrew Baumann             for (j = 0; j <= IRQ_CNTVIRQ; j++) {
113cc28296dSAndrew Baumann                 if ((s->timerirqs[i] & (1 << j)) != 0) {
114cc28296dSAndrew Baumann                     /* local interrupt j is set */
115cc28296dSAndrew Baumann                     deliver_local(s, i, j, s->timercontrol[i], j);
116cc28296dSAndrew Baumann                 }
117cc28296dSAndrew Baumann             }
118cc28296dSAndrew Baumann         }
119cc28296dSAndrew Baumann 
120cc28296dSAndrew Baumann         /* handle mailboxes for this core */
121cc28296dSAndrew Baumann         for (j = 0; j < BCM2836_MBPERCORE; j++) {
122cc28296dSAndrew Baumann             if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) {
123cc28296dSAndrew Baumann                 /* mailbox j is set */
124cc28296dSAndrew Baumann                 deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j);
125cc28296dSAndrew Baumann             }
126cc28296dSAndrew Baumann         }
127cc28296dSAndrew Baumann     }
128cc28296dSAndrew Baumann 
129cc28296dSAndrew Baumann     /* call set_irq appropriately for each output */
130cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES; i++) {
131cc28296dSAndrew Baumann         qemu_set_irq(s->irq[i], s->irqsrc[i] != 0);
132cc28296dSAndrew Baumann         qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0);
133cc28296dSAndrew Baumann     }
134cc28296dSAndrew Baumann }
135cc28296dSAndrew Baumann 
136cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq,
137cc28296dSAndrew Baumann                                           int level)
138cc28296dSAndrew Baumann {
139cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
140cc28296dSAndrew Baumann 
141cc28296dSAndrew Baumann     assert(core >= 0 && core < BCM2836_NCORES);
142cc28296dSAndrew Baumann     assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ);
143cc28296dSAndrew Baumann 
144cc28296dSAndrew Baumann     s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level);
145cc28296dSAndrew Baumann 
146cc28296dSAndrew Baumann     bcm2836_control_update(s);
147cc28296dSAndrew Baumann }
148cc28296dSAndrew Baumann 
149cc28296dSAndrew Baumann /* XXX: the following wrapper functions are a kludgy workaround,
150cc28296dSAndrew Baumann  * needed because I can't seem to pass useful information in the "irq"
151cc28296dSAndrew Baumann  * parameter when using named interrupts. Feel free to clean this up!
152cc28296dSAndrew Baumann  */
153cc28296dSAndrew Baumann 
154cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq0(void *opaque, int core, int level)
155cc28296dSAndrew Baumann {
156cc28296dSAndrew Baumann     bcm2836_control_set_local_irq(opaque, core, 0, level);
157cc28296dSAndrew Baumann }
158cc28296dSAndrew Baumann 
159cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq1(void *opaque, int core, int level)
160cc28296dSAndrew Baumann {
161cc28296dSAndrew Baumann     bcm2836_control_set_local_irq(opaque, core, 1, level);
162cc28296dSAndrew Baumann }
163cc28296dSAndrew Baumann 
164cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq2(void *opaque, int core, int level)
165cc28296dSAndrew Baumann {
166cc28296dSAndrew Baumann     bcm2836_control_set_local_irq(opaque, core, 2, level);
167cc28296dSAndrew Baumann }
168cc28296dSAndrew Baumann 
169cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq3(void *opaque, int core, int level)
170cc28296dSAndrew Baumann {
171cc28296dSAndrew Baumann     bcm2836_control_set_local_irq(opaque, core, 3, level);
172cc28296dSAndrew Baumann }
173cc28296dSAndrew Baumann 
174cc28296dSAndrew Baumann static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level)
175cc28296dSAndrew Baumann {
176cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
177cc28296dSAndrew Baumann 
178cc28296dSAndrew Baumann     s->gpu_irq = level;
179cc28296dSAndrew Baumann 
180cc28296dSAndrew Baumann     bcm2836_control_update(s);
181cc28296dSAndrew Baumann }
182cc28296dSAndrew Baumann 
183cc28296dSAndrew Baumann static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level)
184cc28296dSAndrew Baumann {
185cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
186cc28296dSAndrew Baumann 
187cc28296dSAndrew Baumann     s->gpu_fiq = level;
188cc28296dSAndrew Baumann 
189cc28296dSAndrew Baumann     bcm2836_control_update(s);
190cc28296dSAndrew Baumann }
191cc28296dSAndrew Baumann 
19267d80321SZoltán Baldaszti static void bcm2836_control_local_timer_set_next(void *opaque)
19367d80321SZoltán Baldaszti {
19467d80321SZoltán Baldaszti     BCM2836ControlState *s = opaque;
19567d80321SZoltán Baldaszti     uint64_t next_event;
19667d80321SZoltán Baldaszti 
19767d80321SZoltán Baldaszti     assert(LOCALTIMER_VALUE(s->local_timer_control) > 0);
19867d80321SZoltán Baldaszti 
19967d80321SZoltán Baldaszti     next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
20067d80321SZoltán Baldaszti         muldiv64(LOCALTIMER_VALUE(s->local_timer_control),
20167d80321SZoltán Baldaszti             NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);
20267d80321SZoltán Baldaszti     timer_mod(&s->timer, next_event);
20367d80321SZoltán Baldaszti }
20467d80321SZoltán Baldaszti 
20567d80321SZoltán Baldaszti static void bcm2836_control_local_timer_tick(void *opaque)
20667d80321SZoltán Baldaszti {
20767d80321SZoltán Baldaszti     BCM2836ControlState *s = opaque;
20867d80321SZoltán Baldaszti 
20967d80321SZoltán Baldaszti     bcm2836_control_local_timer_set_next(s);
21067d80321SZoltán Baldaszti 
21167d80321SZoltán Baldaszti     s->local_timer_control |= LOCALTIMER_INTFLAG;
21267d80321SZoltán Baldaszti     bcm2836_control_update(s);
21367d80321SZoltán Baldaszti }
21467d80321SZoltán Baldaszti 
21567d80321SZoltán Baldaszti static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
21667d80321SZoltán Baldaszti {
21767d80321SZoltán Baldaszti     BCM2836ControlState *s = opaque;
21867d80321SZoltán Baldaszti 
21967d80321SZoltán Baldaszti     s->local_timer_control = val;
22067d80321SZoltán Baldaszti     if (val & LOCALTIMER_ENABLE) {
22167d80321SZoltán Baldaszti         bcm2836_control_local_timer_set_next(s);
22267d80321SZoltán Baldaszti     } else {
22367d80321SZoltán Baldaszti         timer_del(&s->timer);
22467d80321SZoltán Baldaszti     }
22567d80321SZoltán Baldaszti }
22667d80321SZoltán Baldaszti 
22767d80321SZoltán Baldaszti static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
22867d80321SZoltán Baldaszti {
22967d80321SZoltán Baldaszti     BCM2836ControlState *s = opaque;
23067d80321SZoltán Baldaszti 
23167d80321SZoltán Baldaszti     if (val & LOCALTIMER_INTFLAG) {
23267d80321SZoltán Baldaszti         s->local_timer_control &= ~LOCALTIMER_INTFLAG;
23367d80321SZoltán Baldaszti     }
23467d80321SZoltán Baldaszti     if ((val & LOCALTIMER_RELOAD) &&
23567d80321SZoltán Baldaszti         (s->local_timer_control & LOCALTIMER_ENABLE)) {
23667d80321SZoltán Baldaszti             bcm2836_control_local_timer_set_next(s);
23767d80321SZoltán Baldaszti     }
23867d80321SZoltán Baldaszti }
23967d80321SZoltán Baldaszti 
240cc28296dSAndrew Baumann static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size)
241cc28296dSAndrew Baumann {
242cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
243cc28296dSAndrew Baumann 
244cc28296dSAndrew Baumann     if (offset == REG_GPU_ROUTE) {
245cc28296dSAndrew Baumann         assert(s->route_gpu_fiq < BCM2836_NCORES
246cc28296dSAndrew Baumann                && s->route_gpu_irq < BCM2836_NCORES);
247cc28296dSAndrew Baumann         return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
24867d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERROUTING) {
24967d80321SZoltán Baldaszti         return s->route_localtimer;
25067d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERCONTROL) {
25167d80321SZoltán Baldaszti         return s->local_timer_control;
25267d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERACK) {
25367d80321SZoltán Baldaszti         return 0;
254cc28296dSAndrew Baumann     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
255cc28296dSAndrew Baumann         return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
256cc28296dSAndrew Baumann     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
257cc28296dSAndrew Baumann         return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2];
258cc28296dSAndrew Baumann     } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) {
259cc28296dSAndrew Baumann         return s->irqsrc[(offset - REG_IRQSRC) >> 2];
260cc28296dSAndrew Baumann     } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) {
261cc28296dSAndrew Baumann         return s->fiqsrc[(offset - REG_FIQSRC) >> 2];
262cc28296dSAndrew Baumann     } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
263cc28296dSAndrew Baumann         return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2];
264cc28296dSAndrew Baumann     } else {
265cc28296dSAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
266cc28296dSAndrew Baumann                       __func__, offset);
267cc28296dSAndrew Baumann         return 0;
268cc28296dSAndrew Baumann     }
269cc28296dSAndrew Baumann }
270cc28296dSAndrew Baumann 
271cc28296dSAndrew Baumann static void bcm2836_control_write(void *opaque, hwaddr offset,
272cc28296dSAndrew Baumann                                   uint64_t val, unsigned size)
273cc28296dSAndrew Baumann {
274cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
275cc28296dSAndrew Baumann 
276cc28296dSAndrew Baumann     if (offset == REG_GPU_ROUTE) {
277cc28296dSAndrew Baumann         s->route_gpu_irq = val & 0x3;
278cc28296dSAndrew Baumann         s->route_gpu_fiq = (val >> 2) & 0x3;
27967d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERROUTING) {
28067d80321SZoltán Baldaszti         s->route_localtimer = val & 7;
28167d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERCONTROL) {
28267d80321SZoltán Baldaszti         bcm2836_control_local_timer_control(s, val);
28367d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERACK) {
28467d80321SZoltán Baldaszti         bcm2836_control_local_timer_ack(s, val);
285cc28296dSAndrew Baumann     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
286cc28296dSAndrew Baumann         s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
287cc28296dSAndrew Baumann     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
288cc28296dSAndrew Baumann         s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff;
289cc28296dSAndrew Baumann     } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) {
290cc28296dSAndrew Baumann         s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val;
291cc28296dSAndrew Baumann     } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
292cc28296dSAndrew Baumann         s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val;
293cc28296dSAndrew Baumann     } else {
294cc28296dSAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
295cc28296dSAndrew Baumann                       __func__, offset);
296cc28296dSAndrew Baumann         return;
297cc28296dSAndrew Baumann     }
298cc28296dSAndrew Baumann 
299cc28296dSAndrew Baumann     bcm2836_control_update(s);
300cc28296dSAndrew Baumann }
301cc28296dSAndrew Baumann 
302cc28296dSAndrew Baumann static const MemoryRegionOps bcm2836_control_ops = {
303cc28296dSAndrew Baumann     .read = bcm2836_control_read,
304cc28296dSAndrew Baumann     .write = bcm2836_control_write,
305cc28296dSAndrew Baumann     .endianness = DEVICE_NATIVE_ENDIAN,
306cc28296dSAndrew Baumann     .valid.min_access_size = 4,
307cc28296dSAndrew Baumann     .valid.max_access_size = 4,
308cc28296dSAndrew Baumann };
309cc28296dSAndrew Baumann 
310cc28296dSAndrew Baumann static void bcm2836_control_reset(DeviceState *d)
311cc28296dSAndrew Baumann {
312cc28296dSAndrew Baumann     BCM2836ControlState *s = BCM2836_CONTROL(d);
313cc28296dSAndrew Baumann     int i;
314cc28296dSAndrew Baumann 
315cc28296dSAndrew Baumann     s->route_gpu_irq = s->route_gpu_fiq = 0;
316cc28296dSAndrew Baumann 
31767d80321SZoltán Baldaszti     timer_del(&s->timer);
31867d80321SZoltán Baldaszti     s->route_localtimer = 0;
31967d80321SZoltán Baldaszti     s->local_timer_control = 0;
32067d80321SZoltán Baldaszti 
321cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES; i++) {
322cc28296dSAndrew Baumann         s->timercontrol[i] = 0;
323cc28296dSAndrew Baumann         s->mailboxcontrol[i] = 0;
324cc28296dSAndrew Baumann     }
325cc28296dSAndrew Baumann 
326cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) {
327cc28296dSAndrew Baumann         s->mailboxes[i] = 0;
328cc28296dSAndrew Baumann     }
329cc28296dSAndrew Baumann }
330cc28296dSAndrew Baumann 
331cc28296dSAndrew Baumann static void bcm2836_control_init(Object *obj)
332cc28296dSAndrew Baumann {
333cc28296dSAndrew Baumann     BCM2836ControlState *s = BCM2836_CONTROL(obj);
334cc28296dSAndrew Baumann     DeviceState *dev = DEVICE(obj);
335cc28296dSAndrew Baumann 
336cc28296dSAndrew Baumann     memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s,
337cc28296dSAndrew Baumann                           TYPE_BCM2836_CONTROL, REG_LIMIT);
338cc28296dSAndrew Baumann     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
339cc28296dSAndrew Baumann 
340cc28296dSAndrew Baumann     /* inputs from each CPU core */
341cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq",
342cc28296dSAndrew Baumann                             BCM2836_NCORES);
343cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq",
344cc28296dSAndrew Baumann                             BCM2836_NCORES);
345cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq",
346cc28296dSAndrew Baumann                             BCM2836_NCORES);
347cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq",
348cc28296dSAndrew Baumann                             BCM2836_NCORES);
349cc28296dSAndrew Baumann 
350cc28296dSAndrew Baumann     /* IRQ and FIQ inputs from upstream bcm2835 controller */
351cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1);
352cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1);
353cc28296dSAndrew Baumann 
354cc28296dSAndrew Baumann     /* outputs to CPU cores */
355cc28296dSAndrew Baumann     qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
356cc28296dSAndrew Baumann     qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
35767d80321SZoltán Baldaszti 
35867d80321SZoltán Baldaszti     /* create a qemu virtual timer */
35967d80321SZoltán Baldaszti     timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL,
36067d80321SZoltán Baldaszti                   bcm2836_control_local_timer_tick, s);
361cc28296dSAndrew Baumann }
362cc28296dSAndrew Baumann 
363cc28296dSAndrew Baumann static const VMStateDescription vmstate_bcm2836_control = {
364cc28296dSAndrew Baumann     .name = TYPE_BCM2836_CONTROL,
36567d80321SZoltán Baldaszti     .version_id = 2,
366cc28296dSAndrew Baumann     .minimum_version_id = 1,
367cc28296dSAndrew Baumann     .fields = (VMStateField[]) {
368cc28296dSAndrew Baumann         VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
369cc28296dSAndrew Baumann                              BCM2836_NCORES * BCM2836_MBPERCORE),
370cc28296dSAndrew Baumann         VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState),
371cc28296dSAndrew Baumann         VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState),
372cc28296dSAndrew Baumann         VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES),
373cc28296dSAndrew Baumann         VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
374cc28296dSAndrew Baumann                              BCM2836_NCORES),
37567d80321SZoltán Baldaszti         VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
37667d80321SZoltán Baldaszti         VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
37767d80321SZoltán Baldaszti         VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),
378cc28296dSAndrew Baumann         VMSTATE_END_OF_LIST()
379cc28296dSAndrew Baumann     }
380cc28296dSAndrew Baumann };
381cc28296dSAndrew Baumann 
382cc28296dSAndrew Baumann static void bcm2836_control_class_init(ObjectClass *klass, void *data)
383cc28296dSAndrew Baumann {
384cc28296dSAndrew Baumann     DeviceClass *dc = DEVICE_CLASS(klass);
385cc28296dSAndrew Baumann 
386cc28296dSAndrew Baumann     dc->reset = bcm2836_control_reset;
387cc28296dSAndrew Baumann     dc->vmsd = &vmstate_bcm2836_control;
388cc28296dSAndrew Baumann }
389cc28296dSAndrew Baumann 
390cc28296dSAndrew Baumann static TypeInfo bcm2836_control_info = {
391cc28296dSAndrew Baumann     .name          = TYPE_BCM2836_CONTROL,
392cc28296dSAndrew Baumann     .parent        = TYPE_SYS_BUS_DEVICE,
393cc28296dSAndrew Baumann     .instance_size = sizeof(BCM2836ControlState),
394cc28296dSAndrew Baumann     .class_init    = bcm2836_control_class_init,
395cc28296dSAndrew Baumann     .instance_init = bcm2836_control_init,
396cc28296dSAndrew Baumann };
397cc28296dSAndrew Baumann 
398cc28296dSAndrew Baumann static void bcm2836_control_register_types(void)
399cc28296dSAndrew Baumann {
400cc28296dSAndrew Baumann     type_register_static(&bcm2836_control_info);
401cc28296dSAndrew Baumann }
402cc28296dSAndrew Baumann 
403cc28296dSAndrew Baumann type_init(bcm2836_control_register_types)
404