2 * Rasperry Pi 2 emulation ARM control logic module.
3 * Copyright (c) 2015, Microsoft
4 * Written by Andrew Baumann
6 * Based on bcm2835_ic.c (Raspberry Pi emulation) (c) 2012 Gregory Estrade
7 * This code is licensed under the GNU GPLv2 and later.
9 * At present, only implements interrupt routing, and mailboxes (i.e.,
10 * not PMU interrupt, or AXI counters).
12 * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti
15 * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
18 #include "qemu/osdep.h"
19 #include "hw/intc/bcm2836_control.h"
21 #include "qemu/module.h"
23 #define REG_GPU_ROUTE 0x0c
24 #define REG_LOCALTIMERROUTING 0x24
25 #define REG_LOCALTIMERCONTROL 0x34
26 #define REG_LOCALTIMERACK 0x38
27 #define REG_TIMERCONTROL 0x40
28 #define REG_MBOXCONTROL 0x50
29 #define REG_IRQSRC 0x60
30 #define REG_FIQSRC 0x70
31 #define REG_MBOX0_WR 0x80
32 #define REG_MBOX0_RDCLR 0xc0
33 #define REG_LIMIT 0x100
35 #define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
36 #define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
38 #define IRQ_CNTPSIRQ 0
39 #define IRQ_CNTPNSIRQ 1
40 #define IRQ_CNTHPIRQ 2
42 #define IRQ_MAILBOX0 4
43 #define IRQ_MAILBOX1 5
44 #define IRQ_MAILBOX2 6
45 #define IRQ_MAILBOX3 7
50 #define IRQ_MAX IRQ_TIMER
52 #define LOCALTIMER_FREQ 38400000
53 #define LOCALTIMER_INTFLAG (1 << 31)
54 #define LOCALTIMER_RELOAD (1 << 30)
55 #define LOCALTIMER_INTENABLE (1 << 29)
56 #define LOCALTIMER_ENABLE (1 << 28)
57 #define LOCALTIMER_VALUE(x) ((x) & 0xfffffff)
59 static void deliver_local(BCM2836ControlState
*s
, uint8_t core
, uint8_t irq
,
60 uint32_t controlreg
, uint8_t controlidx
)
62 if (FIQ_BIT(controlreg
, controlidx
)) {
64 s
->fiqsrc
[core
] |= (uint32_t)1 << irq
;
65 } else if (IRQ_BIT(controlreg
, controlidx
)) {
67 s
->irqsrc
[core
] |= (uint32_t)1 << irq
;
69 /* the interrupt is masked */
73 /* Update interrupts. */
74 static void bcm2836_control_update(BCM2836ControlState
*s
)
78 /* reset pending IRQs/FIQs */
79 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
80 s
->irqsrc
[i
] = s
->fiqsrc
[i
] = 0;
83 /* apply routing logic, update status regs */
85 assert(s
->route_gpu_irq
< BCM2836_NCORES
);
86 s
->irqsrc
[s
->route_gpu_irq
] |= (uint32_t)1 << IRQ_GPU
;
90 assert(s
->route_gpu_fiq
< BCM2836_NCORES
);
91 s
->fiqsrc
[s
->route_gpu_fiq
] |= (uint32_t)1 << IRQ_GPU
;
95 * handle the control module 'local timer' interrupt for one of the
96 * cores' IRQ/FIQ; this is distinct from the per-CPU timer
97 * interrupts handled below.
99 if ((s
->local_timer_control
& LOCALTIMER_INTENABLE
) &&
100 (s
->local_timer_control
& LOCALTIMER_INTFLAG
)) {
101 if (s
->route_localtimer
& 4) {
102 s
->fiqsrc
[(s
->route_localtimer
& 3)] |= (uint32_t)1 << IRQ_TIMER
;
104 s
->irqsrc
[(s
->route_localtimer
& 3)] |= (uint32_t)1 << IRQ_TIMER
;
108 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
109 /* handle local timer interrupts for this core */
110 if (s
->timerirqs
[i
]) {
111 assert(s
->timerirqs
[i
] < (1 << (IRQ_CNTVIRQ
+ 1))); /* sane mask? */
112 for (j
= 0; j
<= IRQ_CNTVIRQ
; j
++) {
113 if ((s
->timerirqs
[i
] & (1 << j
)) != 0) {
114 /* local interrupt j is set */
115 deliver_local(s
, i
, j
, s
->timercontrol
[i
], j
);
120 /* handle mailboxes for this core */
121 for (j
= 0; j
< BCM2836_MBPERCORE
; j
++) {
122 if (s
->mailboxes
[i
* BCM2836_MBPERCORE
+ j
] != 0) {
123 /* mailbox j is set */
124 deliver_local(s
, i
, j
+ IRQ_MAILBOX0
, s
->mailboxcontrol
[i
], j
);
129 /* call set_irq appropriately for each output */
130 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
131 qemu_set_irq(s
->irq
[i
], s
->irqsrc
[i
] != 0);
132 qemu_set_irq(s
->fiq
[i
], s
->fiqsrc
[i
] != 0);
136 static void bcm2836_control_set_local_irq(void *opaque
, int core
, int local_irq
,
139 BCM2836ControlState
*s
= opaque
;
141 assert(core
>= 0 && core
< BCM2836_NCORES
);
142 assert(local_irq
>= 0 && local_irq
<= IRQ_CNTVIRQ
);
144 s
->timerirqs
[core
] = deposit32(s
->timerirqs
[core
], local_irq
, 1, !!level
);
146 bcm2836_control_update(s
);
149 /* XXX: the following wrapper functions are a kludgy workaround,
150 * needed because I can't seem to pass useful information in the "irq"
151 * parameter when using named interrupts. Feel free to clean this up!
154 static void bcm2836_control_set_local_irq0(void *opaque
, int core
, int level
)
156 bcm2836_control_set_local_irq(opaque
, core
, 0, level
);
159 static void bcm2836_control_set_local_irq1(void *opaque
, int core
, int level
)
161 bcm2836_control_set_local_irq(opaque
, core
, 1, level
);
164 static void bcm2836_control_set_local_irq2(void *opaque
, int core
, int level
)
166 bcm2836_control_set_local_irq(opaque
, core
, 2, level
);
169 static void bcm2836_control_set_local_irq3(void *opaque
, int core
, int level
)
171 bcm2836_control_set_local_irq(opaque
, core
, 3, level
);
174 static void bcm2836_control_set_gpu_irq(void *opaque
, int irq
, int level
)
176 BCM2836ControlState
*s
= opaque
;
180 bcm2836_control_update(s
);
183 static void bcm2836_control_set_gpu_fiq(void *opaque
, int irq
, int level
)
185 BCM2836ControlState
*s
= opaque
;
189 bcm2836_control_update(s
);
192 static void bcm2836_control_local_timer_set_next(void *opaque
)
194 BCM2836ControlState
*s
= opaque
;
197 assert(LOCALTIMER_VALUE(s
->local_timer_control
) > 0);
199 next_event
= qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL
) +
200 muldiv64(LOCALTIMER_VALUE(s
->local_timer_control
),
201 NANOSECONDS_PER_SECOND
, LOCALTIMER_FREQ
);
202 timer_mod(&s
->timer
, next_event
);
205 static void bcm2836_control_local_timer_tick(void *opaque
)
207 BCM2836ControlState
*s
= opaque
;
209 bcm2836_control_local_timer_set_next(s
);
211 s
->local_timer_control
|= LOCALTIMER_INTFLAG
;
212 bcm2836_control_update(s
);
215 static void bcm2836_control_local_timer_control(void *opaque
, uint32_t val
)
217 BCM2836ControlState
*s
= opaque
;
219 s
->local_timer_control
= val
;
220 if (val
& LOCALTIMER_ENABLE
) {
221 bcm2836_control_local_timer_set_next(s
);
223 timer_del(&s
->timer
);
227 static void bcm2836_control_local_timer_ack(void *opaque
, uint32_t val
)
229 BCM2836ControlState
*s
= opaque
;
231 if (val
& LOCALTIMER_INTFLAG
) {
232 s
->local_timer_control
&= ~LOCALTIMER_INTFLAG
;
234 if ((val
& LOCALTIMER_RELOAD
) &&
235 (s
->local_timer_control
& LOCALTIMER_ENABLE
)) {
236 bcm2836_control_local_timer_set_next(s
);
240 static uint64_t bcm2836_control_read(void *opaque
, hwaddr offset
, unsigned size
)
242 BCM2836ControlState
*s
= opaque
;
244 if (offset
== REG_GPU_ROUTE
) {
245 assert(s
->route_gpu_fiq
< BCM2836_NCORES
246 && s
->route_gpu_irq
< BCM2836_NCORES
);
247 return ((uint32_t)s
->route_gpu_fiq
<< 2) | s
->route_gpu_irq
;
248 } else if (offset
== REG_LOCALTIMERROUTING
) {
249 return s
->route_localtimer
;
250 } else if (offset
== REG_LOCALTIMERCONTROL
) {
251 return s
->local_timer_control
;
252 } else if (offset
== REG_LOCALTIMERACK
) {
254 } else if (offset
>= REG_TIMERCONTROL
&& offset
< REG_MBOXCONTROL
) {
255 return s
->timercontrol
[(offset
- REG_TIMERCONTROL
) >> 2];
256 } else if (offset
>= REG_MBOXCONTROL
&& offset
< REG_IRQSRC
) {
257 return s
->mailboxcontrol
[(offset
- REG_MBOXCONTROL
) >> 2];
258 } else if (offset
>= REG_IRQSRC
&& offset
< REG_FIQSRC
) {
259 return s
->irqsrc
[(offset
- REG_IRQSRC
) >> 2];
260 } else if (offset
>= REG_FIQSRC
&& offset
< REG_MBOX0_WR
) {
261 return s
->fiqsrc
[(offset
- REG_FIQSRC
) >> 2];
262 } else if (offset
>= REG_MBOX0_RDCLR
&& offset
< REG_LIMIT
) {
263 return s
->mailboxes
[(offset
- REG_MBOX0_RDCLR
) >> 2];
265 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad offset %"HWADDR_PRIx
"\n",
271 static void bcm2836_control_write(void *opaque
, hwaddr offset
,
272 uint64_t val
, unsigned size
)
274 BCM2836ControlState
*s
= opaque
;
276 if (offset
== REG_GPU_ROUTE
) {
277 s
->route_gpu_irq
= val
& 0x3;
278 s
->route_gpu_fiq
= (val
>> 2) & 0x3;
279 } else if (offset
== REG_LOCALTIMERROUTING
) {
280 s
->route_localtimer
= val
& 7;
281 } else if (offset
== REG_LOCALTIMERCONTROL
) {
282 bcm2836_control_local_timer_control(s
, val
);
283 } else if (offset
== REG_LOCALTIMERACK
) {
284 bcm2836_control_local_timer_ack(s
, val
);
285 } else if (offset
>= REG_TIMERCONTROL
&& offset
< REG_MBOXCONTROL
) {
286 s
->timercontrol
[(offset
- REG_TIMERCONTROL
) >> 2] = val
& 0xff;
287 } else if (offset
>= REG_MBOXCONTROL
&& offset
< REG_IRQSRC
) {
288 s
->mailboxcontrol
[(offset
- REG_MBOXCONTROL
) >> 2] = val
& 0xff;
289 } else if (offset
>= REG_MBOX0_WR
&& offset
< REG_MBOX0_RDCLR
) {
290 s
->mailboxes
[(offset
- REG_MBOX0_WR
) >> 2] |= val
;
291 } else if (offset
>= REG_MBOX0_RDCLR
&& offset
< REG_LIMIT
) {
292 s
->mailboxes
[(offset
- REG_MBOX0_RDCLR
) >> 2] &= ~val
;
294 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad offset %"HWADDR_PRIx
"\n",
299 bcm2836_control_update(s
);
302 static const MemoryRegionOps bcm2836_control_ops
= {
303 .read
= bcm2836_control_read
,
304 .write
= bcm2836_control_write
,
305 .endianness
= DEVICE_NATIVE_ENDIAN
,
306 .valid
.min_access_size
= 4,
307 .valid
.max_access_size
= 4,
310 static void bcm2836_control_reset(DeviceState
*d
)
312 BCM2836ControlState
*s
= BCM2836_CONTROL(d
);
315 s
->route_gpu_irq
= s
->route_gpu_fiq
= 0;
317 timer_del(&s
->timer
);
318 s
->route_localtimer
= 0;
319 s
->local_timer_control
= 0;
321 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
322 s
->timercontrol
[i
] = 0;
323 s
->mailboxcontrol
[i
] = 0;
326 for (i
= 0; i
< BCM2836_NCORES
* BCM2836_MBPERCORE
; i
++) {
331 static void bcm2836_control_init(Object
*obj
)
333 BCM2836ControlState
*s
= BCM2836_CONTROL(obj
);
334 DeviceState
*dev
= DEVICE(obj
);
336 memory_region_init_io(&s
->iomem
, obj
, &bcm2836_control_ops
, s
,
337 TYPE_BCM2836_CONTROL
, REG_LIMIT
);
338 sysbus_init_mmio(SYS_BUS_DEVICE(s
), &s
->iomem
);
340 /* inputs from each CPU core */
341 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq0
, "cntpsirq",
343 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq1
, "cntpnsirq",
345 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq2
, "cnthpirq",
347 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq3
, "cntvirq",
350 /* IRQ and FIQ inputs from upstream bcm2835 controller */
351 qdev_init_gpio_in_named(dev
, bcm2836_control_set_gpu_irq
, "gpu-irq", 1);
352 qdev_init_gpio_in_named(dev
, bcm2836_control_set_gpu_fiq
, "gpu-fiq", 1);
354 /* outputs to CPU cores */
355 qdev_init_gpio_out_named(dev
, s
->irq
, "irq", BCM2836_NCORES
);
356 qdev_init_gpio_out_named(dev
, s
->fiq
, "fiq", BCM2836_NCORES
);
358 /* create a qemu virtual timer */
359 timer_init_ns(&s
->timer
, QEMU_CLOCK_VIRTUAL
,
360 bcm2836_control_local_timer_tick
, s
);
363 static const VMStateDescription vmstate_bcm2836_control
= {
364 .name
= TYPE_BCM2836_CONTROL
,
366 .minimum_version_id
= 1,
367 .fields
= (VMStateField
[]) {
368 VMSTATE_UINT32_ARRAY(mailboxes
, BCM2836ControlState
,
369 BCM2836_NCORES
* BCM2836_MBPERCORE
),
370 VMSTATE_UINT8(route_gpu_irq
, BCM2836ControlState
),
371 VMSTATE_UINT8(route_gpu_fiq
, BCM2836ControlState
),
372 VMSTATE_UINT32_ARRAY(timercontrol
, BCM2836ControlState
, BCM2836_NCORES
),
373 VMSTATE_UINT32_ARRAY(mailboxcontrol
, BCM2836ControlState
,
375 VMSTATE_TIMER_V(timer
, BCM2836ControlState
, 2),
376 VMSTATE_UINT32_V(local_timer_control
, BCM2836ControlState
, 2),
377 VMSTATE_UINT8_V(route_localtimer
, BCM2836ControlState
, 2),
378 VMSTATE_END_OF_LIST()
382 static void bcm2836_control_class_init(ObjectClass
*klass
, void *data
)
384 DeviceClass
*dc
= DEVICE_CLASS(klass
);
386 dc
->reset
= bcm2836_control_reset
;
387 dc
->vmsd
= &vmstate_bcm2836_control
;
390 static TypeInfo bcm2836_control_info
= {
391 .name
= TYPE_BCM2836_CONTROL
,
392 .parent
= TYPE_SYS_BUS_DEVICE
,
393 .instance_size
= sizeof(BCM2836ControlState
),
394 .class_init
= bcm2836_control_class_init
,
395 .instance_init
= bcm2836_control_init
,
398 static void bcm2836_control_register_types(void)
400 type_register_static(&bcm2836_control_info
);
403 type_init(bcm2836_control_register_types
)