]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/gpio/pl061.c
Import tzdata 2020c
[FreeBSD/FreeBSD.git] / sys / dev / gpio / pl061.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2020 Amazon.com, Inc. or its affiliates.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/proc.h>
38 #include <sys/rman.h>
39 #include <sys/lock.h>
40 #include <sys/mutex.h>
41 #include <sys/gpio.h>
42 #include <sys/interrupt.h>
43
44 #include <machine/bus.h>
45 #include <machine/intr.h>
46 #include <machine/resource.h>
47
48 #include <dev/gpio/gpiobusvar.h>
49
50 #include "pl061.h"
51
52 #include "gpio_if.h"
53 #include "pic_if.h"
54
55 #define PL061_LOCK(_sc)                 mtx_lock_spin(&(_sc)->sc_mtx)
56 #define PL061_UNLOCK(_sc)               mtx_unlock_spin(&(_sc)->sc_mtx)
57 #define PL061_LOCK_DESTROY(_sc)         mtx_destroy(&(_sc)->sc_mtx)
58 #define PL061_ASSERT_LOCKED(_sc)        mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
59 #define PL061_ASSERT_UNLOCKED(_sc)      mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
60
61 #if 0
62 #define dprintf(fmt, args...) do {      \
63         printf(fmt, ##args);            \
64 } while (0)
65 #else
66 #define dprintf(fmt, args...)
67 #endif
68
69 #define PL061_PIN_TO_ADDR(pin)  (1 << (pin + 2))
70 #define PL061_DATA              0x3FC
71 #define PL061_DIR               0x400
72 #define PL061_INTSENSE          0x404
73 #define PL061_INTBOTHEDGES      0x408
74 #define PL061_INTEVENT          0x40C
75 #define PL061_INTMASK           0x410
76 #define PL061_RAWSTATUS         0x414
77 #define PL061_STATUS            0x418
78 #define PL061_INTCLR            0x41C
79 #define PL061_MODECTRL          0x420
80
81 #define PL061_ALLOWED_CAPS     (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_INTR_EDGE_BOTH | \
82                                 GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | \
83                                 GPIO_INTR_LEVEL_HIGH | GPIO_INTR_LEVEL_LOW )
84
85 #define PIC_INTR_ISRC(sc, irq) (&(sc->sc_isrcs[irq].isrc))
86
87 static device_t
88 pl061_get_bus(device_t dev)
89 {
90         struct pl061_softc *sc;
91
92         sc = device_get_softc(dev);
93         return (sc->sc_busdev);
94 }
95
96 static int
97 pl061_pin_max(device_t dev, int *maxpin)
98 {
99         *maxpin = PL061_NUM_GPIO - 1;
100         return (0);
101 }
102
103 static int
104 pl061_pin_getname(device_t dev, uint32_t pin, char *name)
105 {
106         struct pl061_softc *sc;
107
108         sc = device_get_softc(dev);
109         if (pin >= PL061_NUM_GPIO)
110                 return (EINVAL);
111
112         snprintf(name, GPIOMAXNAME, "p%u", pin);
113         name[GPIOMAXNAME - 1] = '\0';
114
115         return (0);
116 }
117
118 static int
119 pl061_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
120 {
121         struct pl061_softc *sc;
122         uint8_t mask = 1 << pin;
123
124         sc = device_get_softc(dev);
125         if (pin >= PL061_NUM_GPIO)
126                 return (EINVAL);
127
128         PL061_LOCK(sc);
129         *flags = 0;
130
131         if (mask & bus_read_1(sc->sc_mem_res, PL061_DIR))
132                 *flags |= GPIO_PIN_OUTPUT;
133         else
134                 *flags |= GPIO_PIN_INPUT;
135
136         PL061_UNLOCK(sc);
137         return (0);
138 }
139
140 static int
141 pl061_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
142 {
143         struct pl061_softc *sc;
144
145         sc = device_get_softc(dev);
146         if (pin >= PL061_NUM_GPIO)
147                 return (EINVAL);
148
149         *caps = PL061_ALLOWED_CAPS;
150
151         return (0);
152 }
153
154 static void
155 mask_and_set(struct pl061_softc *sc, long a, uint8_t m, uint8_t b)
156 {
157         uint8_t tmp;
158
159         tmp = bus_read_1(sc->sc_mem_res, a);
160         tmp &= ~m;
161         tmp |= b;
162         bus_write_1(sc->sc_mem_res, a, tmp);
163         dprintf("%s: writing %#x to register %#lx\n", __func__, tmp, a);
164 }
165
166 static int
167 pl061_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
168 {
169         struct pl061_softc *sc;
170         uint8_t mask = 1 << pin;
171         const uint32_t in_out = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
172
173         sc = device_get_softc(dev);
174         if (pin >= PL061_NUM_GPIO)
175                 return (EINVAL);
176
177         if (flags & ~PL061_ALLOWED_CAPS)
178                 return (EINVAL);
179
180         /* can't be both input and output */
181         if ((flags & in_out) == in_out)
182                 return (EINVAL);
183
184
185         PL061_LOCK(sc);
186         mask_and_set(sc, PL061_DIR, mask, flags & GPIO_PIN_OUTPUT ? mask : 0);
187         PL061_UNLOCK(sc);
188         return (0);
189 }
190
191 static int
192 pl061_pin_get(device_t dev, uint32_t pin, uint32_t *value)
193 {
194         struct pl061_softc *sc;
195
196         sc = device_get_softc(dev);
197         if (pin >= PL061_NUM_GPIO)
198                 return (EINVAL);
199
200         PL061_LOCK(sc);
201         if (bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin)))
202                 *value = GPIO_PIN_HIGH;
203         else
204                 *value = GPIO_PIN_LOW;
205         PL061_UNLOCK(sc);
206
207         return (0);
208 }
209
210 static int
211 pl061_pin_set(device_t dev, uint32_t pin, uint32_t value)
212 {
213         struct pl061_softc *sc;
214         uint8_t d = (value == GPIO_PIN_HIGH) ? 0xff : 0x00;
215
216         sc = device_get_softc(dev);
217         if (pin >= PL061_NUM_GPIO)
218                 return (EINVAL);
219
220         PL061_LOCK(sc);
221         bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d);
222         PL061_UNLOCK(sc);
223
224         return (0);
225 }
226
227 static int
228 pl061_pin_toggle(device_t dev, uint32_t pin)
229 {
230         struct pl061_softc *sc;
231         uint8_t d;
232
233         sc = device_get_softc(dev);
234         if (pin >= PL061_NUM_GPIO)
235                 return (EINVAL);
236
237         PL061_LOCK(sc);
238         d = ~bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin));
239         bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d);
240         PL061_UNLOCK(sc);
241
242         return (0);
243 }
244
245 static void
246 pl061_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
247 {
248         struct pl061_softc *sc;
249         uint8_t mask;
250
251         sc = device_get_softc(dev);
252         mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
253
254         dprintf("%s: calling disable interrupt %#x\n", __func__, mask);
255         PL061_LOCK(sc);
256         mask_and_set(sc, PL061_INTMASK, mask, 0);
257         PL061_UNLOCK(sc);
258 }
259
260
261
262 static void
263 pl061_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
264 {
265         struct pl061_softc *sc;
266         uint8_t mask;
267
268         sc = device_get_softc(dev);
269         mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
270
271
272         dprintf("%s: calling enable interrupt %#x\n", __func__, mask);
273         PL061_LOCK(sc);
274         mask_and_set(sc, PL061_INTMASK, mask, mask);
275         PL061_UNLOCK(sc);
276 }
277
278 static int
279 pl061_pic_map_intr(device_t dev, struct intr_map_data *data,
280         struct intr_irqsrc **isrcp)
281 {
282         struct pl061_softc *sc;
283         struct intr_map_data_gpio *gdata;
284         uint32_t irq;
285
286         sc = device_get_softc(dev);
287         if (data->type != INTR_MAP_DATA_GPIO)
288                 return (ENOTSUP);
289
290         gdata = (struct intr_map_data_gpio *)data;
291         irq = gdata->gpio_pin_num;
292         if (irq >= PL061_NUM_GPIO) {
293                 device_printf(dev, "invalid interrupt number %u\n", irq);
294                 return (EINVAL);
295         }
296
297         dprintf("%s: calling map interrupt %u\n", __func__, irq);
298         *isrcp = PIC_INTR_ISRC(sc, irq);
299
300         return (0);
301 }
302
303 static int
304 pl061_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
305         struct resource *res, struct intr_map_data *data)
306 {
307         struct pl061_softc *sc;
308         struct intr_map_data_gpio *gdata;
309         struct pl061_pin_irqsrc *irqsrc;
310         uint32_t mode;
311         uint8_t mask;
312
313         if (data == NULL)
314                 return (ENOTSUP);
315
316         sc = device_get_softc(dev);
317         gdata = (struct intr_map_data_gpio *)data;
318         irqsrc = (struct pl061_pin_irqsrc *)isrc;
319
320         mode = gdata->gpio_intr_mode;
321         mask = 1 << gdata->gpio_pin_num;
322
323         dprintf("%s: calling setup interrupt %u mode %#x\n", __func__,
324             irqsrc->irq, mode);
325         if (irqsrc->irq != gdata->gpio_pin_num) {
326                 dprintf("%s: interrupts don't match\n", __func__);
327                 return (EINVAL);
328         }
329
330         if (isrc->isrc_handlers != 0) {
331                 dprintf("%s: handler already attached\n", __func__);
332                 return (irqsrc->mode == mode ? 0 : EINVAL);
333         }
334         irqsrc->mode = mode;
335
336         PL061_LOCK(sc);
337
338         if (mode & GPIO_INTR_EDGE_BOTH) {
339                 mask_and_set(sc, PL061_INTBOTHEDGES, mask, mask);
340                 mask_and_set(sc, PL061_INTSENSE, mask, 0);
341         } else if (mode & GPIO_INTR_EDGE_RISING) {
342                 mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
343                 mask_and_set(sc, PL061_INTSENSE, mask, 0);
344                 mask_and_set(sc, PL061_INTEVENT, mask, mask);
345         } else if (mode & GPIO_INTR_EDGE_FALLING) {
346                 mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
347                 mask_and_set(sc, PL061_INTSENSE, mask, 0);
348                 mask_and_set(sc, PL061_INTEVENT, mask, 0);
349         } else if (mode & GPIO_INTR_LEVEL_HIGH) {
350                 mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
351                 mask_and_set(sc, PL061_INTSENSE, mask, mask);
352                 mask_and_set(sc, PL061_INTEVENT, mask, mask);
353         } else if (mode & GPIO_INTR_LEVEL_LOW) {
354                 mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
355                 mask_and_set(sc, PL061_INTSENSE, mask, mask);
356                 mask_and_set(sc, PL061_INTEVENT, mask, 0);
357         }
358         PL061_UNLOCK(sc);
359         return (0);
360 }
361
362 static int
363 pl061_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
364         struct resource *res, struct intr_map_data *data)
365 {
366         struct pl061_softc *sc;
367         struct pl061_pin_irqsrc *irqsrc;
368         uint8_t mask;
369
370         irqsrc = (struct pl061_pin_irqsrc *)isrc;
371         mask = 1 << irqsrc->irq;
372         dprintf("%s: calling teardown interrupt %#x\n", __func__, mask);
373
374         sc = device_get_softc(dev);
375         if (isrc->isrc_handlers == 0) {
376                 irqsrc->mode = GPIO_INTR_CONFORM;
377                 PL061_LOCK(sc);
378                 mask_and_set(sc, PL061_INTMASK, mask, 0);
379                 PL061_UNLOCK(sc);
380         }
381         return (0);
382 }
383
384 static void
385 pl061_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
386 {
387         struct pl061_softc *sc;
388         uint8_t mask;
389
390         sc = device_get_softc(dev);
391         mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
392         dprintf("%s: calling post filter %#x\n", __func__, mask);
393
394         bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask);
395 }
396
397 static void
398 pl061_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
399 {
400         struct pl061_softc *sc;
401         uint8_t mask;
402
403         sc = device_get_softc(dev);
404         mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
405         dprintf("%s: calling post ithread %#x\n", __func__, mask);
406         bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask);
407
408         pl061_pic_enable_intr(dev, isrc);
409 }
410
411 static void
412 pl061_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
413 {
414         pl061_pic_disable_intr(dev, isrc);
415 }
416
417 static int
418 pl061_intr(void *arg)
419 {
420         struct pl061_softc *sc;
421         struct trapframe *tf;
422         uint8_t status;
423         int pin;
424
425         sc = (struct pl061_softc *)arg;
426         tf = curthread->td_intr_frame;
427
428         status = bus_read_1(sc->sc_mem_res, PL061_STATUS);
429
430         while (status != 0) {
431                 pin = ffs(status) - 1;
432                 status &= ~(1 << pin);
433
434                 if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, pin), tf) != 0)
435                         device_printf(sc->sc_dev, "spurious interrupt %d\n",
436                             pin);
437
438                 dprintf("got IRQ on %d\n", pin);
439
440         }
441         return (FILTER_HANDLED);
442 }
443
444 int
445 pl061_attach(device_t dev)
446 {
447         struct pl061_softc *sc;
448         int ret;
449         int irq;
450         const char *name;
451
452         sc = device_get_softc(dev);
453         sc->sc_dev = dev;
454
455         sc->sc_mem_rid = 0;
456         sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
457             &sc->sc_mem_rid, RF_ACTIVE);
458         if (sc->sc_mem_res == NULL) {
459                 device_printf(dev, "can't allocate memory resource\n");
460                 return (ENXIO);
461         }
462
463         sc->sc_irq_rid = 0;
464         sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
465             &sc->sc_irq_rid, RF_ACTIVE);
466
467         if (sc->sc_irq_res == NULL) {
468                 device_printf(dev, "can't allocate IRQ resource\n");
469                 goto free_mem;
470         }
471
472         ret = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
473             pl061_intr, NULL, sc, &sc->sc_irq_hdlr);
474         if (ret) {
475                 device_printf(dev, "can't setup IRQ\n");
476                 goto free_pic;
477         }
478
479         name = device_get_nameunit(dev);
480
481         for (irq = 0; irq < PL061_NUM_GPIO; irq++) {
482                 if (bootverbose) {
483                         device_printf(dev,
484                             "trying to register pin %d name %s\n", irq, name);
485                 }
486                 sc->sc_isrcs[irq].irq = irq;
487                 sc->sc_isrcs[irq].mode = GPIO_INTR_CONFORM;
488                 ret = intr_isrc_register(PIC_INTR_ISRC(sc, irq), dev, 0,
489                     "%s", name);
490                 if (ret) {
491                         device_printf(dev, "can't register isrc %d\n", ret);
492                         goto free_isrc;
493                 }
494         }
495
496         sc->sc_busdev = gpiobus_attach_bus(dev);
497         if (sc->sc_busdev == NULL) {
498                 device_printf(dev, "couldn't attach gpio bus\n");
499                 goto free_isrc;
500         }
501
502         mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "pl061", MTX_SPIN);
503
504         return (0);
505
506 free_isrc:
507         /*
508          * XXX isrc_release_counters() not implemented
509          * for (irq = 0; irq < PL061_NUM_GPIO; irq++)
510          *      intr_isrc_deregister(PIC_INTR_ISRC(sc, irq));
511         */
512         bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
513             sc->sc_irq_res);
514 free_pic:
515         /*
516          * XXX intr_pic_deregister: not implemented
517          * intr_pic_deregister(dev, 0);
518          */
519
520 free_mem:
521         bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
522             sc->sc_mem_res);
523
524         return (ENXIO);
525
526 }
527
528 int
529 pl061_detach(device_t dev)
530 {
531         struct pl061_softc *sc;
532         sc = device_get_softc(dev);
533
534         if (sc->sc_busdev)
535                 gpiobus_detach_bus(dev);
536
537         if (sc->sc_irq_hdlr != NULL)
538                 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_hdlr);
539
540         if (sc->sc_irq_res != NULL)
541                 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
542                     sc->sc_irq_res);
543
544         if (sc->sc_mem_res != NULL)
545                 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
546                     sc->sc_mem_res);
547         PL061_LOCK_DESTROY(sc);
548         return (0);
549 }
550
551 static device_method_t pl061_methods[] = {
552         /* Device interface */
553         DEVMETHOD(device_attach,        pl061_attach),
554         DEVMETHOD(device_detach,        pl061_detach),
555
556         /* Bus interface */
557         DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
558         DEVMETHOD(bus_activate_resource,        bus_generic_activate_resource),
559         DEVMETHOD(bus_deactivate_resource,      bus_generic_deactivate_resource),
560
561         /* GPIO protocol */
562         DEVMETHOD(gpio_get_bus,         pl061_get_bus),
563         DEVMETHOD(gpio_pin_max,         pl061_pin_max),
564         DEVMETHOD(gpio_pin_getname,     pl061_pin_getname),
565         DEVMETHOD(gpio_pin_getflags,    pl061_pin_getflags),
566         DEVMETHOD(gpio_pin_getcaps,     pl061_pin_getcaps),
567         DEVMETHOD(gpio_pin_setflags,    pl061_pin_setflags),
568         DEVMETHOD(gpio_pin_get,         pl061_pin_get),
569         DEVMETHOD(gpio_pin_set,         pl061_pin_set),
570         DEVMETHOD(gpio_pin_toggle,      pl061_pin_toggle),
571
572         /* Interrupt controller interface */
573         DEVMETHOD(pic_disable_intr,     pl061_pic_disable_intr),
574         DEVMETHOD(pic_enable_intr,      pl061_pic_enable_intr),
575         DEVMETHOD(pic_map_intr,         pl061_pic_map_intr),
576         DEVMETHOD(pic_setup_intr,       pl061_pic_setup_intr),
577         DEVMETHOD(pic_teardown_intr,    pl061_pic_teardown_intr),
578         DEVMETHOD(pic_post_filter,      pl061_pic_post_filter),
579         DEVMETHOD(pic_post_ithread,     pl061_pic_post_ithread),
580         DEVMETHOD(pic_pre_ithread,      pl061_pic_pre_ithread),
581
582         DEVMETHOD_END
583 };
584
585 DEFINE_CLASS_0(gpio, pl061_driver, pl061_methods, sizeof(struct pl061_softc));