Module Name: src
Committed By: thorpej
Date: Sat May 19 13:59:07 UTC 2018
Modified Files:
src/share/man/man4: gpio.4
src/sys/dev/gpio: gpio.c gpiovar.h
src/sys/sys: gpio.h
src/usr.sbin/gpioctl: gpioctl.c
Log Message:
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
To generate a diff of this commit:
cvs rdiff -u -r1.32 -r1.33 src/share/man/man4/gpio.4
cvs rdiff -u -r1.60 -r1.61 src/sys/dev/gpio/gpio.c
cvs rdiff -u -r1.17 -r1.18 src/sys/dev/gpio/gpiovar.h
cvs rdiff -u -r1.15 -r1.16 src/sys/sys/gpio.h
cvs rdiff -u -r1.23 -r1.24 src/usr.sbin/gpioctl/gpioctl.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/share/man/man4/gpio.4
diff -u src/share/man/man4/gpio.4:1.32 src/share/man/man4/gpio.4:1.33
--- src/share/man/man4/gpio.4:1.32 Tue Feb 20 09:37:56 2018
+++ src/share/man/man4/gpio.4 Sat May 19 13:59:06 2018
@@ -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 <[email protected]>
@@ -167,12 +167,6 @@ pulsate output
.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
Index: src/sys/dev/gpio/gpio.c
diff -u src/sys/dev/gpio/gpio.c:1.60 src/sys/dev/gpio/gpio.c:1.61
--- src/sys/dev/gpio/gpio.c:1.60 Sat Oct 28 04:53:56 2017
+++ src/sys/dev/gpio/gpio.c Sat May 19 13:59:06 2018
@@ -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 @@ gpiobus_print(void *aux, const char *pnp
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,54 +404,183 @@ gpio_pin_write(void *gpio, struct gpio_p
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)
+gpio_pin_caps(void *gpio, struct gpio_pinmap *map, int pin)
{
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;
+
+ return sc->sc_pins[map->pm_map[pin]].pin_caps;
+}
+
+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);
}
- 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;
+
+ 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_pin_irqen(void *gpio, struct gpio_pinmap *map, int pin, bool en)
+gpio_intr_disestablish(void *gpio, void *ih)
{
struct gpio_softc *sc = gpio;
- gpiobus_pin_irqen(sc->sc_gc, map->pm_map[pin], en);
+
+ if (sc->sc_gc->gp_intr_disestablish != NULL && ih != NULL)
+ (*sc->sc_gc->gp_intr_disestablish)(sc->sc_gc->gp_cookie, ih);
}
-int
-gpio_pin_caps(void *gpio, struct gpio_pinmap *map, int pin)
+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];
- return sc->sc_pins[map->pm_map[pin]].pin_caps;
+ 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
Index: src/sys/dev/gpio/gpiovar.h
diff -u src/sys/dev/gpio/gpiovar.h:1.17 src/sys/dev/gpio/gpiovar.h:1.18
--- src/sys/dev/gpio/gpiovar.h:1.17 Thu Jul 6 10:43:06 2017
+++ src/sys/dev/gpio/gpiovar.h Sat May 19 13:59:06 2018
@@ -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 @@ typedef struct gpio_chipset_tag {
int (*gp_pin_read)(void *, int);
void (*gp_pin_write)(void *, int, int);
void (*gp_pin_ctl)(void *, int, int);
- void (*gp_pin_irqen)(void *, int, bool);
+
+ void * (*gp_intr_establish)(void *, int, int, int,
+ int (*)(void *), void *);
+ void (*gp_intr_disestablish)(void *, void *);
+ bool (*gp_intr_str)(void *, int, int, char *, size_t);
} *gpio_chipset_tag_t;
/* GPIO pin description */
@@ -42,9 +46,8 @@ typedef struct gpio_pin {
int pin_state; /* current state */
int pin_mapped; /* is mapped */
gpio_chipset_tag_t pin_gc; /* reference the controller */
- void (*pin_callback)(void *); /* irq callback */
- void * pin_callback_arg; /* callback arg */
char pin_defname[GPIOMAXNAME]; /* default name */
+ int pin_intrcaps; /* interrupt capabilities */
} gpio_pin_t;
/* Attach GPIO framework to the controller */
@@ -67,8 +70,6 @@ int gpiobus_print(void *, const char *);
((gc)->gp_pin_write((gc)->gp_cookie, (pin), (value)))
#define gpiobus_pin_ctl(gc, pin, flags) \
((gc)->gp_pin_ctl((gc)->gp_cookie, (pin), (flags)))
-#define gpiobus_pin_irqen(gc, pin, en) \
- ((gc)->gp_pin_irqen((gc)->gp_cookie, (pin), (en)))
/* Attach devices connected to the GPIO pins */
struct gpio_attach_args {
@@ -76,7 +77,7 @@ struct gpio_attach_args {
int ga_offset;
uint32_t ga_mask;
char *ga_dvname;
- uint32_t ga_flags;
+ uint32_t ga_flags; /* driver-specific flags */
};
/* GPIO pin map */
@@ -103,17 +104,22 @@ int gpio_pin_map(void *, int, uint32_t,
void gpio_pin_unmap(void *, struct gpio_pinmap *);
int gpio_pin_read(void *, struct gpio_pinmap *, int);
void gpio_pin_write(void *, struct gpio_pinmap *, int, int);
-void gpio_pin_ctl(void *, struct gpio_pinmap *, int, int);
-int gpio_pin_ctl_intr(void *, struct gpio_pinmap *, int, int,
- int, void (*)(void *), void *);
-void gpio_pin_irqen(void *, struct gpio_pinmap *, int, bool);
int gpio_pin_caps(void *, struct gpio_pinmap *, int);
+int gpio_pin_get_conf(void *, struct gpio_pinmap *, int);
+bool gpio_pin_set_conf(void *, struct gpio_pinmap *, int, int);
+void gpio_pin_ctl(void *, struct gpio_pinmap *, int, int);
+int gpio_pin_intrcaps(void *, struct gpio_pinmap *, int);
+bool gpio_pin_irqmode_issupported(void *, struct gpio_pinmap *, int, int);
int gpio_pin_wait(void *, int);
int gpio_npins(uint32_t);
+void * gpio_intr_establish(void *, struct gpio_pinmap *, int, int, int,
+ int (*)(void *), void *);
+void gpio_intr_disestablish(void *, void *);
+bool gpio_intr_str(void *, struct gpio_pinmap *, int, int,
+ char *, size_t);
+
int gpio_lock(void *);
void gpio_unlock(void *);
-void gpio_intr(device_t, u_int32_t);
-
#endif /* !_DEV_GPIO_GPIOVAR_H_ */
Index: src/sys/sys/gpio.h
diff -u src/sys/sys/gpio.h:1.15 src/sys/sys/gpio.h:1.16
--- src/sys/sys/gpio.h:1.15 Sat Nov 21 09:06:03 2015
+++ src/sys/sys/gpio.h Sat May 19 13:59:06 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: gpio.h,v 1.15 2015/11/21 09:06:03 mlelstv Exp $ */
+/* $NetBSD: gpio.h,v 1.16 2018/05/19 13:59:06 thorpej Exp $ */
/* $OpenBSD: gpio.h,v 1.7 2008/11/26 14:51:20 mbalmer Exp $ */
/*
* Copyright (c) 2009, 2011 Marc Balmer <[email protected]>
@@ -52,9 +52,32 @@
#define GPIO_PIN_ALT5 0x00200000 /* alternate function 5 */
#define GPIO_PIN_ALT6 0x00400000 /* alternate function 6 */
#define GPIO_PIN_ALT7 0x00800000 /* alternate function 7 */
-#define GPIO_PIN_EVENTS 0x10000000 /* deliver events */
-#define GPIO_PIN_LEVEL 0x20000000 /* interrupt on level/edge */
-#define GPIO_PIN_FALLING 0x40000000 /* interrupt on falling/rising */
+
+#define GPIO_PIN_HWCAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
+ GPIO_PIN_INOUT | GPIO_PIN_OPENDRAIN | \
+ GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE | \
+ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | \
+ GPIO_PIN_PULSATE | GPIO_PIN_ALT0 | \
+ GPIO_PIN_ALT1 | GPIO_PIN_ALT2 | \
+ GPIO_PIN_ALT3 | GPIO_PIN_ALT4 | \
+ GPIO_PIN_ALT5 | GPIO_PIN_ALT6 | \
+ GPIO_PIN_ALT7)
+
+/* GPIO interrupt flags */
+#define GPIO_INTR_POS_EDGE 0x00000001 /* interrupt on rising edge */
+#define GPIO_INTR_NEG_EDGE 0x00000002 /* interrupt on falling edge */
+#define GPIO_INTR_DOUBLE_EDGE 0x00000004 /* interrupt on both edges */
+#define GPIO_INTR_HIGH_LEVEL 0x00000008 /* interrupt on high level */
+#define GPIO_INTR_LOW_LEVEL 0x00000010 /* interrupt on low level */
+#define GPIO_INTR_MPSAFE 0x80000000 /* MP-safe handling */
+
+#define GPIO_INTR_EDGE_MASK (GPIO_INTR_POS_EDGE | \
+ GPIO_INTR_NEG_EDGE | \
+ GPIO_INTR_DOUBLE_EDGE)
+#define GPIO_INTR_LEVEL_MASK (GPIO_INTR_HIGH_LEVEL | \
+ GPIO_INTR_LOW_LEVEL)
+#define GPIO_INTR_MODE_MASK (GPIO_INTR_EDGE_MASK | \
+ GPIO_INTR_LEVEL_MASK)
/* GPIO controller description */
struct gpio_info {
Index: src/usr.sbin/gpioctl/gpioctl.c
diff -u src/usr.sbin/gpioctl/gpioctl.c:1.23 src/usr.sbin/gpioctl/gpioctl.c:1.24
--- src/usr.sbin/gpioctl/gpioctl.c:1.23 Tue Apr 5 10:58:04 2016
+++ src/usr.sbin/gpioctl/gpioctl.c Sat May 19 13:59:07 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: gpioctl.c,v 1.23 2016/04/05 10:58:04 bouyer Exp $ */
+/* $NetBSD: gpioctl.c,v 1.24 2018/05/19 13:59:07 thorpej Exp $ */
/*
* Copyright (c) 2008, 2010, 2011, 2013 Marc Balmer <[email protected]>
@@ -72,9 +72,6 @@ static const struct bitstr {
{ GPIO_PIN_ALT5, "alt5" },
{ GPIO_PIN_ALT6, "alt6" },
{ GPIO_PIN_ALT7, "alt7" },
- { GPIO_PIN_EVENTS, "events" },
- { GPIO_PIN_LEVEL, "level" },
- { GPIO_PIN_FALLING, "falling" },
{ GPIO_PIN_USER, "user" },
{ 0, NULL },
};