2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 * This code is licensed under the GNU GPLv2 and later.
7 #include "exec/address-spaces.h"
8 #include "hw/arm/bcm2835_common.h"
9 #include "hw/arm/bcm2835_arm_control.h"
11 #define TYPE_BCM2835_SBM "bcm2835_sbm"
12 #define BCM2835_SBM(obj) \
13 OBJECT_CHECK(Bcm2835SbmState, (obj), TYPE_BCM2835_SBM)
16 uint32_t reg
[MBOX_SIZE
];
23 static void mbox_update_status(Bcm2835Mbox
*mb
)
26 mb
->status
|= ARM_MS_EMPTY
;
28 mb
->status
&= ~ARM_MS_EMPTY
;
30 if (mb
->count
== MBOX_SIZE
) {
31 mb
->status
|= ARM_MS_FULL
;
33 mb
->status
&= ~ARM_MS_FULL
;
37 static void mbox_init(Bcm2835Mbox
*mb
)
42 for (n
= 0; n
< MBOX_SIZE
; n
++) {
43 mb
->reg
[n
] = MBOX_INVALID_DATA
;
45 mbox_update_status(mb
);
48 static uint32_t mbox_pull(Bcm2835Mbox
*mb
, int index
)
53 assert(mb
->count
> 0);
54 assert(index
< mb
->count
);
57 for (n
= index
+ 1; n
< mb
->count
; n
++) {
58 mb
->reg
[n
- 1] = mb
->reg
[n
];
61 mb
->reg
[mb
->count
] = MBOX_INVALID_DATA
;
63 mbox_update_status(mb
);
68 static void mbox_push(Bcm2835Mbox
*mb
, uint32_t val
)
70 assert(mb
->count
< MBOX_SIZE
);
71 mb
->reg
[mb
->count
++] = val
;
72 mbox_update_status(mb
);
78 int mbox_irq_disabled
;
80 int available
[MBOX_CHAN_COUNT
];
84 static void bcm2835_sbm_update(Bcm2835SbmState
*s
)
90 /* Avoid unwanted recursive calls */
91 s
->mbox_irq_disabled
= 1;
93 /* Get pending responses and put them in the vc->arm mbox */
97 if (s
->mbox
[0].status
& ARM_MS_FULL
) {
98 /* vc->arm mbox full, exit */
100 for (n
= 0; n
< MBOX_CHAN_COUNT
; n
++) {
101 if (s
->available
[n
]) {
102 value
= ldl_phys(&address_space_memory
,
103 s
->iomem
.addr
+ 0x400 + (n
<<4));
104 if (value
!= MBOX_INVALID_DATA
) {
105 mbox_push(&s
->mbox
[0], value
);
116 /* Try to push pending requests from the arm->vc mbox */
119 /* Re-enable calls from the IRQ routine */
120 s
->mbox_irq_disabled
= 0;
122 /* Update ARM IRQ status */
124 if (s
->mbox
[0].status
& ARM_MS_EMPTY
) {
125 s
->mbox
[0].config
&= ~ARM_MC_IHAVEDATAIRQPEND
;
127 s
->mbox
[0].config
|= ARM_MC_IHAVEDATAIRQPEND
;
128 if (s
->mbox
[0].config
& ARM_MC_IHAVEDATAIRQEN
) {
132 qemu_set_irq(s
->arm_irq
, set
);
135 static void bcm2835_sbm_set_irq(void *opaque
, int irq
, int level
)
137 Bcm2835SbmState
*s
= (Bcm2835SbmState
*)opaque
;
138 s
->available
[irq
] = level
;
139 if (!s
->mbox_irq_disabled
) {
140 bcm2835_sbm_update(s
);
144 static uint64_t bcm2835_sbm_read(void *opaque
, hwaddr offset
,
147 Bcm2835SbmState
*s
= (Bcm2835SbmState
*)opaque
;
153 case 0x80: /* MAIL0_READ */
157 if (s
->mbox
[0].status
& ARM_MS_EMPTY
) {
158 res
= MBOX_INVALID_DATA
;
160 res
= mbox_pull(&s
->mbox
[0], 0);
163 case 0x90: /* MAIL0_PEEK */
164 res
= s
->mbox
[0].reg
[0];
166 case 0x94: /* MAIL0_SENDER */
168 case 0x98: /* MAIL0_STATUS */
169 res
= s
->mbox
[0].status
;
171 case 0x9c: /* MAIL0_CONFIG */
172 res
= s
->mbox
[0].config
;
174 case 0xb8: /* MAIL1_STATUS */
175 res
= s
->mbox
[1].status
;
178 qemu_log_mask(LOG_GUEST_ERROR
,
179 "bcm2835_sbm_read: Bad offset %x\n", (int)offset
);
183 bcm2835_sbm_update(s
);
188 static void bcm2835_sbm_write(void *opaque
, hwaddr offset
,
189 uint64_t value
, unsigned size
)
193 Bcm2835SbmState
*s
= (Bcm2835SbmState
*)opaque
;
198 case 0x94: /* MAIL0_SENDER */
200 case 0x9c: /* MAIL0_CONFIG */
201 s
->mbox
[0].config
&= ~ARM_MC_IHAVEDATAIRQEN
;
202 s
->mbox
[0].config
|= value
& ARM_MC_IHAVEDATAIRQEN
;
208 if (s
->mbox
[1].status
& ARM_MS_FULL
) {
212 if (ch
< MBOX_CHAN_COUNT
) {
213 if (ldl_phys(&address_space_memory
,
214 s
->iomem
.addr
+ 0x400 + (ch
<<4) + 4)) {
215 /* Push delayed, push it in the arm->vc mbox */
216 mbox_push(&s
->mbox
[1], value
);
218 stl_phys(&address_space_memory
,
219 s
->iomem
.addr
+ 0x400 + (ch
<<4), value
);
222 /* Invalid channel number */
227 qemu_log_mask(LOG_GUEST_ERROR
,
228 "bcm2835_sbm_write: Bad offset %x\n", (int)offset
);
232 bcm2835_sbm_update(s
);
235 static const MemoryRegionOps bcm2835_sbm_ops
= {
236 .read
= bcm2835_sbm_read
,
237 .write
= bcm2835_sbm_write
,
238 .endianness
= DEVICE_NATIVE_ENDIAN
,
241 static const VMStateDescription vmstate_bcm2835_sbm
= {
242 .name
= TYPE_BCM2835_SBM
,
244 .minimum_version_id
= 1,
245 .minimum_version_id_old
= 1,
246 .fields
= (VMStateField
[]) {
247 VMSTATE_END_OF_LIST()
251 static int bcm2835_sbm_init(SysBusDevice
*sbd
)
254 DeviceState
*dev
= DEVICE(sbd
);
255 Bcm2835SbmState
*s
= BCM2835_SBM(dev
);
257 mbox_init(&s
->mbox
[0]);
258 mbox_init(&s
->mbox
[1]);
259 s
->mbox_irq_disabled
= 0;
260 for (n
= 0; n
< MBOX_CHAN_COUNT
; n
++) {
264 sysbus_init_irq(sbd
, &s
->arm_irq
);
265 qdev_init_gpio_in(dev
, bcm2835_sbm_set_irq
, MBOX_CHAN_COUNT
);
267 memory_region_init_io(&s
->iomem
, OBJECT(s
), &bcm2835_sbm_ops
, s
,
268 TYPE_BCM2835_SBM
, 0x400);
269 sysbus_init_mmio(sbd
, &s
->iomem
);
270 vmstate_register(dev
, -1, &vmstate_bcm2835_sbm
, s
);
275 static void bcm2835_sbm_class_init(ObjectClass
*klass
, void *data
)
277 SysBusDeviceClass
*sdc
= SYS_BUS_DEVICE_CLASS(klass
);
279 sdc
->init
= bcm2835_sbm_init
;
282 static TypeInfo bcm2835_sbm_info
= {
283 .name
= TYPE_BCM2835_SBM
,
284 .parent
= TYPE_SYS_BUS_DEVICE
,
285 .instance_size
= sizeof(Bcm2835SbmState
),
286 .class_init
= bcm2835_sbm_class_init
,
289 static void bcm2835_sbm_register_types(void)
291 type_register_static(&bcm2835_sbm_info
);
294 type_init(bcm2835_sbm_register_types
)