xref: /openbmc/qemu/hw/arm/bcm2835_peripherals.c (revision 99d423e5)
1 /*
2  * Raspberry Pi emulation (c) 2012 Gregory Estrade
3  * Upstreaming code cleanup [including bcm2835_*] (c) 2013 Jan Petrous
4  *
5  * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft
6  * Written by Andrew Baumann
7  *
8  * This code is licensed under the GNU GPLv2 and later.
9  */
10 
11 #include "qemu/osdep.h"
12 #include "qapi/error.h"
13 #include "hw/arm/bcm2835_peripherals.h"
14 #include "hw/misc/bcm2835_mbox_defs.h"
15 #include "hw/arm/raspi_platform.h"
16 #include "sysemu/sysemu.h"
17 
18 /* Peripheral base address on the VC (GPU) system bus */
19 #define BCM2835_VC_PERI_BASE 0x7e000000
20 
21 /* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */
22 #define BCM2835_SDHC_CAPAREG 0x52134b4
23 
24 static void bcm2835_peripherals_init(Object *obj)
25 {
26     BCM2835PeripheralState *s = BCM2835_PERIPHERALS(obj);
27 
28     /* Memory region for peripheral devices, which we export to our parent */
29     memory_region_init(&s->peri_mr, obj,"bcm2835-peripherals", 0x1000000);
30     object_property_add_child(obj, "peripheral-io", OBJECT(&s->peri_mr), NULL);
31     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_mr);
32 
33     /* Internal memory region for peripheral bus addresses (not exported) */
34     memory_region_init(&s->gpu_bus_mr, obj, "bcm2835-gpu", (uint64_t)1 << 32);
35     object_property_add_child(obj, "gpu-bus", OBJECT(&s->gpu_bus_mr), NULL);
36 
37     /* Internal memory region for request/response communication with
38      * mailbox-addressable peripherals (not exported)
39      */
40     memory_region_init(&s->mbox_mr, obj, "bcm2835-mbox",
41                        MBOX_CHAN_COUNT << MBOX_AS_CHAN_SHIFT);
42 
43     /* Interrupt Controller */
44     sysbus_init_child_obj(obj, "ic", &s->ic, sizeof(s->ic), TYPE_BCM2835_IC);
45 
46     /* UART0 */
47     sysbus_init_child_obj(obj, "uart0", &s->uart0, sizeof(s->uart0),
48                           TYPE_PL011);
49 
50     /* AUX / UART1 */
51     sysbus_init_child_obj(obj, "aux", &s->aux, sizeof(s->aux),
52                           TYPE_BCM2835_AUX);
53 
54     /* Mailboxes */
55     sysbus_init_child_obj(obj, "mbox", &s->mboxes, sizeof(s->mboxes),
56                           TYPE_BCM2835_MBOX);
57 
58     object_property_add_const_link(OBJECT(&s->mboxes), "mbox-mr",
59                                    OBJECT(&s->mbox_mr), &error_abort);
60 
61     /* Framebuffer */
62     sysbus_init_child_obj(obj, "fb", &s->fb, sizeof(s->fb), TYPE_BCM2835_FB);
63     object_property_add_alias(obj, "vcram-size", OBJECT(&s->fb), "vcram-size",
64                               &error_abort);
65 
66     object_property_add_const_link(OBJECT(&s->fb), "dma-mr",
67                                    OBJECT(&s->gpu_bus_mr), &error_abort);
68 
69     /* Property channel */
70     sysbus_init_child_obj(obj, "property", &s->property, sizeof(s->property),
71                           TYPE_BCM2835_PROPERTY);
72     object_property_add_alias(obj, "board-rev", OBJECT(&s->property),
73                               "board-rev", &error_abort);
74 
75     object_property_add_const_link(OBJECT(&s->property), "fb",
76                                    OBJECT(&s->fb), &error_abort);
77     object_property_add_const_link(OBJECT(&s->property), "dma-mr",
78                                    OBJECT(&s->gpu_bus_mr), &error_abort);
79 
80     /* Random Number Generator */
81     sysbus_init_child_obj(obj, "rng", &s->rng, sizeof(s->rng),
82                           TYPE_BCM2835_RNG);
83 
84     /* Extended Mass Media Controller */
85     sysbus_init_child_obj(obj, "sdhci", &s->sdhci, sizeof(s->sdhci),
86                           TYPE_SYSBUS_SDHCI);
87 
88     /* SDHOST */
89     sysbus_init_child_obj(obj, "sdhost", &s->sdhost, sizeof(s->sdhost),
90                           TYPE_BCM2835_SDHOST);
91 
92     /* DMA Channels */
93     sysbus_init_child_obj(obj, "dma", &s->dma, sizeof(s->dma),
94                           TYPE_BCM2835_DMA);
95 
96     object_property_add_const_link(OBJECT(&s->dma), "dma-mr",
97                                    OBJECT(&s->gpu_bus_mr), &error_abort);
98 
99     /* GPIO */
100     sysbus_init_child_obj(obj, "gpio", &s->gpio, sizeof(s->gpio),
101                           TYPE_BCM2835_GPIO);
102 
103     object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci",
104                                    OBJECT(&s->sdhci.sdbus), &error_abort);
105     object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost",
106                                    OBJECT(&s->sdhost.sdbus), &error_abort);
107 }
108 
109 static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp)
110 {
111     BCM2835PeripheralState *s = BCM2835_PERIPHERALS(dev);
112     Object *obj;
113     MemoryRegion *ram;
114     Error *err = NULL;
115     uint64_t ram_size, vcram_size;
116     int n;
117 
118     obj = object_property_get_link(OBJECT(dev), "ram", &err);
119     if (obj == NULL) {
120         error_setg(errp, "%s: required ram link not found: %s",
121                    __func__, error_get_pretty(err));
122         return;
123     }
124 
125     ram = MEMORY_REGION(obj);
126     ram_size = memory_region_size(ram);
127 
128     /* Map peripherals and RAM into the GPU address space. */
129     memory_region_init_alias(&s->peri_mr_alias, OBJECT(s),
130                              "bcm2835-peripherals", &s->peri_mr, 0,
131                              memory_region_size(&s->peri_mr));
132 
133     memory_region_add_subregion_overlap(&s->gpu_bus_mr, BCM2835_VC_PERI_BASE,
134                                         &s->peri_mr_alias, 1);
135 
136     /* RAM is aliased four times (different cache configurations) on the GPU */
137     for (n = 0; n < 4; n++) {
138         memory_region_init_alias(&s->ram_alias[n], OBJECT(s),
139                                  "bcm2835-gpu-ram-alias[*]", ram, 0, ram_size);
140         memory_region_add_subregion_overlap(&s->gpu_bus_mr, (hwaddr)n << 30,
141                                             &s->ram_alias[n], 0);
142     }
143 
144     /* Interrupt Controller */
145     object_property_set_bool(OBJECT(&s->ic), true, "realized", &err);
146     if (err) {
147         error_propagate(errp, err);
148         return;
149     }
150 
151     memory_region_add_subregion(&s->peri_mr, ARMCTRL_IC_OFFSET,
152                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->ic), 0));
153     sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic));
154 
155     /* UART0 */
156     qdev_prop_set_chr(DEVICE(&s->uart0), "chardev", serial_hd(0));
157     object_property_set_bool(OBJECT(&s->uart0), true, "realized", &err);
158     if (err) {
159         error_propagate(errp, err);
160         return;
161     }
162 
163     memory_region_add_subregion(&s->peri_mr, UART0_OFFSET,
164                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart0), 0));
165     sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart0), 0,
166         qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
167                                INTERRUPT_UART));
168     /* AUX / UART1 */
169     qdev_prop_set_chr(DEVICE(&s->aux), "chardev", serial_hd(1));
170 
171     object_property_set_bool(OBJECT(&s->aux), true, "realized", &err);
172     if (err) {
173         error_propagate(errp, err);
174         return;
175     }
176 
177     memory_region_add_subregion(&s->peri_mr, UART1_OFFSET,
178                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->aux), 0));
179     sysbus_connect_irq(SYS_BUS_DEVICE(&s->aux), 0,
180         qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
181                                INTERRUPT_AUX));
182 
183     /* Mailboxes */
184     object_property_set_bool(OBJECT(&s->mboxes), true, "realized", &err);
185     if (err) {
186         error_propagate(errp, err);
187         return;
188     }
189 
190     memory_region_add_subregion(&s->peri_mr, ARMCTRL_0_SBM_OFFSET,
191                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mboxes), 0));
192     sysbus_connect_irq(SYS_BUS_DEVICE(&s->mboxes), 0,
193         qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_ARM_IRQ,
194                                INTERRUPT_ARM_MAILBOX));
195 
196     /* Framebuffer */
197     vcram_size = object_property_get_uint(OBJECT(s), "vcram-size", &err);
198     if (err) {
199         error_propagate(errp, err);
200         return;
201     }
202 
203     object_property_set_uint(OBJECT(&s->fb), ram_size - vcram_size,
204                              "vcram-base", &err);
205     if (err) {
206         error_propagate(errp, err);
207         return;
208     }
209 
210     object_property_set_bool(OBJECT(&s->fb), true, "realized", &err);
211     if (err) {
212         error_propagate(errp, err);
213         return;
214     }
215 
216     memory_region_add_subregion(&s->mbox_mr, MBOX_CHAN_FB << MBOX_AS_CHAN_SHIFT,
217                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->fb), 0));
218     sysbus_connect_irq(SYS_BUS_DEVICE(&s->fb), 0,
219                        qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_FB));
220 
221     /* Property channel */
222     object_property_set_bool(OBJECT(&s->property), true, "realized", &err);
223     if (err) {
224         error_propagate(errp, err);
225         return;
226     }
227 
228     memory_region_add_subregion(&s->mbox_mr,
229                 MBOX_CHAN_PROPERTY << MBOX_AS_CHAN_SHIFT,
230                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->property), 0));
231     sysbus_connect_irq(SYS_BUS_DEVICE(&s->property), 0,
232                       qdev_get_gpio_in(DEVICE(&s->mboxes), MBOX_CHAN_PROPERTY));
233 
234     /* Random Number Generator */
235     object_property_set_bool(OBJECT(&s->rng), true, "realized", &err);
236     if (err) {
237         error_propagate(errp, err);
238         return;
239     }
240 
241     memory_region_add_subregion(&s->peri_mr, RNG_OFFSET,
242                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0));
243 
244     /* Extended Mass Media Controller
245      *
246      * Compatible with:
247      * - SD Host Controller Specification Version 3.0 Draft 1.0
248      * - SDIO Specification Version 3.0
249      * - MMC Specification Version 4.4
250      *
251      * For the exact details please refer to the Arasan documentation:
252      *   SD3.0_Host_AHB_eMMC4.4_Usersguide_ver5.9_jan11_10.pdf
253      */
254     object_property_set_uint(OBJECT(&s->sdhci), 3, "sd-spec-version", &err);
255     object_property_set_uint(OBJECT(&s->sdhci), BCM2835_SDHC_CAPAREG, "capareg",
256                              &err);
257     object_property_set_bool(OBJECT(&s->sdhci), true, "pending-insert-quirk",
258                              &err);
259     if (err) {
260         error_propagate(errp, err);
261         return;
262     }
263 
264     object_property_set_bool(OBJECT(&s->sdhci), true, "realized", &err);
265     if (err) {
266         error_propagate(errp, err);
267         return;
268     }
269 
270     memory_region_add_subregion(&s->peri_mr, EMMC_OFFSET,
271                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhci), 0));
272     sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
273         qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
274                                INTERRUPT_ARASANSDIO));
275 
276     /* SDHOST */
277     object_property_set_bool(OBJECT(&s->sdhost), true, "realized", &err);
278     if (err) {
279         error_propagate(errp, err);
280         return;
281     }
282 
283     memory_region_add_subregion(&s->peri_mr, MMCI0_OFFSET,
284                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->sdhost), 0));
285     sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhost), 0,
286         qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ,
287                                INTERRUPT_SDIO));
288 
289     /* DMA Channels */
290     object_property_set_bool(OBJECT(&s->dma), true, "realized", &err);
291     if (err) {
292         error_propagate(errp, err);
293         return;
294     }
295 
296     memory_region_add_subregion(&s->peri_mr, DMA_OFFSET,
297                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 0));
298     memory_region_add_subregion(&s->peri_mr, DMA15_OFFSET,
299                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dma), 1));
300 
301     for (n = 0; n <= 12; n++) {
302         sysbus_connect_irq(SYS_BUS_DEVICE(&s->dma), n,
303                            qdev_get_gpio_in_named(DEVICE(&s->ic),
304                                                   BCM2835_IC_GPU_IRQ,
305                                                   INTERRUPT_DMA0 + n));
306     }
307 
308     /* GPIO */
309     object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err);
310     if (err) {
311         error_propagate(errp, err);
312         return;
313     }
314 
315     memory_region_add_subregion(&s->peri_mr, GPIO_OFFSET,
316                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0));
317 
318     object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus",
319                               &err);
320     if (err) {
321         error_propagate(errp, err);
322         return;
323     }
324 }
325 
326 static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data)
327 {
328     DeviceClass *dc = DEVICE_CLASS(oc);
329 
330     dc->realize = bcm2835_peripherals_realize;
331 }
332 
333 static const TypeInfo bcm2835_peripherals_type_info = {
334     .name = TYPE_BCM2835_PERIPHERALS,
335     .parent = TYPE_SYS_BUS_DEVICE,
336     .instance_size = sizeof(BCM2835PeripheralState),
337     .instance_init = bcm2835_peripherals_init,
338     .class_init = bcm2835_peripherals_class_init,
339 };
340 
341 static void bcm2835_peripherals_register_types(void)
342 {
343     type_register_static(&bcm2835_peripherals_type_info);
344 }
345 
346 type_init(bcm2835_peripherals_register_types)
347