Add an example "gpioirq" driver that demonstrates interrupts on GPIO

pins.  Will be enhanced in the future to support sending events to
user space on edge-triggered interrupt events.

Based on initial work by Brad Spencer.
PR kern/51676
This commit is contained in:
thorpej 2018-05-19 14:15:39 +00:00
parent 144dde66b8
commit ce735ef0b5
9 changed files with 364 additions and 7 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.1586 2018/05/15 00:54:01 nat Exp $
# $NetBSD: mi,v 1.1587 2018/05/19 14:15:39 thorpej Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@ -1123,6 +1123,7 @@
./usr/share/man/cat4/gpioiic.0 man-sys-catman .cat
./usr/share/man/cat4/gpiolock.0 man-sys-catman .cat
./usr/share/man/cat4/gpioow.0 man-sys-catman .cat
./usr/share/man/cat4/gpioirq.0 man-sys-catman .cat
./usr/share/man/cat4/gpiopwm.0 man-sys-catman .cat
./usr/share/man/cat4/gpiosim.0 man-sys-catman .cat
./usr/share/man/cat4/gre.0 man-sys-catman .cat
@ -4263,6 +4264,7 @@
./usr/share/man/html4/gpioiic.html man-sys-htmlman html
./usr/share/man/html4/gpiolock.html man-sys-htmlman html
./usr/share/man/html4/gpioow.html man-sys-htmlman html
./usr/share/man/html4/gpioirq.html man-sys-htmlman html
./usr/share/man/html4/gpiopwm.html man-sys-htmlman html
./usr/share/man/html4/gpiosim.html man-sys-htmlman html
./usr/share/man/html4/gre.html man-sys-htmlman html
@ -7177,6 +7179,7 @@
./usr/share/man/man4/gpioiic.4 man-sys-man .man
./usr/share/man/man4/gpiolock.4 man-sys-man .man
./usr/share/man/man4/gpioow.4 man-sys-man .man
./usr/share/man/man4/gpioirq.4 man-sys-man .man
./usr/share/man/man4/gpiopwm.4 man-sys-man .man
./usr/share/man/man4/gpiosim.4 man-sys-man .man
./usr/share/man/man4/gre.4 man-sys-man .man

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.114 2018/01/09 03:31:14 christos Exp $
# $NetBSD: mi,v 1.115 2018/05/19 14:15:39 thorpej Exp $
#
# Note: don't delete entries from here - mark them as "obsolete" instead.
#
@ -132,6 +132,8 @@
./@MODULEDIR@/gpio/gpio.kmod base-kernel-modules kmod
./@MODULEDIR@/gpioiic base-kernel-modules kmod
./@MODULEDIR@/gpioiic/gpioiic.kmod base-kernel-modules kmod
./@MODULEDIR@/gpioirq base-kernel-modules kmod
./@MODULEDIR@/gpioirq/gpioirq.kmod base-kernel-modules kmod
./@MODULEDIR@/gpioow base-kernel-modules kmod
./@MODULEDIR@/gpioow/gpioow.kmod base-kernel-modules kmod
./@MODULEDIR@/gpiosim base-kernel-modules kmod

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.652 2018/05/09 08:22:53 msaitoh Exp $
# $NetBSD: Makefile,v 1.653 2018/05/19 14:15:39 thorpej Exp $
# @(#)Makefile 8.1 (Berkeley) 6/18/93
MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
@ -26,8 +26,8 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
fast_ipsec.4 fd.4 filemon.4 finsio.4 flash.4 fpa.4 fms.4 fss.4 \
fujbp.4 full.4 fxp.4 \
gcscaudio.4 gem.4 genfb.4 gentbi.4 geodeide.4 \
glxtphy.4 gpib.4 gpio.4 gpiolock.4 gpiopwm.4 gpiosim.4 gre.4 \
gphyter.4 gsip.4 \
glxtphy.4 gpib.4 gpio.4 gpioirq.4 gpiolock.4 gpiopwm.4 \
gpiosim.4 gre.4 gphyter.4 gsip.4 \
hdaudio.4 hifn.4 hme.4 hpacel.4 hpqlb.4 hptide.4 hythygtemp.4 \
ibmcd.4 ibmhawk.4 ichsmb.4 icmp.4 icp.4 icsphy.4 iee.4 ieee80211.4 \
ifmedia.4 igmafb.4 igphy.4 igsfb.4 iha.4 ihidev.4 ihphy.4 iic.4 ims.4 \

97
share/man/man4/gpioirq.4 Normal file
View File

