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