xref: /openbmc/qemu/hw/intc/bcm2836_control.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
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  *
8cc28296dSAndrew Baumann  * At present, only implements interrupt routing, and mailboxes (i.e.,
967d80321SZoltán Baldaszti  * not PMU interrupt, or AXI counters).
1067d80321SZoltán Baldaszti  *
1167d80321SZoltán Baldaszti  * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
12cc28296dSAndrew Baumann  *
13cc28296dSAndrew Baumann  * Ref:
14cc28296dSAndrew Baumann  * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
156111a0c0SPhilippe Mathieu-Daudé  *
166111a0c0SPhilippe Mathieu-Daudé  * This work is licensed under the terms of the GNU GPL, version 2 or later.
176111a0c0SPhilippe Mathieu-Daudé  * See the COPYING file in the top-level directory.
18cc28296dSAndrew Baumann  */
19cc28296dSAndrew Baumann 
20c964b660SPeter Maydell #include "qemu/osdep.h"
21cc28296dSAndrew Baumann #include "hw/intc/bcm2836_control.h"
2264552b6bSMarkus Armbruster #include "hw/irq.h"
23d6454270SMarkus Armbruster #include "migration/vmstate.h"
2403dd024fSPaolo Bonzini #include "qemu/log.h"
250b8fa32fSMarkus Armbruster #include "qemu/module.h"
26cc28296dSAndrew Baumann 
27cc28296dSAndrew Baumann #define REG_GPU_ROUTE           0x0c
2867d80321SZoltán Baldaszti #define REG_LOCALTIMERROUTING   0x24
2967d80321SZoltán Baldaszti #define REG_LOCALTIMERCONTROL   0x34
3067d80321SZoltán Baldaszti #define REG_LOCALTIMERACK       0x38
31cc28296dSAndrew Baumann #define REG_TIMERCONTROL        0x40
32cc28296dSAndrew Baumann #define REG_MBOXCONTROL         0x50
33cc28296dSAndrew Baumann #define REG_IRQSRC              0x60
34cc28296dSAndrew Baumann #define REG_FIQSRC              0x70
35cc28296dSAndrew Baumann #define REG_MBOX0_WR            0x80
36cc28296dSAndrew Baumann #define REG_MBOX0_RDCLR         0xc0
37cc28296dSAndrew Baumann #define REG_LIMIT              0x100
38cc28296dSAndrew Baumann 
39cc28296dSAndrew Baumann #define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
40cc28296dSAndrew Baumann #define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
41cc28296dSAndrew Baumann 
42cc28296dSAndrew Baumann #define IRQ_CNTPSIRQ    0
43cc28296dSAndrew Baumann #define IRQ_CNTPNSIRQ   1
44cc28296dSAndrew Baumann #define IRQ_CNTHPIRQ    2
45cc28296dSAndrew Baumann #define IRQ_CNTVIRQ     3
46cc28296dSAndrew Baumann #define IRQ_MAILBOX0    4
47cc28296dSAndrew Baumann #define IRQ_MAILBOX1    5
48cc28296dSAndrew Baumann #define IRQ_MAILBOX2    6
49cc28296dSAndrew Baumann #define IRQ_MAILBOX3    7
50cc28296dSAndrew Baumann #define IRQ_GPU         8
51cc28296dSAndrew Baumann #define IRQ_PMU         9
52cc28296dSAndrew Baumann #define IRQ_AXI         10
53cc28296dSAndrew Baumann #define IRQ_TIMER       11
54cc28296dSAndrew Baumann #define IRQ_MAX         IRQ_TIMER
55cc28296dSAndrew Baumann 
5667d80321SZoltán Baldaszti #define LOCALTIMER_FREQ      38400000
5767d80321SZoltán Baldaszti #define LOCALTIMER_INTFLAG   (1 << 31)
5867d80321SZoltán Baldaszti #define LOCALTIMER_RELOAD    (1 << 30)
5967d80321SZoltán Baldaszti #define LOCALTIMER_INTENABLE (1 << 29)
6067d80321SZoltán Baldaszti #define LOCALTIMER_ENABLE    (1 << 28)
6167d80321SZoltán Baldaszti #define LOCALTIMER_VALUE(x)  ((x) & 0xfffffff)
6267d80321SZoltán Baldaszti 
deliver_local(BCM2836ControlState * s,uint8_t core,uint8_t irq,uint32_t controlreg,uint8_t controlidx)63cc28296dSAndrew Baumann static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq,
64cc28296dSAndrew Baumann                           uint32_t controlreg, uint8_t controlidx)
65cc28296dSAndrew Baumann {
66cc28296dSAndrew Baumann     if (FIQ_BIT(controlreg, controlidx)) {
67cc28296dSAndrew Baumann         /* deliver a FIQ */
68cc28296dSAndrew Baumann         s->fiqsrc[core] |= (uint32_t)1 << irq;
69cc28296dSAndrew Baumann     } else if (IRQ_BIT(controlreg, controlidx)) {
70cc28296dSAndrew Baumann         /* deliver an IRQ */
71cc28296dSAndrew Baumann         s->irqsrc[core] |= (uint32_t)1 << irq;
72cc28296dSAndrew Baumann     } else {
73cc28296dSAndrew Baumann         /* the interrupt is masked */
74cc28296dSAndrew Baumann     }
75cc28296dSAndrew Baumann }
76cc28296dSAndrew Baumann 
77cc28296dSAndrew Baumann /* Update interrupts.  */
bcm2836_control_update(BCM2836ControlState * s)78cc28296dSAndrew Baumann static void bcm2836_control_update(BCM2836ControlState *s)
79cc28296dSAndrew Baumann {
80cc28296dSAndrew Baumann     int i, j;
81cc28296dSAndrew Baumann 
82cc28296dSAndrew Baumann     /* reset pending IRQs/FIQs */
83cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES; i++) {
84cc28296dSAndrew Baumann         s->irqsrc[i] = s->fiqsrc[i] = 0;
85cc28296dSAndrew Baumann     }
86cc28296dSAndrew Baumann 
87cc28296dSAndrew Baumann     /* apply routing logic, update status regs */
88cc28296dSAndrew Baumann     if (s->gpu_irq) {
89cc28296dSAndrew Baumann         assert(s->route_gpu_irq < BCM2836_NCORES);
90cc28296dSAndrew Baumann         s->irqsrc[s->route_gpu_irq] |= (uint32_t)1 << IRQ_GPU;
91cc28296dSAndrew Baumann     }
92cc28296dSAndrew Baumann 
93cc28296dSAndrew Baumann     if (s->gpu_fiq) {
94cc28296dSAndrew Baumann         assert(s->route_gpu_fiq < BCM2836_NCORES);
95cc28296dSAndrew Baumann         s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU;
96cc28296dSAndrew Baumann     }
97cc28296dSAndrew Baumann 
9867d80321SZoltán Baldaszti     /*
9967d80321SZoltán Baldaszti      * handle the control module 'local timer' interrupt for one of the
10067d80321SZoltán Baldaszti      * cores' IRQ/FIQ;  this is distinct from the per-CPU timer
10167d80321SZoltán Baldaszti      * interrupts handled below.
10267d80321SZoltán Baldaszti      */
10367d80321SZoltán Baldaszti     if ((s->local_timer_control & LOCALTIMER_INTENABLE) &&
10467d80321SZoltán Baldaszti         (s->local_timer_control & LOCALTIMER_INTFLAG)) {
10567d80321SZoltán Baldaszti         if (s->route_localtimer & 4) {
10667d80321SZoltán Baldaszti             s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
10767d80321SZoltán Baldaszti         } else {
10867d80321SZoltán Baldaszti             s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER;
10967d80321SZoltán Baldaszti         }
11067d80321SZoltán Baldaszti     }
11167d80321SZoltán Baldaszti 
112cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES; i++) {
113cc28296dSAndrew Baumann         /* handle local timer interrupts for this core */
114cc28296dSAndrew Baumann         if (s->timerirqs[i]) {
115cc28296dSAndrew Baumann             assert(s->timerirqs[i] < (1 << (IRQ_CNTVIRQ + 1))); /* sane mask? */
116cc28296dSAndrew Baumann             for (j = 0; j <= IRQ_CNTVIRQ; j++) {
117cc28296dSAndrew Baumann                 if ((s->timerirqs[i] & (1 << j)) != 0) {
118cc28296dSAndrew Baumann                     /* local interrupt j is set */
119cc28296dSAndrew Baumann                     deliver_local(s, i, j, s->timercontrol[i], j);
120cc28296dSAndrew Baumann                 }
121cc28296dSAndrew Baumann             }
122cc28296dSAndrew Baumann         }
123cc28296dSAndrew Baumann 
124cc28296dSAndrew Baumann         /* handle mailboxes for this core */
125cc28296dSAndrew Baumann         for (j = 0; j < BCM2836_MBPERCORE; j++) {
126cc28296dSAndrew Baumann             if (s->mailboxes[i * BCM2836_MBPERCORE + j] != 0) {
127cc28296dSAndrew Baumann                 /* mailbox j is set */
128cc28296dSAndrew Baumann                 deliver_local(s, i, j + IRQ_MAILBOX0, s->mailboxcontrol[i], j);
129cc28296dSAndrew Baumann             }
130cc28296dSAndrew Baumann         }
131cc28296dSAndrew Baumann     }
132cc28296dSAndrew Baumann 
133cc28296dSAndrew Baumann     /* call set_irq appropriately for each output */
134cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES; i++) {
135cc28296dSAndrew Baumann         qemu_set_irq(s->irq[i], s->irqsrc[i] != 0);
136cc28296dSAndrew Baumann         qemu_set_irq(s->fiq[i], s->fiqsrc[i] != 0);
137cc28296dSAndrew Baumann     }
138cc28296dSAndrew Baumann }
139cc28296dSAndrew Baumann 
bcm2836_control_set_local_irq(void * opaque,int core,int local_irq,int level)140cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq(void *opaque, int core, int local_irq,
141cc28296dSAndrew Baumann                                           int level)
142cc28296dSAndrew Baumann {
143cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
144cc28296dSAndrew Baumann 
145cc28296dSAndrew Baumann     assert(core >= 0 && core < BCM2836_NCORES);
146cc28296dSAndrew Baumann     assert(local_irq >= 0 && local_irq <= IRQ_CNTVIRQ);
147cc28296dSAndrew Baumann 
148cc28296dSAndrew Baumann     s->timerirqs[core] = deposit32(s->timerirqs[core], local_irq, 1, !!level);
149cc28296dSAndrew Baumann 
150cc28296dSAndrew Baumann     bcm2836_control_update(s);
151cc28296dSAndrew Baumann }
152cc28296dSAndrew Baumann 
153cc28296dSAndrew Baumann /* XXX: the following wrapper functions are a kludgy workaround,
154cc28296dSAndrew Baumann  * needed because I can't seem to pass useful information in the "irq"
155cc28296dSAndrew Baumann  * parameter when using named interrupts. Feel free to clean this up!
156cc28296dSAndrew Baumann  */
157cc28296dSAndrew Baumann 
bcm2836_control_set_local_irq0(void * opaque,int core,int level)158cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq0(void *opaque, int core, int level)
159cc28296dSAndrew Baumann {
160e7534f29SPhilippe Mathieu-Daudé     bcm2836_control_set_local_irq(opaque, core, IRQ_CNTPSIRQ, level);
161cc28296dSAndrew Baumann }
162cc28296dSAndrew Baumann 
bcm2836_control_set_local_irq1(void * opaque,int core,int level)163cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq1(void *opaque, int core, int level)
164cc28296dSAndrew Baumann {
165e7534f29SPhilippe Mathieu-Daudé     bcm2836_control_set_local_irq(opaque, core, IRQ_CNTPNSIRQ, level);
166cc28296dSAndrew Baumann }
167cc28296dSAndrew Baumann 
bcm2836_control_set_local_irq2(void * opaque,int core,int level)168cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq2(void *opaque, int core, int level)
169cc28296dSAndrew Baumann {
170e7534f29SPhilippe Mathieu-Daudé     bcm2836_control_set_local_irq(opaque, core, IRQ_CNTHPIRQ, level);
171cc28296dSAndrew Baumann }
172cc28296dSAndrew Baumann 
bcm2836_control_set_local_irq3(void * opaque,int core,int level)173cc28296dSAndrew Baumann static void bcm2836_control_set_local_irq3(void *opaque, int core, int level)
174cc28296dSAndrew Baumann {
175e7534f29SPhilippe Mathieu-Daudé     bcm2836_control_set_local_irq(opaque, core, IRQ_CNTVIRQ, level);
176cc28296dSAndrew Baumann }
177cc28296dSAndrew Baumann 
bcm2836_control_set_gpu_irq(void * opaque,int irq,int level)178cc28296dSAndrew Baumann static void bcm2836_control_set_gpu_irq(void *opaque, int irq, int level)
179cc28296dSAndrew Baumann {
180cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
181cc28296dSAndrew Baumann 
182cc28296dSAndrew Baumann     s->gpu_irq = level;
183cc28296dSAndrew Baumann 
184cc28296dSAndrew Baumann     bcm2836_control_update(s);
185cc28296dSAndrew Baumann }
186cc28296dSAndrew Baumann 
bcm2836_control_set_gpu_fiq(void * opaque,int irq,int level)187cc28296dSAndrew Baumann static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level)
188cc28296dSAndrew Baumann {
189cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
190cc28296dSAndrew Baumann 
191cc28296dSAndrew Baumann     s->gpu_fiq = level;
192cc28296dSAndrew Baumann 
193cc28296dSAndrew Baumann     bcm2836_control_update(s);
194cc28296dSAndrew Baumann }
195cc28296dSAndrew Baumann 
bcm2836_control_local_timer_set_next(void * opaque)19667d80321SZoltán Baldaszti static void bcm2836_control_local_timer_set_next(void *opaque)
19767d80321SZoltán Baldaszti {
19867d80321SZoltán Baldaszti     BCM2836ControlState *s = opaque;
19967d80321SZoltán Baldaszti     uint64_t next_event;
20067d80321SZoltán Baldaszti 
20167d80321SZoltán Baldaszti     assert(LOCALTIMER_VALUE(s->local_timer_control) > 0);
20267d80321SZoltán Baldaszti 
20367d80321SZoltán Baldaszti     next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
20467d80321SZoltán Baldaszti         muldiv64(LOCALTIMER_VALUE(s->local_timer_control),
20567d80321SZoltán Baldaszti             NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ);
20667d80321SZoltán Baldaszti     timer_mod(&s->timer, next_event);
20767d80321SZoltán Baldaszti }
20867d80321SZoltán Baldaszti 
bcm2836_control_local_timer_tick(void * opaque)20967d80321SZoltán Baldaszti static void bcm2836_control_local_timer_tick(void *opaque)
21067d80321SZoltán Baldaszti {
21167d80321SZoltán Baldaszti     BCM2836ControlState *s = opaque;
21267d80321SZoltán Baldaszti 
21367d80321SZoltán Baldaszti     bcm2836_control_local_timer_set_next(s);
21467d80321SZoltán Baldaszti 
21567d80321SZoltán Baldaszti     s->local_timer_control |= LOCALTIMER_INTFLAG;
21667d80321SZoltán Baldaszti     bcm2836_control_update(s);
21767d80321SZoltán Baldaszti }
21867d80321SZoltán Baldaszti 
bcm2836_control_local_timer_control(void * opaque,uint32_t val)21967d80321SZoltán Baldaszti static void bcm2836_control_local_timer_control(void *opaque, uint32_t val)
22067d80321SZoltán Baldaszti {
22167d80321SZoltán Baldaszti     BCM2836ControlState *s = opaque;
22267d80321SZoltán Baldaszti 
22367d80321SZoltán Baldaszti     s->local_timer_control = val;
22467d80321SZoltán Baldaszti     if (val & LOCALTIMER_ENABLE) {
22567d80321SZoltán Baldaszti         bcm2836_control_local_timer_set_next(s);
22667d80321SZoltán Baldaszti     } else {
22767d80321SZoltán Baldaszti         timer_del(&s->timer);
22867d80321SZoltán Baldaszti     }
22967d80321SZoltán Baldaszti }
23067d80321SZoltán Baldaszti 
bcm2836_control_local_timer_ack(void * opaque,uint32_t val)23167d80321SZoltán Baldaszti static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val)
23267d80321SZoltán Baldaszti {
23367d80321SZoltán Baldaszti     BCM2836ControlState *s = opaque;
23467d80321SZoltán Baldaszti 
23567d80321SZoltán Baldaszti     if (val & LOCALTIMER_INTFLAG) {
23667d80321SZoltán Baldaszti         s->local_timer_control &= ~LOCALTIMER_INTFLAG;
23767d80321SZoltán Baldaszti     }
23867d80321SZoltán Baldaszti     if ((val & LOCALTIMER_RELOAD) &&
23967d80321SZoltán Baldaszti         (s->local_timer_control & LOCALTIMER_ENABLE)) {
24067d80321SZoltán Baldaszti             bcm2836_control_local_timer_set_next(s);
24167d80321SZoltán Baldaszti     }
24267d80321SZoltán Baldaszti }
24367d80321SZoltán Baldaszti 
bcm2836_control_read(void * opaque,hwaddr offset,unsigned size)244cc28296dSAndrew Baumann static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size)
245cc28296dSAndrew Baumann {
246cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
247cc28296dSAndrew Baumann 
248cc28296dSAndrew Baumann     if (offset == REG_GPU_ROUTE) {
249cc28296dSAndrew Baumann         assert(s->route_gpu_fiq < BCM2836_NCORES
250cc28296dSAndrew Baumann                && s->route_gpu_irq < BCM2836_NCORES);
251cc28296dSAndrew Baumann         return ((uint32_t)s->route_gpu_fiq << 2) | s->route_gpu_irq;
25267d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERROUTING) {
25367d80321SZoltán Baldaszti         return s->route_localtimer;
25467d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERCONTROL) {
25567d80321SZoltán Baldaszti         return s->local_timer_control;
25667d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERACK) {
25767d80321SZoltán Baldaszti         return 0;
258cc28296dSAndrew Baumann     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
259cc28296dSAndrew Baumann         return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2];
260cc28296dSAndrew Baumann     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
261cc28296dSAndrew Baumann         return s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2];
262cc28296dSAndrew Baumann     } else if (offset >= REG_IRQSRC && offset < REG_FIQSRC) {
263cc28296dSAndrew Baumann         return s->irqsrc[(offset - REG_IRQSRC) >> 2];
264cc28296dSAndrew Baumann     } else if (offset >= REG_FIQSRC && offset < REG_MBOX0_WR) {
265cc28296dSAndrew Baumann         return s->fiqsrc[(offset - REG_FIQSRC) >> 2];
266cc28296dSAndrew Baumann     } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
267cc28296dSAndrew Baumann         return s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2];
268cc28296dSAndrew Baumann     } else {
269e1ecf8c8SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx"\n",
270cc28296dSAndrew Baumann                       __func__, offset);
271cc28296dSAndrew Baumann         return 0;
272cc28296dSAndrew Baumann     }
273cc28296dSAndrew Baumann }
274cc28296dSAndrew Baumann 
bcm2836_control_write(void * opaque,hwaddr offset,uint64_t val,unsigned size)275cc28296dSAndrew Baumann static void bcm2836_control_write(void *opaque, hwaddr offset,
276cc28296dSAndrew Baumann                                   uint64_t val, unsigned size)
277cc28296dSAndrew Baumann {
278cc28296dSAndrew Baumann     BCM2836ControlState *s = opaque;
279cc28296dSAndrew Baumann 
280cc28296dSAndrew Baumann     if (offset == REG_GPU_ROUTE) {
281cc28296dSAndrew Baumann         s->route_gpu_irq = val & 0x3;
282cc28296dSAndrew Baumann         s->route_gpu_fiq = (val >> 2) & 0x3;
28367d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERROUTING) {
28467d80321SZoltán Baldaszti         s->route_localtimer = val & 7;
28567d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERCONTROL) {
28667d80321SZoltán Baldaszti         bcm2836_control_local_timer_control(s, val);
28767d80321SZoltán Baldaszti     } else if (offset == REG_LOCALTIMERACK) {
28867d80321SZoltán Baldaszti         bcm2836_control_local_timer_ack(s, val);
289cc28296dSAndrew Baumann     } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) {
290cc28296dSAndrew Baumann         s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff;
291cc28296dSAndrew Baumann     } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) {
292cc28296dSAndrew Baumann         s->mailboxcontrol[(offset - REG_MBOXCONTROL) >> 2] = val & 0xff;
293cc28296dSAndrew Baumann     } else if (offset >= REG_MBOX0_WR && offset < REG_MBOX0_RDCLR) {
294cc28296dSAndrew Baumann         s->mailboxes[(offset - REG_MBOX0_WR) >> 2] |= val;
295cc28296dSAndrew Baumann     } else if (offset >= REG_MBOX0_RDCLR && offset < REG_LIMIT) {
296cc28296dSAndrew Baumann         s->mailboxes[(offset - REG_MBOX0_RDCLR) >> 2] &= ~val;
297cc28296dSAndrew Baumann     } else {
298e1ecf8c8SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_UNIMP, "%s: Unsupported offset 0x%"HWADDR_PRIx
299e1ecf8c8SPhilippe Mathieu-Daudé                                  " value 0x%"PRIx64"\n",
300e1ecf8c8SPhilippe Mathieu-Daudé                       __func__, offset, val);
301cc28296dSAndrew Baumann         return;
302cc28296dSAndrew Baumann     }
303cc28296dSAndrew Baumann 
304cc28296dSAndrew Baumann     bcm2836_control_update(s);
305cc28296dSAndrew Baumann }
306cc28296dSAndrew Baumann 
307cc28296dSAndrew Baumann static const MemoryRegionOps bcm2836_control_ops = {
308cc28296dSAndrew Baumann     .read = bcm2836_control_read,
309cc28296dSAndrew Baumann     .write = bcm2836_control_write,
310cc28296dSAndrew Baumann     .endianness = DEVICE_NATIVE_ENDIAN,
311cc28296dSAndrew Baumann     .valid.min_access_size = 4,
312cc28296dSAndrew Baumann     .valid.max_access_size = 4,
313cc28296dSAndrew Baumann };
314cc28296dSAndrew Baumann 
bcm2836_control_reset(DeviceState * d)315cc28296dSAndrew Baumann static void bcm2836_control_reset(DeviceState *d)
316cc28296dSAndrew Baumann {
317cc28296dSAndrew Baumann     BCM2836ControlState *s = BCM2836_CONTROL(d);
318cc28296dSAndrew Baumann     int i;
319cc28296dSAndrew Baumann 
320cc28296dSAndrew Baumann     s->route_gpu_irq = s->route_gpu_fiq = 0;
321cc28296dSAndrew Baumann 
32267d80321SZoltán Baldaszti     timer_del(&s->timer);
32367d80321SZoltán Baldaszti     s->route_localtimer = 0;
32467d80321SZoltán Baldaszti     s->local_timer_control = 0;
32567d80321SZoltán Baldaszti 
326cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES; i++) {
327cc28296dSAndrew Baumann         s->timercontrol[i] = 0;
328cc28296dSAndrew Baumann         s->mailboxcontrol[i] = 0;
329cc28296dSAndrew Baumann     }
330cc28296dSAndrew Baumann 
331cc28296dSAndrew Baumann     for (i = 0; i < BCM2836_NCORES * BCM2836_MBPERCORE; i++) {
332cc28296dSAndrew Baumann         s->mailboxes[i] = 0;
333cc28296dSAndrew Baumann     }
334cc28296dSAndrew Baumann }
335cc28296dSAndrew Baumann 
bcm2836_control_init(Object * obj)336cc28296dSAndrew Baumann static void bcm2836_control_init(Object *obj)
337cc28296dSAndrew Baumann {
338cc28296dSAndrew Baumann     BCM2836ControlState *s = BCM2836_CONTROL(obj);
339cc28296dSAndrew Baumann     DeviceState *dev = DEVICE(obj);
340cc28296dSAndrew Baumann 
341cc28296dSAndrew Baumann     memory_region_init_io(&s->iomem, obj, &bcm2836_control_ops, s,
342cc28296dSAndrew Baumann                           TYPE_BCM2836_CONTROL, REG_LIMIT);
343cc28296dSAndrew Baumann     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
344cc28296dSAndrew Baumann 
345cc28296dSAndrew Baumann     /* inputs from each CPU core */
346cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq0, "cntpsirq",
347cc28296dSAndrew Baumann                             BCM2836_NCORES);
348cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq1, "cntpnsirq",
349cc28296dSAndrew Baumann                             BCM2836_NCORES);
350cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq2, "cnthpirq",
351cc28296dSAndrew Baumann                             BCM2836_NCORES);
352cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_local_irq3, "cntvirq",
353cc28296dSAndrew Baumann                             BCM2836_NCORES);
354cc28296dSAndrew Baumann 
355cc28296dSAndrew Baumann     /* IRQ and FIQ inputs from upstream bcm2835 controller */
356cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_irq, "gpu-irq", 1);
357cc28296dSAndrew Baumann     qdev_init_gpio_in_named(dev, bcm2836_control_set_gpu_fiq, "gpu-fiq", 1);
358cc28296dSAndrew Baumann 
359cc28296dSAndrew Baumann     /* outputs to CPU cores */
360cc28296dSAndrew Baumann     qdev_init_gpio_out_named(dev, s->irq, "irq", BCM2836_NCORES);
361cc28296dSAndrew Baumann     qdev_init_gpio_out_named(dev, s->fiq, "fiq", BCM2836_NCORES);
36267d80321SZoltán Baldaszti 
36367d80321SZoltán Baldaszti     /* create a qemu virtual timer */
36467d80321SZoltán Baldaszti     timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL,
36567d80321SZoltán Baldaszti                   bcm2836_control_local_timer_tick, s);
366cc28296dSAndrew Baumann }
367cc28296dSAndrew Baumann 
368cc28296dSAndrew Baumann static const VMStateDescription vmstate_bcm2836_control = {
369cc28296dSAndrew Baumann     .name = TYPE_BCM2836_CONTROL,
37067d80321SZoltán Baldaszti     .version_id = 2,
371cc28296dSAndrew Baumann     .minimum_version_id = 1,
37245b1f81dSRichard Henderson     .fields = (const VMStateField[]) {
373cc28296dSAndrew Baumann         VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState,
374cc28296dSAndrew Baumann                              BCM2836_NCORES * BCM2836_MBPERCORE),
375cc28296dSAndrew Baumann         VMSTATE_UINT8(route_gpu_irq, BCM2836ControlState),
376cc28296dSAndrew Baumann         VMSTATE_UINT8(route_gpu_fiq, BCM2836ControlState),
377cc28296dSAndrew Baumann         VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES),
378cc28296dSAndrew Baumann         VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState,
379cc28296dSAndrew Baumann                              BCM2836_NCORES),
38067d80321SZoltán Baldaszti         VMSTATE_TIMER_V(timer, BCM2836ControlState, 2),
38167d80321SZoltán Baldaszti         VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2),
38267d80321SZoltán Baldaszti         VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2),
383cc28296dSAndrew Baumann         VMSTATE_END_OF_LIST()
384cc28296dSAndrew Baumann     }
385cc28296dSAndrew Baumann };
386cc28296dSAndrew Baumann 
bcm2836_control_class_init(ObjectClass * klass,void * data)387cc28296dSAndrew Baumann static void bcm2836_control_class_init(ObjectClass *klass, void *data)
388cc28296dSAndrew Baumann {
389cc28296dSAndrew Baumann     DeviceClass *dc = DEVICE_CLASS(klass);
390cc28296dSAndrew Baumann 
391*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, bcm2836_control_reset);
392cc28296dSAndrew Baumann     dc->vmsd = &vmstate_bcm2836_control;
393cc28296dSAndrew Baumann }
394cc28296dSAndrew Baumann 
3955e78c98bSBernhard Beschow static const TypeInfo bcm2836_control_info = {
396cc28296dSAndrew Baumann     .name          = TYPE_BCM2836_CONTROL,
397cc28296dSAndrew Baumann     .parent        = TYPE_SYS_BUS_DEVICE,
398cc28296dSAndrew Baumann     .instance_size = sizeof(BCM2836ControlState),
399cc28296dSAndrew Baumann     .class_init    = bcm2836_control_class_init,
400cc28296dSAndrew Baumann     .instance_init = bcm2836_control_init,
401cc28296dSAndrew Baumann };
402cc28296dSAndrew Baumann 
bcm2836_control_register_types(void)403cc28296dSAndrew Baumann static void bcm2836_control_register_types(void)
404cc28296dSAndrew Baumann {
405cc28296dSAndrew Baumann     type_register_static(&bcm2836_control_info);
406cc28296dSAndrew Baumann }
407cc28296dSAndrew Baumann 
408cc28296dSAndrew Baumann type_init(bcm2836_control_register_types)
409