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