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"
20 #define REG_GPU_ROUTE 0x0c
21 #define REG_TIMERCONTROL 0x40
22 #define REG_MBOXCONTROL 0x50
23 #define REG_IRQSRC 0x60
24 #define REG_FIQSRC 0x70
25 #define REG_MBOX0_WR 0x80
26 #define REG_MBOX0_RDCLR 0xc0
27 #define REG_LIMIT 0x100
29 #define IRQ_BIT(cntrl, num) (((cntrl) & (1 << (num))) != 0)
30 #define FIQ_BIT(cntrl, num) (((cntrl) & (1 << ((num) + 4))) != 0)
32 #define IRQ_CNTPSIRQ 0
33 #define IRQ_CNTPNSIRQ 1
34 #define IRQ_CNTHPIRQ 2
36 #define IRQ_MAILBOX0 4
37 #define IRQ_MAILBOX1 5
38 #define IRQ_MAILBOX2 6
39 #define IRQ_MAILBOX3 7
44 #define IRQ_MAX IRQ_TIMER
46 static void deliver_local(BCM2836ControlState
*s
, uint8_t core
, uint8_t irq
,
47 uint32_t controlreg
, uint8_t controlidx
)
49 if (FIQ_BIT(controlreg
, controlidx
)) {
51 s
->fiqsrc
[core
] |= (uint32_t)1 << irq
;
52 } else if (IRQ_BIT(controlreg
, controlidx
)) {
54 s
->irqsrc
[core
] |= (uint32_t)1 << irq
;
56 /* the interrupt is masked */
60 /* Update interrupts. */
61 static void bcm2836_control_update(BCM2836ControlState
*s
)
65 /* reset pending IRQs/FIQs */
66 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
67 s
->irqsrc
[i
] = s
->fiqsrc
[i
] = 0;
70 /* apply routing logic, update status regs */
72 assert(s
->route_gpu_irq
< BCM2836_NCORES
);
73 s
->irqsrc
[s
->route_gpu_irq
] |= (uint32_t)1 << IRQ_GPU
;
77 assert(s
->route_gpu_fiq
< BCM2836_NCORES
);
78 s
->fiqsrc
[s
->route_gpu_fiq
] |= (uint32_t)1 << IRQ_GPU
;
81 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
82 /* handle local timer interrupts for this core */
83 if (s
->timerirqs
[i
]) {
84 assert(s
->timerirqs
[i
] < (1 << (IRQ_CNTVIRQ
+ 1))); /* sane mask? */
85 for (j
= 0; j
<= IRQ_CNTVIRQ
; j
++) {
86 if ((s
->timerirqs
[i
] & (1 << j
)) != 0) {
87 /* local interrupt j is set */
88 deliver_local(s
, i
, j
, s
->timercontrol
[i
], j
);
93 /* handle mailboxes for this core */
94 for (j
= 0; j
< BCM2836_MBPERCORE
; j
++) {
95 if (s
->mailboxes
[i
* BCM2836_MBPERCORE
+ j
] != 0) {
96 /* mailbox j is set */
97 deliver_local(s
, i
, j
+ IRQ_MAILBOX0
, s
->mailboxcontrol
[i
], j
);
102 /* call set_irq appropriately for each output */
103 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
104 qemu_set_irq(s
->irq
[i
], s
->irqsrc
[i
] != 0);
105 qemu_set_irq(s
->fiq
[i
], s
->fiqsrc
[i
] != 0);
109 static void bcm2836_control_set_local_irq(void *opaque
, int core
, int local_irq
,
112 BCM2836ControlState
*s
= opaque
;
114 assert(core
>= 0 && core
< BCM2836_NCORES
);
115 assert(local_irq
>= 0 && local_irq
<= IRQ_CNTVIRQ
);
117 s
->timerirqs
[core
] = deposit32(s
->timerirqs
[core
], local_irq
, 1, !!level
);
119 bcm2836_control_update(s
);
122 /* XXX: the following wrapper functions are a kludgy workaround,
123 * needed because I can't seem to pass useful information in the "irq"
124 * parameter when using named interrupts. Feel free to clean this up!
127 static void bcm2836_control_set_local_irq0(void *opaque
, int core
, int level
)
129 bcm2836_control_set_local_irq(opaque
, core
, 0, level
);
132 static void bcm2836_control_set_local_irq1(void *opaque
, int core
, int level
)
134 bcm2836_control_set_local_irq(opaque
, core
, 1, level
);
137 static void bcm2836_control_set_local_irq2(void *opaque
, int core
, int level
)
139 bcm2836_control_set_local_irq(opaque
, core
, 2, level
);
142 static void bcm2836_control_set_local_irq3(void *opaque
, int core
, int level
)
144 bcm2836_control_set_local_irq(opaque
, core
, 3, level
);
147 static void bcm2836_control_set_gpu_irq(void *opaque
, int irq
, int level
)
149 BCM2836ControlState
*s
= opaque
;
153 bcm2836_control_update(s
);
156 static void bcm2836_control_set_gpu_fiq(void *opaque
, int irq
, int level
)
158 BCM2836ControlState
*s
= opaque
;
162 bcm2836_control_update(s
);
165 static uint64_t bcm2836_control_read(void *opaque
, hwaddr offset
, unsigned size
)
167 BCM2836ControlState
*s
= opaque
;
169 if (offset
== REG_GPU_ROUTE
) {
170 assert(s
->route_gpu_fiq
< BCM2836_NCORES
171 && s
->route_gpu_irq
< BCM2836_NCORES
);
172 return ((uint32_t)s
->route_gpu_fiq
<< 2) | s
->route_gpu_irq
;
173 } else if (offset
>= REG_TIMERCONTROL
&& offset
< REG_MBOXCONTROL
) {
174 return s
->timercontrol
[(offset
- REG_TIMERCONTROL
) >> 2];
175 } else if (offset
>= REG_MBOXCONTROL
&& offset
< REG_IRQSRC
) {
176 return s
->mailboxcontrol
[(offset
- REG_MBOXCONTROL
) >> 2];
177 } else if (offset
>= REG_IRQSRC
&& offset
< REG_FIQSRC
) {
178 return s
->irqsrc
[(offset
- REG_IRQSRC
) >> 2];
179 } else if (offset
>= REG_FIQSRC
&& offset
< REG_MBOX0_WR
) {
180 return s
->fiqsrc
[(offset
- REG_FIQSRC
) >> 2];
181 } else if (offset
>= REG_MBOX0_RDCLR
&& offset
< REG_LIMIT
) {
182 return s
->mailboxes
[(offset
- REG_MBOX0_RDCLR
) >> 2];
184 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad offset %"HWADDR_PRIx
"\n",
190 static void bcm2836_control_write(void *opaque
, hwaddr offset
,
191 uint64_t val
, unsigned size
)
193 BCM2836ControlState
*s
= opaque
;
195 if (offset
== REG_GPU_ROUTE
) {
196 s
->route_gpu_irq
= val
& 0x3;
197 s
->route_gpu_fiq
= (val
>> 2) & 0x3;
198 } else if (offset
>= REG_TIMERCONTROL
&& offset
< REG_MBOXCONTROL
) {
199 s
->timercontrol
[(offset
- REG_TIMERCONTROL
) >> 2] = val
& 0xff;
200 } else if (offset
>= REG_MBOXCONTROL
&& offset
< REG_IRQSRC
) {
201 s
->mailboxcontrol
[(offset
- REG_MBOXCONTROL
) >> 2] = val
& 0xff;
202 } else if (offset
>= REG_MBOX0_WR
&& offset
< REG_MBOX0_RDCLR
) {
203 s
->mailboxes
[(offset
- REG_MBOX0_WR
) >> 2] |= val
;
204 } else if (offset
>= REG_MBOX0_RDCLR
&& offset
< REG_LIMIT
) {
205 s
->mailboxes
[(offset
- REG_MBOX0_RDCLR
) >> 2] &= ~val
;
207 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad offset %"HWADDR_PRIx
"\n",
212 bcm2836_control_update(s
);
215 static const MemoryRegionOps bcm2836_control_ops
= {
216 .read
= bcm2836_control_read
,
217 .write
= bcm2836_control_write
,
218 .endianness
= DEVICE_NATIVE_ENDIAN
,
219 .valid
.min_access_size
= 4,
220 .valid
.max_access_size
= 4,
223 static void bcm2836_control_reset(DeviceState
*d
)
225 BCM2836ControlState
*s
= BCM2836_CONTROL(d
);
228 s
->route_gpu_irq
= s
->route_gpu_fiq
= 0;
230 for (i
= 0; i
< BCM2836_NCORES
; i
++) {
231 s
->timercontrol
[i
] = 0;
232 s
->mailboxcontrol
[i
] = 0;
235 for (i
= 0; i
< BCM2836_NCORES
* BCM2836_MBPERCORE
; i
++) {
240 static void bcm2836_control_init(Object
*obj
)
242 BCM2836ControlState
*s
= BCM2836_CONTROL(obj
);
243 DeviceState
*dev
= DEVICE(obj
);
245 memory_region_init_io(&s
->iomem
, obj
, &bcm2836_control_ops
, s
,
246 TYPE_BCM2836_CONTROL
, REG_LIMIT
);
247 sysbus_init_mmio(SYS_BUS_DEVICE(s
), &s
->iomem
);
249 /* inputs from each CPU core */
250 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq0
, "cntpsirq",
252 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq1
, "cntpnsirq",
254 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq2
, "cnthpirq",
256 qdev_init_gpio_in_named(dev
, bcm2836_control_set_local_irq3
, "cntvirq",
259 /* IRQ and FIQ inputs from upstream bcm2835 controller */
260 qdev_init_gpio_in_named(dev
, bcm2836_control_set_gpu_irq
, "gpu-irq", 1);
261 qdev_init_gpio_in_named(dev
, bcm2836_control_set_gpu_fiq
, "gpu-fiq", 1);
263 /* outputs to CPU cores */
264 qdev_init_gpio_out_named(dev
, s
->irq
, "irq", BCM2836_NCORES
);
265 qdev_init_gpio_out_named(dev
, s
->fiq
, "fiq", BCM2836_NCORES
);
268 static const VMStateDescription vmstate_bcm2836_control
= {
269 .name
= TYPE_BCM2836_CONTROL
,
271 .minimum_version_id
= 1,
272 .fields
= (VMStateField
[]) {
273 VMSTATE_UINT32_ARRAY(mailboxes
, BCM2836ControlState
,
274 BCM2836_NCORES
* BCM2836_MBPERCORE
),
275 VMSTATE_UINT8(route_gpu_irq
, BCM2836ControlState
),
276 VMSTATE_UINT8(route_gpu_fiq
, BCM2836ControlState
),
277 VMSTATE_UINT32_ARRAY(timercontrol
, BCM2836ControlState
, BCM2836_NCORES
),
278 VMSTATE_UINT32_ARRAY(mailboxcontrol
, BCM2836ControlState
,
280 VMSTATE_END_OF_LIST()
284 static void bcm2836_control_class_init(ObjectClass
*klass
, void *data
)
286 DeviceClass
*dc
= DEVICE_CLASS(klass
);
288 dc
->reset
= bcm2836_control_reset
;
289 dc
->vmsd
= &vmstate_bcm2836_control
;
292 static TypeInfo bcm2836_control_info
= {
293 .name
= TYPE_BCM2836_CONTROL
,
294 .parent
= TYPE_SYS_BUS_DEVICE
,
295 .instance_size
= sizeof(BCM2836ControlState
),
296 .class_init
= bcm2836_control_class_init
,
297 .instance_init
= bcm2836_control_init
,
300 static void bcm2836_control_register_types(void)
302 type_register_static(&bcm2836_control_info
);
305 type_init(bcm2836_control_register_types
)