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/raspi_platform.h"
9 #include "hw/arm/bcm2835_common.h"
10 #include "hw/arm/bcm2835_arm_control.h"
12 #define TYPE_BCM2835_SBM "bcm2835_sbm"
13 #define BCM2835_SBM(obj) \
14 OBJECT_CHECK(Bcm2835SbmState, (obj), TYPE_BCM2835_SBM)
17 extern AddressSpace
*bcm2835_peripheral_as
;
20 uint32_t reg
[MBOX_SIZE
];
27 static void mbox_update_status(Bcm2835Mbox
*mb
)
30 mb
->status
|= ARM_MS_EMPTY
;
32 mb
->status
&= ~ARM_MS_EMPTY
;
34 if (mb
->count
== MBOX_SIZE
) {
35 mb
->status
|= ARM_MS_FULL
;
37 mb
->status
&= ~ARM_MS_FULL
;
41 static void mbox_init(Bcm2835Mbox
*mb
)
46 for (n
= 0; n
< MBOX_SIZE
; n
++) {
47 mb
->reg
[n
] = MBOX_INVALID_DATA
;
49 mbox_update_status(mb
);
52 static uint32_t mbox_pull(Bcm2835Mbox
*mb
, int index
)
57 assert(mb
->count
> 0);
58 assert(index
< mb
->count
);
61 for (n
= index
+ 1; n
< mb
->count
; n
++) {
62 mb
->reg
[n
- 1] = mb
->reg
[n
];
65 mb
->reg
[mb
->count
] = MBOX_INVALID_DATA
;
67 mbox_update_status(mb
);
72 static void mbox_push(Bcm2835Mbox
*mb
, uint32_t val
)
74 assert(mb
->count
< MBOX_SIZE
);
75 mb
->reg
[mb
->count
++] = val
;
76 mbox_update_status(mb
);
82 int mbox_irq_disabled
;
84 int available
[MBOX_CHAN_COUNT
];
88 static void bcm2835_sbm_update(Bcm2835SbmState
*s
)
94 /* Avoid unwanted recursive calls */
95 s
->mbox_irq_disabled
= 1;
97 /* Get pending responses and put them in the vc->arm mbox */
101 if (s
->mbox
[0].status
& ARM_MS_FULL
) {
102 /* vc->arm mbox full, exit */
104 for (n
= 0; n
< MBOX_CHAN_COUNT
; n
++) {
105 if (s
->available
[n
]) {
106 value
= ldl_phys(bcm2835_peripheral_as
,
107 BCM2835_VC_PERI_BASE
+ ARMCTRL_0_SBM_OFFSET
+ 0x400 + (n
<<4));
108 if (value
!= MBOX_INVALID_DATA
) {
109 mbox_push(&s
->mbox
[0], value
);
120 /* Try to push pending requests from the arm->vc mbox */
123 /* Re-enable calls from the IRQ routine */
124 s
->mbox_irq_disabled
= 0;
126 /* Update ARM IRQ status */
128 if (s
->mbox
[0].status
& ARM_MS_EMPTY
) {
129 s
->mbox
[0].config
&= ~ARM_MC_IHAVEDATAIRQPEND
;
131 s
->mbox
[0].config
|= ARM_MC_IHAVEDATAIRQPEND
;
132 if (s
->mbox
[0].config
& ARM_MC_IHAVEDATAIRQEN
) {
136 qemu_set_irq(s
->arm_irq
, set
);
139 static void bcm2835_sbm_set_irq(void *opaque
, int irq
, int level
)
141 Bcm2835SbmState
*s
= (Bcm2835SbmState
*)opaque
;
142 s
->available
[irq
] = level
;
143 if (!s
->mbox_irq_disabled
) {
144 bcm2835_sbm_update(s
);
148 static uint64_t bcm2835_sbm_read(void *opaque
, hwaddr offset
,
151 Bcm2835SbmState
*s
= (Bcm2835SbmState
*)opaque
;
157 case 0x80: /* MAIL0_READ */
161 if (s
->mbox
[0].status
& ARM_MS_EMPTY
) {
162 res
= MBOX_INVALID_DATA
;
164 res
= mbox_pull(&s
->mbox
[0], 0);
167 case 0x90: /* MAIL0_PEEK */
168 res
= s
->mbox
[0].reg
[0];
170 case 0x94: /* MAIL0_SENDER */
172 case 0x98: /* MAIL0_STATUS */
173 res
= s
->mbox
[0].status
;
175 case 0x9c: /* MAIL0_CONFIG */
176 res
= s
->mbox
[0].config
;
178 case 0xb8: /* MAIL1_STATUS */
179 res
= s
->mbox
[1].status
;
182 qemu_log_mask(LOG_GUEST_ERROR
,
183 "bcm2835_sbm_read: Bad offset %x\n", (int)offset
);
187 bcm2835_sbm_update(s
);
192 static void bcm2835_sbm_write(void *opaque
, hwaddr offset
,
193 uint64_t value
, unsigned size
)
197 Bcm2835SbmState
*s
= (Bcm2835SbmState
*)opaque
;
202 case 0x94: /* MAIL0_SENDER */
204 case 0x9c: /* MAIL0_CONFIG */
205 s
->mbox
[0].config
&= ~ARM_MC_IHAVEDATAIRQEN
;
206 s
->mbox
[0].config
|= value
& ARM_MC_IHAVEDATAIRQEN
;
212 if (s
->mbox
[1].status
& ARM_MS_FULL
) {
216 if (ch
< MBOX_CHAN_COUNT
) {
217 if (ldl_phys(bcm2835_peripheral_as
,
218 BCM2835_VC_PERI_BASE
+ ARMCTRL_0_SBM_OFFSET
+ 0x400 + (ch
<<4) + 4)) {
219 /* Push delayed, push it in the arm->vc mbox */
220 mbox_push(&s
->mbox
[1], value
);
222 stl_phys(bcm2835_peripheral_as
,
223 BCM2835_VC_PERI_BASE
+ ARMCTRL_0_SBM_OFFSET
+ 0x400 + (ch
<<4), value
);
226 /* Invalid channel number */
231 qemu_log_mask(LOG_GUEST_ERROR
,
232 "bcm2835_sbm_write: Bad offset %x\n", (int)offset
);
236 bcm2835_sbm_update(s
);
239 static const MemoryRegionOps bcm2835_sbm_ops
= {
240 .read
= bcm2835_sbm_read
,
241 .write
= bcm2835_sbm_write
,
242 .endianness
= DEVICE_NATIVE_ENDIAN
,
245 static const VMStateDescription vmstate_bcm2835_sbm
= {
246 .name
= TYPE_BCM2835_SBM
,
248 .minimum_version_id
= 1,
249 .minimum_version_id_old
= 1,
250 .fields
= (VMStateField
[]) {
251 VMSTATE_END_OF_LIST()
255 static int bcm2835_sbm_init(SysBusDevice
*sbd
)
258 DeviceState
*dev
= DEVICE(sbd
);
259 Bcm2835SbmState
*s
= BCM2835_SBM(dev
);
261 mbox_init(&s
->mbox
[0]);
262 mbox_init(&s
->mbox
[1]);
263 s
->mbox_irq_disabled
= 0;
264 for (n
= 0; n
< MBOX_CHAN_COUNT
; n
++) {
268 sysbus_init_irq(sbd
, &s
->arm_irq
);
269 qdev_init_gpio_in(dev
, bcm2835_sbm_set_irq
, MBOX_CHAN_COUNT
);
271 memory_region_init_io(&s
->iomem
, OBJECT(s
), &bcm2835_sbm_ops
, s
,
272 TYPE_BCM2835_SBM
, 0x400);
273 sysbus_init_mmio(sbd
, &s
->iomem
);
274 vmstate_register(dev
, -1, &vmstate_bcm2835_sbm
, s
);
279 static void bcm2835_sbm_class_init(ObjectClass
*klass
, void *data
)
281 SysBusDeviceClass
*sdc
= SYS_BUS_DEVICE_CLASS(klass
);
283 sdc
->init
= bcm2835_sbm_init
;
286 static TypeInfo bcm2835_sbm_info
= {
287 .name
= TYPE_BCM2835_SBM
,
288 .parent
= TYPE_SYS_BUS_DEVICE
,
289 .instance_size
= sizeof(Bcm2835SbmState
),
290 .class_init
= bcm2835_sbm_class_init
,
293 static void bcm2835_sbm_register_types(void)
295 type_register_static(&bcm2835_sbm_info
);
298 type_init(bcm2835_sbm_register_types
)