xref: /openbmc/qemu/hw/misc/bcm2835_property.c (revision e1ecf8c8)
1 /*
2  * Raspberry Pi emulation (c) 2012 Gregory Estrade
3  * This code is licensed under the GNU GPLv2 and later.
4  */
5 
6 #include "qemu/osdep.h"
7 #include "qapi/error.h"
8 #include "hw/misc/bcm2835_property.h"
9 #include "hw/qdev-properties.h"
10 #include "migration/vmstate.h"
11 #include "hw/irq.h"
12 #include "hw/misc/bcm2835_mbox_defs.h"
13 #include "sysemu/dma.h"
14 #include "qemu/log.h"
15 #include "qemu/module.h"
16 
17 /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
18 
19 static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
20 {
21     uint32_t tag;
22     uint32_t bufsize;
23     uint32_t tot_len;
24     size_t resplen;
25     uint32_t tmp;
26     int n;
27     uint32_t offset, length, color;
28 
29     /*
30      * Copy the current state of the framebuffer config; we will update
31      * this copy as we process tags and then ask the framebuffer to use
32      * it at the end.
33      */
34     BCM2835FBConfig fbconfig = s->fbdev->config;
35     bool fbconfig_updated = false;
36 
37     value &= ~0xf;
38 
39     s->addr = value;
40 
41     tot_len = ldl_le_phys(&s->dma_as, value);
42 
43     /* @(addr + 4) : Buffer response code */
44     value = s->addr + 8;
45     while (value + 8 <= s->addr + tot_len) {
46         tag = ldl_le_phys(&s->dma_as, value);
47         bufsize = ldl_le_phys(&s->dma_as, value + 4);
48         /* @(value + 8) : Request/response indicator */
49         resplen = 0;
50         switch (tag) {
51         case 0x00000000: /* End tag */
52             break;
53         case 0x00000001: /* Get firmware revision */
54             stl_le_phys(&s->dma_as, value + 12, 346337);
55             resplen = 4;
56             break;
57         case 0x00010001: /* Get board model */
58             qemu_log_mask(LOG_UNIMP,
59                           "bcm2835_property: 0x%08x get board model NYI\n",
60                           tag);
61             resplen = 4;
62             break;
63         case 0x00010002: /* Get board revision */
64             stl_le_phys(&s->dma_as, value + 12, s->board_rev);
65             resplen = 4;
66             break;
67         case 0x00010003: /* Get board MAC address */
68             resplen = sizeof(s->macaddr.a);
69             dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen);
70             break;
71         case 0x00010004: /* Get board serial */
72             qemu_log_mask(LOG_UNIMP,
73                           "bcm2835_property: 0x%08x get board serial NYI\n",
74                           tag);
75             resplen = 8;
76             break;
77         case 0x00010005: /* Get ARM memory */
78             /* base */
79             stl_le_phys(&s->dma_as, value + 12, 0);
80             /* size */
81             stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_base);
82             resplen = 8;
83             break;
84         case 0x00010006: /* Get VC memory */
85             /* base */
86             stl_le_phys(&s->dma_as, value + 12, s->fbdev->vcram_base);
87             /* size */
88             stl_le_phys(&s->dma_as, value + 16, s->fbdev->vcram_size);
89             resplen = 8;
90             break;
91         case 0x00028001: /* Set power state */
92             /* Assume that whatever device they asked for exists,
93              * and we'll just claim we set it to the desired state
94              */
95             tmp = ldl_le_phys(&s->dma_as, value + 16);
96             stl_le_phys(&s->dma_as, value + 16, (tmp & 1));
97             resplen = 8;
98             break;
99 
100         /* Clocks */
101 
102         case 0x00030001: /* Get clock state */
103             stl_le_phys(&s->dma_as, value + 16, 0x1);
104             resplen = 8;
105             break;
106 
107         case 0x00038001: /* Set clock state */
108             qemu_log_mask(LOG_UNIMP,
109                           "bcm2835_property: 0x%08x set clock state NYI\n",
110                           tag);
111             resplen = 8;
112             break;
113 
114         case 0x00030002: /* Get clock rate */
115         case 0x00030004: /* Get max clock rate */
116         case 0x00030007: /* Get min clock rate */
117             switch (ldl_le_phys(&s->dma_as, value + 12)) {
118             case 1: /* EMMC */
119                 stl_le_phys(&s->dma_as, value + 16, 50000000);
120                 break;
121             case 2: /* UART */
122                 stl_le_phys(&s->dma_as, value + 16, 3000000);
123                 break;
124             default:
125                 stl_le_phys(&s->dma_as, value + 16, 700000000);
126                 break;
127             }
128             resplen = 8;
129             break;
130 
131         case 0x00038002: /* Set clock rate */
132         case 0x00038004: /* Set max clock rate */
133         case 0x00038007: /* Set min clock rate */
134             qemu_log_mask(LOG_UNIMP,
135                           "bcm2835_property: 0x%08x set clock rate NYI\n",
136                           tag);
137             resplen = 8;
138             break;
139 
140         /* Temperature */
141 
142         case 0x00030006: /* Get temperature */
143             stl_le_phys(&s->dma_as, value + 16, 25000);
144             resplen = 8;
145             break;
146 
147         case 0x0003000A: /* Get max temperature */
148             stl_le_phys(&s->dma_as, value + 16, 99000);
149             resplen = 8;
150             break;
151 
152         /* Frame buffer */
153 
154         case 0x00040001: /* Allocate buffer */
155             stl_le_phys(&s->dma_as, value + 12, fbconfig.base);
156             stl_le_phys(&s->dma_as, value + 16,
157                         bcm2835_fb_get_size(&fbconfig));
158             resplen = 8;
159             break;
160         case 0x00048001: /* Release buffer */
161             resplen = 0;
162             break;
163         case 0x00040002: /* Blank screen */
164             resplen = 4;
165             break;
166         case 0x00044003: /* Test physical display width/height */
167         case 0x00044004: /* Test virtual display width/height */
168             resplen = 8;
169             break;
170         case 0x00048003: /* Set physical display width/height */
171             fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12);
172             fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16);
173             bcm2835_fb_validate_config(&fbconfig);
174             fbconfig_updated = true;
175             /* fall through */
176         case 0x00040003: /* Get physical display width/height */
177             stl_le_phys(&s->dma_as, value + 12, fbconfig.xres);
178             stl_le_phys(&s->dma_as, value + 16, fbconfig.yres);
179             resplen = 8;
180             break;
181         case 0x00048004: /* Set virtual display width/height */
182             fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12);
183             fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16);
184             bcm2835_fb_validate_config(&fbconfig);
185             fbconfig_updated = true;
186             /* fall through */
187         case 0x00040004: /* Get virtual display width/height */
188             stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual);
189             stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual);
190             resplen = 8;
191             break;
192         case 0x00044005: /* Test depth */
193             resplen = 4;
194             break;
195         case 0x00048005: /* Set depth */
196             fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12);
197             bcm2835_fb_validate_config(&fbconfig);
198             fbconfig_updated = true;
199             /* fall through */
200         case 0x00040005: /* Get depth */
201             stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp);
202             resplen = 4;
203             break;
204         case 0x00044006: /* Test pixel order */
205             resplen = 4;
206             break;
207         case 0x00048006: /* Set pixel order */
208             fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12);
209             bcm2835_fb_validate_config(&fbconfig);
210             fbconfig_updated = true;
211             /* fall through */
212         case 0x00040006: /* Get pixel order */
213             stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo);
214             resplen = 4;
215             break;
216         case 0x00044007: /* Test pixel alpha */
217             resplen = 4;
218             break;
219         case 0x00048007: /* Set alpha */
220             fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12);
221             bcm2835_fb_validate_config(&fbconfig);
222             fbconfig_updated = true;
223             /* fall through */
224         case 0x00040007: /* Get alpha */
225             stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha);
226             resplen = 4;
227             break;
228         case 0x00040008: /* Get pitch */
229             stl_le_phys(&s->dma_as, value + 12,
230                         bcm2835_fb_get_pitch(&fbconfig));
231             resplen = 4;
232             break;
233         case 0x00044009: /* Test virtual offset */
234             resplen = 8;
235             break;
236         case 0x00048009: /* Set virtual offset */
237             fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12);
238             fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16);
239             bcm2835_fb_validate_config(&fbconfig);
240             fbconfig_updated = true;
241             /* fall through */
242         case 0x00040009: /* Get virtual offset */
243             stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset);
244             stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset);
245             resplen = 8;
246             break;
247         case 0x0004000a: /* Get/Test/Set overscan */
248         case 0x0004400a:
249         case 0x0004800a:
250             stl_le_phys(&s->dma_as, value + 12, 0);
251             stl_le_phys(&s->dma_as, value + 16, 0);
252             stl_le_phys(&s->dma_as, value + 20, 0);
253             stl_le_phys(&s->dma_as, value + 24, 0);
254             resplen = 16;
255             break;
256         case 0x0004800b: /* Set palette */
257             offset = ldl_le_phys(&s->dma_as, value + 12);
258             length = ldl_le_phys(&s->dma_as, value + 16);
259             n = 0;
260             while (n < length - offset) {
261                 color = ldl_le_phys(&s->dma_as, value + 20 + (n << 2));
262                 stl_le_phys(&s->dma_as,
263                             s->fbdev->vcram_base + ((offset + n) << 2), color);
264                 n++;
265             }
266             stl_le_phys(&s->dma_as, value + 12, 0);
267             resplen = 4;
268             break;
269 
270         case 0x00060001: /* Get DMA channels */
271             /* channels 2-5 */
272             stl_le_phys(&s->dma_as, value + 12, 0x003C);
273             resplen = 4;
274             break;
275 
276         case 0x00050001: /* Get command line */
277             resplen = 0;
278             break;
279 
280         default:
281             qemu_log_mask(LOG_UNIMP,
282                           "bcm2835_property: unhandled tag 0x%08x\n", tag);
283             break;
284         }
285 
286         if (tag == 0) {
287             break;
288         }
289 
290         stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
291         value += bufsize + 12;
292     }
293 
294     /* Reconfigure framebuffer if required */
295     if (fbconfig_updated) {
296         bcm2835_fb_reconfigure(s->fbdev, &fbconfig);
297     }
298 
299     /* Buffer response code */
300     stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
301 }
302 
303 static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
304                                       unsigned size)
305 {
306     BCM2835PropertyState *s = opaque;
307     uint32_t res = 0;
308 
309     switch (offset) {
310     case MBOX_AS_DATA:
311         res = MBOX_CHAN_PROPERTY | s->addr;
312         s->pending = false;
313         qemu_set_irq(s->mbox_irq, 0);
314         break;
315 
316     case MBOX_AS_PENDING:
317         res = s->pending;
318         break;
319 
320     default:
321         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
322                       __func__, offset);
323         return 0;
324     }
325 
326     return res;
327 }
328 
329 static void bcm2835_property_write(void *opaque, hwaddr offset,
330                                    uint64_t value, unsigned size)
331 {
332     BCM2835PropertyState *s = opaque;
333 
334     switch (offset) {
335     case MBOX_AS_DATA:
336         /* bcm2835_mbox should check our pending status before pushing */
337         assert(!s->pending);
338         s->pending = true;
339         bcm2835_property_mbox_push(s, value);
340         qemu_set_irq(s->mbox_irq, 1);
341         break;
342 
343     default:
344         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
345                       __func__, offset);
346         return;
347     }
348 }
349 
350 static const MemoryRegionOps bcm2835_property_ops = {
351     .read = bcm2835_property_read,
352     .write = bcm2835_property_write,
353     .endianness = DEVICE_NATIVE_ENDIAN,
354     .valid.min_access_size = 4,
355     .valid.max_access_size = 4,
356 };
357 
358 static const VMStateDescription vmstate_bcm2835_property = {
359     .name = TYPE_BCM2835_PROPERTY,
360     .version_id = 1,
361     .minimum_version_id = 1,
362     .fields      = (VMStateField[]) {
363         VMSTATE_MACADDR(macaddr, BCM2835PropertyState),
364         VMSTATE_UINT32(addr, BCM2835PropertyState),
365         VMSTATE_BOOL(pending, BCM2835PropertyState),
366         VMSTATE_END_OF_LIST()
367     }
368 };
369 
370 static void bcm2835_property_init(Object *obj)
371 {
372     BCM2835PropertyState *s = BCM2835_PROPERTY(obj);
373 
374     memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s,
375                           TYPE_BCM2835_PROPERTY, 0x10);
376     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
377     sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
378 }
379 
380 static void bcm2835_property_reset(DeviceState *dev)
381 {
382     BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
383 
384     s->pending = false;
385 }
386 
387 static void bcm2835_property_realize(DeviceState *dev, Error **errp)
388 {
389     BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
390     Object *obj;
391     Error *err = NULL;
392 
393     obj = object_property_get_link(OBJECT(dev), "fb", &err);
394     if (obj == NULL) {
395         error_setg(errp, "%s: required fb link not found: %s",
396                    __func__, error_get_pretty(err));
397         return;
398     }
399 
400     s->fbdev = BCM2835_FB(obj);
401 
402     obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
403     if (obj == NULL) {
404         error_setg(errp, "%s: required dma-mr link not found: %s",
405                    __func__, error_get_pretty(err));
406         return;
407     }
408 
409     s->dma_mr = MEMORY_REGION(obj);
410     address_space_init(&s->dma_as, s->dma_mr, NULL);
411 
412     /* TODO: connect to MAC address of USB NIC device, once we emulate it */
413     qemu_macaddr_default_if_unset(&s->macaddr);
414 
415     bcm2835_property_reset(dev);
416 }
417 
418 static Property bcm2835_property_props[] = {
419     DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
420     DEFINE_PROP_END_OF_LIST()
421 };
422 
423 static void bcm2835_property_class_init(ObjectClass *klass, void *data)
424 {
425     DeviceClass *dc = DEVICE_CLASS(klass);
426 
427     dc->props = bcm2835_property_props;
428     dc->realize = bcm2835_property_realize;
429     dc->vmsd = &vmstate_bcm2835_property;
430 }
431 
432 static TypeInfo bcm2835_property_info = {
433     .name          = TYPE_BCM2835_PROPERTY,
434     .parent        = TYPE_SYS_BUS_DEVICE,
435     .instance_size = sizeof(BCM2835PropertyState),
436     .class_init    = bcm2835_property_class_init,
437     .instance_init = bcm2835_property_init,
438 };
439 
440 static void bcm2835_property_register_types(void)
441 {
442     type_register_static(&bcm2835_property_info);
443 }
444 
445 type_init(bcm2835_property_register_types)
446