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 }, };