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 local timer, PMU interrupt, or AXI counters).
13 * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf
16 #include "qemu/osdep.h"
17 #include "hw/intc/bcm2836_control.h"
19 #define REG_GPU_ROUTE 0x0c
20 #define REG_TIMERCONTROL 0x40
21 #define REG_MBOXCONTROL 0x50
22 #define REG_IRQSRC 0x60
23 #define REG_FIQSRC 0x70
24 #define REG_MBOX0_WR 0x80
25 #define REG_MBOX0_RDCLR 0xc0
26 #define REG_LIMIT 0x100
28 #define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
29 #define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
31 #define IRQ_CNTPSIRQ 0
32 #define IRQ_CNTPNSIRQ 1
33 #define IRQ_CNTHPIRQ 2
35 #define IRQ_MAILBOX0 4
36 #define IRQ_MAILBOX1 5
37 #define IRQ_MAILBOX2 6
38 #define IRQ_MAILBOX3 7
43 #define IRQ_MAX IRQ_TIMER
45 static void deliver_local(BCM2836ControlState
*s
, uint8_t core
, uint8_t irq
,
46 uint32_t controlreg
, uint8_t controlidx
)
48 if (FIQ_BIT(controlreg
, controlidx
)) {
50 s
->fiqsrc
[core
] |= (uint32_t)1 << irq
;
51 } else if (IRQ_BIT(controlreg
, controlidx
)) {
53 s
->irqsrc
[core
] |= (uint32_t)1 << irq
;
55 /* the interrupt is masked */
59 /* Update interrupts. */
60 static void bcm2836_control_update(BCM2836ControlState
*s
)
64 /* reset pending IRQs/FIQs */
65 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
66 s
->irqsrc
[i
] = s
->fiqsrc
[i
] = 0;
69 /* apply routing logic, update status regs */
71 assert(s
->route_gpu_irq
< BCM2836_NCORES
);
72 s
->irqsrc
[s
->route_gpu_irq
] |= (uint32_t)1 << IRQ_GPU
;
76 assert(s
->route_gpu_fiq
< BCM2836_NCORES
);
77 s
->fiqsrc
[s
->route_gpu_fiq
] |= (uint32_t)1 << IRQ_GPU
;
80 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
81 /* handle local timer interrupts for this core */
82 if (s
->timerirqs
[i
]) {
83 assert(s
->timerirqs
[i
] < (1 << (IRQ_CNTVIRQ
+ 1))); /* sane mask? */
84 for (j
= 0; j
<= IRQ_CNTVIRQ
; j
++) {
85 if ((s
->timerirqs
[i
] & (1 << j
)) != 0) {
86 /* local interrupt j is set */
87 deliver_local(s
, i
, j
, s
->timercontrol
[i
], j
);
92 /* handle mailboxes for this core */
93 for (j
= 0; j
< BCM2836_MBPERCORE
; j
++) {
94 if (s
->mailboxes
[i
* BCM2836_MBPERCORE
+ j
] != 0) {
95 /* mailbox j is set */
96 deliver_local(s
, i
, j
+ IRQ_MAILBOX0
, s
->mailboxcontrol
[i
], j
);
101 /* call set_irq appropriately for each output */
102 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
103 qemu_set_irq(s
->irq
[i
], s
->irqsrc
[i
] != 0);
104 qemu_set_irq(s
->fiq
[i
], s
->fiqsrc
[i
] != 0);
108 static void bcm2836_control_set_local_irq(void *opaque
, int core
, int local_irq
,
111 BCM2836ControlState
*s
= opaque
;
113 assert(core
>= 0 && core
< BCM2836_NCORES
);
114 assert(local_irq
>= 0 && local_irq
<= IRQ_CNTVIRQ
);
116 s
->timerirqs
[core
] = deposit32(s
->timerirqs
[core
], local_irq
, 1, !!level
);
118 bcm2836_control_update(s
);
121 /* XXX: the following wrapper functions are a kludgy workaround,
122 * needed because I can't seem to pass useful information in the "irq"
123 * parameter when using named interrupts. Feel free to clean this up!
126 static void bcm2836_control_set_local_irq0(void *opaque
, int core
, int level
)
128 bcm2836_control_set_local_irq(opaque
, core
, 0, level
);
131 static void bcm2836_control_set_local_irq1(void *opaque
, int core
, int level
)
133 bcm2836_control_set_local_irq(opaque
, core
, 1, level
);
136 static void bcm2836_control_set_local_irq2(void *opaque
, int core
, int level
)
138 bcm2836_control_set_local_irq(opaque
, core
, 2, level
);
141 static void bcm2836_control_set_local_irq3(void *opaque
, int core
, int level
)
143 bcm2836_control_set_local_irq(opaque
, core
, 3, level
);
146 static void bcm2836_control_set_gpu_irq(void *opaque
, int irq
, int level
)
148 BCM2836ControlState
*s
= opaque
;
152 bcm2836_control_update(s
);
155 static void bcm2836_control_set_gpu_fiq(void *opaque
, int irq
, int level
)
157 BCM2836ControlState
*s
= opaque
;
161 bcm2836_control_update(s
);
164 static uint64_t bcm2836_control_read(void *opaque
, hwaddr offset
, unsigned size
)
166 BCM2836ControlState
*s
= opaque
;
168 if (offset
== REG_GPU_ROUTE
) {
169 assert(s
->route_gpu_fiq
< BCM2836_NCORES
170 && s
->route_gpu_irq
< BCM2836_NCORES
);
171 return ((uint32_t)s
->route_gpu_fiq
<< 2) | s
->route_gpu_irq
;
172 } else if (offset
>= REG_TIMERCONTROL
&& offset
< REG_MBOXCONTROL
) {
173 return s
->timercontrol
[(offset
- REG_TIMERCONTROL
) >> 2];
174 } else if (offset
>= REG_MBOXCONTROL
&& offset
< REG_IRQSRC
) {
175 return s
->mailboxcontrol
[(offset
- REG_MBOXCONTROL
) >> 2];
176 } else if (offset
>= REG_IRQSRC
&& offset
< REG_FIQSRC
) {
177 return s
->irqsrc
[(offset
- REG_IRQSRC
) >> 2];
178 } else if (offset
>= REG_FIQSRC
&& offset
< REG_MBOX0_WR
) {
179 return s
->fiqsrc
[(offset
- REG_FIQSRC
) >> 2];
180 } else if (offset
>= REG_MBOX0_RDCLR
&& offset
< REG_LIMIT
) {
181 return s
->mailboxes
[(offset
- REG_MBOX0_RDCLR
) >> 2];
183 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad offset %"HWADDR_PRIx
"\n",
189 static void bcm2836_control_write(void *opaque
, hwaddr offset
,
190 uint64_t val
, unsigned size
)
192 BCM2836ControlState
*s
= opaque
;
194 if (offset
== REG_GPU_ROUTE
) {
195 s
->route_gpu_irq
= val
& 0x3;
196 s
->route_gpu_fiq
= (val
>> 2) & 0x3;
197 } else if (offset
>= REG_TIMERCONTROL
&& offset
< REG_MBOXCONTROL
) {
198 s
->timercontrol
[(offset
- REG_TIMERCONTROL
) >> 2] = val
& 0xff;
199 } else if (offset
>= REG_MBOXCONTROL
&& offset
< REG_IRQSRC
) {
200 s
->mailboxcontrol
[(offset
- REG_MBOXCONTROL
) >> 2] = val
& 0xff;
201 } else if (offset
>= REG_MBOX0_WR
&& offset
< REG_MBOX0_RDCLR
) {
202 s
->mailboxes
[(offset
- REG_MBOX0_WR
) >> 2] |= val
;
203 } else if (offset
>= REG_MBOX0_RDCLR
&& offset
< REG_LIMIT
) {
204 s
->mailboxes
[(offset
- REG_MBOX0_RDCLR
) >> 2] &= ~val
;
206 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad offset %"HWADDR_PRIx
"\n",
211 bcm2836_control_update(s
);
214 static const MemoryRegionOps bcm2836_control_ops
= {
215 .read
= bcm2836_control_read
,
216 .write
= bcm2836_control_write
,
217 .endianness
= DEVICE_NATIVE_ENDIAN
,
218 .valid
.min_access_size
= 4,
219 .valid
.max_access_size
= 4,
222 static void bcm2836_control_reset(DeviceState
*d
)
224 BCM2836ControlState
*s
= BCM2836_CONTROL(d
);
227 s
->route_gpu_irq
= s
->route_gpu_fiq
= 0;
229 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
230 s
->timercontrol
[i
] = 0;
231 s
->mailboxcontrol
[i
] = 0;
234 for (i
= 0; i
< BCM2836_NCORES
* BCM2836_MBPERCORE
; i
++) {
239 static void bcm2836_control_init(Object
*obj
)
241 BCM2836ControlState
*s
= BCM2836_CONTROL(obj
);
242 DeviceState
*dev
= DEVICE(obj
);
244 memory_region_init_io(&s
->iomem
, obj
, &bcm2836_control_ops
, s
,
245 TYPE_BCM2836_CONTROL
, REG_LIMIT
);
246 sysbus_init_mmio(SYS_BUS_DEVICE(s
), &s
->iomem
);
248 /* inputs from each CPU core */
249 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq0
, "cntpsirq",
251 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq1
, "cntpnsirq",
253 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq2
, "cnthpirq",
255 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq3
, "cntvirq",
258 /* IRQ and FIQ inputs from upstream bcm2835 controller */
259 qdev_init_gpio_in_named(dev
, bcm2836_control_set_gpu_irq
, "gpu-irq", 1);
260 qdev_init_gpio_in_named(dev
, bcm2836_control_set_gpu_fiq
, "gpu-fiq", 1);
262 /* outputs to CPU cores */
263 qdev_init_gpio_out_named(dev
, s
->irq
, "irq", BCM2836_NCORES
);
264 qdev_init_gpio_out_named(dev
, s
->fiq
, "fiq", BCM2836_NCORES
);
267 static const VMStateDescription vmstate_bcm2836_control
= {
268 .name
= TYPE_BCM2836_CONTROL
,
270 .minimum_version_id
= 1,
271 .fields
= (VMStateField
[]) {
272 VMSTATE_UINT32_ARRAY(mailboxes
, BCM2836ControlState
,
273 BCM2836_NCORES
* BCM2836_MBPERCORE
),
274 VMSTATE_UINT8(route_gpu_irq
, BCM2836ControlState
),
275 VMSTATE_UINT8(route_gpu_fiq
, BCM2836ControlState
),
276 VMSTATE_UINT32_ARRAY(timercontrol
, BCM2836ControlState
, BCM2836_NCORES
),
277 VMSTATE_UINT32_ARRAY(mailboxcontrol
, BCM2836ControlState
,
279 VMSTATE_END_OF_LIST()
283 static void bcm2836_control_class_init(ObjectClass
*klass
, void *data
)
285 DeviceClass
*dc
= DEVICE_CLASS(klass
);
287 dc
->reset
= bcm2836_control_reset
;
288 dc
->vmsd
= &vmstate_bcm2836_control
;
291 static TypeInfo bcm2836_control_info
= {
292 .name
= TYPE_BCM2836_CONTROL
,
293 .parent
= TYPE_SYS_BUS_DEVICE
,
294 .instance_size
= sizeof(BCM2836ControlState
),
295 .class_init
= bcm2836_control_class_init
,
296 .instance_init
= bcm2836_control_init
,
299 static void bcm2836_control_register_types(void)
301 type_register_static(&bcm2836_control_info
);
304 type_init(bcm2836_control_register_types
)