xref: /qemu/hw/misc/bcm2835_property.c (revision 04f1ab15b9f60bbd0f71adbe7659aef12ca1a448)
1*04f1ab15SAndrew Baumann /*
2*04f1ab15SAndrew Baumann  * Raspberry Pi emulation (c) 2012 Gregory Estrade
3*04f1ab15SAndrew Baumann  * This code is licensed under the GNU GPLv2 and later.
4*04f1ab15SAndrew Baumann  */
5*04f1ab15SAndrew Baumann 
6*04f1ab15SAndrew Baumann #include "hw/misc/bcm2835_property.h"
7*04f1ab15SAndrew Baumann #include "hw/misc/bcm2835_mbox_defs.h"
8*04f1ab15SAndrew Baumann #include "sysemu/dma.h"
9*04f1ab15SAndrew Baumann 
10*04f1ab15SAndrew Baumann /* https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface */
11*04f1ab15SAndrew Baumann 
12*04f1ab15SAndrew Baumann static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value)
13*04f1ab15SAndrew Baumann {
14*04f1ab15SAndrew Baumann     uint32_t tag;
15*04f1ab15SAndrew Baumann     uint32_t bufsize;
16*04f1ab15SAndrew Baumann     uint32_t tot_len;
17*04f1ab15SAndrew Baumann     size_t resplen;
18*04f1ab15SAndrew Baumann     uint32_t tmp;
19*04f1ab15SAndrew Baumann 
20*04f1ab15SAndrew Baumann     value &= ~0xf;
21*04f1ab15SAndrew Baumann 
22*04f1ab15SAndrew Baumann     s->addr = value;
23*04f1ab15SAndrew Baumann 
24*04f1ab15SAndrew Baumann     tot_len = ldl_phys(&s->dma_as, value);
25*04f1ab15SAndrew Baumann 
26*04f1ab15SAndrew Baumann     /* @(addr + 4) : Buffer response code */
27*04f1ab15SAndrew Baumann     value = s->addr + 8;
28*04f1ab15SAndrew Baumann     while (value + 8 <= s->addr + tot_len) {
29*04f1ab15SAndrew Baumann         tag = ldl_phys(&s->dma_as, value);
30*04f1ab15SAndrew Baumann         bufsize = ldl_phys(&s->dma_as, value + 4);
31*04f1ab15SAndrew Baumann         /* @(value + 8) : Request/response indicator */
32*04f1ab15SAndrew Baumann         resplen = 0;
33*04f1ab15SAndrew Baumann         switch (tag) {
34*04f1ab15SAndrew Baumann         case 0x00000000: /* End tag */
35*04f1ab15SAndrew Baumann             break;
36*04f1ab15SAndrew Baumann         case 0x00000001: /* Get firmware revision */
37*04f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 12, 346337);
38*04f1ab15SAndrew Baumann             resplen = 4;
39*04f1ab15SAndrew Baumann             break;
40*04f1ab15SAndrew Baumann         case 0x00010001: /* Get board model */
41*04f1ab15SAndrew Baumann             qemu_log_mask(LOG_UNIMP,
42*04f1ab15SAndrew Baumann                           "bcm2835_property: %x get board model NYI\n", tag);
43*04f1ab15SAndrew Baumann             resplen = 4;
44*04f1ab15SAndrew Baumann             break;
45*04f1ab15SAndrew Baumann         case 0x00010002: /* Get board revision */
46*04f1ab15SAndrew Baumann             qemu_log_mask(LOG_UNIMP,
47*04f1ab15SAndrew Baumann                           "bcm2835_property: %x get board revision NYI\n", tag);
48*04f1ab15SAndrew Baumann             resplen = 4;
49*04f1ab15SAndrew Baumann             break;
50*04f1ab15SAndrew Baumann         case 0x00010003: /* Get board MAC address */
51*04f1ab15SAndrew Baumann             resplen = sizeof(s->macaddr.a);
52*04f1ab15SAndrew Baumann             dma_memory_write(&s->dma_as, value + 12, s->macaddr.a, resplen);
53*04f1ab15SAndrew Baumann             break;
54*04f1ab15SAndrew Baumann         case 0x00010004: /* Get board serial */
55*04f1ab15SAndrew Baumann             qemu_log_mask(LOG_UNIMP,
56*04f1ab15SAndrew Baumann                           "bcm2835_property: %x get board serial NYI\n", tag);
57*04f1ab15SAndrew Baumann             resplen = 8;
58*04f1ab15SAndrew Baumann             break;
59*04f1ab15SAndrew Baumann         case 0x00010005: /* Get ARM memory */
60*04f1ab15SAndrew Baumann             /* base */
61*04f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 12, 0);
62*04f1ab15SAndrew Baumann             /* size */
63*04f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, s->ram_size);
64*04f1ab15SAndrew Baumann             resplen = 8;
65*04f1ab15SAndrew Baumann             break;
66*04f1ab15SAndrew Baumann         case 0x00028001: /* Set power state */
67*04f1ab15SAndrew Baumann             /* Assume that whatever device they asked for exists,
68*04f1ab15SAndrew Baumann              * and we'll just claim we set it to the desired state
69*04f1ab15SAndrew Baumann              */
70*04f1ab15SAndrew Baumann             tmp = ldl_phys(&s->dma_as, value + 16);
71*04f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, (tmp & 1));
72*04f1ab15SAndrew Baumann             resplen = 8;
73*04f1ab15SAndrew Baumann             break;
74*04f1ab15SAndrew Baumann 
75*04f1ab15SAndrew Baumann         /* Clocks */
76*04f1ab15SAndrew Baumann 
77*04f1ab15SAndrew Baumann         case 0x00030001: /* Get clock state */
78*04f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, 0x1);
79*04f1ab15SAndrew Baumann             resplen = 8;
80*04f1ab15SAndrew Baumann             break;
81*04f1ab15SAndrew Baumann 
82*04f1ab15SAndrew Baumann         case 0x00038001: /* Set clock state */
83*04f1ab15SAndrew Baumann             qemu_log_mask(LOG_UNIMP,
84*04f1ab15SAndrew Baumann                           "bcm2835_property: %x set clock state NYI\n", tag);
85*04f1ab15SAndrew Baumann             resplen = 8;
86*04f1ab15SAndrew Baumann             break;
87*04f1ab15SAndrew Baumann 
88*04f1ab15SAndrew Baumann         case 0x00030002: /* Get clock rate */
89*04f1ab15SAndrew Baumann         case 0x00030004: /* Get max clock rate */
90*04f1ab15SAndrew Baumann         case 0x00030007: /* Get min clock rate */
91*04f1ab15SAndrew Baumann             switch (ldl_phys(&s->dma_as, value + 12)) {
92*04f1ab15SAndrew Baumann             case 1: /* EMMC */
93*04f1ab15SAndrew Baumann                 stl_phys(&s->dma_as, value + 16, 50000000);
94*04f1ab15SAndrew Baumann                 break;
95*04f1ab15SAndrew Baumann             case 2: /* UART */
96*04f1ab15SAndrew Baumann                 stl_phys(&s->dma_as, value + 16, 3000000);
97*04f1ab15SAndrew Baumann                 break;
98*04f1ab15SAndrew Baumann             default:
99*04f1ab15SAndrew Baumann                 stl_phys(&s->dma_as, value + 16, 700000000);
100*04f1ab15SAndrew Baumann                 break;
101*04f1ab15SAndrew Baumann             }
102*04f1ab15SAndrew Baumann             resplen = 8;
103*04f1ab15SAndrew Baumann             break;
104*04f1ab15SAndrew Baumann 
105*04f1ab15SAndrew Baumann         case 0x00038002: /* Set clock rate */
106*04f1ab15SAndrew Baumann         case 0x00038004: /* Set max clock rate */
107*04f1ab15SAndrew Baumann         case 0x00038007: /* Set min clock rate */
108*04f1ab15SAndrew Baumann             qemu_log_mask(LOG_UNIMP,
109*04f1ab15SAndrew Baumann                           "bcm2835_property: %x set clock rates NYI\n", tag);
110*04f1ab15SAndrew Baumann             resplen = 8;
111*04f1ab15SAndrew Baumann             break;
112*04f1ab15SAndrew Baumann 
113*04f1ab15SAndrew Baumann         /* Temperature */
114*04f1ab15SAndrew Baumann 
115*04f1ab15SAndrew Baumann         case 0x00030006: /* Get temperature */
116*04f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, 25000);
117*04f1ab15SAndrew Baumann             resplen = 8;
118*04f1ab15SAndrew Baumann             break;
119*04f1ab15SAndrew Baumann 
120*04f1ab15SAndrew Baumann         case 0x0003000A: /* Get max temperature */
121*04f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 16, 99000);
122*04f1ab15SAndrew Baumann             resplen = 8;
123*04f1ab15SAndrew Baumann             break;
124*04f1ab15SAndrew Baumann 
125*04f1ab15SAndrew Baumann 
126*04f1ab15SAndrew Baumann         case 0x00060001: /* Get DMA channels */
127*04f1ab15SAndrew Baumann             /* channels 2-5 */
128*04f1ab15SAndrew Baumann             stl_phys(&s->dma_as, value + 12, 0x003C);
129*04f1ab15SAndrew Baumann             resplen = 4;
130*04f1ab15SAndrew Baumann             break;
131*04f1ab15SAndrew Baumann 
132*04f1ab15SAndrew Baumann         case 0x00050001: /* Get command line */
133*04f1ab15SAndrew Baumann             resplen = 0;
134*04f1ab15SAndrew Baumann             break;
135*04f1ab15SAndrew Baumann 
136*04f1ab15SAndrew Baumann         default:
137*04f1ab15SAndrew Baumann             qemu_log_mask(LOG_GUEST_ERROR,
138*04f1ab15SAndrew Baumann                           "bcm2835_property: unhandled tag %08x\n", tag);
139*04f1ab15SAndrew Baumann             break;
140*04f1ab15SAndrew Baumann         }
141*04f1ab15SAndrew Baumann 
142*04f1ab15SAndrew Baumann         if (tag == 0) {
143*04f1ab15SAndrew Baumann             break;
144*04f1ab15SAndrew Baumann         }
145*04f1ab15SAndrew Baumann 
146*04f1ab15SAndrew Baumann         stl_phys(&s->dma_as, value + 8, (1 << 31) | resplen);
147*04f1ab15SAndrew Baumann         value += bufsize + 12;
148*04f1ab15SAndrew Baumann     }
149*04f1ab15SAndrew Baumann 
150*04f1ab15SAndrew Baumann     /* Buffer response code */
151*04f1ab15SAndrew Baumann     stl_phys(&s->dma_as, s->addr + 4, (1 << 31));
152*04f1ab15SAndrew Baumann }
153*04f1ab15SAndrew Baumann 
154*04f1ab15SAndrew Baumann static uint64_t bcm2835_property_read(void *opaque, hwaddr offset,
155*04f1ab15SAndrew Baumann                                       unsigned size)
156*04f1ab15SAndrew Baumann {
157*04f1ab15SAndrew Baumann     BCM2835PropertyState *s = opaque;
158*04f1ab15SAndrew Baumann     uint32_t res = 0;
159*04f1ab15SAndrew Baumann 
160*04f1ab15SAndrew Baumann     switch (offset) {
161*04f1ab15SAndrew Baumann     case MBOX_AS_DATA:
162*04f1ab15SAndrew Baumann         res = MBOX_CHAN_PROPERTY | s->addr;
163*04f1ab15SAndrew Baumann         s->pending = false;
164*04f1ab15SAndrew Baumann         qemu_set_irq(s->mbox_irq, 0);
165*04f1ab15SAndrew Baumann         break;
166*04f1ab15SAndrew Baumann 
167*04f1ab15SAndrew Baumann     case MBOX_AS_PENDING:
168*04f1ab15SAndrew Baumann         res = s->pending;
169*04f1ab15SAndrew Baumann         break;
170*04f1ab15SAndrew Baumann 
171*04f1ab15SAndrew Baumann     default:
172*04f1ab15SAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
173*04f1ab15SAndrew Baumann                       __func__, offset);
174*04f1ab15SAndrew Baumann         return 0;
175*04f1ab15SAndrew Baumann     }
176*04f1ab15SAndrew Baumann 
177*04f1ab15SAndrew Baumann     return res;
178*04f1ab15SAndrew Baumann }
179*04f1ab15SAndrew Baumann 
180*04f1ab15SAndrew Baumann static void bcm2835_property_write(void *opaque, hwaddr offset,
181*04f1ab15SAndrew Baumann                                    uint64_t value, unsigned size)
182*04f1ab15SAndrew Baumann {
183*04f1ab15SAndrew Baumann     BCM2835PropertyState *s = opaque;
184*04f1ab15SAndrew Baumann 
185*04f1ab15SAndrew Baumann     switch (offset) {
186*04f1ab15SAndrew Baumann     case MBOX_AS_DATA:
187*04f1ab15SAndrew Baumann         /* bcm2835_mbox should check our pending status before pushing */
188*04f1ab15SAndrew Baumann         assert(!s->pending);
189*04f1ab15SAndrew Baumann         s->pending = true;
190*04f1ab15SAndrew Baumann         bcm2835_property_mbox_push(s, value);
191*04f1ab15SAndrew Baumann         qemu_set_irq(s->mbox_irq, 1);
192*04f1ab15SAndrew Baumann         break;
193*04f1ab15SAndrew Baumann 
194*04f1ab15SAndrew Baumann     default:
195*04f1ab15SAndrew Baumann         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
196*04f1ab15SAndrew Baumann                       __func__, offset);
197*04f1ab15SAndrew Baumann         return;
198*04f1ab15SAndrew Baumann     }
199*04f1ab15SAndrew Baumann }
200*04f1ab15SAndrew Baumann 
201*04f1ab15SAndrew Baumann static const MemoryRegionOps bcm2835_property_ops = {
202*04f1ab15SAndrew Baumann     .read = bcm2835_property_read,
203*04f1ab15SAndrew Baumann     .write = bcm2835_property_write,
204*04f1ab15SAndrew Baumann     .endianness = DEVICE_NATIVE_ENDIAN,
205*04f1ab15SAndrew Baumann     .valid.min_access_size = 4,
206*04f1ab15SAndrew Baumann     .valid.max_access_size = 4,
207*04f1ab15SAndrew Baumann };
208*04f1ab15SAndrew Baumann 
209*04f1ab15SAndrew Baumann static const VMStateDescription vmstate_bcm2835_property = {
210*04f1ab15SAndrew Baumann     .name = TYPE_BCM2835_PROPERTY,
211*04f1ab15SAndrew Baumann     .version_id = 1,
212*04f1ab15SAndrew Baumann     .minimum_version_id = 1,
213*04f1ab15SAndrew Baumann     .fields      = (VMStateField[]) {
214*04f1ab15SAndrew Baumann         VMSTATE_MACADDR(macaddr, BCM2835PropertyState),
215*04f1ab15SAndrew Baumann         VMSTATE_UINT32(addr, BCM2835PropertyState),
216*04f1ab15SAndrew Baumann         VMSTATE_BOOL(pending, BCM2835PropertyState),
217*04f1ab15SAndrew Baumann         VMSTATE_END_OF_LIST()
218*04f1ab15SAndrew Baumann     }
219*04f1ab15SAndrew Baumann };
220*04f1ab15SAndrew Baumann 
221*04f1ab15SAndrew Baumann static void bcm2835_property_init(Object *obj)
222*04f1ab15SAndrew Baumann {
223*04f1ab15SAndrew Baumann     BCM2835PropertyState *s = BCM2835_PROPERTY(obj);
224*04f1ab15SAndrew Baumann 
225*04f1ab15SAndrew Baumann     memory_region_init_io(&s->iomem, OBJECT(s), &bcm2835_property_ops, s,
226*04f1ab15SAndrew Baumann                           TYPE_BCM2835_PROPERTY, 0x10);
227*04f1ab15SAndrew Baumann     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem);
228*04f1ab15SAndrew Baumann     sysbus_init_irq(SYS_BUS_DEVICE(s), &s->mbox_irq);
229*04f1ab15SAndrew Baumann }
230*04f1ab15SAndrew Baumann 
231*04f1ab15SAndrew Baumann static void bcm2835_property_reset(DeviceState *dev)
232*04f1ab15SAndrew Baumann {
233*04f1ab15SAndrew Baumann     BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
234*04f1ab15SAndrew Baumann 
235*04f1ab15SAndrew Baumann     s->pending = false;
236*04f1ab15SAndrew Baumann }
237*04f1ab15SAndrew Baumann 
238*04f1ab15SAndrew Baumann static void bcm2835_property_realize(DeviceState *dev, Error **errp)
239*04f1ab15SAndrew Baumann {
240*04f1ab15SAndrew Baumann     BCM2835PropertyState *s = BCM2835_PROPERTY(dev);
241*04f1ab15SAndrew Baumann     Object *obj;
242*04f1ab15SAndrew Baumann     Error *err = NULL;
243*04f1ab15SAndrew Baumann 
244*04f1ab15SAndrew Baumann     obj = object_property_get_link(OBJECT(dev), "dma-mr", &err);
245*04f1ab15SAndrew Baumann     if (obj == NULL) {
246*04f1ab15SAndrew Baumann         error_setg(errp, "%s: required dma-mr link not found: %s",
247*04f1ab15SAndrew Baumann                    __func__, error_get_pretty(err));
248*04f1ab15SAndrew Baumann         return;
249*04f1ab15SAndrew Baumann     }
250*04f1ab15SAndrew Baumann 
251*04f1ab15SAndrew Baumann     s->dma_mr = MEMORY_REGION(obj);
252*04f1ab15SAndrew Baumann     address_space_init(&s->dma_as, s->dma_mr, NULL);
253*04f1ab15SAndrew Baumann 
254*04f1ab15SAndrew Baumann     /* TODO: connect to MAC address of USB NIC device, once we emulate it */
255*04f1ab15SAndrew Baumann     qemu_macaddr_default_if_unset(&s->macaddr);
256*04f1ab15SAndrew Baumann 
257*04f1ab15SAndrew Baumann     bcm2835_property_reset(dev);
258*04f1ab15SAndrew Baumann }
259*04f1ab15SAndrew Baumann 
260*04f1ab15SAndrew Baumann static Property bcm2835_property_props[] = {
261*04f1ab15SAndrew Baumann     DEFINE_PROP_UINT32("ram-size", BCM2835PropertyState, ram_size, 0),
262*04f1ab15SAndrew Baumann     DEFINE_PROP_END_OF_LIST()
263*04f1ab15SAndrew Baumann };
264*04f1ab15SAndrew Baumann 
265*04f1ab15SAndrew Baumann static void bcm2835_property_class_init(ObjectClass *klass, void *data)
266*04f1ab15SAndrew Baumann {
267*04f1ab15SAndrew Baumann     DeviceClass *dc = DEVICE_CLASS(klass);
268*04f1ab15SAndrew Baumann 
269*04f1ab15SAndrew Baumann     dc->props = bcm2835_property_props;
270*04f1ab15SAndrew Baumann     dc->realize = bcm2835_property_realize;
271*04f1ab15SAndrew Baumann     dc->vmsd = &vmstate_bcm2835_property;
272*04f1ab15SAndrew Baumann }
273*04f1ab15SAndrew Baumann 
274*04f1ab15SAndrew Baumann static TypeInfo bcm2835_property_info = {
275*04f1ab15SAndrew Baumann     .name          = TYPE_BCM2835_PROPERTY,
276*04f1ab15SAndrew Baumann     .parent        = TYPE_SYS_BUS_DEVICE,
277*04f1ab15SAndrew Baumann     .instance_size = sizeof(BCM2835PropertyState),
278*04f1ab15SAndrew Baumann     .class_init    = bcm2835_property_class_init,
279*04f1ab15SAndrew Baumann     .instance_init = bcm2835_property_init,
280*04f1ab15SAndrew Baumann };
281*04f1ab15SAndrew Baumann 
282*04f1ab15SAndrew Baumann static void bcm2835_property_register_types(void)
283*04f1ab15SAndrew Baumann {
284*04f1ab15SAndrew Baumann     type_register_static(&bcm2835_property_info);
285*04f1ab15SAndrew Baumann }
286*04f1ab15SAndrew Baumann 
287*04f1ab15SAndrew Baumann type_init(bcm2835_property_register_types)
288