Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src Overhaul of GPIO interrupt support (that wasn't...



details:   https://anonhg.NetBSD.org/src/rev/43b2ead06c3f
branches:  trunk
changeset: 319165:43b2ead06c3f
user:      thorpej <thorpej%NetBSD.org@localhost>
date:      Sat May 19 13:59:06 2018 +0000

description:
Overhaul of GPIO interrupt support (that wasn't even used by anything).
- Remove the old, not-expressive-enough interrupt flags, and replace them
  with a new set of interrupt-specific flags that can express a wide
  variety of interrupt configurations (pos, neg, and double-edge, high
  and low level).
- Remove old, unused gpio_pin_ctl_intr() and gpio_pin_irqen(), and
  replace them with gpio_intr_establish(), gpio_intr_disestablish(),
  and gpio_intr_str().  Corresponding fields in the gpio_chipset_tag
  are also added for back-end controllers, which now handle the actual
  dispatch of GPIO interrupts in order to properly support level-triggered
  interrupts as well as interoperate properly with FDT-registered
  interrupts.

Piggy-back on the 8.99.18 version bump.

Inspired by initial work from Brad Spencer.
PR kern/51676

diffstat:

 share/man/man4/gpio.4      |    8 +-
 sys/dev/gpio/gpio.c        |  219 +++++++++++++++++++++++++++++++++-----------
 sys/dev/gpio/gpiovar.h     |   32 +++--
 sys/sys/gpio.h             |   31 +++++-
 usr.sbin/gpioctl/gpioctl.c |    5 +-
 5 files changed, 211 insertions(+), 84 deletions(-)

diffs (truncated from 431 to 300 lines):

diff -r b67527bed806 -r 43b2ead06c3f share/man/man4/gpio.4
--- a/share/man/man4/gpio.4     Sat May 19 11:40:22 2018 +0000
+++ b/share/man/man4/gpio.4     Sat May 19 13:59:06 2018 +0000
@@ -1,4 +1,4 @@
-.\" $NetBSD: gpio.4,v 1.32 2018/02/20 09:37:56 wiz Exp $
+.\" $NetBSD: gpio.4,v 1.33 2018/05/19 13:59:06 thorpej Exp $
 .\"    $OpenBSD: gpio.4,v 1.5 2004/11/23 09:39:29 reyk Exp $
 .\"
 .\" Copyright (c) 2004 Alexander Yurchenko <grange%openbsd.org@localhost>
@@ -167,12 +167,6 @@
 .It Dv GPIO_PIN_ALT0 -
 .It Dv GPIO_PIN_ALT7
 select alternate pin function 0 to 7
-.It Dv GPIO_PIN_EVENTS
-deliver events
-.It Dv GPIO_PIN_LEVEL
-trigger on pin level instead of edge
-.It Dv GPIO_PIN_FALLING
-trigger on falling instead of rising edge
 .El
 .Pp
 Note that the GPIO controller
diff -r b67527bed806 -r 43b2ead06c3f sys/dev/gpio/gpio.c
--- a/sys/dev/gpio/gpio.c       Sat May 19 11:40:22 2018 +0000
+++ b/sys/dev/gpio/gpio.c       Sat May 19 13:59:06 2018 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gpio.c,v 1.60 2017/10/28 04:53:56 riastradh Exp $ */
+/* $NetBSD: gpio.c,v 1.61 2018/05/19 13:59:06 thorpej Exp $ */
 /*     $OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $ */
 
 /*
@@ -19,7 +19,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.60 2017/10/28 04:53:56 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.61 2018/05/19 13:59:06 thorpej Exp $");
 
 /*
  * General Purpose Input/Output framework.
@@ -310,28 +310,6 @@
        return UNCONF;
 }
 
-/* called from backends when a interrupt even occurs */
-void
-gpio_intr(device_t self, uint32_t evts)
-{
-       struct gpio_softc *sc = device_private(self);
-       void (*callback)(void *);
-       void *callback_arg;
-
-       for (int i = 0; i < sc->sc_npins; i++) {
-               if (evts & (1 << i)) {
-                       mutex_enter(&sc->sc_mtx);
-                       callback = sc->sc_pins[i].pin_callback;
-                       callback_arg = sc->sc_pins[i].pin_callback_arg;
-                       DPRINTFN(2, ("gpio pin %d event callback %p\n", i, callback));
-                       if (callback != NULL) {
-                               callback(callback_arg);
-                       }
-                       mutex_exit(&sc->sc_mtx);
-               }
-       }
-}
-
 void *
 gpio_find_device(const char *name)
 {
@@ -426,49 +404,47 @@
        sc->sc_pins[map->pm_map[pin]].pin_state = value;
 }
 
+int
+gpio_pin_get_conf(void *gpio, struct gpio_pinmap *map, int pin)
+{
+       struct gpio_softc *sc = gpio;
+       int rv;
+
+       mutex_enter(&sc->sc_mtx);
+       rv = sc->sc_pins[map->pm_map[pin]].pin_flags;
+       mutex_exit(&sc->sc_mtx);
+
+       return (rv);
+}
+
+bool
+gpio_pin_set_conf(void *gpio, struct gpio_pinmap *map, int pin, int flags)
+{
+       struct gpio_softc *sc = gpio;
+       int checkflags = flags & GPIO_PIN_HWCAPS;
+
+       if ((sc->sc_pins[map->pm_map[pin]].pin_caps & checkflags) != checkflags)
+               return (false);
+
+       gpio_pin_ctl(gpio, map, pin, flags);
+
+       return (true);
+}
+
 void
 gpio_pin_ctl(void *gpio, struct gpio_pinmap *map, int pin, int flags)
 {
        struct gpio_softc *sc = gpio;
-       struct gpio_pin *pinp = &sc->sc_pins[map->pm_map[pin]];
 
-       KASSERT((flags & GPIO_PIN_EVENTS) == 0);
+       /* loosey-goosey version of gpio_pin_set_conf(). */
+
        mutex_enter(&sc->sc_mtx);
        gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags);
-       pinp->pin_callback = NULL;
-       pinp->pin_callback_arg = NULL;
+       sc->sc_pins[map->pm_map[pin]].pin_flags = flags;
        mutex_exit(&sc->sc_mtx);
 }
 
 int
-gpio_pin_ctl_intr(void *gpio, struct gpio_pinmap *map, int pin, int flags,
-    int ipl, void (*callback)(void *), void *arg)
-{
-       struct gpio_softc *sc = gpio;
-       struct gpio_pin *pinp = &sc->sc_pins[map->pm_map[pin]];
-       KASSERT((flags & GPIO_PIN_EVENTS) != 0);
-       if (ipl != IPL_VM)
-               return EINVAL;
-       mutex_enter(&sc->sc_mtx);
-       if (pinp->pin_callback != NULL) {
-               mutex_exit(&sc->sc_mtx);
-               return EEXIST;
-       }
-       pinp->pin_callback = callback;
-       pinp->pin_callback_arg = arg;
-       gpiobus_pin_ctl(sc->sc_gc, map->pm_map[pin], flags);
-       mutex_exit(&sc->sc_mtx);
-       return 0;
-}
-
-void
-gpio_pin_irqen(void *gpio, struct gpio_pinmap *map, int pin, bool en)
-{
-       struct gpio_softc *sc = gpio;
-       gpiobus_pin_irqen(sc->sc_gc, map->pm_map[pin], en);
-}
-
-int
 gpio_pin_caps(void *gpio, struct gpio_pinmap *map, int pin)
 {
        struct gpio_softc *sc = gpio;
@@ -477,6 +453,137 @@
 }
 
 int
+gpio_pin_intrcaps(void *gpio, struct gpio_pinmap *map, int pin)
+{
+       struct gpio_softc *sc = gpio;
+
+       return sc->sc_pins[map->pm_map[pin]].pin_intrcaps;
+}
+
+static int
+gpio_irqmode_sanitize(int irqmode)
+{
+       int has_edge, has_level;
+
+       has_edge  = irqmode & GPIO_INTR_EDGE_MASK;
+       has_level = irqmode & GPIO_INTR_LEVEL_MASK;
+
+       /* Must specify an interrupt mode. */
+       if ((irqmode & GPIO_INTR_MODE_MASK) == 0)
+               return (0);
+
+       /* Can't specify edge and level together */
+       if (has_level && has_edge)
+               return (0);
+
+       /* "Be liberal in what you accept..." */
+       if (has_edge) {
+               if (irqmode & GPIO_INTR_DOUBLE_EDGE) {
+                       /* if DOUBLE is set, just pass through DOUBLE */
+                       irqmode = (irqmode & ~GPIO_INTR_EDGE_MASK) |
+                           GPIO_INTR_DOUBLE_EDGE;
+               } else if ((irqmode ^
+                           (GPIO_INTR_POS_EDGE | GPIO_INTR_NEG_EDGE)) == 0) {
+                       /* both POS and NEG set; treat as DOUBLE */
+                       irqmode = (irqmode & ~GPIO_INTR_EDGE_MASK) |
+                           GPIO_INTR_DOUBLE_EDGE;
+               }
+       } else {
+               /* Can't specify both levels together. */
+               if (has_level == GPIO_INTR_LEVEL_MASK)
+                       return (0);
+       }
+
+       return (irqmode);
+}
+
+bool
+gpio_pin_irqmode_issupported(void *gpio, struct gpio_pinmap *map,
+                            int pin, int irqmode)
+{
+       struct gpio_softc *sc = gpio;
+       int match;
+
+       irqmode = gpio_irqmode_sanitize(irqmode) & GPIO_INTR_MODE_MASK;
+
+       /* Make sure the pin can do what is being asked. */
+       match = sc->sc_pins[map->pm_map[pin]].pin_intrcaps & irqmode;
+
+       return (irqmode && irqmode == match);
+}
+
+void *
+gpio_intr_establish(void *gpio, struct gpio_pinmap *map, int pin, int ipl,
+                   int irqmode, int (*func)(void *), void *arg)
+{
+       struct gpio_softc *sc = gpio;
+
+       if (sc->sc_gc->gp_intr_establish == NULL)
+               return (NULL);
+
+       irqmode = gpio_irqmode_sanitize(irqmode);
+       if (irqmode == 0)
+               return (NULL);
+
+       if (! gpio_pin_irqmode_issupported(gpio, map, pin, irqmode))
+               return (NULL);
+
+       /* XXX Right now, everything has to be at IPL_VM. */
+       if (ipl != IPL_VM)
+               return (NULL);
+
+       return ((*sc->sc_gc->gp_intr_establish)(sc->sc_gc->gp_cookie,
+           sc->sc_pins[map->pm_map[pin]].pin_num, ipl, irqmode, func, arg));
+}
+
+void
+gpio_intr_disestablish(void *gpio, void *ih)
+{
+       struct gpio_softc *sc = gpio;
+
+       if (sc->sc_gc->gp_intr_disestablish != NULL && ih != NULL)
+               (*sc->sc_gc->gp_intr_disestablish)(sc->sc_gc->gp_cookie, ih);
+}
+
+bool
+gpio_intr_str(void *gpio, struct gpio_pinmap *map, int pin, int irqmode,
+             char *intrstr, size_t intrstrlen)
+{
+       struct gpio_softc *sc = gpio;
+       const char *mode;
+       char hwstr[64];
+
+       if (sc->sc_gc->gp_intr_str == NULL)
+               return (false);
+
+       irqmode = gpio_irqmode_sanitize(irqmode);
+       if (irqmode == 0)
+               return (false);
+
+       if (irqmode & GPIO_INTR_DOUBLE_EDGE)
+               mode = "double edge";
+       else if (irqmode & GPIO_INTR_POS_EDGE)
+               mode = "positive edge";
+       else if (irqmode & GPIO_INTR_NEG_EDGE)
+               mode = "negative edge";
+       else if (irqmode & GPIO_INTR_HIGH_LEVEL)
+               mode = "high level";
+       else if (irqmode & GPIO_INTR_LOW_LEVEL)
+               mode = "low level";
+       else
+               return (false);
+
+       if (! (*sc->sc_gc->gp_intr_str)(sc->sc_gc->gp_cookie,
+                                       sc->sc_pins[map->pm_map[pin]].pin_num,
+                                       irqmode, hwstr, sizeof(hwstr)))
+               return (false);
+
+       (void) snprintf(intrstr, intrstrlen, "%s (%s)", hwstr, mode);
+       
+       return (true);
+}
+
+int
 gpio_npins(uint32_t mask)
 {
        int npins, i;
diff -r b67527bed806 -r 43b2ead06c3f sys/dev/gpio/gpiovar.h
--- a/sys/dev/gpio/gpiovar.h    Sat May 19 11:40:22 2018 +0000
+++ b/sys/dev/gpio/gpiovar.h    Sat May 19 13:59:06 2018 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gpiovar.h,v 1.17 2017/07/06 10:43:06 jmcneill Exp $ */
+/* $NetBSD: gpiovar.h,v 1.18 2018/05/19 13:59:06 thorpej Exp $ */
 /*     $OpenBSD: gpiovar.h,v 1.3 2006/01/14 12:33:49 grange Exp $      */
 
 /*
@@ -31,7 +31,11 @@
        int     (*gp_pin_read)(void *, int);
        void    (*gp_pin_write)(void *, int, int);
        void    (*gp_pin_ctl)(void *, int, int);



Home | Main Index | Thread Index | Old Index