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 "migration/vmstate.h"
23 #include "qemu/module.h"
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
37 #define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
38 #define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
40 #define IRQ_CNTPSIRQ 0
41 #define IRQ_CNTPNSIRQ 1
42 #define IRQ_CNTHPIRQ 2
44 #define IRQ_MAILBOX0 4
45 #define IRQ_MAILBOX1 5
46 #define IRQ_MAILBOX2 6
47 #define IRQ_MAILBOX3 7
52 #define IRQ_MAX IRQ_TIMER
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)
61 static void deliver_local(BCM2836ControlState
*s
, uint8_t core
, uint8_t irq
,
62 uint32_t controlreg
, uint8_t controlidx
)
64 if (FIQ_BIT(controlreg
, controlidx
)) {
66 s
->fiqsrc
[core
] |= (uint32_t)1 << irq
;
67 } else if (IRQ_BIT(controlreg
, controlidx
)) {
69 s
->irqsrc
[core
] |= (uint32_t)1 << irq
;
71 /* the interrupt is masked */
75 /* Update interrupts. */
76 static void bcm2836_control_update(BCM2836ControlState
*s
)
80 /* reset pending IRQs/FIQs */
81 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
82 s
->irqsrc
[i
] = s
->fiqsrc
[i
] = 0;
85 /* apply routing logic, update status regs */
87 assert(s
->route_gpu_irq
< BCM2836_NCORES
);
88 s
->irqsrc
[s
->route_gpu_irq
] |= (uint32_t)1 << IRQ_GPU
;
92 assert(s
->route_gpu_fiq
< BCM2836_NCORES
);
93 s
->fiqsrc
[s
->route_gpu_fiq
] |= (uint32_t)1 << IRQ_GPU
;
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.
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
;
106 s
->irqsrc
[(s
->route_localtimer
& 3)] |= (uint32_t)1 << IRQ_TIMER
;
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
);
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
);
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);
138 static void bcm2836_control_set_local_irq(void *opaque
, int core
, int local_irq
,
141 BCM2836ControlState
*s
= opaque
;
143 assert(core
>= 0 && core
< BCM2836_NCORES
);
144 assert(local_irq
>= 0 && local_irq
<= IRQ_CNTVIRQ
);
146 s
->timerirqs
[core
] = deposit32(s
->timerirqs
[core
], local_irq
, 1, !!level
);
148 bcm2836_control_update(s
);
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!
156 static void bcm2836_control_set_local_irq0(void *opaque
, int core
, int level
)
158 bcm2836_control_set_local_irq(opaque
, core
, 0, level
);
161 static void bcm2836_control_set_local_irq1(void *opaque
, int core
, int level
)
163 bcm2836_control_set_local_irq(opaque
, core
, 1, level
);
166 static void bcm2836_control_set_local_irq2(void *opaque
, int core
, int level
)
168 bcm2836_control_set_local_irq(opaque
, core
, 2, level
);
171 static void bcm2836_control_set_local_irq3(void *opaque
, int core
, int level
)
173 bcm2836_control_set_local_irq(opaque
, core
, 3, level
);
176 static void bcm2836_control_set_gpu_irq(void *opaque
, int irq
, int level
)
178 BCM2836ControlState
*s
= opaque
;
182 bcm2836_control_update(s
);
185 static void bcm2836_control_set_gpu_fiq(void *opaque
, int irq
, int level
)
187 BCM2836ControlState
*s
= opaque
;
191 bcm2836_control_update(s
);
194 static void bcm2836_control_local_timer_set_next(void *opaque
)
196 BCM2836ControlState
*s
= opaque
;
199 assert(LOCALTIMER_VALUE(s
->local_timer_control
) > 0);
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
);
207 static void bcm2836_control_local_timer_tick(void *opaque
)
209 BCM2836ControlState
*s
= opaque
;
211 bcm2836_control_local_timer_set_next(s
);
213 s
->local_timer_control
|= LOCALTIMER_INTFLAG
;
214 bcm2836_control_update(s
);
217 static void bcm2836_control_local_timer_control(void *opaque
, uint32_t val
)
219 BCM2836ControlState
*s
= opaque
;
221 s
->local_timer_control
= val
;
222 if (val
& LOCALTIMER_ENABLE
) {
223 bcm2836_control_local_timer_set_next(s
);
225 timer_del(&s
->timer
);
229 static void bcm2836_control_local_timer_ack(void *opaque
, uint32_t val
)
231 BCM2836ControlState
*s
= opaque
;
233 if (val
& LOCALTIMER_INTFLAG
) {
234 s
->local_timer_control
&= ~LOCALTIMER_INTFLAG
;
236 if ((val
& LOCALTIMER_RELOAD
) &&
237 (s
->local_timer_control
& LOCALTIMER_ENABLE
)) {
238 bcm2836_control_local_timer_set_next(s
);
242 static uint64_t bcm2836_control_read(void *opaque
, hwaddr offset
, unsigned size
)
244 BCM2836ControlState
*s
= opaque
;
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
) {
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];
267 qemu_log_mask(LOG_UNIMP
, "%s: Unsupported offset 0x%"HWADDR_PRIx
"\n",
273 static void bcm2836_control_write(void *opaque
, hwaddr offset
,
274 uint64_t val
, unsigned size
)
276 BCM2836ControlState
*s
= opaque
;
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
;
296 qemu_log_mask(LOG_UNIMP
, "%s: Unsupported offset 0x%"HWADDR_PRIx
297 " value 0x%"PRIx64
"\n",
298 __func__
, offset
, val
);
302 bcm2836_control_update(s
);
305 static const MemoryRegionOps bcm2836_control_ops
= {
306 .read
= bcm2836_control_read
,
307 .write
= bcm2836_control_write
,
308 .endianness
= DEVICE_NATIVE_ENDIAN
,
309 .valid
.min_access_size
= 4,
310 .valid
.max_access_size
= 4,
313 static void bcm2836_control_reset(DeviceState
*d
)
315 BCM2836ControlState
*s
= BCM2836_CONTROL(d
);
318 s
->route_gpu_irq
= s
->route_gpu_fiq
= 0;
320 timer_del(&s
->timer
);
321 s
->route_localtimer
= 0;
322 s
->local_timer_control
= 0;
324 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
325 s
->timercontrol
[i
] = 0;
326 s
->mailboxcontrol
[i
] = 0;
329 for (i
= 0; i
< BCM2836_NCORES
* BCM2836_MBPERCORE
; i
++) {
334 static void bcm2836_control_init(Object
*obj
)
336 BCM2836ControlState
*s
= BCM2836_CONTROL(obj
);
337 DeviceState
*dev
= DEVICE(obj
);
339 memory_region_init_io(&s
->iomem
, obj
, &bcm2836_control_ops
, s
,
340 TYPE_BCM2836_CONTROL
, REG_LIMIT
);
341 sysbus_init_mmio(SYS_BUS_DEVICE(s
), &s
->iomem
);
343 /* inputs from each CPU core */
344 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq0
, "cntpsirq",
346 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq1
, "cntpnsirq",
348 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq2
, "cnthpirq",
350 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq3
, "cntvirq",
353 /* IRQ and FIQ inputs from upstream bcm2835 controller */
354 qdev_init_gpio_in_named(dev
, bcm2836_control_set_gpu_irq
, "gpu-irq", 1);
355 qdev_init_gpio_in_named(dev
, bcm2836_control_set_gpu_fiq
, "gpu-fiq", 1);
357 /* outputs to CPU cores */
358 qdev_init_gpio_out_named(dev
, s
->irq
, "irq", BCM2836_NCORES
);
359 qdev_init_gpio_out_named(dev
, s
->fiq
, "fiq", BCM2836_NCORES
);
361 /* create a qemu virtual timer */
362 timer_init_ns(&s
->timer
, QEMU_CLOCK_VIRTUAL
,
363 bcm2836_control_local_timer_tick
, s
);
366 static const VMStateDescription vmstate_bcm2836_control
= {
367 .name
= TYPE_BCM2836_CONTROL
,
369 .minimum_version_id
= 1,
370 .fields
= (VMStateField
[]) {
371 VMSTATE_UINT32_ARRAY(mailboxes
, BCM2836ControlState
,
372 BCM2836_NCORES
* BCM2836_MBPERCORE
),
373 VMSTATE_UINT8(route_gpu_irq
, BCM2836ControlState
),
374 VMSTATE_UINT8(route_gpu_fiq
, BCM2836ControlState
),
375 VMSTATE_UINT32_ARRAY(timercontrol
, BCM2836ControlState
, BCM2836_NCORES
),
376 VMSTATE_UINT32_ARRAY(mailboxcontrol
, BCM2836ControlState
,
378 VMSTATE_TIMER_V(timer
, BCM2836ControlState
, 2),
379 VMSTATE_UINT32_V(local_timer_control
, BCM2836ControlState
, 2),
380 VMSTATE_UINT8_V(route_localtimer
, BCM2836ControlState
, 2),
381 VMSTATE_END_OF_LIST()
385 static void bcm2836_control_class_init(ObjectClass
*klass
, void *data
)
387 DeviceClass
*dc
= DEVICE_CLASS(klass
);
389 dc
->reset
= bcm2836_control_reset
;
390 dc
->vmsd
= &vmstate_bcm2836_control
;
393 static TypeInfo bcm2836_control_info
= {
394 .name
= TYPE_BCM2836_CONTROL
,
395 .parent
= TYPE_SYS_BUS_DEVICE
,
396 .instance_size
= sizeof(BCM2836ControlState
),
397 .class_init
= bcm2836_control_class_init
,
398 .instance_init
= bcm2836_control_init
,
401 static void bcm2836_control_register_types(void)
403 type_register_static(&bcm2836_control_info
);
406 type_init(bcm2836_control_register_types
)