@ -0,0 +1,97 @@
.\" $NetBSD: gpioirq.4,v 1.1 2018/05/19 14:15:39 thorpej Exp $
.\"
.\" Copyright (c) 2016 Brad Spencer <brad@anduin.eldar.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd May 11, 2018
.Dt GPIOIRQ 4
.Os
.Sh NAME
.Nm gpioirq
.Nd Install an interrupt handler on a GPIO pin
.Sh SYNOPSIS
.Cd "gpioirq* at gpio? offset 0 mask 0x1 flag 0x00"
.Sh DESCRIPTION
The
.Nm
driver attaches an interrupt handler to a single GPIO pin.
.Pp
The pin number is specified in the kernel configuration file with the
.Ar offset
locator.
The
.Ar mask
locator should always be 0x1.
.Pp
The
.Ar flag
locator specifies the interrupt mode to use:
.Bl -tag -width "XXXXXXXX"
.It Dv 0x01
Interrupt on the positive
.Pq rising
edge of the pin.
.It Dv 0x02
Interrupt on the negative
.Pq falling
edge of the pin.
.It Dv 0x04
Interrupt on both edges of the pin.
.It Dv 0x08
Assert the intrerrupt as long as the pin is high.
.It Dv 0x10
Assert the interrupt as long as the pin is low.
.El
.Pp
Note that the interrupts modes are mutually-exclusive, and exactly one
interrupt mode must be specified.
These flags correspond to the
.Dv GPIO_INTR
mode bits defined in
.Pa sys/gpio.h .
In addition to the interrupt mode, setting
.Dv 0x1000
in
.Ar flags
will enable the printing of a message to the console whenever the
interrupt handler is called.
.Pp
The
.Ar offset ,
.Ar mask ,
and
.Ar flag
locators can also be specified when
.Nm
is attached at runtime using the
.Dv GPIOATTACH
.Xr ioctl 2
on the
.Xr gpio 4
device.
.Sh SEE ALSO
.Xr gpio 4 ,
.Xr drvctl 8 ,
.Xr gpioctl 8
.Sh HISTORY
The
.Nm
driver first appeared in
.Nx 9.0 .
.Sh AUTHORS
.An -nosplit
The
.Nm
driver was written by
.An Brad Spencer Aq Mt brad@anduin.eldar.org

View File

@ -1,4 +1,4 @@
# $NetBSD: files.gpio,v 1.14 2015/10/15 09:07:49 jmcneill Exp $
# $NetBSD: files.gpio,v 1.15 2018/05/19 14:15:39 thorpej Exp $
define gpio {[offset = -1], [mask = 0], [flag = 0]}
@ -39,3 +39,8 @@ file dev/gpio/gpiorfkill.c gpiorfkill
device gpiobutton: gpiobus, sysmon_power, sysmon_taskq
attach gpiobutton at gpio
file dev/gpio/gpiobutton.c gpiobutton
# GPIO interrupt example
device gpioirq: gpiobus
attach gpioirq at gpio
file dev/gpio/gpioirq.c gpioirq

228
sys/dev/gpio/gpioirq.c Normal file
View File

