xref: /openbmc/qemu/hw/gpio/bcm2838_gpio.c (revision b54a9a56)
1 /*
2  * Raspberry Pi (BCM2838) GPIO Controller
3  * This implementation is based on bcm2835_gpio (hw/gpio/bcm2835_gpio.c)
4  *
5  * Copyright (c) 2022 Auriga LLC
6  *
7  * Authors:
8  *  Lotosh, Aleksey <aleksey.lotosh@auriga.com>
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include "qemu/osdep.h"
14 #include "qemu/log.h"
15 #include "qemu/module.h"
16 #include "qemu/timer.h"
17 #include "qapi/error.h"
18 #include "hw/sysbus.h"
19 #include "migration/vmstate.h"
20 #include "hw/sd/sd.h"
21 #include "hw/gpio/bcm2838_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 
51 #define GPIO_PUP_PDN_CNTRL_REG0 0xE4
52 #define GPIO_PUP_PDN_CNTRL_REG1 0xE8
53 #define GPIO_PUP_PDN_CNTRL_REG2 0xEC
54 #define GPIO_PUP_PDN_CNTRL_REG3 0xF0
55 
56 #define RESET_VAL_CNTRL_REG0 0xAAA95555
57 #define RESET_VAL_CNTRL_REG1 0xA0AAAAAA
58 #define RESET_VAL_CNTRL_REG2 0x50AAA95A
59 #define RESET_VAL_CNTRL_REG3 0x00055555
60 
61 #define NUM_FSELN_IN_GPFSELN 10
62 #define NUM_BITS_FSELN       3
63 #define MASK_FSELN           0x7
64 
65 #define BYTES_IN_WORD        4
66 
67 /* bcm,function property */
68 #define BCM2838_FSEL_GPIO_IN    0
69 #define BCM2838_FSEL_GPIO_OUT   1
70 #define BCM2838_FSEL_ALT5       2
71 #define BCM2838_FSEL_ALT4       3
72 #define BCM2838_FSEL_ALT0       4
73 #define BCM2838_FSEL_ALT1       5
74 #define BCM2838_FSEL_ALT2       6
75 #define BCM2838_FSEL_ALT3       7
76 
gpfsel_get(BCM2838GpioState * s,uint8_t reg)77 static uint32_t gpfsel_get(BCM2838GpioState *s, uint8_t reg)
78 {
79     int i;
80     uint32_t value = 0;
81     for (i = 0; i < NUM_FSELN_IN_GPFSELN; i++) {
82         uint32_t index = NUM_FSELN_IN_GPFSELN * reg + i;
83         if (index < sizeof(s->fsel)) {
84             value |= (s->fsel[index] & MASK_FSELN) << (NUM_BITS_FSELN * i);
85         }
86     }
87     return value;
88 }
89 
gpfsel_set(BCM2838GpioState * s,uint8_t reg,uint32_t value)90 static void gpfsel_set(BCM2838GpioState *s, uint8_t reg, uint32_t value)
91 {
92     int i;
93     for (i = 0; i < NUM_FSELN_IN_GPFSELN; i++) {
94         uint32_t index = NUM_FSELN_IN_GPFSELN * reg + i;
95         if (index < sizeof(s->fsel)) {
96             int fsel = (value >> (NUM_BITS_FSELN * i)) & MASK_FSELN;
97             s->fsel[index] = fsel;
98         }
99     }
100 
101     /* SD controller selection (48-53) */
102     if (s->sd_fsel != BCM2838_FSEL_GPIO_IN
103         && (s->fsel[48] == BCM2838_FSEL_GPIO_IN)
104         && (s->fsel[49] == BCM2838_FSEL_GPIO_IN)
105         && (s->fsel[50] == BCM2838_FSEL_GPIO_IN)
106         && (s->fsel[51] == BCM2838_FSEL_GPIO_IN)
107         && (s->fsel[52] == BCM2838_FSEL_GPIO_IN)
108         && (s->fsel[53] == BCM2838_FSEL_GPIO_IN)
109        ) {
110         /* SDHCI controller selected */
111         sdbus_reparent_card(s->sdbus_sdhost, s->sdbus_sdhci);
112         s->sd_fsel = BCM2838_FSEL_GPIO_IN;
113     } else if (s->sd_fsel != BCM2838_FSEL_ALT0
114                && (s->fsel[48] == BCM2838_FSEL_ALT0) /* SD_CLK_R */
115                && (s->fsel[49] == BCM2838_FSEL_ALT0) /* SD_CMD_R */
116                && (s->fsel[50] == BCM2838_FSEL_ALT0) /* SD_DATA0_R */
117                && (s->fsel[51] == BCM2838_FSEL_ALT0) /* SD_DATA1_R */
118                && (s->fsel[52] == BCM2838_FSEL_ALT0) /* SD_DATA2_R */
119                && (s->fsel[53] == BCM2838_FSEL_ALT0) /* SD_DATA3_R */
120               ) {
121         /* SDHost controller selected */
122         sdbus_reparent_card(s->sdbus_sdhci, s->sdbus_sdhost);
123         s->sd_fsel = BCM2838_FSEL_ALT0;
124     }
125 }
126 
gpfsel_is_out(BCM2838GpioState * s,int index)127 static int gpfsel_is_out(BCM2838GpioState *s, int index)
128 {
129     if (index >= 0 && index < BCM2838_GPIO_NUM) {
130         return s->fsel[index] == 1;
131     }
132     return 0;
133 }
134 
gpset(BCM2838GpioState * s,uint32_t val,uint8_t start,uint8_t count,uint32_t * lev)135 static void gpset(BCM2838GpioState *s, uint32_t val, uint8_t start,
136                   uint8_t count, uint32_t *lev)
137 {
138     uint32_t changes = val & ~*lev;
139     uint32_t cur = 1;
140 
141     int i;
142     for (i = 0; i < count; i++) {
143         if ((changes & cur) && (gpfsel_is_out(s, start + i))) {
144             qemu_set_irq(s->out[start + i], 1);
145         }
146         cur <<= 1;
147     }
148 
149     *lev |= val;
150 }
151 
gpclr(BCM2838GpioState * s,uint32_t val,uint8_t start,uint8_t count,uint32_t * lev)152 static void gpclr(BCM2838GpioState *s, uint32_t val, uint8_t start,
153                   uint8_t count, uint32_t *lev)
154 {
155     uint32_t changes = val & *lev;
156     uint32_t cur = 1;
157 
158     int i;
159     for (i = 0; i < count; i++) {
160         if ((changes & cur) && (gpfsel_is_out(s, start + i))) {
161             qemu_set_irq(s->out[start + i], 0);
162         }
163         cur <<= 1;
164     }
165 
166     *lev &= ~val;
167 }
168 
bcm2838_gpio_read(void * opaque,hwaddr offset,unsigned size)169 static uint64_t bcm2838_gpio_read(void *opaque, hwaddr offset, unsigned size)
170 {
171     BCM2838GpioState *s = (BCM2838GpioState *)opaque;
172     uint64_t value = 0;
173 
174     switch (offset) {
175     case GPFSEL0:
176     case GPFSEL1:
177     case GPFSEL2:
178     case GPFSEL3:
179     case GPFSEL4:
180     case GPFSEL5:
181         value = gpfsel_get(s, offset / BYTES_IN_WORD);
182         break;
183     case GPSET0:
184     case GPSET1:
185     case GPCLR0:
186     case GPCLR1:
187         /* Write Only */
188         qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Attempt reading from write only"
189                       " register. 0x%"PRIx64" will be returned."
190                       " Address 0x%"HWADDR_PRIx", size %u\n",
191                       TYPE_BCM2838_GPIO, __func__, value, offset, size);
192         break;
193     case GPLEV0:
194         value = s->lev0;
195         break;
196     case GPLEV1:
197         value = s->lev1;
198         break;
199     case GPEDS0:
200     case GPEDS1:
201     case GPREN0:
202     case GPREN1:
203     case GPFEN0:
204     case GPFEN1:
205     case GPHEN0:
206     case GPHEN1:
207     case GPLEN0:
208     case GPLEN1:
209     case GPAREN0:
210     case GPAREN1:
211     case GPAFEN0:
212     case GPAFEN1:
213         /* Not implemented */
214         qemu_log_mask(LOG_UNIMP, "%s: %s: not implemented for %"HWADDR_PRIx"\n",
215                       TYPE_BCM2838_GPIO, __func__, offset);
216         break;
217     case GPIO_PUP_PDN_CNTRL_REG0:
218     case GPIO_PUP_PDN_CNTRL_REG1:
219     case GPIO_PUP_PDN_CNTRL_REG2:
220     case GPIO_PUP_PDN_CNTRL_REG3:
221         value = s->pup_cntrl_reg[(offset - GPIO_PUP_PDN_CNTRL_REG0)
222                                  / sizeof(s->pup_cntrl_reg[0])];
223         break;
224     default:
225         qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: bad offset %"HWADDR_PRIx"\n",
226                       TYPE_BCM2838_GPIO, __func__, offset);
227         break;
228     }
229 
230     return value;
231 }
232 
bcm2838_gpio_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)233 static void bcm2838_gpio_write(void *opaque, hwaddr offset, uint64_t value,
234                                unsigned size)
235 {
236     BCM2838GpioState *s = (BCM2838GpioState *)opaque;
237 
238     switch (offset) {
239     case GPFSEL0:
240     case GPFSEL1:
241     case GPFSEL2:
242     case GPFSEL3:
243     case GPFSEL4:
244     case GPFSEL5:
245         gpfsel_set(s, offset / BYTES_IN_WORD, value);
246         break;
247     case GPSET0:
248         gpset(s, value, 0, 32, &s->lev0);
249         break;
250     case GPSET1:
251         gpset(s, value, 32, 22, &s->lev1);
252         break;
253     case GPCLR0:
254         gpclr(s, value, 0, 32, &s->lev0);
255         break;
256     case GPCLR1:
257         gpclr(s, value, 32, 22, &s->lev1);
258         break;
259     case GPLEV0:
260     case GPLEV1:
261         /* Read Only */
262         qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: Attempt writing 0x%"PRIx64""
263                       " to read only register. Ignored."
264                       " Address 0x%"HWADDR_PRIx", size %u\n",
265                       TYPE_BCM2838_GPIO, __func__, value, offset, size);
266         break;
267     case GPEDS0:
268     case GPEDS1:
269     case GPREN0:
270     case GPREN1:
271     case GPFEN0:
272     case GPFEN1:
273     case GPHEN0:
274     case GPHEN1:
275     case GPLEN0:
276     case GPLEN1:
277     case GPAREN0:
278     case GPAREN1:
279     case GPAFEN0:
280     case GPAFEN1:
281         /* Not implemented */
282         qemu_log_mask(LOG_UNIMP, "%s: %s: not implemented for %"HWADDR_PRIx"\n",
283                       TYPE_BCM2838_GPIO, __func__, offset);
284         break;
285     case GPIO_PUP_PDN_CNTRL_REG0:
286     case GPIO_PUP_PDN_CNTRL_REG1:
287     case GPIO_PUP_PDN_CNTRL_REG2:
288     case GPIO_PUP_PDN_CNTRL_REG3:
289         s->pup_cntrl_reg[(offset - GPIO_PUP_PDN_CNTRL_REG0)
290                          / sizeof(s->pup_cntrl_reg[0])] = value;
291         break;
292     default:
293         qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: bad offset %"HWADDR_PRIx"\n",
294                   TYPE_BCM2838_GPIO, __func__, offset);
295     }
296     return;
297 }
298 
bcm2838_gpio_reset(DeviceState * dev)299 static void bcm2838_gpio_reset(DeviceState *dev)
300 {
301     BCM2838GpioState *s = BCM2838_GPIO(dev);
302 
303     memset(s->fsel, 0, sizeof(s->fsel));
304 
305     s->sd_fsel = 0;
306 
307     /* SDHCI is selected by default */
308     sdbus_reparent_card(&s->sdbus, s->sdbus_sdhci);
309 
310     s->lev0 = 0;
311     s->lev1 = 0;
312 
313     memset(s->fsel, 0, sizeof(s->fsel));
314 
315     s->pup_cntrl_reg[0] = RESET_VAL_CNTRL_REG0;
316     s->pup_cntrl_reg[1] = RESET_VAL_CNTRL_REG1;
317     s->pup_cntrl_reg[2] = RESET_VAL_CNTRL_REG2;
318     s->pup_cntrl_reg[3] = RESET_VAL_CNTRL_REG3;
319 }
320 
321 static const MemoryRegionOps bcm2838_gpio_ops = {
322     .read = bcm2838_gpio_read,
323     .write = bcm2838_gpio_write,
324     .endianness = DEVICE_NATIVE_ENDIAN,
325 };
326 
327 static const VMStateDescription vmstate_bcm2838_gpio = {
328     .name = "bcm2838_gpio",
329     .version_id = 1,
330     .minimum_version_id = 1,
331     .fields = (VMStateField[]) {
332         VMSTATE_UINT8_ARRAY(fsel, BCM2838GpioState, BCM2838_GPIO_NUM),
333         VMSTATE_UINT32(lev0, BCM2838GpioState),
334         VMSTATE_UINT32(lev1, BCM2838GpioState),
335         VMSTATE_UINT8(sd_fsel, BCM2838GpioState),
336         VMSTATE_UINT32_ARRAY(pup_cntrl_reg, BCM2838GpioState,
337                              GPIO_PUP_PDN_CNTRL_NUM),
338         VMSTATE_END_OF_LIST()
339     }
340 };
341 
bcm2838_gpio_init(Object * obj)342 static void bcm2838_gpio_init(Object *obj)
343 {
344     BCM2838GpioState *s = BCM2838_GPIO(obj);
345     DeviceState *dev = DEVICE(obj);
346     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
347 
348     qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(s), "sd-bus");
349 
350     memory_region_init_io(&s->iomem, obj, &bcm2838_gpio_ops, s,
351                           "bcm2838_gpio", BCM2838_GPIO_REGS_SIZE);
352     sysbus_init_mmio(sbd, &s->iomem);
353     qdev_init_gpio_out(dev, s->out, BCM2838_GPIO_NUM);
354 }
355 
bcm2838_gpio_realize(DeviceState * dev,Error ** errp)356 static void bcm2838_gpio_realize(DeviceState *dev, Error **errp)
357 {
358     BCM2838GpioState *s = BCM2838_GPIO(dev);
359     Object *obj;
360 
361     obj = object_property_get_link(OBJECT(dev), "sdbus-sdhci", &error_abort);
362     s->sdbus_sdhci = SD_BUS(obj);
363 
364     obj = object_property_get_link(OBJECT(dev), "sdbus-sdhost", &error_abort);
365     s->sdbus_sdhost = SD_BUS(obj);
366 }
367 
bcm2838_gpio_class_init(ObjectClass * klass,void * data)368 static void bcm2838_gpio_class_init(ObjectClass *klass, void *data)
369 {
370     DeviceClass *dc = DEVICE_CLASS(klass);
371 
372     dc->vmsd = &vmstate_bcm2838_gpio;
373     dc->realize = &bcm2838_gpio_realize;
374     dc->reset = &bcm2838_gpio_reset;
375 }
376 
377 static const TypeInfo bcm2838_gpio_info = {
378     .name          = TYPE_BCM2838_GPIO,
379     .parent        = TYPE_SYS_BUS_DEVICE,
380     .instance_size = sizeof(BCM2838GpioState),
381     .instance_init = bcm2838_gpio_init,
382     .class_init    = bcm2838_gpio_class_init,
383 };
384 
bcm2838_gpio_register_types(void)385 static void bcm2838_gpio_register_types(void)
386 {
387     type_register_static(&bcm2838_gpio_info);
388 }
389 
390 type_init(bcm2838_gpio_register_types)
391