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