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