xref: /qemu/hw/arm/bcm2838_peripherals.c (revision bd41b27508d896a5c48c6c92a55a60704cb6ec8b)
1 /*
2  * BCM2838 peripherals emulation
3  *
4  * Copyright (C) 2022 Ovchinnikov Vitalii <vitalii.ovchinnikov@auriga.com>
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 
9 #include "qemu/osdep.h"
10 #include "qapi/error.h"
11 #include "qemu/module.h"
12 #include "hw/arm/raspi_platform.h"
13 #include "hw/arm/bcm2838_peripherals.h"
14 
15 /* Lower peripheral base address on the VC (GPU) system bus */
16 #define BCM2838_VC_PERI_LOW_BASE 0x7c000000
17 
18 /* Capabilities for SD controller: no DMA, high-speed, default clocks etc. */
19 #define BCM2835_SDHC_CAPAREG 0x52134b4
20 
21 static void bcm2838_peripherals_init(Object *obj)
22 {
23     BCM2838PeripheralState *s = BCM2838_PERIPHERALS(obj);
24     BCM2838PeripheralClass *bc = BCM2838_PERIPHERALS_GET_CLASS(obj);
25     BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(obj);
26 
27     /* Lower memory region for peripheral devices (exported to the Soc) */
28     memory_region_init(&s->peri_low_mr, obj, "bcm2838-peripherals",
29                        bc->peri_low_size);
30     sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->peri_low_mr);
31 
32     /* Extended Mass Media Controller 2 */
33     object_initialize_child(obj, "emmc2", &s->emmc2, TYPE_SYSBUS_SDHCI);
34 
35     /* GPIO */
36     object_initialize_child(obj, "gpio", &s->gpio, TYPE_BCM2838_GPIO);
37 
38     object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhci",
39                                    OBJECT(&s_base->sdhci.sdbus));
40     object_property_add_const_link(OBJECT(&s->gpio), "sdbus-sdhost",
41                                    OBJECT(&s_base->sdhost.sdbus));
42 
43     object_initialize_child(obj, "mmc_irq_orgate", &s->mmc_irq_orgate,
44                             TYPE_OR_IRQ);
45     object_property_set_int(OBJECT(&s->mmc_irq_orgate), "num-lines", 2,
46                             &error_abort);
47 
48     object_initialize_child(obj, "dma_7_8_irq_orgate", &s->dma_7_8_irq_orgate,
49                             TYPE_OR_IRQ);
50     object_property_set_int(OBJECT(&s->dma_7_8_irq_orgate), "num-lines", 2,
51                             &error_abort);
52 
53     object_initialize_child(obj, "dma_9_10_irq_orgate", &s->dma_9_10_irq_orgate,
54                             TYPE_OR_IRQ);
55     object_property_set_int(OBJECT(&s->dma_9_10_irq_orgate), "num-lines", 2,
56                             &error_abort);
57 }
58 
59 static void bcm2838_peripherals_realize(DeviceState *dev, Error **errp)
60 {
61     DeviceState *mmc_irq_orgate;
62     DeviceState *dma_7_8_irq_orgate;
63     DeviceState *dma_9_10_irq_orgate;
64     MemoryRegion *mphi_mr;
65     BCM2838PeripheralState *s = BCM2838_PERIPHERALS(dev);
66     BCMSocPeripheralBaseState *s_base = BCM_SOC_PERIPHERALS_BASE(dev);
67     int n;
68 
69     bcm_soc_peripherals_common_realize(dev, errp);
70 
71     /* Map lower peripherals into the GPU address space */
72     memory_region_init_alias(&s->peri_low_mr_alias, OBJECT(s),
73                              "bcm2838-peripherals", &s->peri_low_mr, 0,
74                              memory_region_size(&s->peri_low_mr));
75     memory_region_add_subregion_overlap(&s_base->gpu_bus_mr,
76                                         BCM2838_VC_PERI_LOW_BASE,
77                                         &s->peri_low_mr_alias, 1);
78 
79     /* Extended Mass Media Controller 2 */
80     object_property_set_uint(OBJECT(&s->emmc2), "sd-spec-version", 3,
81                              &error_abort);
82     object_property_set_uint(OBJECT(&s->emmc2), "capareg",
83                              BCM2835_SDHC_CAPAREG, &error_abort);
84     object_property_set_bool(OBJECT(&s->emmc2), "pending-insert-quirk", true,
85                              &error_abort);
86     if (!sysbus_realize(SYS_BUS_DEVICE(&s->emmc2), errp)) {
87         return;
88     }
89 
90     memory_region_add_subregion(&s_base->peri_mr, EMMC2_OFFSET,
91                                 sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->emmc2),
92                                 0));
93 
94     /* According to DTS, EMMC and EMMC2 share one irq */
95     if (!qdev_realize(DEVICE(&s->mmc_irq_orgate), NULL, errp)) {
96         return;
97     }
98 
99     mmc_irq_orgate = DEVICE(&s->mmc_irq_orgate);
100     sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc2), 0,
101                        qdev_get_gpio_in(mmc_irq_orgate, 0));
102 
103     sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->sdhci), 0,
104                        qdev_get_gpio_in(mmc_irq_orgate, 1));
105 
106    /* Connect EMMC and EMMC2 to the interrupt controller */
107     qdev_connect_gpio_out(mmc_irq_orgate, 0,
108                           qdev_get_gpio_in_named(DEVICE(&s_base->ic),
109                                                  BCM2835_IC_GPU_IRQ,
110                                                  INTERRUPT_ARASANSDIO));
111 
112     /* Connect DMA 0-6 to the interrupt controller */
113     for (n = 0; n < 7; n++) {
114         sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n,
115                            qdev_get_gpio_in_named(DEVICE(&s_base->ic),
116                                                   BCM2835_IC_GPU_IRQ,
117                                                   GPU_INTERRUPT_DMA0 + n));
118     }
119 
120    /* According to DTS, DMA 7 and 8 share one irq */
121     if (!qdev_realize(DEVICE(&s->dma_7_8_irq_orgate), NULL, errp)) {
122         return;
123     }
124     dma_7_8_irq_orgate = DEVICE(&s->dma_7_8_irq_orgate);
125 
126     /* Connect DMA 7-8 to the interrupt controller */
127     sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 7,
128                        qdev_get_gpio_in(dma_7_8_irq_orgate, 0));
129     sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 8,
130                        qdev_get_gpio_in(dma_7_8_irq_orgate, 1));
131 
132     qdev_connect_gpio_out(dma_7_8_irq_orgate, 0,
133                           qdev_get_gpio_in_named(DEVICE(&s_base->ic),
134                                                  BCM2835_IC_GPU_IRQ,
135                                                  GPU_INTERRUPT_DMA7_8));
136 
137      /* According to DTS, DMA 9 and 10 share one irq */
138     if (!qdev_realize(DEVICE(&s->dma_9_10_irq_orgate), NULL, errp)) {
139         return;
140     }
141     dma_9_10_irq_orgate = DEVICE(&s->dma_9_10_irq_orgate);
142 
143    /* Connect DMA 9-10 to the interrupt controller */
144     sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 9,
145                        qdev_get_gpio_in(dma_9_10_irq_orgate, 0));
146     sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 10,
147                        qdev_get_gpio_in(dma_9_10_irq_orgate, 1));
148 
149     qdev_connect_gpio_out(dma_9_10_irq_orgate, 0,
150                           qdev_get_gpio_in_named(DEVICE(&s_base->ic),
151                                                  BCM2835_IC_GPU_IRQ,
152                                                  GPU_INTERRUPT_DMA9_10));
153 
154     /* Connect DMA 11-14 to the interrupt controller */
155     for (n = 11; n < 15; n++) {
156         sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), n,
157                            qdev_get_gpio_in_named(DEVICE(&s_base->ic),
158                                                   BCM2835_IC_GPU_IRQ,
159                                                   GPU_INTERRUPT_DMA11 + n
160                                                   - 11));
161     }
162 
163     /*
164      * Connect DMA 15 to the interrupt controller, it is physically removed
165      * from other DMA channels and exclusively used by the GPU
166      */
167     sysbus_connect_irq(SYS_BUS_DEVICE(&s_base->dma), 15,
168                         qdev_get_gpio_in_named(DEVICE(&s_base->ic),
169                                                BCM2835_IC_GPU_IRQ,
170                                                GPU_INTERRUPT_DMA15));
171 
172     /* Map MPHI to BCM2838 memory map */
173     mphi_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s_base->mphi), 0);
174     memory_region_init_alias(&s->mphi_mr_alias, OBJECT(s), "mphi", mphi_mr, 0,
175                              BCM2838_MPHI_SIZE);
176     memory_region_add_subregion(&s_base->peri_mr, BCM2838_MPHI_OFFSET,
177                                 &s->mphi_mr_alias);
178 
179     /* GPIO */
180     if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) {
181         return;
182     }
183     memory_region_add_subregion(
184         &s_base->peri_mr, GPIO_OFFSET,
185         sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0));
186 
187     object_property_add_alias(OBJECT(s), "sd-bus", OBJECT(&s->gpio), "sd-bus");
188 
189     /* BCM2838 RPiVid ASB must be mapped to prevent kernel crash */
190     create_unimp(s_base, &s->asb, "bcm2838-asb", BRDG_OFFSET, 0x24);
191 }
192 
193 static void bcm2838_peripherals_class_init(ObjectClass *oc, void *data)
194 {
195     DeviceClass *dc = DEVICE_CLASS(oc);
196     BCM2838PeripheralClass *bc = BCM2838_PERIPHERALS_CLASS(oc);
197     BCMSocPeripheralBaseClass *bc_base = BCM_SOC_PERIPHERALS_BASE_CLASS(oc);
198 
199     bc->peri_low_size = 0x2000000;
200     bc_base->peri_size = 0x1800000;
201     dc->realize = bcm2838_peripherals_realize;
202 }
203 
204 static const TypeInfo bcm2838_peripherals_type_info = {
205     .name = TYPE_BCM2838_PERIPHERALS,
206     .parent = TYPE_BCM_SOC_PERIPHERALS_BASE,
207     .instance_size = sizeof(BCM2838PeripheralState),
208     .instance_init = bcm2838_peripherals_init,
209     .class_size = sizeof(BCM2838PeripheralClass),
210     .class_init = bcm2838_peripherals_class_init,
211 };
212 
213 static void bcm2838_peripherals_register_types(void)
214 {
215     type_register_static(&bcm2838_peripherals_type_info);
216 }
217 
218 type_init(bcm2838_peripherals_register_types)
219