Index: sys/conf/files =================================================================== --- sys/conf/files (revision 278919) +++ sys/conf/files (working copy) @@ -1422,6 +1422,7 @@ dev/gpio/gpioc.c optional gpio \ dependency "gpio_if.h" dev/gpio/gpioiic.c optional gpioiic +dev/gpio/gpiointr.c optional gpiointr dev/gpio/gpioled.c optional gpioled dev/gpio/gpio_if.m optional gpio dev/gpio/gpiobus_if.m optional gpio --- /dev/null 2015-02-25 10:22:00.105505000 -0300 +++ sys/dev/gpio/gpiointr.c 2015-02-25 10:27:55.145072000 -0300 @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2013-2015 Luiz Otavio O Souza + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef FDT +#include +#include +#include +#endif + +#include +#include + +#include "gpiobus_if.h" + +#define GPIOINTR_PIN 0 + +static MALLOC_DEFINE(M_GPIOINTR, "gpiointr", "gpiointr memory"); + +static void gpiointr_intr(void *); + +struct gpiointr_softc +{ + void *sc_intrhand; + device_t sc_dev; + device_t sc_busdev; + struct mtx sc_mtx; + struct resource *sc_irq_res; + LIST_HEAD(intr_listhead, gpio_intr) sc_intrs; +}; + +static int +gpiointr_probe(device_t dev) +{ + +#ifdef FDT + if (!ofw_bus_is_compatible(dev, "freebsd,gpiointr")) + return (ENXIO); +#endif + device_set_desc(dev, "Generic GPIO interrupt handler"); + + return (0); +} + +static int +gpiointr_attach(device_t dev) +{ + char opt[32]; + int pol, rid, type; + phandle_t node; + struct gpiobus_ivar *devi; + struct gpiointr_softc *sc; + uint32_t caps, flags; + + sc = device_get_softc(dev); + sc->sc_busdev = device_get_parent(dev); + sc->sc_dev = dev; + + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (!sc->sc_irq_res) { + device_printf(dev, "cannot allocate the interrupt resource.\n"); + return (ENXIO); + } + /* Hook up our interrupt handler. */ + if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, gpiointr_intr, sc, &sc->sc_intrhand)) { + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); + device_printf(dev, "cannot setup the interrupt handler.\n"); + return (ENXIO); + } + + LIST_INIT(&sc->sc_intrs); + mtx_init(&sc->sc_mtx, "gpiointr", "gpio", MTX_DEF); + + /* Read our settings. */ + /* XXX - implement hints knobs for non-FDT systems. */ + node = ofw_bus_get_node(sc->sc_dev); + type = INTR_TRIGGER_LEVEL; + memset(opt, 0, sizeof(opt)); + if (OF_getprop(node, "interrupt-type", &opt, sizeof(opt) - 1) > 0) { + if (strcasecmp(opt, "level") == 0) + type = INTR_TRIGGER_LEVEL; + else if (strcasecmp(opt, "edge") == 0) + type = INTR_TRIGGER_EDGE; + } + pol = INTR_POLARITY_LOW; + memset(opt, 0, sizeof(opt)); + if (OF_getprop(node, "interrupt-polarity", &opt, sizeof(opt) - 1) > 0) { + if (strcasecmp(opt, "low") == 0) + pol = INTR_POLARITY_LOW; + else if (strcasecmp(opt, "high") == 0) + pol = INTR_POLARITY_HIGH; + } + flags = GPIO_PIN_INPUT; + memset(opt, 0, sizeof(opt)); + if (OF_getprop(node, "pin-flags", &opt, sizeof(opt) - 1) > 0) { + if (strcasecmp(opt, "pulldown") == 0) + flags |= GPIO_PIN_PULLDOWN; + else if (strcasecmp(opt, "pullup") == 0) + flags |= GPIO_PIN_PULLUP; + } + + devi = GPIOBUS_IVAR(dev); + GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev, GPIOBUS_WAIT); + /* + * Set the pin as input. The pull-up/down is optional, setting the + * pin as an input is not. + */ + GPIOBUS_PIN_GETCAPS(sc->sc_busdev, sc->sc_dev, GPIOINTR_PIN, &caps); + if ((caps & GPIO_PIN_INPUT) == 0) { + GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev); + device_printf(dev, "pin %d cannot be used as an input.\n", + devi->pins[GPIOINTR_PIN]); + return (ENXIO); + } + GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOINTR_PIN, flags); + /* Configure the interrupt type and polarity. */ + BUS_CONFIG_INTR(dev, devi->pins[GPIOINTR_PIN], type, pol); + GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev); + + return (0); +} + +static int +gpiointr_detach(device_t dev) +{ + struct gpio_intr *intr, *tmp; + struct gpiointr_softc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->sc_mtx); + LIST_FOREACH_SAFE(intr, &sc->sc_intrs, intr_entry, tmp) { + gpiointr_deregister_interrupt(dev, intr); + } + mtx_unlock(&sc->sc_mtx); + mtx_destroy(&sc->sc_mtx); + if (sc->sc_intrhand) + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); + if (sc->sc_irq_res) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); + + return (bus_generic_detach(dev)); +} + +static void +gpiointr_intr(void *arg) +{ + struct gpio_intr *intr; + struct gpiobus_ivar *devi; + struct gpiointr_softc *sc; + + sc = (struct gpiointr_softc *)arg; + /* Run the registered interrupt handlers. */ + mtx_lock(&sc->sc_mtx); + LIST_FOREACH(intr, &sc->sc_intrs, intr_entry) + intr->fn(intr->arg); + mtx_unlock(&sc->sc_mtx); + devi = GPIOBUS_IVAR(sc->sc_dev); +printf("%s: hey! intr: %d\n", __func__, devi->pins[GPIOINTR_PIN]); +} + +void +gpiointr_register_interrupt(device_t dev, void (*fn)(void *), void *arg) +{ + struct gpio_intr *intr; + struct gpiointr_softc *sc; + + sc = device_get_softc(dev); + intr = malloc(sizeof(*intr), M_GPIOINTR, M_WAITOK | M_ZERO); + intr->fn = fn; + intr->arg = arg; + mtx_lock(&sc->sc_mtx); + LIST_INSERT_HEAD(&sc->sc_intrs, intr, intr_entry); + mtx_unlock(&sc->sc_mtx); +} + +void +gpiointr_deregister_interrupt(device_t dev, struct gpio_intr *intr) +{ + struct gpiointr_softc *sc; + + sc = device_get_softc(dev); + mtx_lock(&sc->sc_mtx); + LIST_REMOVE(intr, intr_entry); + mtx_unlock(&sc->sc_mtx); + free(intr, M_GPIOINTR); +} + +static device_method_t gpiointr_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, gpiointr_probe), + DEVMETHOD(device_attach, gpiointr_attach), + DEVMETHOD(device_detach, gpiointr_detach), + + DEVMETHOD_END +}; + +static devclass_t gpiointr_devclass; + +static driver_t gpiointr_driver = { + "gpiointr", + gpiointr_methods, + sizeof(struct gpiointr_softc), +}; + +DRIVER_MODULE(gpiointr, gpiobus, gpiointr_driver, gpiointr_devclass, 0, 0); --- /dev/null 2015-02-25 10:22:00.105505000 -0300 +++ sys/dev/gpio/gpiointrvar.h 2015-02-25 10:28:13.543073000 -0300 @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2013-2015 Luiz Otavio O Souza + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _GPIOINTRVAR_H_ +#define _GPIOINTRVAR_H_ + +struct gpio_intr +{ + LIST_ENTRY(gpio_intr) intr_entry; + void (*fn)(void *); + void *arg; +}; + +void gpiointr_register_interrupt(device_t, void (*)(void *), void *); +void gpiointr_deregister_interrupt(device_t, struct gpio_intr *); + +#endif /* _GPIOINTRVAR_H_ */