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