2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 * This code is licensed under the GNU GPLv2 and later.
6 #include "hw/misc/bcm2835_sbm.h"
7 #include "hw/arm/bcm2835_arm_control.h"
9 static void mbox_update_status(BCM2835Mbox
*mb
)
12 mb
->status
|= ARM_MS_EMPTY
;
14 mb
->status
&= ~ARM_MS_EMPTY
;
16 if (mb
->count
== MBOX_SIZE
) {
17 mb
->status
|= ARM_MS_FULL
;
19 mb
->status
&= ~ARM_MS_FULL
;
23 static void mbox_init(BCM2835Mbox
*mb
)
28 for (n
= 0; n
< MBOX_SIZE
; n
++) {
29 mb
->reg
[n
] = MBOX_INVALID_DATA
;
31 mbox_update_status(mb
);
34 static uint32_t mbox_pull(BCM2835Mbox
*mb
, int index
)
39 assert(mb
->count
> 0);
40 assert(index
< mb
->count
);
43 for (n
= index
+ 1; n
< mb
->count
; n
++) {
44 mb
->reg
[n
- 1] = mb
->reg
[n
];
47 mb
->reg
[mb
->count
] = MBOX_INVALID_DATA
;
49 mbox_update_status(mb
);
54 static void mbox_push(BCM2835Mbox
*mb
, uint32_t val
)
56 assert(mb
->count
< MBOX_SIZE
);
57 mb
->reg
[mb
->count
++] = val
;
58 mbox_update_status(mb
);
61 static void bcm2835_sbm_update(BCM2835SbmState
*s
)
67 /* Avoid unwanted recursive calls */
68 s
->mbox_irq_disabled
= 1;
70 /* Get pending responses and put them in the vc->arm mbox */
74 if (s
->mbox
[0].status
& ARM_MS_FULL
) {
75 /* vc->arm mbox full, exit */
77 for (n
= 0; n
< MBOX_CHAN_COUNT
; n
++) {
78 if (s
->available
[n
]) {
79 value
= ldl_phys(&s
->mbox_as
, n
<<4);
80 if (value
!= MBOX_INVALID_DATA
) {
81 mbox_push(&s
->mbox
[0], value
);
92 /* Try to push pending requests from the arm->vc mbox */
95 /* Re-enable calls from the IRQ routine */
96 s
->mbox_irq_disabled
= 0;
98 /* Update ARM IRQ status */
100 if (s
->mbox
[0].status
& ARM_MS_EMPTY
) {
101 s
->mbox
[0].config
&= ~ARM_MC_IHAVEDATAIRQPEND
;
103 s
->mbox
[0].config
|= ARM_MC_IHAVEDATAIRQPEND
;
104 if (s
->mbox
[0].config
& ARM_MC_IHAVEDATAIRQEN
) {
108 qemu_set_irq(s
->arm_irq
, set
);
111 static void bcm2835_sbm_set_irq(void *opaque
, int irq
, int level
)
113 BCM2835SbmState
*s
= (BCM2835SbmState
*)opaque
;
114 s
->available
[irq
] = level
;
115 if (!s
->mbox_irq_disabled
) {
116 bcm2835_sbm_update(s
);
120 static uint64_t bcm2835_sbm_read(void *opaque
, hwaddr offset
,
123 BCM2835SbmState
*s
= (BCM2835SbmState
*)opaque
;
129 case 0x80: /* MAIL0_READ */
133 if (s
->mbox
[0].status
& ARM_MS_EMPTY
) {
134 res
= MBOX_INVALID_DATA
;
136 res
= mbox_pull(&s
->mbox
[0], 0);
139 case 0x90: /* MAIL0_PEEK */
140 res
= s
->mbox
[0].reg
[0];
142 case 0x94: /* MAIL0_SENDER */
144 case 0x98: /* MAIL0_STATUS */
145 res
= s
->mbox
[0].status
;
147 case 0x9c: /* MAIL0_CONFIG */
148 res
= s
->mbox
[0].config
;
150 case 0xb8: /* MAIL1_STATUS */
151 res
= s
->mbox
[1].status
;
154 qemu_log_mask(LOG_GUEST_ERROR
,
155 "bcm2835_sbm_read: Bad offset %x\n", (int)offset
);
159 bcm2835_sbm_update(s
);
164 static void bcm2835_sbm_write(void *opaque
, hwaddr offset
,
165 uint64_t value
, unsigned size
)
169 BCM2835SbmState
*s
= (BCM2835SbmState
*)opaque
;
174 case 0x94: /* MAIL0_SENDER */
176 case 0x9c: /* MAIL0_CONFIG */
177 s
->mbox
[0].config
&= ~ARM_MC_IHAVEDATAIRQEN
;
178 s
->mbox
[0].config
|= value
& ARM_MC_IHAVEDATAIRQEN
;
184 if (s
->mbox
[1].status
& ARM_MS_FULL
) {
188 if (ch
< MBOX_CHAN_COUNT
) {
189 if (ldl_phys(&s
->mbox_as
, (ch
<<4) + 4)) {
190 /* Push delayed, push it in the arm->vc mbox */
191 mbox_push(&s
->mbox
[1], value
);
193 stl_phys(&s
->mbox_as
, ch
<<4, value
);
196 /* Invalid channel number */
201 qemu_log_mask(LOG_GUEST_ERROR
,
202 "bcm2835_sbm_write: Bad offset %x\n", (int)offset
);
206 bcm2835_sbm_update(s
);
209 static const MemoryRegionOps bcm2835_sbm_ops
= {
210 .read
= bcm2835_sbm_read
,
211 .write
= bcm2835_sbm_write
,
212 .endianness
= DEVICE_NATIVE_ENDIAN
,
215 static const VMStateDescription vmstate_bcm2835_sbm
= {
216 .name
= TYPE_BCM2835_SBM
,
218 .minimum_version_id
= 1,
219 .minimum_version_id_old
= 1,
220 .fields
= (VMStateField
[]) {
221 VMSTATE_END_OF_LIST()
225 static void bcm2835_sbm_init(Object
*obj
)
227 BCM2835SbmState
*s
= BCM2835_SBM(obj
);
228 memory_region_init_io(&s
->iomem
, obj
, &bcm2835_sbm_ops
, s
,
229 TYPE_BCM2835_SBM
, 0x400);
230 sysbus_init_mmio(SYS_BUS_DEVICE(s
), &s
->iomem
);
231 sysbus_init_irq(SYS_BUS_DEVICE(s
), &s
->arm_irq
);
232 qdev_init_gpio_in(DEVICE(s
), bcm2835_sbm_set_irq
, MBOX_CHAN_COUNT
);
235 static void bcm2835_sbm_realize(DeviceState
*dev
, Error
**errp
)
238 BCM2835SbmState
*s
= BCM2835_SBM(dev
);
242 obj
= object_property_get_link(OBJECT(dev
), "mbox_mr", &err
);
243 if (err
|| obj
== NULL
) {
244 error_setg(errp
, "bcm2835_sbm: required mbox_mr link not found");
248 s
->mbox_mr
= MEMORY_REGION(obj
);
249 address_space_init(&s
->mbox_as
, s
->mbox_mr
, NULL
);
251 mbox_init(&s
->mbox
[0]);
252 mbox_init(&s
->mbox
[1]);
253 s
->mbox_irq_disabled
= 0;
254 for (n
= 0; n
< MBOX_CHAN_COUNT
; n
++) {
259 static void bcm2835_sbm_class_init(ObjectClass
*klass
, void *data
)
261 DeviceClass
*dc
= DEVICE_CLASS(klass
);
263 dc
->realize
= bcm2835_sbm_realize
;
264 dc
->vmsd
= &vmstate_bcm2835_sbm
;
267 static TypeInfo bcm2835_sbm_info
= {
268 .name
= TYPE_BCM2835_SBM
,
269 .parent
= TYPE_SYS_BUS_DEVICE
,
270 .instance_size
= sizeof(BCM2835SbmState
),
271 .class_init
= bcm2835_sbm_class_init
,
272 .instance_init
= bcm2835_sbm_init
,
275 static void bcm2835_sbm_register_types(void)
277 type_register_static(&bcm2835_sbm_info
);
280 type_init(bcm2835_sbm_register_types
)