xref: /qemu/hw/misc/bcm2835_property.c (revision c964b660223308150bb72c627b320c79372c7128)
104f1ab15SAndrew Baumann /*
204f1ab15SAndrew Baumann  * Raspberry Pi emulation (c) 2012 Gregory Estrade
304f1ab15SAndrew Baumann  * This code is licensed under the GNU GPLv2 and later.
404f1ab15SAndrew Baumann  */
504f1ab15SAndrew Baumann 
6*c964b660SPeter Maydell #include "qemu/osdep.h"
704f1ab15SAndrew Baumann #include "hw/misc/bcm2835_property.h"
804f1ab15SAndrew Baumann #include "hw/misc/bcm2835_mbox_defs.h"
904f1ab15SAndrew Baumann #include "sysemu/dma.h"
1004f1ab15SAndrew Baumann 
1104f1ab15SAndrew Baumann /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
1204f1ab15SAndrew Baumann 
1304f1ab15SAndrew Baumann static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
1404f1ab15SAndrew Baumann {
1504f1ab15SAndrew Baumann     uint32_t tag;
1604f1ab15SAndrew Baumann     uint32_t bufsize;
1704f1ab15SAndrew Baumann     uint32_t tot_len;
1804f1ab15SAndrew Baumann     size_t resplen;
1904f1ab15SAndrew Baumann     uint32_t tmp;
2004f1ab15SAndrew Baumann 
2104f1ab15SAndrew Baumann     value &= ~0xf;
2204f1ab15SAndrew Baumann 
2304f1ab15SAndrew Baumann     s->addr = value;
2404f1ab15SAndrew Baumann 
2504f1ab15SAndrew Baumann     tot_len = ldl_phys(&s->dma_as, value);
2604f1ab15SAndrew Baumann 
2704f1ab15SAndrew Baumann     /* @(addr + 4) : Buffer response code */
2804f1ab15SAndrew Baumann     value = s->addr + 8;
2904f1ab15SAndrew Baumann     while (value + 8 <= s->addr + tot_len) {
3004f1ab15SAndrew Baumann         tag = ldl_phys(&s->dma_as, value);
3104f1ab15SAndrew Baumann         bufsize = ldl_phys(&s->dma_as, value + 4);
3204f1ab15SAndrew Baumann         /* @(value + 8) : Request/response indicator */
3304f1ab15SAndrew Baumann         resplen = 0;
3404f1ab15SAndrew Baumann         switch (tag) {
3504f1ab15SAndrew Baumann         case 0x00000000: /* End tag */
3604f1ab15SAndrew Baumann             break;
3704f1ab15SAndrew Baumann         case 0x00000001: /* Get firmware revision */
3804f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 12, 346337);
3904f1ab15SAndrew Baumann             resplen = 4;
4004f1ab15SAndrew Baumann             break;
4104f1ab15SAndrew Baumann         case 0x00010001: /* Get board model */
4204f1ab15SAndrew Baumann             qemu_log_mask(LOG_UNIMP,
4304f1ab15SAndrew Baumann                           "bcm2835_property: %x get board model NYI\n", tag);
4404f1ab15SAndrew Baumann             resplen = 4;
4504f1ab15SAndrew Baumann             break;
4604f1ab15SAndrew Baumann         case 0x00010002: /* Get board revision */
47f0afa731SStephen Warren             stl_phys(&s->dma_as, value + 12, s->board_rev);
4804f1ab15SAndrew Baumann             resplen = 4;
4904f1ab15SAndrew Baumann             break;
5004f1ab15SAndrew Baumann         case 0x00010003: /* Get board MAC address */
5104f1ab15SAndrew Baumann             resplen = sizeof(s->macaddr.a);
5204f1ab15SAndrew Baumann             dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen);
5304f1ab15SAndrew Baumann             break;
5404f1ab15SAndrew Baumann         case 0x00010004: /* Get board serial */
5504f1ab15SAndrew Baumann             qemu_log_mask(LOG_UNIMP,
5604f1ab15SAndrew Baumann                           "bcm2835_property: %x get board serial NYI\n", tag);
5704f1ab15SAndrew Baumann             resplen = 8;
5804f1ab15SAndrew Baumann             break;
5904f1ab15SAndrew Baumann         case 0x00010005: /* Get ARM memory */
6004f1ab15SAndrew Baumann             /* base */
6104f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 12, 0);
6204f1ab15SAndrew Baumann             /* size */
6304f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, s->ram_size);
6404f1ab15SAndrew Baumann             resplen = 8;
6504f1ab15SAndrew Baumann             break;
6604f1ab15SAndrew Baumann         case 0x00028001: /* Set power state */
6704f1ab15SAndrew Baumann             /* Assume that whatever device they asked for exists,
6804f1ab15SAndrew Baumann              * and we'll just claim we set it to the desired state
6904f1ab15SAndrew Baumann              */
7004f1ab15SAndrew Baumann             tmp = ldl_phys(&s->dma_as, value + 16);
7104f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, (tmp & 1));
7204f1ab15SAndrew Baumann             resplen = 8;
7304f1ab15SAndrew Baumann             break;
7404f1ab15SAndrew Baumann 
7504f1ab15SAndrew Baumann         /* Clocks */
7604f1ab15SAndrew Baumann 
7704f1ab15SAndrew Baumann         case 0x00030001: /* Get clock state */
7804f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, 0x1);
7904f1ab15SAndrew Baumann             resplen = 8;
8004f1ab15SAndrew Baumann             break;
8104f1ab15SAndrew Baumann 
8204f1ab15SAndrew Baumann         case 0x00038001: /* Set clock state */
8304f1ab15SAndrew Baumann             qemu_log_mask(LOG_UNIMP,
8404f1ab15SAndrew Baumann                           "bcm2835_property: %x set clock state NYI\n", tag);
8504f1ab15SAndrew Baumann             resplen = 8;
8604f1ab15SAndrew Baumann             break;
8704f1ab15SAndrew Baumann 
8804f1ab15SAndrew Baumann         case 0x00030002: /* Get clock rate */
8904f1ab15SAndrew Baumann         case 0x00030004: /* Get max clock rate */
9004f1ab15SAndrew Baumann         case 0x00030007: /* Get min clock rate */
9104f1ab15SAndrew Baumann             switch (ldl_phys(&s->dma_as, value + 12)) {
9204f1ab15SAndrew Baumann             case 1: /* EMMC */
9304f1ab15SAndrew Baumann                 stl_phys(&s->dma_as, value + 16, 50000000);
9404f1ab15SAndrew Baumann                 break;
9504f1ab15SAndrew Baumann             case 2: /* UART */
9604f1ab15SAndrew Baumann                 stl_phys(&s->dma_as, value + 16, 3000000);
9704f1ab15SAndrew Baumann                 break;
9804f1ab15SAndrew Baumann             default:
9904f1ab15SAndrew Baumann                 stl_phys(&s->dma_as, value + 16, 700000000);
10004f1ab15SAndrew Baumann                 break;
10104f1ab15SAndrew Baumann             }
10204f1ab15SAndrew Baumann             resplen = 8;
10304f1ab15SAndrew Baumann             break;
10404f1ab15SAndrew Baumann 
10504f1ab15SAndrew Baumann         case 0x00038002: /* Set clock rate */
10604f1ab15SAndrew Baumann         case 0x00038004: /* Set max clock rate */
10704f1ab15SAndrew Baumann         case 0x00038007: /* Set min clock rate */
10804f1ab15SAndrew Baumann             qemu_log_mask(LOG_UNIMP,
10904f1ab15SAndrew Baumann                           "bcm2835_property: %x set clock rates NYI\n", tag);
11004f1ab15SAndrew Baumann             resplen = 8;
11104f1ab15SAndrew Baumann             break;
11204f1ab15SAndrew Baumann 
11304f1ab15SAndrew Baumann         /* Temperature */
11404f1ab15SAndrew Baumann 
11504f1ab15SAndrew Baumann         case 0x00030006: /* Get temperature */
11604f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, 25000);
11704f1ab15SAndrew Baumann             resplen = 8;
11804f1ab15SAndrew Baumann             break;
11904f1ab15SAndrew Baumann 
12004f1ab15SAndrew Baumann         case 0x0003000A: /* Get max temperature */
12104f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, 99000);
12204f1ab15SAndrew Baumann             resplen = 8;
12304f1ab15SAndrew Baumann             break;
12404f1ab15SAndrew Baumann 
12504f1ab15SAndrew Baumann 
12604f1ab15SAndrew Baumann         case 0x00060001: /* Get DMA channels */
12704f1ab15SAndrew Baumann             /* channels 2-5 */
12804f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 12, 0x003C);
12904f1ab15SAndrew Baumann             resplen = 4;
13004f1ab15SAndrew Baumann             break;
13104f1ab15SAndrew Baumann 
13204f1ab15SAndrew Baumann         case 0x00050001: /* Get command line */
13304f1ab15SAndrew Baumann             resplen = 0;
13404f1ab15SAndrew Baumann             break;
13504f1ab15SAndrew Baumann 
13604f1ab15SAndrew Baumann         default:
13704f1ab15SAndrew Baumann             qemu_log_mask(LOG_GUEST_ERROR,
13804f1ab15SAndrew Baumann                           "bcm2835_property: unhandled tag %08x\n", tag);
13904f1ab15SAndrew Baumann             break;
14004f1ab15SAndrew Baumann         }
14104f1ab15SAndrew Baumann 
14204f1ab15SAndrew Baumann         if (tag == 0) {
14304f1ab15SAndrew Baumann             break;
14404f1ab15SAndrew Baumann         }
14504f1ab15SAndrew Baumann 
14604f1ab15SAndrew Baumann         stl_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
14704f1ab15SAndrew Baumann         value += bufsize + 12;
14804f1ab15SAndrew Baumann     }
14904f1ab15SAndrew Baumann 
15004f1ab15SAndrew Baumann     /* Buffer response code */
15104f1ab15SAndrew Baumann     stl_phys(&s->dma_as, s->addr + 4, (1 << 31));
15204f1ab15SAndrew Baumann }
15304f1ab15SAndrew Baumann 
15404f1ab15SAndrew Baumann static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
15504f1ab15SAndrew Baumann                                       unsigned size)
15604f1ab15SAndrew Baumann {
15704f1ab15SAndrew Baumann     BCM2835PropertyState *s = opaque;
15804f1ab15SAndrew Baumann     uint32_t res = 0;
15904f1ab15SAndrew Baumann 
16004f1ab15SAndrew Baumann     switch (offset) {
16104f1ab15SAndrew Baumann     case MBOX_AS_DATA:
16204f1ab15SAndrew Baumann         res = MBOX_CHAN_PROPERTY | s->addr;
16304f1ab15SAndrew Baumann         s->pending = false;
16404f1ab15SAndrew Baumann         qemu_set_irq(s->mbox_irq, 0);
16504f1ab15SAndrew Baumann         break;
16604f1ab15SAndrew Baumann 
16704f1ab15SAndrew Baumann     case MBOX_AS_PENDING:
16804f1ab15SAndrew Baumann         res = s->pending;
16904f1ab15SAndrew Baumann         break;
17004f1ab15SAndrew Baumann 
17104f1ab15SAndrew Baumann     default:
17204f1ab15SAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
17304f1ab15SAndrew Baumann                       __func__, offset);
17404f1ab15SAndrew Baumann         return 0;
17504f1ab15SAndrew Baumann     }
17604f1ab15SAndrew Baumann 
17704f1ab15SAndrew Baumann     return res;
17804f1ab15SAndrew Baumann }
17904f1ab15SAndrew Baumann 
18004f1ab15SAndrew Baumann static void bcm2835_property_write(void *opaque, hwaddr offset,
18104f1ab15SAndrew Baumann                                    uint64_t value, unsigned size)
18204f1ab15SAndrew Baumann {
18304f1ab15SAndrew Baumann     BCM2835PropertyState *s = opaque;
18404f1ab15SAndrew Baumann 
18504f1ab15SAndrew Baumann     switch (offset) {
18604f1ab15SAndrew Baumann     case MBOX_AS_DATA:
18704f1ab15SAndrew Baumann         /* bcm2835_mbox should check our pending status before pushing */
18804f1ab15SAndrew Baumann         assert(!s->pending);
18904f1ab15SAndrew Baumann         s->pending = true;
19004f1ab15SAndrew Baumann         bcm2835_property_mbox_push(s, value);
19104f1ab15SAndrew Baumann         qemu_set_irq(s->mbox_irq, 1);
19204f1ab15SAndrew Baumann         break;
19304f1ab15SAndrew Baumann 
19404f1ab15SAndrew Baumann     default:
19504f1ab15SAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
19604f1ab15SAndrew Baumann                       __func__, offset);
19704f1ab15SAndrew Baumann         return;
19804f1ab15SAndrew Baumann     }
19904f1ab15SAndrew Baumann }
20004f1ab15SAndrew Baumann 
20104f1ab15SAndrew Baumann static const MemoryRegionOps bcm2835_property_ops = {
20204f1ab15SAndrew Baumann     .read = bcm2835_property_read,
20304f1ab15SAndrew Baumann     .write = bcm2835_property_write,
20404f1ab15SAndrew Baumann     .endianness = DEVICE_NATIVE_ENDIAN,
20504f1ab15SAndrew Baumann     .valid.min_access_size = 4,
20604f1ab15SAndrew Baumann     .valid.max_access_size = 4,
20704f1ab15SAndrew Baumann };
20804f1ab15SAndrew Baumann 
20904f1ab15SAndrew Baumann static const VMStateDescription vmstate_bcm2835_property = {
21004f1ab15SAndrew Baumann     .name = TYPE_BCM2835_PROPERTY,
21104f1ab15SAndrew Baumann     .version_id = 1,
21204f1ab15SAndrew Baumann     .minimum_version_id = 1,
21304f1ab15SAndrew Baumann     .fields      = (VMStateField[]) {
21404f1ab15SAndrew Baumann         VMSTATE_MACADDR(macaddr, BCM2835PropertyState),
21504f1ab15SAndrew Baumann         VMSTATE_UINT32(addr, BCM2835PropertyState),
21604f1ab15SAndrew Baumann         VMSTATE_BOOL(pending, BCM2835PropertyState),
21704f1ab15SAndrew Baumann         VMSTATE_END_OF_LIST()
21804f1ab15SAndrew Baumann     }
21904f1ab15SAndrew Baumann };
22004f1ab15SAndrew Baumann 
22104f1ab15SAndrew Baumann static void bcm2835_property_init(Object *obj)
22204f1ab15SAndrew Baumann {
22304f1ab15SAndrew Baumann     BCM2835PropertyState *s = BCM2835_PROPERTY(obj);
22404f1ab15SAndrew Baumann 
22504f1ab15SAndrew Baumann     memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s,
22604f1ab15SAndrew Baumann                           TYPE_BCM2835_PROPERTY, 0x10);
22704f1ab15SAndrew Baumann     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
22804f1ab15SAndrew Baumann     sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
22904f1ab15SAndrew Baumann }
23004f1ab15SAndrew Baumann 
23104f1ab15SAndrew Baumann static void bcm2835_property_reset(DeviceState *dev)
23204f1ab15SAndrew Baumann {
23304f1ab15SAndrew Baumann     BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
23404f1ab15SAndrew Baumann 
23504f1ab15SAndrew Baumann     s->pending = false;
23604f1ab15SAndrew Baumann }
23704f1ab15SAndrew Baumann 
23804f1ab15SAndrew Baumann static void bcm2835_property_realize(DeviceState *dev, Error **errp)
23904f1ab15SAndrew Baumann {
24004f1ab15SAndrew Baumann     BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
24104f1ab15SAndrew Baumann     Object *obj;
24204f1ab15SAndrew Baumann     Error *err = NULL;
24304f1ab15SAndrew Baumann 
24404f1ab15SAndrew Baumann     obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
24504f1ab15SAndrew Baumann     if (obj == NULL) {
24604f1ab15SAndrew Baumann         error_setg(errp, "%s: required dma-mr link not found: %s",
24704f1ab15SAndrew Baumann                    __func__, error_get_pretty(err));
24804f1ab15SAndrew Baumann         return;
24904f1ab15SAndrew Baumann     }
25004f1ab15SAndrew Baumann 
25104f1ab15SAndrew Baumann     s->dma_mr = MEMORY_REGION(obj);
25204f1ab15SAndrew Baumann     address_space_init(&s->dma_as, s->dma_mr, NULL);
25304f1ab15SAndrew Baumann 
25404f1ab15SAndrew Baumann     /* TODO: connect to MAC address of USB NIC device, once we emulate it */
25504f1ab15SAndrew Baumann     qemu_macaddr_default_if_unset(&s->macaddr);
25604f1ab15SAndrew Baumann 
25704f1ab15SAndrew Baumann     bcm2835_property_reset(dev);
25804f1ab15SAndrew Baumann }
25904f1ab15SAndrew Baumann 
26004f1ab15SAndrew Baumann static Property bcm2835_property_props[] = {
261f0afa731SStephen Warren     DEFINE_PROP_UINT32("board-rev", BCM2835PropertyState, board_rev, 0),
26204f1ab15SAndrew Baumann     DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0),
26304f1ab15SAndrew Baumann     DEFINE_PROP_END_OF_LIST()
26404f1ab15SAndrew Baumann };
26504f1ab15SAndrew Baumann 
26604f1ab15SAndrew Baumann static void bcm2835_property_class_init(ObjectClass *klass, void *data)
26704f1ab15SAndrew Baumann {
26804f1ab15SAndrew Baumann     DeviceClass *dc = DEVICE_CLASS(klass);
26904f1ab15SAndrew Baumann 
27004f1ab15SAndrew Baumann     dc->props = bcm2835_property_props;
27104f1ab15SAndrew Baumann     dc->realize = bcm2835_property_realize;
27204f1ab15SAndrew Baumann     dc->vmsd = &vmstate_bcm2835_property;
27304f1ab15SAndrew Baumann }
27404f1ab15SAndrew Baumann 
27504f1ab15SAndrew Baumann static TypeInfo bcm2835_property_info = {
27604f1ab15SAndrew Baumann     .name          = TYPE_BCM2835_PROPERTY,
27704f1ab15SAndrew Baumann     .parent        = TYPE_SYS_BUS_DEVICE,
27804f1ab15SAndrew Baumann     .instance_size = sizeof(BCM2835PropertyState),
27904f1ab15SAndrew Baumann     .class_init    = bcm2835_property_class_init,
28004f1ab15SAndrew Baumann     .instance_init = bcm2835_property_init,
28104f1ab15SAndrew Baumann };
28204f1ab15SAndrew Baumann 
28304f1ab15SAndrew Baumann static void bcm2835_property_register_types(void)
28404f1ab15SAndrew Baumann {
28504f1ab15SAndrew Baumann     type_register_static(&bcm2835_property_info);
28604f1ab15SAndrew Baumann }
28704f1ab15SAndrew Baumann 
28804f1ab15SAndrew Baumann type_init(bcm2835_property_register_types)
289