xref: /openbmc/qemu/hw/gpio/bcm2835_gpio.c (revision 59a3a1c0)
1 /*
2  * Raspberry Pi (BCM2835) GPIO Controller
3  *
4  * Copyright (c) 2017 Antfield SAS
5  *
6  * Authors:
7  *  Clement Deschamps <clement.deschamps@antfield.fr>
8  *  Luc Michel <luc.michel@antfield.fr>
9  *
10  * This work is licensed under the terms of the GNU GPL, version 2 or later.
11  * See the COPYING file in the top-level directory.
12  */
13 
14 #include "qemu/osdep.h"
15 #include "qemu/log.h"
16 #include "qemu/module.h"
17 #include "qemu/timer.h"
18 #include "qapi/error.h"
19 #include "hw/sysbus.h"
20 #include "migration/vmstate.h"
21 #include "hw/sd/sd.h"
22 #include "hw/gpio/bcm2835_gpio.h"
23 #include "hw/irq.h"
24 
25 #define GPFSEL0   0x00
26 #define GPFSEL1   0x04
27 #define GPFSEL2   0x08
28 #define GPFSEL3   0x0C
29 #define GPFSEL4   0x10
30 #define GPFSEL5   0x14
31 #define GPSET0    0x1C
32 #define GPSET1    0x20
33 #define GPCLR0    0x28
34 #define GPCLR1    0x2C
35 #define GPLEV0    0x34
36 #define GPLEV1    0x38
37 #define GPEDS0    0x40
38 #define GPEDS1    0x44
39 #define GPREN0    0x4C
40 #define GPREN1    0x50
41 #define GPFEN0    0x58
42 #define GPFEN1    0x5C
43 #define GPHEN0    0x64
44 #define GPHEN1    0x68
45 #define GPLEN0    0x70
46 #define GPLEN1    0x74
47 #define GPAREN0   0x7C
48 #define GPAREN1   0x80
49 #define GPAFEN0   0x88
50 #define GPAFEN1   0x8C
51 #define GPPUD     0x94
52 #define GPPUDCLK0 0x98
53 #define GPPUDCLK1 0x9C
54 
55 static uint32_t gpfsel_get(BCM2835GpioState *s, uint8_t reg)
56 {
57     int i;
58     uint32_t value = 0;
59     for (i = 0; i < 10; i++) {
60         uint32_t index = 10 * reg + i;
61         if (index < sizeof(s->fsel)) {
62             value |= (s->fsel[index] & 0x7) << (3 * i);
63         }
64     }
65     return value;
66 }
67 
68 static void gpfsel_set(BCM2835GpioState *s, uint8_t reg, uint32_t value)
69 {
70     int i;
71     for (i = 0; i < 10; i++) {
72         uint32_t index = 10 * reg + i;
73         if (index < sizeof(s->fsel)) {
74             int fsel = (value >> (3 * i)) & 0x7;
75             s->fsel[index] = fsel;
76         }
77     }
78 
79     /* SD controller selection (48-53) */
80     if (s->sd_fsel != 0
81             && (s->fsel[48] == 0) /* SD_CLK_R */
82             && (s->fsel[49] == 0) /* SD_CMD_R */
83             && (s->fsel[50] == 0) /* SD_DATA0_R */
84             && (s->fsel[51] == 0) /* SD_DATA1_R */
85             && (s->fsel[52] == 0) /* SD_DATA2_R */
86             && (s->fsel[53] == 0) /* SD_DATA3_R */
87             ) {
88         /* SDHCI controller selected */
89         sdbus_reparent_card(s->sdbus_sdhost, s->sdbus_sdhci);
90         s->sd_fsel = 0;
91     } else if (s->sd_fsel != 4
92             && (s->fsel[48] == 4) /* SD_CLK_R */
93             && (s->fsel[49] == 4) /* SD_CMD_R */
94             && (s->fsel[50] == 4) /* SD_DATA0_R */
95             && (s->fsel[51] == 4) /* SD_DATA1_R */
96             && (s->fsel[52] == 4) /* SD_DATA2_R */
97             && (s->fsel[53] == 4) /* SD_DATA3_R */
98             ) {
99         /* SDHost controller selected */
100         sdbus_reparent_card(s->sdbus_sdhci, s->sdbus_sdhost);
101         s->sd_fsel = 4;
102     }
103 }
104 
105 static int gpfsel_is_out(BCM2835GpioState *s, int index)
106 {
107     if (index >= 0 && index < 54) {
108         return s->fsel[index] == 1;
109     }
110     return 0;
111 }
112 
113 static void gpset(BCM2835GpioState *s,
114         uint32_t val, uint8_t start, uint8_t count, uint32_t *lev)
115 {
116     uint32_t changes = val & ~*lev;
117     uint32_t cur = 1;
118 
119     int i;
120     for (i = 0; i < count; i++) {
121         if ((changes & cur) && (gpfsel_is_out(s, start + i))) {
122             qemu_set_irq(s->out[start + i], 1);
123         }
124         cur <<= 1;
125     }
126 
127     *lev |= val;
128 }
129 
130 static void gpclr(BCM2835GpioState *s,
131         uint32_t val, uint8_t start, uint8_t count, uint32_t *lev)
132 {
133     uint32_t changes = val & *lev;
134     uint32_t cur = 1;
135 
136     int i;
137     for (i = 0; i < count; i++) {
138         if ((changes & cur) && (gpfsel_is_out(s, start + i))) {
139             qemu_set_irq(s->out[start + i], 0);
140         }
141         cur <<= 1;
142     }
143 
144     *lev &= ~val;
145 }
146 
147 static uint64_t bcm2835_gpio_read(void *opaque, hwaddr offset,
148         unsigned size)
149 {
150     BCM2835GpioState *s = (BCM2835GpioState *)opaque;
151 
152     switch (offset) {
153     case GPFSEL0:
154     case GPFSEL1:
155     case GPFSEL2:
156     case GPFSEL3:
157     case GPFSEL4:
158     case GPFSEL5:
159         return gpfsel_get(s, offset / 4);
160     case GPSET0:
161     case GPSET1:
162         /* Write Only */
163         return 0;
164     case GPCLR0:
165     case GPCLR1:
166         /* Write Only */
167         return 0;
168     case GPLEV0:
169         return s->lev0;
170     case GPLEV1:
171         return s->lev1;
172     case GPEDS0:
173     case GPEDS1:
174     case GPREN0:
175     case GPREN1:
176     case GPFEN0:
177     case GPFEN1:
178     case GPHEN0:
179     case GPHEN1:
180     case GPLEN0:
181     case GPLEN1:
182     case GPAREN0:
183     case GPAREN1:
184     case GPAFEN0:
185     case GPAFEN1:
186     case GPPUD:
187     case GPPUDCLK0:
188     case GPPUDCLK1:
189         /* Not implemented */
190         return 0;
191     default:
192         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
193                 __func__, offset);
194         break;
195     }
196 
197     return 0;
198 }
199 
200 static void bcm2835_gpio_write(void *opaque, hwaddr offset,
201         uint64_t value, unsigned size)
202 {
203     BCM2835GpioState *s = (BCM2835GpioState *)opaque;
204 
205     switch (offset) {
206     case GPFSEL0:
207     case GPFSEL1:
208     case GPFSEL2:
209     case GPFSEL3:
210     case GPFSEL4:
211     case GPFSEL5:
212         gpfsel_set(s, offset / 4, value);
213         break;
214     case GPSET0:
215         gpset(s, value, 0, 32, &s->lev0);
216         break;
217     case GPSET1:
218         gpset(s, value, 32, 22, &s->lev1);
219         break;
220     case GPCLR0:
221         gpclr(s, value, 0, 32, &s->lev0);
222         break;
223     case GPCLR1:
224         gpclr(s, value, 32, 22, &s->lev1);
225         break;
226     case GPLEV0:
227     case GPLEV1:
228         /* Read Only */
229         break;
230     case GPEDS0:
231     case GPEDS1:
232     case GPREN0:
233     case GPREN1:
234     case GPFEN0:
235     case GPFEN1:
236     case GPHEN0:
237     case GPHEN1:
238     case GPLEN0:
239     case GPLEN1:
240     case GPAREN0:
241     case GPAREN1:
242     case GPAFEN0:
243     case GPAFEN1:
244     case GPPUD:
245     case GPPUDCLK0:
246     case GPPUDCLK1:
247         /* Not implemented */
248         break;
249     default:
250         goto err_out;
251     }
252     return;
253 
254 err_out:
255     qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset %"HWADDR_PRIx"\n",
256             __func__, offset);
257 }
258 
259 static void bcm2835_gpio_reset(DeviceState *dev)
260 {
261     BCM2835GpioState *s = BCM2835_GPIO(dev);
262 
263     int i;
264     for (i = 0; i < 6; i++) {
265         gpfsel_set(s, i, 0);
266     }
267 
268     s->sd_fsel = 0;
269 
270     /* SDHCI is selected by default */
271     sdbus_reparent_card(&s->sdbus, s->sdbus_sdhci);
272 
273     s->lev0 = 0;
274     s->lev1 = 0;
275 }
276 
277 static const MemoryRegionOps bcm2835_gpio_ops = {
278     .read = bcm2835_gpio_read,
279     .write = bcm2835_gpio_write,
280     .endianness = DEVICE_NATIVE_ENDIAN,
281 };
282 
283 static const VMStateDescription vmstate_bcm2835_gpio = {
284     .name = "bcm2835_gpio",
285     .version_id = 1,
286     .minimum_version_id = 1,
287     .fields = (VMStateField[]) {
288         VMSTATE_UINT8_ARRAY(fsel, BCM2835GpioState, 54),
289         VMSTATE_UINT32(lev0, BCM2835GpioState),
290         VMSTATE_UINT32(lev1, BCM2835GpioState),
291         VMSTATE_UINT8(sd_fsel, BCM2835GpioState),
292         VMSTATE_END_OF_LIST()
293     }
294 };
295 
296 static void bcm2835_gpio_init(Object *obj)
297 {
298     BCM2835GpioState *s = BCM2835_GPIO(obj);
299     DeviceState *dev = DEVICE(obj);
300     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
301 
302     qbus_create_inplace(&s->sdbus, sizeof(s->sdbus),
303                         TYPE_SD_BUS, DEVICE(s), "sd-bus");
304 
305     memory_region_init_io(&s->iomem, obj,
306             &bcm2835_gpio_ops, s, "bcm2835_gpio", 0x1000);
307     sysbus_init_mmio(sbd, &s->iomem);
308     qdev_init_gpio_out(dev, s->out, 54);
309 }
310 
311 static void bcm2835_gpio_realize(DeviceState *dev, Error **errp)
312 {
313     BCM2835GpioState *s = BCM2835_GPIO(dev);
314     Object *obj;
315     Error *err = NULL;
316 
317     obj = object_property_get_link(OBJECT(dev), "sdbus-sdhci", &err);
318     if (obj == NULL) {
319         error_setg(errp, "%s: required sdhci link not found: %s",
320                 __func__, error_get_pretty(err));
321         return;
322     }
323     s->sdbus_sdhci = SD_BUS(obj);
324 
325     obj = object_property_get_link(OBJECT(dev), "sdbus-sdhost", &err);
326     if (obj == NULL) {
327         error_setg(errp, "%s: required sdhost link not found: %s",
328                 __func__, error_get_pretty(err));
329         return;
330     }
331     s->sdbus_sdhost = SD_BUS(obj);
332 }
333 
334 static void bcm2835_gpio_class_init(ObjectClass *klass, void *data)
335 {
336     DeviceClass *dc = DEVICE_CLASS(klass);
337 
338     dc->vmsd = &vmstate_bcm2835_gpio;
339     dc->realize = &bcm2835_gpio_realize;
340     dc->reset = &bcm2835_gpio_reset;
341 }
342 
343 static const TypeInfo bcm2835_gpio_info = {
344     .name          = TYPE_BCM2835_GPIO,
345     .parent        = TYPE_SYS_BUS_DEVICE,
346     .instance_size = sizeof(BCM2835GpioState),
347     .instance_init = bcm2835_gpio_init,
348     .class_init    = bcm2835_gpio_class_init,
349 };
350 
351 static void bcm2835_gpio_register_types(void)
352 {
353     type_register_static(&bcm2835_gpio_info);
354 }
355 
356 type_init(bcm2835_gpio_register_types)
357