hw/sd: tweak power up delay timer, to handle more cases
[qemu/ar7.git] / hw / misc / bcm2835_sbm.c
bloba5c9324ff889d22cf80d04388cb6cd1696090dc3
1 /*
2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 * This code is licensed under the GNU GPLv2 and later.
4 */
6 #include "hw/misc/bcm2835_sbm.h"
7 #include "hw/arm/bcm2835_arm_control.h"
9 static void mbox_update_status(BCM2835Mbox *mb)
11 if (mb->count == 0) {
12 mb->status |= ARM_MS_EMPTY;
13 } else {
14 mb->status &= ~ARM_MS_EMPTY;
16 if (mb->count == MBOX_SIZE) {
17 mb->status |= ARM_MS_FULL;
18 } else {
19 mb->status &= ~ARM_MS_FULL;
23 static void mbox_init(BCM2835Mbox *mb)
25 int n;
26 mb->count = 0;
27 mb->config = 0;
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)
36 int n;
37 uint32_t val;
39 assert(mb->count > 0);
40 assert(index < mb->count);
42 val = mb->reg[index];
43 for (n = index + 1; n < mb->count; n++) {
44 mb->reg[n - 1] = mb->reg[n];
46 mb->count--;
47 mb->reg[mb->count] = MBOX_INVALID_DATA;
49 mbox_update_status(mb);
51 return val;
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)
63 int set;
64 int done, n;
65 uint32_t value;
67 /* Avoid unwanted recursive calls */
68 s->mbox_irq_disabled = 1;
70 /* Get pending responses and put them in the vc->arm mbox */
71 done = 0;
72 while (!done) {
73 done = 1;
74 if (s->mbox[0].status & ARM_MS_FULL) {
75 /* vc->arm mbox full, exit */
76 } else {
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);
82 } else {
83 /* Hmmm... */
85 done = 0;
86 break;
92 /* Try to push pending requests from the arm->vc mbox */
93 /* TODO (?) */
95 /* Re-enable calls from the IRQ routine */
96 s->mbox_irq_disabled = 0;
98 /* Update ARM IRQ status */
99 set = 0;
100 if (s->mbox[0].status & ARM_MS_EMPTY) {
101 s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQPEND;
102 } else {
103 s->mbox[0].config |= ARM_MC_IHAVEDATAIRQPEND;
104 if (s->mbox[0].config & ARM_MC_IHAVEDATAIRQEN) {
105 set = 1;
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,
121 unsigned size)
123 BCM2835SbmState *s = (BCM2835SbmState *)opaque;
124 uint32_t res = 0;
126 offset &= 0xff;
128 switch (offset) {
129 case 0x80: /* MAIL0_READ */
130 case 0x84:
131 case 0x88:
132 case 0x8c:
133 if (s->mbox[0].status & ARM_MS_EMPTY) {
134 res = MBOX_INVALID_DATA;
135 } else {
136 res = mbox_pull(&s->mbox[0], 0);
138 break;
139 case 0x90: /* MAIL0_PEEK */
140 res = s->mbox[0].reg[0];
141 break;
142 case 0x94: /* MAIL0_SENDER */
143 break;
144 case 0x98: /* MAIL0_STATUS */
145 res = s->mbox[0].status;
146 break;
147 case 0x9c: /* MAIL0_CONFIG */
148 res = s->mbox[0].config;
149 break;
150 case 0xb8: /* MAIL1_STATUS */
151 res = s->mbox[1].status;
152 break;
153 default:
154 qemu_log_mask(LOG_GUEST_ERROR,
155 "bcm2835_sbm_read: Bad offset %x\n", (int)offset);
156 return 0;
159 bcm2835_sbm_update(s);
161 return res;
164 static void bcm2835_sbm_write(void *opaque, hwaddr offset,
165 uint64_t value, unsigned size)
167 int ch;
169 BCM2835SbmState *s = (BCM2835SbmState *)opaque;
171 offset &= 0xff;
173 switch (offset) {
174 case 0x94: /* MAIL0_SENDER */
175 break;
176 case 0x9c: /* MAIL0_CONFIG */
177 s->mbox[0].config &= ~ARM_MC_IHAVEDATAIRQEN;
178 s->mbox[0].config |= value & ARM_MC_IHAVEDATAIRQEN;
179 break;
180 case 0xa0:
181 case 0xa4:
182 case 0xa8:
183 case 0xac:
184 if (s->mbox[1].status & ARM_MS_FULL) {
185 /* Guest error */
186 } else {
187 ch = value & 0xf;
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);
192 } else {
193 stl_phys(&s->mbox_as, ch<<4, value);
195 } else {
196 /* Invalid channel number */
199 break;
200 default:
201 qemu_log_mask(LOG_GUEST_ERROR,
202 "bcm2835_sbm_write: Bad offset %x\n", (int)offset);
203 return;
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,
217 .version_id = 1,
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)
237 int n;
238 BCM2835SbmState *s = BCM2835_SBM(dev);
239 Object *obj;
240 Error *err = NULL;
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");
245 return;
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++) {
255 s->available[n] = 0;
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)