@ -0,0 +1,228 @@
/* $NetBSD: gpioirq.c,v 1.1 2018/05/19 14:15:39 thorpej Exp $ */
/*
* Copyright (c) 2016 Brad Spencer <brad@anduin.eldar.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: gpioirq.c,v 1.1 2018/05/19 14:15:39 thorpej Exp $");
/*
* Example GPIO driver that uses interrupts.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/gpio.h>
#include <sys/module.h>
#include <dev/gpio/gpiovar.h>
#define GPIOIRQ_NPINS 1
struct gpioirq_softc {
device_t sc_dev;
void * sc_gpio;
struct gpio_pinmap sc_map;
int _map[GPIOIRQ_NPINS];
char sc_intrstr[128];
void * sc_ih;
kmutex_t sc_lock;
bool sc_verbose;
bool sc_functional;
};
#define GPIOIRQ_FLAGS_IRQMODE GPIO_INTR_MODE_MASK
#define GPIOIRQ_FLAGS_VERBOSE 0x1000
static int gpioirq_match(device_t, cfdata_t, void *);
static void gpioirq_attach(device_t, device_t, void *);
static int gpioirq_detach(device_t, int);
static int gpioirq_activate(device_t, enum devact);
static int gpioirq_intr(void *);
CFATTACH_DECL_NEW(gpioirq, sizeof(struct gpioirq_softc),
gpioirq_match, gpioirq_attach,
gpioirq_detach, gpioirq_activate);
extern struct cfdriver gpioirq_cd;
static int
gpioirq_match(device_t parent, cfdata_t cf, void *aux)
{
struct gpio_attach_args *ga = aux;
int npins;
if (strcmp(ga->ga_dvname, cf->cf_name))
return (0);
if (ga->ga_offset == -1)
return (0);
npins = gpio_npins(ga->ga_mask);
if (npins > 1)
return (0);
return (1);
}
static void
gpioirq_attach(device_t parent, device_t self, void *aux)
{
struct gpioirq_softc *sc = device_private(self);
struct gpio_attach_args *ga = aux;
int npins = gpio_npins(ga->ga_mask);
int irqmode, flags;
sc->sc_dev = self;
/* Map pins */
sc->sc_gpio = ga->ga_gpio;
sc->sc_map.pm_map = sc->_map;
/* We always map just 1 pin. */
if (gpio_pin_map(sc->sc_gpio, ga->ga_offset,
npins ? ga->ga_mask : 0x1, &sc->sc_map)) {
aprint_error(": can't map pins\n");
return;
}
aprint_normal("\n");
if (ga->ga_flags & GPIOIRQ_FLAGS_VERBOSE)
sc->sc_verbose = true;
irqmode = ga->ga_flags & GPIOIRQ_FLAGS_IRQMODE;
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
if (!gpio_intr_str(sc->sc_gpio, &sc->sc_map, 0, irqmode,
sc->sc_intrstr, sizeof(sc->sc_intrstr))) {
aprint_error_dev(self, "failed to decode interrupt\n");
return;
}
if (!gpio_pin_irqmode_issupported(sc->sc_gpio, &sc->sc_map, 0,
irqmode)) {
aprint_error_dev(self,
"irqmode not supported: %s\n", sc->sc_intrstr);
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
return;
}
flags = gpio_pin_get_conf(sc->sc_gpio, &sc->sc_map, 0);
flags = (flags & ~(GPIO_PIN_OUTPUT|GPIO_PIN_INOUT)) |
GPIO_PIN_INPUT;
if (!gpio_pin_set_conf(sc->sc_gpio, &sc->sc_map, 0, flags)) {
aprint_error_dev(sc->sc_dev, "pin not capable of input\n");
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
return;
}
sc->sc_ih = gpio_intr_establish(sc->sc_gpio, &sc->sc_map, 0, IPL_VM,
irqmode | GPIO_INTR_MPSAFE,
gpioirq_intr, sc);
if (sc->sc_ih == NULL) {
aprint_error_dev(self,
"unable to establish interrupt on %s\n", sc->sc_intrstr);
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
return;
}
aprint_normal_dev(self, "interrupting on %s\n", sc->sc_intrstr);
sc->sc_functional = true;
}
int
gpioirq_intr(void *arg)
{
struct gpioirq_softc *sc = arg;
int val;
mutex_enter(&sc->sc_lock);
val = gpio_pin_read(sc->sc_gpio, &sc->sc_map, 0);
if (sc->sc_verbose)
printf("%s: interrupt on %s --> %d\n",
device_xname(sc->sc_dev), sc->sc_intrstr, val);
mutex_exit(&sc->sc_lock);
return (1);
}
int
gpioirq_detach(device_t self, int flags)
{
struct gpioirq_softc *sc = device_private(self);
/* Clear the handler and disable the interrupt. */
gpio_intr_disestablish(sc->sc_gpio, sc->sc_ih);
/* Release the pin. */
gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
return (0);
}
int
gpioirq_activate(device_t self, enum devact act)
{
switch (act) {
case DVACT_DEACTIVATE:
/* We don't really need to do anything. */
return (0);
default:
return (EOPNOTSUPP);
}
}
MODULE(MODULE_CLASS_DRIVER, gpioirq, "gpio");
#ifdef _MODULE
#include "ioconf.c"
#endif
static int
gpioirq_modcmd(modcmd_t cmd, void *opaque)
{
int error = 0;
switch (cmd) {
case MODULE_CMD_INIT:
#ifdef _MODULE
error = config_init_component(cfdriver_ioconf_gpioirq,
cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq);
if (error)
aprint_error("%s: unable to init component\n",
gpioirq_cd.cd_name);
#endif
break;
case MODULE_CMD_FINI:
#ifdef _MODULE
config_fini_component(cfdriver_ioconf_gpioirq,
cfattach_ioconf_gpioirq, cfdata_ioconf_gpioirq);
#endif
break;
default:
error = ENOTTY;
}
return (error);
}

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.202 2018/02/26 07:29:24 pgoyette Exp $
# $NetBSD: Makefile,v 1.203 2018/05/19 14:15:40 thorpej Exp $
.include <bsd.own.mk>
@ -47,6 +47,7 @@ SUBDIR+= gpio
SUBDIR+= gpioiic
SUBDIR+= gpioow
SUBDIR+= gpiosim
SUBDIR+= gpioirq
SUBDIR+= hfs
SUBDIR+= hythygtemp
SUBDIR+= si70xxtemp

View File

@ -0,0 +1,13 @@
# $NetBSD: Makefile,v 1.1 2018/05/19 14:15:40 thorpej Exp $
.include "../Makefile.inc"
.PATH: ${S}/dev/gpio
KMOD= gpioirq
IOCONF= gpioirq.ioconf
SRCS= gpioirq.c
CPPFLAGS+= -I${S}/gpio
.include <bsd.kmodule.mk>

View File

@ -0,0 +1,8 @@
# $NetBSD: gpioirq.ioconf,v 1.1 2018/05/19 14:15:40 thorpej Exp $
ioconf gpioirq
include "conf/files"
pseudo-root gpio*
gpioirq* at gpio? offset ? mask ? flag ?