bcm283* refactoring continues
[qemu/ar7.git] / hw / misc / bcm2835_sbm.c
blob62f58873cdded6fc9bb402b6f499dc39ff2412ac
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/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)
16 // XXX: FIXME:
17 extern AddressSpace *bcm2835_peripheral_as;
19 typedef struct {
20 uint32_t reg[MBOX_SIZE];
21 int count;
22 uint32_t status;
23 uint32_t config;
24 } Bcm2835Mbox;
27 static void mbox_update_status(Bcm2835Mbox *mb)
29 if (mb->count == 0) {
30 mb->status |= ARM_MS_EMPTY;
31 } else {
32 mb->status &= ~ARM_MS_EMPTY;
34 if (mb->count == MBOX_SIZE) {
35 mb->status |= ARM_MS_FULL;
36 } else {
37 mb->status &= ~ARM_MS_FULL;
41 static void mbox_init(Bcm2835Mbox *mb)
43 int n;
44 mb->count = 0;
45 mb->config = 0;
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)
54 int n;
55 uint32_t val;
57 assert(mb->count > 0);
58 assert(index < mb->count);
60 val = mb->reg[index];
61 for (n = index + 1; n < mb->count; n++) {
62 mb->reg[n - 1] = mb->reg[n];
64 mb->count--;
65 mb->reg[mb->count] = MBOX_INVALID_DATA;
67 mbox_update_status(mb);
69 return val;
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);
79 typedef struct {
80 SysBusDevice busdev;
81 MemoryRegion iomem;
82 int mbox_irq_disabled;
83 qemu_irq arm_irq;
84 int available[MBOX_CHAN_COUNT];
85 Bcm2835Mbox mbox[2];
86 } Bcm2835SbmState;
88 static void bcm2835_sbm_update(Bcm2835SbmState *s)
90 int set;
91 int done, n;
92 uint32_t value;
94 /* Avoid unwanted recursive calls */
95 s->mbox_irq_disabled = 1;
97 /* Get pending responses and put them in the vc->arm mbox */
98 done = 0;
99 while (!done) {
100 done = 1;
101 if (s->mbox[0].status & ARM_MS_FULL) {
102 /* vc->arm mbox full, exit */
103 } else {
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);
110 } else {
111 /* Hmmm... */
113 done = 0;
114 break;
120 /* Try to push pending requests from the arm->vc mbox */
121 /* TODO (?) */
123 /* Re-enable calls from the IRQ routine */
124 s->mbox_irq_disabled = 0;
126 /* Update ARM IRQ status */
127 set = 0;
128 if (s->mbox[0].status & ARM_MS_EMPTY) {
129 s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQPEND;
130 } else {
131 s->mbox[0].config |= ARM_MC_IHAVEDATAIRQPEND;
132 if (s->mbox[0].config & ARM_MC_IHAVEDATAIRQEN) {
133 set = 1;
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,
149 unsigned size)
151 Bcm2835SbmState *s = (Bcm2835SbmState *)opaque;
152 uint32_t res = 0;
154 offset &= 0xff;
156 switch (offset) {
157 case 0x80: /* MAIL0_READ */
158 case 0x84:
159 case 0x88:
160 case 0x8c:
161 if (s->mbox[0].status & ARM_MS_EMPTY) {
162 res = MBOX_INVALID_DATA;
163 } else {
164 res = mbox_pull(&s->mbox[0], 0);
166 break;
167 case 0x90: /* MAIL0_PEEK */
168 res = s->mbox[0].reg[0];
169 break;
170 case 0x94: /* MAIL0_SENDER */
171 break;
172 case 0x98: /* MAIL0_STATUS */
173 res = s->mbox[0].status;
174 break;
175 case 0x9c: /* MAIL0_CONFIG */
176 res = s->mbox[0].config;
177 break;
178 case 0xb8: /* MAIL1_STATUS */
179 res = s->mbox[1].status;
180 break;
181 default:
182 qemu_log_mask(LOG_GUEST_ERROR,
183 "bcm2835_sbm_read: Bad offset %x\n", (int)offset);
184 return 0;
187 bcm2835_sbm_update(s);
189 return res;
192 static void bcm2835_sbm_write(void *opaque, hwaddr offset,
193 uint64_t value, unsigned size)
195 int ch;
197 Bcm2835SbmState *s = (Bcm2835SbmState *)opaque;
199 offset &= 0xff;
201 switch (offset) {
202 case 0x94: /* MAIL0_SENDER */
203 break;
204 case 0x9c: /* MAIL0_CONFIG */
205 s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQEN;
206 s->mbox[0].config |= value & ARM_MC_IHAVEDATAIRQEN;
207 break;
208 case 0xa0:
209 case 0xa4:
210 case 0xa8:
211 case 0xac:
212 if (s->mbox[1].status & ARM_MS_FULL) {
213 /* Guest error */
214 } else {
215 ch = value & 0xf;
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);
221 } else {
222 stl_phys(bcm2835_peripheral_as,
223 BCM2835_VC_PERI_BASE + ARMCTRL_0_SBM_OFFSET + 0x400 + (ch<<4), value);
225 } else {
226 /* Invalid channel number */
229 break;
230 default:
231 qemu_log_mask(LOG_GUEST_ERROR,
232 "bcm2835_sbm_write: Bad offset %x\n", (int)offset);
233 return;
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,
247 .version_id = 1,
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)
257 int n;
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++) {
265 s->available[n] = 0;
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);
276 return 0;
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)