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