bcm283* refactoring continues
[qemu/ar7.git] / hw / misc / bcm2835_property.c
blobd172531d0e8faf558429fdf5d3715bed9caf8e42
1 /*
2 * Raspberry Pi emulation (c) 2012 Gregory Estrade
3 * This code is licensed under the GNU GPLv2 and later.
4 */
6 #include "hw/display/framebuffer.h"
7 #include "hw/sysbus.h"
8 #include "ui/console.h"
9 #include "ui/pixel_ops.h"
10 #include "exec/address-spaces.h"
12 #include "hw/arm/bcm2835_common.h"
14 // XXX: FIXME:
15 extern AddressSpace *bcm2835_peripheral_as;
17 #define TYPE_BCM2835_PROPERTY "bcm2835_property"
18 #define BCM2835_PROPERTY(obj) \
19 OBJECT_CHECK(Bcm2835PropertyState, (obj), TYPE_BCM2835_PROPERTY)
21 typedef struct {
22 SysBusDevice busdev;
23 MemoryRegion iomem;
24 int pending;
25 qemu_irq mbox_irq;
27 uint32_t addr;
28 } Bcm2835PropertyState;
30 static void update_fb(void)
32 bcm2835_fb.lock = 1;
34 bcm2835_fb.base = bcm2835_vcram_base;
35 bcm2835_fb.base += BCM2835_FB_OFFSET;
37 /* TODO - Manage properly virtual resolution */
39 bcm2835_fb.pitch = bcm2835_fb.xres * (bcm2835_fb.bpp >> 3);
40 bcm2835_fb.size = bcm2835_fb.yres * bcm2835_fb.pitch;
43 /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
45 static void bcm2835_property_mbox_push(Bcm2835PropertyState *s,
46 uint32_t value)
48 uint32_t tag;
49 uint32_t bufsize;
50 uint32_t tot_len;
51 int n;
52 int resplen;
53 uint32_t offset, length, color;
54 uint32_t tmp;
56 value &= ~0xf;
58 s->addr = value;
60 tot_len = ldl_phys(bcm2835_peripheral_as, value);
62 /* @(s->addr + 4) : Buffer response code */
63 value = s->addr + 8;
64 while (value + 8 <= s->addr + tot_len) {
65 tag = ldl_phys(bcm2835_peripheral_as, value);
66 bufsize = ldl_phys(bcm2835_peripheral_as, value + 4);
67 /* @(value + 8) : Request/response indicator */
68 resplen = 0;
69 switch (tag) {
70 case 0x00000000: /* End tag */
71 break;
72 case 0x00000001: /* Get firmware revision */
73 stl_phys(bcm2835_peripheral_as, value + 12, 346337);
74 resplen = 4;
75 break;
77 case 0x00010001: /* Get board model */
78 resplen = 4;
79 break;
80 case 0x00010002: /* Get board revision */
81 resplen = 4;
82 break;
83 case 0x00010003: /* Get board MAC address */
84 /* write the first four bytes of the 6-byte MAC */
85 stl_phys(bcm2835_peripheral_as, value + 12, 0xB827EBD0);
86 /* write the last two bytes, avoid any write past the buffer end */
87 stb_phys(bcm2835_peripheral_as, value + 16, 0xEE);
88 stb_phys(bcm2835_peripheral_as, value + 17, 0xDF);
89 resplen = 6;
90 break;
91 case 0x00010004: /* Get board serial */
92 resplen = 8;
93 break;
94 case 0x00010005: /* Get ARM memory */
95 /* base */
96 stl_phys(bcm2835_peripheral_as, value + 12, 0);
97 /* size */
98 stl_phys(bcm2835_peripheral_as, value + 16, bcm2835_vcram_base);
99 resplen = 8;
100 break;
101 case 0x00010006: /* Get VC memory */
102 /* base */
103 stl_phys(bcm2835_peripheral_as, value + 12, bcm2835_vcram_base);
104 /* size */
105 stl_phys(bcm2835_peripheral_as, value + 16, VCRAM_SIZE);
106 resplen = 8;
107 break;
108 case 0x00028001: /* Set power state */
109 /* Assume that whatever device they asked for exists,
110 * and we'll just claim we set it to the desired state */
111 tmp = ldl_phys(bcm2835_peripheral_as, value + 16);
112 stl_phys(bcm2835_peripheral_as, value + 16, (tmp & 1));
113 resplen = 8;
114 break;
116 /* Clocks */
118 case 0x00030001: /* Get clock state */
119 stl_phys(bcm2835_peripheral_as, value + 16, 0x1);
120 resplen = 8;
121 break;
123 case 0x00038001: /* Set clock state */
124 resplen = 8;
125 break;
127 case 0x00030002: /* Get clock rate */
128 case 0x00030004: /* Get max clock rate */
129 case 0x00030007: /* Get min clock rate */
130 switch (ldl_phys(bcm2835_peripheral_as, value + 12)) {
131 case 1: /* EMMC */
132 stl_phys(bcm2835_peripheral_as, value + 16, 50000000);
133 break;
134 case 2: /* UART */
135 stl_phys(bcm2835_peripheral_as, value + 16, 3000000);
136 break;
137 default:
138 stl_phys(bcm2835_peripheral_as, value + 16, 700000000);
139 break;
141 resplen = 8;
142 break;
144 case 0x00038002: /* Set clock rate */
145 case 0x00038004: /* Set max clock rate */
146 case 0x00038007: /* Set min clock rate */
147 resplen = 8;
148 break;
150 /* Temperature */
152 case 0x00030006: /* Get temperature */
153 stl_phys(bcm2835_peripheral_as, value + 16, 25000);
154 resplen = 8;
155 break;
157 case 0x0003000A: /* Get max temperature */
158 stl_phys(bcm2835_peripheral_as, value + 16, 99000);
159 resplen = 8;
160 break;
163 /* Frame buffer */
165 case 0x00040001: /* Allocate buffer */
166 stl_phys(bcm2835_peripheral_as, value + 12, bcm2835_fb.base);
167 stl_phys(bcm2835_peripheral_as, value + 16, bcm2835_fb.size);
168 resplen = 8;
169 break;
170 case 0x00048001: /* Release buffer */
171 resplen = 0;
172 break;
173 case 0x00040002: /* Blank screen */
174 resplen = 4;
175 break;
176 case 0x00040003: /* Get display width/height */
177 case 0x00040004:
178 stl_phys(bcm2835_peripheral_as, value + 12, bcm2835_fb.xres);
179 stl_phys(bcm2835_peripheral_as, value + 16, bcm2835_fb.yres);
180 resplen = 8;
181 break;
182 case 0x00044003: /* Test display width/height */
183 case 0x00044004:
184 resplen = 8;
185 break;
186 case 0x00048003: /* Set display width/height */
187 case 0x00048004:
188 bcm2835_fb.xres = ldl_phys(bcm2835_peripheral_as, value + 12);
189 bcm2835_fb.yres = ldl_phys(bcm2835_peripheral_as, value + 16);
190 update_fb();
191 resplen = 8;
192 break;
193 case 0x00040005: /* Get depth */
194 stl_phys(bcm2835_peripheral_as, value + 12, bcm2835_fb.bpp);
195 resplen = 4;
196 break;
197 case 0x00044005: /* Test depth */
198 resplen = 4;
199 break;
200 case 0x00048005: /* Set depth */
201 bcm2835_fb.bpp = ldl_phys(bcm2835_peripheral_as, value + 12);
202 update_fb();
203 resplen = 4;
204 break;
205 case 0x00040006: /* Get pixel order */
206 stl_phys(bcm2835_peripheral_as, value + 12, bcm2835_fb.pixo);
207 resplen = 4;
208 break;
209 case 0x00044006: /* Test pixel order */
210 resplen = 4;
211 break;
212 case 0x00048006: /* Set pixel order */
213 bcm2835_fb.pixo = ldl_phys(bcm2835_peripheral_as, value + 12);
214 update_fb();
215 resplen = 4;
216 break;
217 case 0x00040007: /* Get alpha */
218 stl_phys(bcm2835_peripheral_as, value + 12, bcm2835_fb.alpha);
219 resplen = 4;
220 break;
221 case 0x00044007: /* Test pixel alpha */
222 resplen = 4;
223 break;
224 case 0x00048007: /* Set alpha */
225 bcm2835_fb.alpha = ldl_phys(bcm2835_peripheral_as, value + 12);
226 update_fb();
227 resplen = 4;
228 break;
229 case 0x00040008: /* Get pitch */
230 stl_phys(bcm2835_peripheral_as, value + 12, bcm2835_fb.pitch);
231 resplen = 4;
232 break;
233 case 0x00040009: /* Get virtual offset */
234 stl_phys(bcm2835_peripheral_as, value + 12, bcm2835_fb.xoffset);
235 stl_phys(bcm2835_peripheral_as, value + 16, bcm2835_fb.yoffset);
236 resplen = 8;
237 break;
238 case 0x00044009: /* Test virtual offset */
239 resplen = 8;
240 break;
241 case 0x00048009: /* Set virtual offset */
242 bcm2835_fb.xoffset = ldl_phys(bcm2835_peripheral_as, value + 12);
243 bcm2835_fb.yoffset = ldl_phys(bcm2835_peripheral_as, value + 16);
244 update_fb();
245 stl_phys(bcm2835_peripheral_as, value + 12, bcm2835_fb.xres);
246 stl_phys(bcm2835_peripheral_as, value + 16, bcm2835_fb.yres);
247 resplen = 8;
248 break;
249 case 0x0004000a: /* Get/Test/Set overscan */
250 case 0x0004400a:
251 case 0x0004800a:
252 stl_phys(bcm2835_peripheral_as, value + 12, 0);
253 stl_phys(bcm2835_peripheral_as, value + 16, 0);
254 stl_phys(bcm2835_peripheral_as, value + 20, 0);
255 stl_phys(bcm2835_peripheral_as, value + 24, 0);
256 resplen = 16;
257 break;
259 case 0x0004800b: /* Set palette */
260 offset = ldl_phys(bcm2835_peripheral_as, value + 12);
261 length = ldl_phys(bcm2835_peripheral_as, value + 16);
262 n = 0;
263 while (n < length - offset) {
264 color = ldl_phys(bcm2835_peripheral_as, value + 20 + (n << 2));
265 stl_phys(bcm2835_peripheral_as,
266 bcm2835_vcram_base + ((offset + n) << 2), color);
267 n++;
269 stl_phys(bcm2835_peripheral_as, value + 12, 0);
270 resplen = 4;
271 break;
273 case 0x00060001: /* Get DMA channels */
274 /* channels 2-5 */
275 stl_phys(bcm2835_peripheral_as, value + 12, 0x003C);
276 resplen = 4;
277 break;
279 case 0x00050001: /* Get command line */
280 resplen = 0;
281 break;
283 default:
284 qemu_log_mask(LOG_GUEST_ERROR,
285 "bcm2835_property: unhandled tag %08x\n", tag);
286 break;
289 if (tag == 0) {
290 break;
293 stl_phys(bcm2835_peripheral_as, value + 8, (1 << 31) | resplen);
294 value += bufsize + 12;
297 /* Buffer response code */
298 stl_phys(bcm2835_peripheral_as, s->addr + 4, (1 << 31));
300 if (bcm2835_fb.lock) {
301 bcm2835_fb.invalidate = 1;
302 qemu_console_resize(bcm2835_fb.con, bcm2835_fb.xres, bcm2835_fb.yres);
303 bcm2835_fb.lock = 0;
307 static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
308 unsigned size)
310 Bcm2835PropertyState *s = (Bcm2835PropertyState *)opaque;
311 uint32_t res = 0;
313 switch (offset) {
314 case 0:
315 res = MBOX_CHAN_PROPERTY | s->addr;
316 s->pending = 0;
317 qemu_set_irq(s->mbox_irq, 0);
318 break;
319 case 4:
320 res = s->pending;
321 break;
322 default:
323 qemu_log_mask(LOG_GUEST_ERROR,
324 "bcm2835_property_read: Bad offset %x\n", (int)offset);
325 return 0;
327 return res;
329 static void bcm2835_property_write(void *opaque, hwaddr offset,
330 uint64_t value, unsigned size)
332 Bcm2835PropertyState *s = (Bcm2835PropertyState *)opaque;
333 switch (offset) {
334 case 0:
335 if (!s->pending) {
336 s->pending = 1;
337 bcm2835_property_mbox_push(s, value);
338 qemu_set_irq(s->mbox_irq, 1);
340 break;
341 default:
342 qemu_log_mask(LOG_GUEST_ERROR,
343 "bcm2835_property_write: Bad offset %x\n", (int)offset);
344 return;
350 static const MemoryRegionOps bcm2835_property_ops = {
351 .read = bcm2835_property_read,
352 .write = bcm2835_property_write,
353 .endianness = DEVICE_NATIVE_ENDIAN,
357 static const VMStateDescription vmstate_bcm2835_property = {
358 .name = TYPE_BCM2835_PROPERTY,
359 .version_id = 1,
360 .minimum_version_id = 1,
361 .minimum_version_id_old = 1,
362 .fields = (VMStateField[]) {
363 VMSTATE_END_OF_LIST()
367 static int bcm2835_property_init(SysBusDevice *sbd)
369 DeviceState *dev = DEVICE(sbd);
370 Bcm2835PropertyState *s = BCM2835_PROPERTY(dev);
372 s->pending = 0;
373 s->addr = 0;
375 sysbus_init_irq(sbd, &s->mbox_irq);
376 memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s,
377 TYPE_BCM2835_PROPERTY, 0x10);
378 sysbus_init_mmio(sbd, &s->iomem);
379 vmstate_register(dev, -1, &vmstate_bcm2835_property, s);
381 return 0;
384 static void bcm2835_property_class_init(ObjectClass *klass, void *data)
386 SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
388 sdc->init = bcm2835_property_init;
391 static TypeInfo bcm2835_property_info = {
392 .name = TYPE_BCM2835_PROPERTY,
393 .parent = TYPE_SYS_BUS_DEVICE,
394 .instance_size = sizeof(Bcm2835PropertyState),
395 .class_init = bcm2835_property_class_init,
398 static void bcm2835_property_register_types(void)
400 type_register_static(&bcm2835_property_info);
403 type_init(bcm2835_property_register_types)