bcm2835_sbm: implement mailbox 1 status register
[qemu/ar7.git] / hw / misc / bcm2835_sbm.c
blob07d554ecd5500d70441f97ed40cf470acd87594b
1 /*
2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 * This code is licensed under the GNU GPLv2 and later.
4 */
6 #include "hw/sysbus.h"
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)
15 typedef struct {
16 uint32_t reg[MBOX_SIZE];
17 int count;
18 uint32_t status;
19 uint32_t config;
20 } Bcm2835Mbox;
23 static void mbox_update_status(Bcm2835Mbox *mb)
25 if (mb->count == 0) {
26 mb->status |= ARM_MS_EMPTY;
27 } else {
28 mb->status &= ~ARM_MS_EMPTY;
30 if (mb->count == MBOX_SIZE) {
31 mb->status |= ARM_MS_FULL;
32 } else {
33 mb->status &= ~ARM_MS_FULL;
37 static void mbox_init(Bcm2835Mbox *mb)
39 int n;
40 mb->count = 0;
41 mb->config = 0;
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)
50 int n;
51 uint32_t val;
53 assert(mb->count > 0);
54 assert(index < mb->count);
56 val = mb->reg[index];
57 for (n = index + 1; n < mb->count; n++) {
58 mb->reg[n - 1] = mb->reg[n];
60 mb->count--;
61 mb->reg[mb->count] = MBOX_INVALID_DATA;
63 mbox_update_status(mb);
65 return val;
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);
75 typedef struct {
76 SysBusDevice busdev;
77 MemoryRegion iomem;
78 int mbox_irq_disabled;
79 qemu_irq arm_irq;
80 int available[MBOX_CHAN_COUNT];
81 Bcm2835Mbox mbox[2];
82 } Bcm2835SbmState;
84 static void bcm2835_sbm_update(Bcm2835SbmState *s)
86 int set;
87 int done, n;
88 uint32_t value;
90 /* Avoid unwanted recursive calls */
91 s->mbox_irq_disabled = 1;
93 /* Get pending responses and put them in the vc->arm mbox */
94 done = 0;
95 while (!done) {
96 done = 1;
97 if (s->mbox[0].status & ARM_MS_FULL) {
98 /* vc->arm mbox full, exit */
99 } else {
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);
106 } else {
107 /* Hmmm... */
109 done = 0;
110 break;
116 /* Try to push pending requests from the arm->vc mbox */
117 /* TODO (?) */
119 /* Re-enable calls from the IRQ routine */
120 s->mbox_irq_disabled = 0;
122 /* Update ARM IRQ status */
123 set = 0;
124 if (s->mbox[0].status & ARM_MS_EMPTY) {
125 s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQPEND;
126 } else {
127 s->mbox[0].config |= ARM_MC_IHAVEDATAIRQPEND;
128 if (s->mbox[0].config & ARM_MC_IHAVEDATAIRQEN) {
129 set = 1;
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,
145 unsigned size)
147 Bcm2835SbmState *s = (Bcm2835SbmState *)opaque;
148 uint32_t res = 0;
150 offset &= 0xff;
152 switch (offset) {
153 case 0x80: /* MAIL0_READ */
154 case 0x84:
155 case 0x88:
156 case 0x8c:
157 if (s->mbox[0].status & ARM_MS_EMPTY) {
158 res = MBOX_INVALID_DATA;
159 } else {
160 res = mbox_pull(&s->mbox[0], 0);
162 break;
163 case 0x90: /* MAIL0_PEEK */
164 res = s->mbox[0].reg[0];
165 break;
166 case 0x94: /* MAIL0_SENDER */
167 break;
168 case 0x98: /* MAIL0_STATUS */
169 res = s->mbox[0].status;
170 break;
171 case 0x9c: /* MAIL0_CONFIG */
172 res = s->mbox[0].config;
173 break;
174 case 0xb8: /* MAIL1_STATUS */
175 res = s->mbox[1].status;
176 break;
177 default:
178 qemu_log_mask(LOG_GUEST_ERROR,
179 "bcm2835_sbm_read: Bad offset %x\n", (int)offset);
180 return 0;
183 bcm2835_sbm_update(s);
185 return res;
188 static void bcm2835_sbm_write(void *opaque, hwaddr offset,
189 uint64_t value, unsigned size)
191 int ch;
193 Bcm2835SbmState *s = (Bcm2835SbmState *)opaque;
195 offset &= 0xff;
197 switch (offset) {
198 case 0x94: /* MAIL0_SENDER */
199 break;
200 case 0x9c: /* MAIL0_CONFIG */
201 s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQEN;
202 s->mbox[0].config |= value & ARM_MC_IHAVEDATAIRQEN;
203 break;
204 case 0xa0:
205 case 0xa4:
206 case 0xa8:
207 case 0xac:
208 if (s->mbox[1].status & ARM_MS_FULL) {
209 /* Guest error */
210 } else {
211 ch = value & 0xf;
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);
217 } else {
218 stl_phys(&address_space_memory,
219 s->iomem.addr + 0x400 + (ch<<4), value);
221 } else {
222 /* Invalid channel number */
225 break;
226 default:
227 qemu_log_mask(LOG_GUEST_ERROR,
228 "bcm2835_sbm_write: Bad offset %x\n", (int)offset);
229 return;
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,
243 .version_id = 1,
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)
253 int n;
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++) {
261 s->available[n] = 0;
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);
272 return 0;
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)