2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 * This code is licensed under the GNU GPLv2 and later.
5 * This file models the system mailboxes, which are used for
6 * communication with low-bandwidth GPU peripherals. Refs:
7 * https://github.com/raspberrypi/firmware/wiki/Mailboxes
8 * https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes
11 #include "qemu/osdep.h"
12 #include "qapi/error.h"
14 #include "hw/misc/bcm2835_mbox.h"
15 #include "migration/vmstate.h"
17 #include "qemu/module.h"
20 #define MAIL0_PEEK 0x90
21 #define MAIL0_SENDER 0x94
22 #define MAIL1_STATUS 0xb8
24 /* Mailbox status register */
25 #define MAIL0_STATUS 0x98
26 #define ARM_MS_FULL 0x80000000
27 #define ARM_MS_EMPTY 0x40000000
28 #define ARM_MS_LEVEL 0x400000FF /* Max. value depends on mailbox depth */
30 /* MAILBOX config/status register */
31 #define MAIL0_CONFIG 0x9c
32 /* ANY write to this register clears the error bits! */
33 #define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mbox irq enable: has data */
34 #define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mbox irq enable: has space */
35 #define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mbox irq enable: Opp is empty */
36 #define ARM_MC_MAIL_CLEAR 0x00000008 /* mbox clear write 1, then 0 */
37 #define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mbox irq pending: has space */
38 #define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mbox irq pending: Opp is empty */
39 #define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mbox irq pending */
41 #define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */
42 #define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */
43 #define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */
45 static void mbox_update_status(BCM2835Mbox
*mb
)
47 mb
->status
&= ~(ARM_MS_EMPTY
| ARM_MS_FULL
);
49 mb
->status
|= ARM_MS_EMPTY
;
50 } else if (mb
->count
== MBOX_SIZE
) {
51 mb
->status
|= ARM_MS_FULL
;
55 static void mbox_reset(BCM2835Mbox
*mb
)
61 for (n
= 0; n
< MBOX_SIZE
; n
++) {
62 mb
->reg
[n
] = MBOX_INVALID_DATA
;
64 mbox_update_status(mb
);
67 static uint32_t mbox_pull(BCM2835Mbox
*mb
, int index
)
72 assert(mb
->count
> 0);
73 assert(index
< mb
->count
);
76 for (n
= index
+ 1; n
< mb
->count
; n
++) {
77 mb
->reg
[n
- 1] = mb
->reg
[n
];
80 mb
->reg
[mb
->count
] = MBOX_INVALID_DATA
;
82 mbox_update_status(mb
);
87 static void mbox_push(BCM2835Mbox
*mb
, uint32_t val
)
89 assert(mb
->count
< MBOX_SIZE
);
90 mb
->reg
[mb
->count
++] = val
;
91 mbox_update_status(mb
);
94 static void bcm2835_mbox_update(BCM2835MboxState
*s
)
100 s
->mbox_irq_disabled
= true;
102 /* Get pending responses and put them in the vc->arm mbox,
103 * as long as it's not full
105 for (n
= 0; n
< MBOX_CHAN_COUNT
; n
++) {
106 while (s
->available
[n
] && !(s
->mbox
[0].status
& ARM_MS_FULL
)) {
107 value
= ldl_le_phys(&s
->mbox_as
, n
<< MBOX_AS_CHAN_SHIFT
);
108 assert(value
!= MBOX_INVALID_DATA
); /* Pending interrupt but no data */
109 mbox_push(&s
->mbox
[0], value
);
113 /* TODO (?): Try to push pending requests from the arm->vc mbox */
115 /* Re-enable calls from the IRQ routine */
116 s
->mbox_irq_disabled
= false;
118 /* Update ARM IRQ status */
120 s
->mbox
[0].config
&= ~ARM_MC_IHAVEDATAIRQPEND
;
121 if (!(s
->mbox
[0].status
& ARM_MS_EMPTY
)) {
122 s
->mbox
[0].config
|= ARM_MC_IHAVEDATAIRQPEND
;
123 if (s
->mbox
[0].config
& ARM_MC_IHAVEDATAIRQEN
) {
127 trace_bcm2835_mbox_irq(set
);
128 qemu_set_irq(s
->arm_irq
, set
);
131 static void bcm2835_mbox_set_irq(void *opaque
, int irq
, int level
)
133 BCM2835MboxState
*s
= opaque
;
135 s
->available
[irq
] = level
;
137 /* avoid recursively calling bcm2835_mbox_update when the interrupt
138 * status changes due to the ldl_phys call within that function
140 if (!s
->mbox_irq_disabled
) {
141 bcm2835_mbox_update(s
);
145 static uint64_t bcm2835_mbox_read(void *opaque
, hwaddr offset
, unsigned size
)
147 BCM2835MboxState
*s
= opaque
;
153 case 0x80 ... 0x8c: /* MAIL0_READ */
154 if (s
->mbox
[0].status
& ARM_MS_EMPTY
) {
155 res
= MBOX_INVALID_DATA
;
157 res
= mbox_pull(&s
->mbox
[0], 0);
162 res
= s
->mbox
[0].reg
[0];
169 res
= s
->mbox
[0].status
;
173 res
= s
->mbox
[0].config
;
177 res
= s
->mbox
[1].status
;
181 qemu_log_mask(LOG_UNIMP
, "%s: Unsupported offset 0x%"HWADDR_PRIx
"\n",
183 trace_bcm2835_mbox_read(size
, offset
, res
);
186 trace_bcm2835_mbox_read(size
, offset
, res
);
188 bcm2835_mbox_update(s
);
193 static void bcm2835_mbox_write(void *opaque
, hwaddr offset
,
194 uint64_t value
, unsigned size
)
196 BCM2835MboxState
*s
= opaque
;
202 trace_bcm2835_mbox_write(size
, offset
, value
);
208 s
->mbox
[0].config
&= ~ARM_MC_IHAVEDATAIRQEN
;
209 s
->mbox
[0].config
|= value
& ARM_MC_IHAVEDATAIRQEN
;
212 case 0xa0 ... 0xac: /* MAIL1_WRITE */
213 if (s
->mbox
[1].status
& ARM_MS_FULL
) {
215 qemu_log_mask(LOG_GUEST_ERROR
, "%s: mailbox full\n", __func__
);
218 if (ch
< MBOX_CHAN_COUNT
) {
219 childaddr
= ch
<< MBOX_AS_CHAN_SHIFT
;
220 if (ldl_le_phys(&s
->mbox_as
, childaddr
+ MBOX_AS_PENDING
)) {
221 /* Child busy, push delayed. Push it in the arm->vc mbox */
222 mbox_push(&s
->mbox
[1], value
);
224 /* Push it directly to the child device */
225 stl_le_phys(&s
->mbox_as
, childaddr
, value
);
228 /* Invalid channel number */
229 qemu_log_mask(LOG_GUEST_ERROR
, "%s: invalid channel %u\n",
236 qemu_log_mask(LOG_UNIMP
, "%s: Unsupported offset 0x%"HWADDR_PRIx
237 " value 0x%"PRIx64
"\n",
238 __func__
, offset
, value
);
242 bcm2835_mbox_update(s
);
245 static const MemoryRegionOps bcm2835_mbox_ops
= {
246 .read
= bcm2835_mbox_read
,
247 .write
= bcm2835_mbox_write
,
248 .endianness
= DEVICE_NATIVE_ENDIAN
,
249 .valid
.min_access_size
= 4,
250 .valid
.max_access_size
= 4,
253 /* vmstate of a single mailbox */
254 static const VMStateDescription vmstate_bcm2835_mbox_box
= {
255 .name
= TYPE_BCM2835_MBOX
"_box",
257 .minimum_version_id
= 1,
258 .fields
= (VMStateField
[]) {
259 VMSTATE_UINT32_ARRAY(reg
, BCM2835Mbox
, MBOX_SIZE
),
260 VMSTATE_UINT32(count
, BCM2835Mbox
),
261 VMSTATE_UINT32(status
, BCM2835Mbox
),
262 VMSTATE_UINT32(config
, BCM2835Mbox
),
263 VMSTATE_END_OF_LIST()
267 /* vmstate of the entire device */
268 static const VMStateDescription vmstate_bcm2835_mbox
= {
269 .name
= TYPE_BCM2835_MBOX
,
271 .minimum_version_id
= 1,
272 .minimum_version_id_old
= 1,
273 .fields
= (VMStateField
[]) {
274 VMSTATE_BOOL_ARRAY(available
, BCM2835MboxState
, MBOX_CHAN_COUNT
),
275 VMSTATE_STRUCT_ARRAY(mbox
, BCM2835MboxState
, 2, 1,
276 vmstate_bcm2835_mbox_box
, BCM2835Mbox
),
277 VMSTATE_END_OF_LIST()
281 static void bcm2835_mbox_init(Object
*obj
)
283 BCM2835MboxState
*s
= BCM2835_MBOX(obj
);
285 memory_region_init_io(&s
->iomem
, obj
, &bcm2835_mbox_ops
, s
,
286 TYPE_BCM2835_MBOX
, 0x400);
287 sysbus_init_mmio(SYS_BUS_DEVICE(s
), &s
->iomem
);
288 sysbus_init_irq(SYS_BUS_DEVICE(s
), &s
->arm_irq
);
289 qdev_init_gpio_in(DEVICE(s
), bcm2835_mbox_set_irq
, MBOX_CHAN_COUNT
);
292 static void bcm2835_mbox_reset(DeviceState
*dev
)
294 BCM2835MboxState
*s
= BCM2835_MBOX(dev
);
297 mbox_reset(&s
->mbox
[0]);
298 mbox_reset(&s
->mbox
[1]);
299 s
->mbox_irq_disabled
= false;
300 for (n
= 0; n
< MBOX_CHAN_COUNT
; n
++) {
301 s
->available
[n
] = false;
305 static void bcm2835_mbox_realize(DeviceState
*dev
, Error
**errp
)
307 BCM2835MboxState
*s
= BCM2835_MBOX(dev
);
311 obj
= object_property_get_link(OBJECT(dev
), "mbox-mr", &err
);
313 error_setg(errp
, "%s: required mbox-mr link not found: %s",
314 __func__
, error_get_pretty(err
));
318 s
->mbox_mr
= MEMORY_REGION(obj
);
319 address_space_init(&s
->mbox_as
, s
->mbox_mr
, TYPE_BCM2835_MBOX
"-memory");
320 bcm2835_mbox_reset(dev
);
323 static void bcm2835_mbox_class_init(ObjectClass
*klass
, void *data
)
325 DeviceClass
*dc
= DEVICE_CLASS(klass
);
327 dc
->realize
= bcm2835_mbox_realize
;
328 dc
->reset
= bcm2835_mbox_reset
;
329 dc
->vmsd
= &vmstate_bcm2835_mbox
;
332 static TypeInfo bcm2835_mbox_info
= {
333 .name
= TYPE_BCM2835_MBOX
,
334 .parent
= TYPE_SYS_BUS_DEVICE
,
335 .instance_size
= sizeof(BCM2835MboxState
),
336 .class_init
= bcm2835_mbox_class_init
,
337 .instance_init
= bcm2835_mbox_init
,
340 static void bcm2835_mbox_register_types(void)
342 type_register_static(&bcm2835_mbox_info
);
345 type_init(bcm2835_mbox_register_types
)