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