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 <gra...@openbsd.org>
@@ -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 <m...@msys.ch>
@@ -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 <mbal...@netbsd.org>
@@ -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 },
 };

Reply via email to