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
  12/* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
  13
  14static 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;
  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;
  26
  27    value &= ~0xf;
  28
  29    s->addr = value;
  30
  31    tot_len = ldl_le_phys(&s->dma_as, value);
  32
  33    /* @(addr + 4) : Buffer response code */
  34    value = s->addr + 8;
  35    while (value + 8 <= s->addr + tot_len) {
  36        tag = ldl_le_phys(&s->dma_as, value);
  37        bufsize = ldl_le_phys(&s->dma_as, value + 4);
  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 */
  44            stl_le_phys(&s->dma_as, value + 12, 346337);
  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 */
  53            stl_le_phys(&s->dma_as, value + 12, s->board_rev);
  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 */
  67            stl_le_phys(&s->dma_as, value + 12, 0);
  68            /* size */
  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);
  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             */
  83            tmp = ldl_le_phys(&s->dma_as, value + 16);
  84            stl_le_phys(&s->dma_as, value + 16, (tmp & 1));
  85            resplen = 8;
  86            break;
  87
  88        /* Clocks */
  89
  90        case 0x00030001: /* Get clock state */
  91            stl_le_phys(&s->dma_as, value + 16, 0x1);
  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 */
 104            switch (ldl_le_phys(&s->dma_as, value + 12)) {
 105            case 1: /* EMMC */
 106                stl_le_phys(&s->dma_as, value + 16, 50000000);
 107                break;
 108            case 2: /* UART */
 109                stl_le_phys(&s->dma_as, value + 16, 3000000);
 110                break;
 111            default:
 112                stl_le_phys(&s->dma_as, value + 16, 700000000);
 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 */
 129            stl_le_phys(&s->dma_as, value + 16, 25000);
 130            resplen = 8;
 131            break;
 132
 133        case 0x0003000A: /* Get max temperature */
 134            stl_le_phys(&s->dma_as, value + 16, 99000);
 135            resplen = 8;
 136            break;
 137
 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 = &alpha;
 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;
 246
 247        case 0x00060001: /* Get DMA channels */
 248            /* channels 2-5 */
 249            stl_le_phys(&s->dma_as, value + 12, 0x003C);
 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
 267        stl_le_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
 268        value += bufsize + 12;
 269    }
 270
 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
 278    /* Buffer response code */
 279    stl_le_phys(&s->dma_as, s->addr + 4, (1 << 31));
 280}
 281
 282static 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
 308static 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
 329static 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
 337static 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
 349static 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
 359static void bcm2835_property_reset(DeviceState *dev)
 360{
 361    BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
 362
 363    s->pending = false;
 364}
 365
 366static void bcm2835_property_realize(DeviceState *dev, Error **errp)
 367{
 368    BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
 369    Object *obj;
 370    Error *err = NULL;
 371
 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
 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
 397static Property bcm2835_property_props[] = {
 398    DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
 399    DEFINE_PROP_END_OF_LIST()
 400};
 401
 402static 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
 411static 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
 419static void bcm2835_property_register_types(void)
 420{
 421    type_register_static(&bcm2835_property_info);
 422}
 423
 424type_init(bcm2835_property_register_types)
 425