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