[RFC] GPIO interrupt support
Luiz Otavio O Souza
loos.br at gmail.com
Wed May 14 19:30:59 UTC 2014
Hi,
I've been working on the attached patches which adds the GPIO interrupt support.
gpio-intr.diff has the bus changes for FDT and non-FDT systems.
ar71xx_gpio-intr.diff has the changes to AR71xx GPIO driver.
bcm2835_gpio-intr.diff has the changes to BCM2835 (RPi) GPIO driver.
ti_gpio-intr.diff has the changes for AM335x (Beaglebone), OMAP3 and
OMAP4 (panda board).
The other attached file (gpio-intr-kqueue.c) is an example program
i've been using to check for interrupts from userland.
Userland is notified about GPIO interrupts by a kqueue event.
The changes have been tested on BBB, RPi and RSPRO.
The manual page changes are still to be done (but it will surely be done).
Comments ?
Regards,
Luiz
-------------- next part --------------
Index: sys/mips/atheros/ar71xx_gpio.c
===================================================================
--- sys/mips/atheros/ar71xx_gpio.c (revision 265817)
+++ sys/mips/atheros/ar71xx_gpio.c (working copy)
@@ -44,6 +44,7 @@
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/gpio.h>
+#include <sys/interrupt.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -74,7 +75,8 @@
static int ar71xx_gpio_attach(device_t dev);
static int ar71xx_gpio_detach(device_t dev);
static int ar71xx_gpio_filter(void *arg);
-static void ar71xx_gpio_intr(void *arg);
+static int ar71xx_gpio_config_intr(device_t dev, int irq,
+ enum intr_trigger trig, enum intr_polarity pol);
/*
* GPIO interface
@@ -89,6 +91,8 @@
static int ar71xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val);
static int ar71xx_gpio_pin_toggle(device_t dev, uint32_t pin);
+static struct ar71xx_gpio_softc *ar71xx_gpio_sc = NULL;
+
static void
ar71xx_gpio_function_enable(struct ar71xx_gpio_softc *sc, uint32_t mask)
{
@@ -321,20 +325,25 @@
static int
ar71xx_gpio_filter(void *arg)
{
+ int irq;
+ struct ar71xx_gpio_softc *sc;
+ struct intr_event *event;
+ uint32_t status;
- /* TODO: something useful */
- return (FILTER_STRAY);
-}
+ sc = (struct ar71xx_gpio_softc *)arg;
+ status = GPIO_READ(sc, AR71XX_GPIO_INT_PENDING);
+ for (irq = 0; irq <= sc->gpio_maxpin; irq++) {
+ if (status & (1U << irq)) {
+ event = sc->gpio_events[irq];
+ if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers))
+ intr_event_handle(event, NULL);
+ else
+ device_printf(sc->dev, "Stray IRQ %d\n", irq);
+ }
+ }
-
-static void
-ar71xx_gpio_intr(void *arg)
-{
- struct ar71xx_gpio_softc *sc = arg;
- GPIO_LOCK(sc);
- /* TODO: something useful */
- GPIO_UNLOCK(sc);
+ return (FILTER_STRAY);
}
static int
@@ -348,9 +357,9 @@
static int
ar71xx_gpio_attach(device_t dev)
{
- struct ar71xx_gpio_softc *sc = device_get_softc(dev);
+ struct ar71xx_gpio_softc *sc;
int error = 0;
- int i, j, maxpin;
+ int i, j;
int mask, pinon;
uint32_t oe;
@@ -357,8 +366,13 @@
KASSERT((device_get_unit(dev) == 0),
("ar71xx_gpio: Only one gpio module supported"));
- mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+ if (ar71xx_gpio_sc != NULL)
+ return (ENXIO);
+ ar71xx_gpio_sc = sc = device_get_softc(dev);
+
+ mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), NULL, MTX_SPIN);
+
/* Map control/status registers. */
sc->gpio_mem_rid = 0;
sc->gpio_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
@@ -378,7 +392,7 @@
}
if ((bus_setup_intr(dev, sc->gpio_irq_res, INTR_TYPE_MISC,
- ar71xx_gpio_filter, ar71xx_gpio_intr, sc, &sc->gpio_ih))) {
+ ar71xx_gpio_filter, NULL, sc, &sc->gpio_ih))) {
device_printf(dev,
"WARNING: unable to register interrupt handler\n");
return (ENXIO);
@@ -400,10 +414,26 @@
}
/* Disable interrupts for all pins. */
+ GPIO_WRITE(sc, AR71XX_GPIO_INT, 0);
GPIO_WRITE(sc, AR71XX_GPIO_INT_MASK, 0);
+ /* Initialize the interrupt event data. */
+ (void) ar71xx_gpio_pin_max(dev, &sc->gpio_maxpin);
+ sc->gpio_events = malloc(sizeof(struct intr_event *) * sc->gpio_maxpin,
+ M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->gpio_irq_trigger = malloc(sizeof(enum intr_trigger) *
+ sc->gpio_maxpin, M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->gpio_irq_polarity = malloc(sizeof(enum intr_polarity) *
+ sc->gpio_maxpin, M_DEVBUF, M_WAITOK | M_ZERO);
+ /* The default is active-low interrupts. */
+ for (i = 0; i <= sc->gpio_maxpin; i++) {
+ sc->gpio_irq_trigger[i] = INTR_TRIGGER_LEVEL;
+ sc->gpio_irq_polarity[i] = INTR_POLARITY_LOW;
+ ar71xx_gpio_config_intr(dev, i, sc->gpio_irq_trigger[i],
+ sc->gpio_irq_polarity[i]);
+ }
+
/* Initialise all pins specified in the mask, up to the pin count */
- (void) ar71xx_gpio_pin_max(dev, &maxpin);
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
"pinmask", &mask) != 0)
mask = 0;
@@ -411,7 +441,7 @@
"pinon", &pinon) != 0)
pinon = 0;
device_printf(dev, "gpio pinmask=0x%x\n", mask);
- for (j = 0; j <= maxpin; j++) {
+ for (j = 0; j <= sc->gpio_maxpin; j++) {
if ((mask & (1 << j)) == 0)
continue;
sc->gpio_npins++;
@@ -420,7 +450,7 @@
oe = GPIO_READ(sc, AR71XX_GPIO_OE);
sc->gpio_pins = malloc(sizeof(*sc->gpio_pins) * sc->gpio_npins,
M_DEVBUF, M_WAITOK | M_ZERO);
- for (i = 0, j = 0; j <= maxpin; j++) {
+ for (i = 0, j = 0; j <= sc->gpio_maxpin; j++) {
if ((mask & (1 << j)) == 0)
continue;
snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME,
@@ -459,12 +489,204 @@
bus_release_resource(dev, SYS_RES_MEMORY, sc->gpio_mem_rid,
sc->gpio_mem_res);
- free(sc->gpio_pins, M_DEVBUF);
+ if (sc->gpio_irq_polarity != NULL) {
+ free(sc->gpio_irq_polarity, M_DEVBUF);
+ sc->gpio_irq_polarity = NULL;
+ }
+ if (sc->gpio_irq_trigger != NULL) {
+ free(sc->gpio_irq_trigger, M_DEVBUF);
+ sc->gpio_irq_trigger = NULL;
+ }
+ if (sc->gpio_events != NULL) {
+ free(sc->gpio_events, M_DEVBUF);
+ sc->gpio_events = NULL;
+ }
+ if (sc->gpio_pins != NULL) {
+ free(sc->gpio_pins, M_DEVBUF);
+ sc->gpio_pins = NULL;
+ }
mtx_destroy(&sc->gpio_mtx);
return(0);
}
+static void
+ar71xx_gpio_mask_irq(void *source)
+{
+ int irq;
+ uint32_t mask;
+
+ irq = (int)source;
+ if (irq > ar71xx_gpio_sc->gpio_maxpin)
+ return;
+
+ mask = 1U << irq;
+ GPIO_LOCK(ar71xx_gpio_sc);
+ GPIO_CLEAR_BITS(ar71xx_gpio_sc, AR71XX_GPIO_INT_MASK, mask);
+ GPIO_UNLOCK(ar71xx_gpio_sc);
+}
+
+static void
+ar71xx_gpio_unmask_irq(void *source)
+{
+ int irq;
+ uint32_t mask;
+
+ irq = (int)source;
+ if (irq > ar71xx_gpio_sc->gpio_maxpin)
+ return;
+
+ mask = 1U << irq;
+ GPIO_LOCK(ar71xx_gpio_sc);
+ GPIO_SET_BITS(ar71xx_gpio_sc, AR71XX_GPIO_INT_MASK, mask);
+ GPIO_UNLOCK(ar71xx_gpio_sc);
+}
+
+static int
+ar71xx_gpio_activate_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *res)
+{
+ int pin;
+ struct ar71xx_gpio_softc *sc;
+
+ if (type != SYS_RES_IRQ)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+
+ /* Enable the interrupt for this pin. */
+ pin = rman_get_start(res);
+ GPIO_LOCK(ar71xx_gpio_sc);
+ GPIO_SET_BITS(ar71xx_gpio_sc, AR71XX_GPIO_INT, (1U << pin));
+ GPIO_UNLOCK(ar71xx_gpio_sc);
+
+ /* Unmask the interrupt. */
+ ar71xx_gpio_unmask_irq((void *)(uintptr_t)pin);
+
+ return (0);
+}
+
+static int
+ar71xx_gpio_deactivate_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *res)
+{
+ int pin;
+ struct ar71xx_gpio_softc *sc;
+
+ if (type != SYS_RES_IRQ)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+
+ /* Mask the interrupt. */
+ pin = rman_get_start(res);
+ ar71xx_gpio_mask_irq((void *)(uintptr_t)pin);
+
+ /* Disable the interrupt for this pin. */
+ GPIO_LOCK(ar71xx_gpio_sc);
+ GPIO_CLEAR_BITS(ar71xx_gpio_sc, AR71XX_GPIO_INT, (1U << pin));
+ GPIO_UNLOCK(ar71xx_gpio_sc);
+
+ return (0);
+}
+
+static int
+ar71xx_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ struct ar71xx_gpio_softc *sc;
+ uint32_t mask, reg;
+
+ sc = device_get_softc(dev);
+ if (irq > sc->gpio_maxpin)
+ return (EINVAL);
+
+ /* There is no standard trigger or polarity. */
+ if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
+ return (EINVAL);
+
+ mask = 1UL << irq;
+
+ GPIO_LOCK(sc);
+
+ /* Set the appropriate interrupt type. */
+ reg = GPIO_READ(sc, AR71XX_GPIO_INT_TYPE);
+ reg &= ~mask;
+ if (trig == INTR_TRIGGER_LEVEL)
+ reg |= mask;
+ GPIO_WRITE(sc, AR71XX_GPIO_INT_TYPE, reg);
+
+ /* Set the appropriate interrupt polarity. */
+ reg = GPIO_READ(sc, AR71XX_GPIO_INT_POLARITY);
+ reg &= ~mask;
+ if (pol == INTR_POLARITY_HIGH)
+ reg |= mask;
+ GPIO_WRITE(sc, AR71XX_GPIO_INT_POLARITY, reg);
+
+ sc->gpio_irq_trigger[irq] = trig;
+ sc->gpio_irq_polarity[irq] = pol;
+
+ GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+ar71xx_gpio_setup_intr(device_t dev, device_t child, struct resource *ires,
+ int flags, driver_filter_t *filt, driver_intr_t *handler,
+ void *arg, void **cookiep)
+{
+ struct ar71xx_gpio_softc *sc;
+ struct intr_event *event;
+ int pin, error;
+
+ sc = device_get_softc(dev);
+ pin = rman_get_start(ires);
+
+ if (pin > sc->gpio_maxpin)
+ panic("%s: bad pin %d", __func__, pin);
+
+ event = sc->gpio_events[pin];
+ if (event == NULL) {
+ error = intr_event_create(&event, (void *)(uintptr_t)pin, 0,
+ pin, ar71xx_gpio_mask_irq, ar71xx_gpio_unmask_irq,
+ NULL, NULL, "gpio%d pin%d:", device_get_unit(dev), pin);
+
+ if (error != 0)
+ return (error);
+
+ sc->gpio_events[pin] = event;
+ }
+
+ intr_event_add_handler(event, device_get_nameunit(child), filt,
+ handler, arg, intr_priority(flags), flags, cookiep);
+
+ return (0);
+}
+
+static int
+ar71xx_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires,
+ void *cookie)
+{
+ struct ar71xx_gpio_softc *sc;
+ int pin, err;
+
+ sc = device_get_softc(dev);
+ pin = rman_get_start(ires);
+
+ if (pin > sc->gpio_maxpin)
+ panic("%s: bad pin %d", __func__, pin);
+
+ if (sc->gpio_events[pin] == NULL)
+ panic("Trying to teardown unoccupied IRQ");
+
+ err = intr_event_remove_handler(cookie);
+ if (!err)
+ sc->gpio_events[pin] = NULL;
+
+ return (err);
+}
+
static device_method_t ar71xx_gpio_methods[] = {
DEVMETHOD(device_probe, ar71xx_gpio_probe),
DEVMETHOD(device_attach, ar71xx_gpio_attach),
@@ -479,7 +701,15 @@
DEVMETHOD(gpio_pin_get, ar71xx_gpio_pin_get),
DEVMETHOD(gpio_pin_set, ar71xx_gpio_pin_set),
DEVMETHOD(gpio_pin_toggle, ar71xx_gpio_pin_toggle),
- {0, 0},
+
+ /* Bus interface */
+ DEVMETHOD(bus_activate_resource, ar71xx_gpio_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, ar71xx_gpio_deactivate_resource),
+ DEVMETHOD(bus_config_intr, ar71xx_gpio_config_intr),
+ DEVMETHOD(bus_setup_intr, ar71xx_gpio_setup_intr),
+ DEVMETHOD(bus_teardown_intr, ar71xx_gpio_teardown_intr),
+
+ DEVMETHOD_END
};
static driver_t ar71xx_gpio_driver = {
Index: sys/mips/atheros/ar71xx_gpiovar.h
===================================================================
--- sys/mips/atheros/ar71xx_gpiovar.h (revision 265817)
+++ sys/mips/atheros/ar71xx_gpiovar.h (working copy)
@@ -32,8 +32,8 @@
#ifndef __AR71XX_GPIOVAR_H__
#define __AR71XX_GPIOVAR_H__
-#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->gpio_mtx)
-#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->gpio_mtx)
+#define GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->gpio_mtx)
+#define GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->gpio_mtx)
#define GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->gpio_mtx, MA_OWNED)
/*
@@ -65,6 +65,10 @@
void *gpio_ih;
int gpio_npins;
struct gpio_pin *gpio_pins;
+ int gpio_maxpin;
+ struct intr_event **gpio_events;
+ enum intr_trigger *gpio_irq_trigger;
+ enum intr_polarity *gpio_irq_polarity;
};
#endif /* __AR71XX_GPIOVAR_H__ */
-------------- next part --------------
Index: sys/arm/broadcom/bcm2835/bcm2835_gpio.c
===================================================================
--- sys/arm/broadcom/bcm2835/bcm2835_gpio.c (revision 265842)
+++ sys/arm/broadcom/bcm2835/bcm2835_gpio.c (working copy)
@@ -39,6 +39,7 @@
#include <sys/mutex.h>
#include <sys/gpio.h>
#include <sys/sysctl.h>
+#include <sys/interrupt.h>
#include <machine/bus.h>
#include <machine/cpu.h>
@@ -62,10 +63,20 @@
#define dprintf(fmt, args...)
#endif
+#define BCM_GPIO_IRQS 4
#define BCM_GPIO_PINS 54
+#define BCM_GPIO_PINS_PER_BANK 32
#define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)
+static struct resource_spec bcm_gpio_irq_spec[] = {
+ { SYS_RES_IRQ, 0, RF_ACTIVE },
+ { SYS_RES_IRQ, 1, RF_ACTIVE },
+ { SYS_RES_IRQ, 2, RF_ACTIVE },
+ { SYS_RES_IRQ, 3, RF_ACTIVE },
+ { -1, 0, 0 }
+};
+
struct bcm_gpio_sysctl {
struct bcm_gpio_softc *sc;
uint32_t pin;
@@ -75,15 +86,18 @@
device_t sc_dev;
struct mtx sc_mtx;
struct resource * sc_mem_res;
- struct resource * sc_irq_res;
+ struct resource * sc_irq_res[BCM_GPIO_IRQS];
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
- void * sc_intrhand;
+ void * sc_intrhand[BCM_GPIO_IRQS];
int sc_gpio_npins;
int sc_ro_npins;
int sc_ro_pins[BCM_GPIO_PINS];
struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS];
+ struct intr_event * sc_events[BCM_GPIO_PINS];
struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS];
+ enum intr_trigger sc_irq_trigger[BCM_GPIO_PINS];
+ enum intr_polarity sc_irq_polarity[BCM_GPIO_PINS];
};
enum bcm_gpio_pud {
@@ -96,18 +110,33 @@
#define BCM_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx)
#define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED)
-#define BCM_GPIO_GPFSEL(_bank) 0x00 + _bank * 4
-#define BCM_GPIO_GPSET(_bank) 0x1c + _bank * 4
-#define BCM_GPIO_GPCLR(_bank) 0x28 + _bank * 4
-#define BCM_GPIO_GPLEV(_bank) 0x34 + _bank * 4
-#define BCM_GPIO_GPPUD(_bank) 0x94
-#define BCM_GPIO_GPPUDCLK(_bank) 0x98 + _bank * 4
+#define BCM_GPIO_GPFSEL(_bank) (0x00 + _bank * 4) /* Function Select */
+#define BCM_GPIO_GPSET(_bank) (0x1c + _bank * 4) /* Pin Out Set */
+#define BCM_GPIO_GPCLR(_bank) (0x28 + _bank * 4) /* Pin Out Clear */
+#define BCM_GPIO_GPLEV(_bank) (0x34 + _bank * 4) /* Pin Level */
+#define BCM_GPIO_GPEDS(_bank) (0x40 + _bank * 4) /* Event Status */
+#define BCM_GPIO_GPREN(_bank) (0x4c + _bank * 4) /* Rising Edge isr */
+#define BCM_GPIO_GPFEN(_bank) (0x58 + _bank * 4) /* Falling Edge isr */
+#define BCM_GPIO_GPHEN(_bank) (0x64 + _bank * 4) /* High Level isr */
+#define BCM_GPIO_GPLEN(_bank) (0x70 + _bank * 4) /* Low Level isr */
+#define BCM_GPIO_GPAREN(_bank) (0x7c + _bank * 4) /* Async Rising Edge */
+#define BCM_GPIO_GPAFEN(_bank) (0x88 + _bank * 4) /* Async Falling Egde */
+#define BCM_GPIO_GPPUD(_bank) (0x94) /* Pin Pull up/down */
+#define BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4) /* Pin Pull up clock */
#define BCM_GPIO_WRITE(_sc, _off, _val) \
bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val)
#define BCM_GPIO_READ(_sc, _off) \
bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off)
+#define BCM_GPIO_CLEAR_BITS(_sc, _off, _bits) \
+ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits));
+#define BCM_GPIO_SET_BITS(_sc, _off, _bits) \
+ BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits);
+static struct bcm_gpio_softc *bcm_gpio_sc = NULL;
+
+static int bcm_gpio_intr(void *);
+
static int
bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin)
{
@@ -686,15 +715,50 @@
}
static int
+bcm_gpio_intr_attach(device_t dev)
+{
+ struct bcm_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < BCM_GPIO_IRQS; i++) {
+ if (bus_setup_intr(dev, sc->sc_irq_res[i], INTR_TYPE_MISC,
+ bcm_gpio_intr, NULL, sc, &sc->sc_intrhand[i]) != 0) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+bcm_gpio_intr_detach(device_t dev)
+{
+ struct bcm_gpio_softc *sc;
+ int i;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < BCM_GPIO_IRQS; i++) {
+ if (sc->sc_intrhand[i]) {
+ bus_teardown_intr(dev, sc->sc_irq_res[i],
+ sc->sc_intrhand[i]);
+ }
+ }
+}
+
+static int
bcm_gpio_attach(device_t dev)
{
- struct bcm_gpio_softc *sc = device_get_softc(dev);
+ struct bcm_gpio_softc *sc;
uint32_t func;
int i, j, rid;
phandle_t gpio;
- sc->sc_dev = dev;
+ if (bcm_gpio_sc != NULL)
+ return (ENXIO);
+ bcm_gpio_sc = sc = device_get_softc(dev);
+
mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_DEF);
rid = 0;
@@ -708,15 +772,19 @@
sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
- rid = 0;
- sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
- RF_ACTIVE);
- if (!sc->sc_irq_res) {
+ /* Request the IRQ resources. */
+ if (bus_alloc_resources(dev, bcm_gpio_irq_spec, sc->sc_irq_res)) {
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
- device_printf(dev, "cannot allocate interrupt\n");
+ device_printf(dev, "cannot allocate irq resources\n");
return (ENXIO);
}
+ /* Setup the GPIO interrupt handler. */
+ if (bcm_gpio_intr_attach(dev)) {
+ device_printf(dev, "unable to setup the gpio irq handler\n");
+ goto fail;
+ }
+
/* Find our node. */
gpio = ofw_bus_get_node(sc->sc_dev);
@@ -741,6 +809,9 @@
sc->sc_gpio_pins[i].gp_pin = j;
sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS;
sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func);
+ /* The default is active-low interrupts. */
+ sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL;
+ sc->sc_irq_polarity[i] = INTR_POLARITY_LOW;
i++;
}
sc->sc_gpio_npins = i;
@@ -749,13 +820,15 @@
device_add_child(dev, "gpioc", device_get_unit(dev));
device_add_child(dev, "gpiobus", device_get_unit(dev));
+
return (bus_generic_attach(dev));
fail:
- if (sc->sc_irq_res)
- bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+ bcm_gpio_intr_detach(dev);
+ bus_release_resources(dev, bcm_gpio_irq_spec, sc->sc_irq_res);
if (sc->sc_mem_res)
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
return (ENXIO);
}
@@ -766,6 +839,233 @@
return (EBUSY);
}
+static uint32_t
+bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank)
+{
+
+ if (irq > BCM_GPIO_PINS)
+ return (0);
+
+ if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) {
+ if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
+ return (BCM_GPIO_GPLEN(bank));
+ else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
+ return (BCM_GPIO_GPHEN(bank));
+ } else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) {
+ if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
+ return (BCM_GPIO_GPFEN(bank));
+ else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
+ return (BCM_GPIO_GPREN(bank));
+ }
+
+ return (0);
+}
+
+static void
+bcm_gpio_mask_irq(void *source)
+{
+ uint32_t bank, mask, reg;
+ unsigned int irq;
+
+ irq = (unsigned int)source;
+ if (irq > BCM_GPIO_PINS)
+ return;
+ if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq))
+ return;
+
+ bank = irq / BCM_GPIO_PINS_PER_BANK;
+ mask = (1U << (irq % BCM_GPIO_PINS_PER_BANK));
+
+ BCM_GPIO_LOCK(bcm_gpio_sc);
+ reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank);
+ if (reg != 0)
+ BCM_GPIO_CLEAR_BITS(bcm_gpio_sc, reg, mask);
+ BCM_GPIO_UNLOCK(bcm_gpio_sc);
+}
+
+static void
+bcm_gpio_unmask_irq(void *source)
+{
+ uint32_t bank, mask, reg;
+ unsigned int irq;
+
+ irq = (unsigned int)source;
+ if (irq > BCM_GPIO_PINS)
+ return;
+ if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq))
+ return;
+
+ bank = irq / BCM_GPIO_PINS_PER_BANK;
+ mask = (1U << (irq % BCM_GPIO_PINS_PER_BANK));
+
+ BCM_GPIO_LOCK(bcm_gpio_sc);
+ reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank);
+ if (reg != 0)
+ BCM_GPIO_SET_BITS(bcm_gpio_sc, reg, mask);
+ BCM_GPIO_UNLOCK(bcm_gpio_sc);
+}
+
+static int
+bcm_gpio_activate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ int pin;
+
+ if (type != SYS_RES_IRQ)
+ return (ENXIO);
+
+ /* Unmask the interrupt. */
+ pin = rman_get_start(res);
+ bcm_gpio_unmask_irq((void *)pin);
+
+ return (0);
+}
+
+static int
+bcm_gpio_deactivate_resource(device_t bus, device_t child, int type, int rid,
+ struct resource *res)
+{
+ int pin;
+
+ if (type != SYS_RES_IRQ)
+ return (ENXIO);
+
+ /* Mask the interrupt. */
+ pin = rman_get_start(res);
+ bcm_gpio_mask_irq((void *)pin);
+
+ return (0);
+}
+
+static int
+bcm_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ int bank;
+ struct bcm_gpio_softc *sc;
+ uint32_t mask, oldreg, reg;
+
+ if (irq > BCM_GPIO_PINS)
+ return (EINVAL);
+
+ /* There is no standard trigger or polarity. */
+ if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ if (bcm_gpio_pin_is_ro(sc, irq))
+ return (EINVAL);
+
+ bank = irq / BCM_GPIO_PINS_PER_BANK;
+ mask = (1UL << (irq % BCM_GPIO_PINS_PER_BANK));
+
+ BCM_GPIO_LOCK(sc);
+ oldreg = bcm_gpio_intr_reg(sc, irq, bank);
+ sc->sc_irq_trigger[irq] = trig;
+ sc->sc_irq_polarity[irq] = pol;
+ reg = bcm_gpio_intr_reg(sc, irq, bank);
+ if (reg != 0)
+ BCM_GPIO_SET_BITS(sc, reg, mask);
+ if (oldreg != 0)
+ BCM_GPIO_CLEAR_BITS(sc, oldreg, mask);
+ BCM_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+bcm_gpio_setup_intr(device_t bus, device_t child, struct resource *ires,
+ int flags, driver_filter_t *filt, driver_intr_t *handler,
+ void *arg, void **cookiep)
+{
+ struct bcm_gpio_softc *sc;
+ struct intr_event *event;
+ int pin, error;
+
+ sc = device_get_softc(bus);
+ pin = rman_get_start(ires);
+
+ if (pin > BCM_GPIO_PINS)
+ panic("%s: bad pin %d", __func__, pin);
+
+ event = sc->sc_events[pin];
+ if (event == NULL) {
+ error = intr_event_create(&event, (void *)pin, 0, pin,
+ bcm_gpio_mask_irq, bcm_gpio_unmask_irq, NULL, NULL,
+ "gpio%d pin%d:", device_get_unit(bus), pin);
+
+ if (error != 0)
+ return (error);
+
+ sc->sc_events[pin] = event;
+ }
+
+ intr_event_add_handler(event, device_get_nameunit(child), filt,
+ handler, arg, intr_priority(flags), flags, cookiep);
+
+ return (0);
+}
+
+static int
+bcm_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires,
+ void *cookie)
+{
+ struct bcm_gpio_softc *sc;
+ int pin, err;
+
+ sc = device_get_softc(dev);
+ pin = rman_get_start(ires);
+ if (pin > BCM_GPIO_PINS)
+ panic("%s: bad pin %d", __func__, pin);
+
+ if (sc->sc_events[pin] == NULL)
+ panic("Trying to teardown unoccupied IRQ");
+
+ err = intr_event_remove_handler(cookie);
+ if (!err)
+ sc->sc_events[pin] = NULL;
+
+ return (err);
+}
+
+static int
+bcm_gpio_intr(void *arg)
+{
+ struct bcm_gpio_softc *sc;
+ struct intr_event *event;
+ uint32_t bank, mask, reg;
+ int bank_last, irq;
+
+ sc = (struct bcm_gpio_softc *)arg;
+
+ bank_last = -1;
+ for (irq = 0; irq < BCM_GPIO_PINS; irq++) {
+
+ bank = irq / BCM_GPIO_PINS_PER_BANK;
+ mask = (1U << (irq % BCM_GPIO_PINS_PER_BANK));
+
+ if (bank != bank_last) {
+ reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank));
+ bank_last = bank;
+ }
+
+ if (reg & mask) {
+ event = sc->sc_events[irq];
+ if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers))
+ intr_event_handle(event, NULL);
+ else {
+ device_printf(sc->sc_dev, "Stray IRQ %d\n",
+ irq);
+ }
+
+ /* Clear the Status bit by writing '1' to it. */
+ BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), mask);
+ }
+ }
+
+ return (FILTER_HANDLED);
+}
+
static phandle_t
bcm_gpio_get_node(device_t bus, device_t dev)
{
@@ -790,6 +1090,13 @@
DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set),
DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle),
+ /* Bus interface */
+ DEVMETHOD(bus_activate_resource, bcm_gpio_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bcm_gpio_deactivate_resource),
+ DEVMETHOD(bus_config_intr, bcm_gpio_config_intr),
+ DEVMETHOD(bus_setup_intr, bcm_gpio_setup_intr),
+ DEVMETHOD(bus_teardown_intr, bcm_gpio_teardown_intr),
+
/* ofw_bus interface */
DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node),
-------------- next part --------------
Index: sys/conf/files
===================================================================
--- sys/conf/files (revision 265817)
+++ sys/conf/files (working copy)
@@ -1397,9 +1397,8 @@
dev/gem/if_gem_sbus.c optional gem sbus
dev/gpio/gpiobus.c optional gpio \
dependency "gpiobus_if.h"
-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
Index: sys/dev/gpio/gpiobus.c
===================================================================
--- sys/dev/gpio/gpiobus.c (revision 265817)
+++ sys/dev/gpio/gpiobus.c (working copy)
@@ -30,6 +30,8 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/gpio.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
@@ -38,6 +40,13 @@
#include "gpiobus_if.h"
+#undef GPIOBUS_DEBUG
+#ifdef GPIOBUS_DEBUG
+#define dprintf printf
+#else
+#define dprintf(x, arg...)
+#endif
+
static int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int);
static int gpiobus_probe(device_t);
static int gpiobus_attach(device_t);
@@ -64,6 +73,24 @@
static int gpiobus_pin_get(device_t, device_t, uint32_t, unsigned int*);
static int gpiobus_pin_toggle(device_t, device_t, uint32_t);
+static d_ioctl_t gpiobus_ioctl;
+static d_kqfilter_t gpiobus_kqfilter;
+static void gpiobus_filt_detach(struct knote *kn);
+static int gpiobus_filt_read(struct knote *kn, long hint);
+
+static struct cdevsw gpiobus_cdevsw = {
+ .d_version = D_VERSION,
+ .d_ioctl = gpiobus_ioctl,
+ .d_kqfilter = gpiobus_kqfilter,
+ .d_name = "gpioc",
+};
+
+struct filterops gpiobus_rfiltops = {
+ .f_isfd = 1,
+ .f_detach = gpiobus_filt_detach,
+ .f_event = gpiobus_filt_read,
+};
+
void
gpiobus_print_pins(struct gpiobus_ivar *devi)
{
@@ -99,6 +126,62 @@
printf("%d", range_start);
}
+int
+gpiobus_init_softc(device_t dev)
+{
+ struct gpiobus_softc *sc;
+ int i;
+
+ sc = GPIOBUS_SOFTC(dev);
+ sc->sc_busdev = dev;
+ sc->sc_dev = device_get_parent(dev);
+ sc->sc_intr_rman.rm_type = RMAN_ARRAY;
+ sc->sc_intr_rman.rm_descr = "GPIO Interrupts";
+ if (rman_init(&sc->sc_intr_rman) != 0 ||
+ rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0)
+ panic("%s: failed to set up rman.", __func__);
+
+ if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0)
+ return (ENXIO);
+
+ KASSERT(sc->sc_npins != 0, ("GPIO device with no pins"));
+
+ /* Increase the number of pins. */
+ sc->sc_npins++;
+
+ /* Alloc the pin data. */
+ sc->sc_pins = malloc(sizeof(*sc->sc_pins) * sc->sc_npins, M_DEVBUF,
+ M_NOWAIT | M_ZERO);
+ if (sc->sc_pins == NULL)
+ return (ENOMEM);
+ sc->sc_pin_arg = malloc(sizeof(struct gpiobus_pin_arg) * sc->sc_npins,
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->sc_pin_arg == NULL) {
+ free(sc->sc_pins, M_DEVBUF);
+ return (ENOMEM);
+ }
+
+ /* Create the gpioc%d device. */
+ sc->sc_ctl_dev = make_dev(&gpiobus_cdevsw, device_get_unit(dev),
+ UID_ROOT, GID_WHEEL, 0600, "gpioc%d", device_get_unit(dev));
+ if (sc->sc_ctl_dev == NULL) {
+ printf("Failed to create gpioc%d", device_get_unit(dev));
+ free(sc->sc_pin_arg, M_DEVBUF);
+ free(sc->sc_pins, M_DEVBUF);
+ return (ENXIO);
+ }
+ sc->sc_ctl_dev->si_drv1 = sc;
+
+ /* Initialize the bus lock. */
+ GPIOBUS_LOCK_INIT(sc);
+
+ /* Initialize the pins knlist. */
+ for (i = 0; i < sc->sc_npins; i++)
+ knlist_init_mtx(&sc->sc_pins[i].sel.si_note, &sc->sc_mtx);
+
+ return (0);
+}
+
static int
gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int mask)
{
@@ -140,13 +223,13 @@
/*
* Mark pin as mapped and give warning if it's already mapped
*/
- if (sc->sc_pins_mapped[i]) {
+ if (sc->sc_pins[i].mapped) {
device_printf(child,
"warning: pin %d is already mapped\n", i);
free(devi->pins, M_DEVBUF);
return (EINVAL);
}
- sc->sc_pins_mapped[i] = 1;
+ sc->sc_pins[i].mapped = 1;
}
return (0);
@@ -153,41 +236,147 @@
}
static int
-gpiobus_probe(device_t dev)
+gpiobus_kqfilter(struct cdev *cdev, struct knote *kn)
{
- device_set_desc(dev, "GPIO bus");
+ int pin;
+ struct gpiobus_softc *sc;
- return (BUS_PROBE_GENERIC);
+ sc = (struct gpiobus_softc *)cdev->si_drv1;
+ pin = kn->kn_sdata;
+
+ /* Check if we are monitoring a valid interrupt. */
+ if (sc->sc_pin_arg[pin].sc != sc)
+ return (EINVAL);
+
+ switch (kn->kn_filter) {
+ case EVFILT_READ:
+ kn->kn_hook = &sc->sc_pin_arg[pin];
+ kn->kn_fop = &gpiobus_rfiltops;
+ knlist_add(&sc->sc_pins[pin].sel.si_note, kn, 0);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
}
+static void
+gpiobus_filt_detach(struct knote *kn)
+{
+ int pin;
+ struct gpiobus_softc *sc;
+ struct gpiobus_pin_arg *arg;
+
+ arg = (struct gpiobus_pin_arg *)kn->kn_hook;
+ sc = arg->sc;
+ pin = arg->pin;
+ knlist_remove(&sc->sc_pins[pin].sel.si_note, kn, 0);
+}
+
static int
-gpiobus_attach(device_t dev)
+gpiobus_filt_read(struct knote *kn, long hint)
{
- struct gpiobus_softc *sc = GPIOBUS_SOFTC(dev);
- int res;
+ static long last = -1;
+ int notify;
- sc->sc_busdev = dev;
- sc->sc_dev = device_get_parent(dev);
- res = GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins);
- if (res)
- return (ENXIO);
+ if (hint > 0)
+ /* Return the original pin value. */
+ kn->kn_data = hint - 1;
- KASSERT(sc->sc_npins != 0, ("GPIO device with no pins"));
+ if (hint > 0 || last > 0)
+ notify = 1;
+ else
+ notify = 0;
- /*
- * Increase to get number of pins
- */
- sc->sc_npins++;
+ last = hint;
- sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF,
- M_NOWAIT | M_ZERO);
+ return (notify);
+}
- if (!sc->sc_pins_mapped)
- return (ENOMEM);
+static int
+gpiobus_ioctl(struct cdev *cdev, u_long cmd, caddr_t arg, int fflag,
+ struct thread *td)
+{
+ int max_pin, res;
+ struct gpiobus_softc *sc;
+ struct gpio_pin pin;
+ struct gpio_req req;
- /* init bus lock */
- GPIOBUS_LOCK_INIT(sc);
+ sc = cdev->si_drv1;
+ GPIOBUS_LOCK(sc);
+ switch (cmd) {
+ case GPIOMAXPIN:
+ max_pin = -1;
+ res = GPIO_PIN_MAX(sc->sc_dev, &max_pin);
+ bcopy(&max_pin, arg, sizeof(max_pin));
+ break;
+ case GPIOGETCONFIG:
+ bcopy(arg, &pin, sizeof(pin));
+ dprintf("get config pin %d\n", pin.gp_pin);
+ res = GPIO_PIN_GETFLAGS(sc->sc_dev, pin.gp_pin,
+ &pin.gp_flags);
+ /* Fail early */
+ if (res)
+ break;
+ GPIO_PIN_GETCAPS(sc->sc_dev, pin.gp_pin, &pin.gp_caps);
+ GPIO_PIN_GETNAME(sc->sc_dev, pin.gp_pin, pin.gp_name);
+ bcopy(&pin, arg, sizeof(pin));
+ break;
+ case GPIOSETCONFIG:
+ bcopy(arg, &pin, sizeof(pin));
+ dprintf("set config pin %d\n", pin.gp_pin);
+ res = GPIO_PIN_SETFLAGS(sc->sc_dev, pin.gp_pin,
+ pin.gp_flags);
+ break;
+ case GPIOGET:
+ bcopy(arg, &req, sizeof(req));
+ res = GPIO_PIN_GET(sc->sc_dev, req.gp_pin,
+ &req.gp_value);
+ dprintf("read pin %d -> %d\n",
+ req.gp_pin, req.gp_value);
+ bcopy(&req, arg, sizeof(req));
+ break;
+ case GPIOSET:
+ bcopy(arg, &req, sizeof(req));
+ res = GPIO_PIN_SET(sc->sc_dev, req.gp_pin,
+ req.gp_value);
+ dprintf("write pin %d -> %d\n",
+ req.gp_pin, req.gp_value);
+ break;
+ case GPIOTOGGLE:
+ bcopy(arg, &req, sizeof(req));
+ dprintf("toggle pin %d\n",
+ req.gp_pin);
+ res = GPIO_PIN_TOGGLE(sc->sc_dev, req.gp_pin);
+ break;
+ default:
+ GPIOBUS_UNLOCK(sc);
+ return (ENOTTY);
+ break;
+ }
+ GPIOBUS_UNLOCK(sc);
+ return (res);
+}
+
+static int
+gpiobus_probe(device_t dev)
+{
+ device_set_desc(dev, "GPIO bus");
+
+ return (BUS_PROBE_GENERIC);
+}
+
+static int
+gpiobus_attach(device_t dev)
+{
+ int err;
+
+ err = gpiobus_init_softc(dev);
+ if (err != 0)
+ return (err);
+
/*
* Get parent's pins and mark them as unmapped
*/
@@ -214,6 +403,9 @@
("gpiobus mutex not initialized"));
GPIOBUS_LOCK_DESTROY(sc);
+ if (sc->sc_ctl_dev)
+ destroy_dev(sc->sc_ctl_dev);
+
if ((err = bus_generic_detach(dev)) != 0)
return (err);
@@ -229,10 +421,14 @@
}
free(devlist, M_TEMP);
- if (sc->sc_pins_mapped) {
- free(sc->sc_pins_mapped, M_DEVBUF);
- sc->sc_pins_mapped = NULL;
+ if (sc->sc_pin_arg) {
+ free(sc->sc_pin_arg, M_DEVBUF);
+ sc->sc_pin_arg = NULL;
}
+ if (sc->sc_pins) {
+ free(sc->sc_pins, M_DEVBUF);
+ sc->sc_pins = NULL;
+ }
return (0);
}
@@ -260,6 +456,7 @@
retval += bus_print_child_header(dev, child);
retval += printf(" at pin(s) ");
gpiobus_print_pins(devi);
+ resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld");
retval += bus_print_child_footer(dev, child);
return (retval);
@@ -297,6 +494,7 @@
device_delete_child(dev, child);
return (0);
}
+ resource_list_init(&devi->rl);
device_set_ivars(child, devi);
return (child);
}
@@ -307,16 +505,42 @@
struct gpiobus_softc *sc = GPIOBUS_SOFTC(bus);
struct gpiobus_ivar *devi;
device_t child;
- int pins;
+ int irq, pins;
-
child = BUS_ADD_CHILD(bus, 0, dname, dunit);
devi = GPIOBUS_IVAR(child);
resource_int_value(dname, dunit, "pins", &pins);
if (gpiobus_parse_pins(sc, child, pins))
device_delete_child(bus, child);
+ if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
+ if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0)
+ device_printf(bus,
+ "warning: bus_set_resource() failed\n");
+ }
}
+static int
+gpiobus_set_resource(device_t dev, device_t child, int type, int rid,
+ u_long start, u_long count)
+{
+ struct gpiobus_ivar *devi;
+ struct resource_list *rl;
+ struct resource_list_entry *rle;
+
+ devi = GPIOBUS_IVAR(child);
+ rl = &devi->rl;
+
+ dprintf("%s: entry (%p, %p, %d, %d, %p, %ld)\n",
+ __func__, dev, child, type, rid, (void *)(intptr_t)start, count);
+
+ rle = resource_list_add(rl, type, rid, start, start + count - 1,
+ count);
+ if (rle == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
static void
gpiobus_lock_bus(device_t busdev)
{
@@ -443,6 +667,180 @@
return GPIO_PIN_TOGGLE(sc->sc_dev, devi->pins[pin]);
}
+static int
+gpiobus_intr_filter(void *arg)
+{
+ int pin;
+ struct gpiobus_pin_arg *intr;
+ struct gpiobus_softc *sc;
+ struct intr_event *event;
+
+ intr = (struct gpiobus_pin_arg *)arg;
+ sc = intr->sc;
+ pin = intr->pin;
+
+ event = sc->sc_pins[pin].event;
+ if (event == NULL || TAILQ_EMPTY(&event->ie_handlers)) {
+ device_printf(sc->sc_dev, "Stray IRQ %d\n", pin);
+ return (FILTER_HANDLED);
+ }
+
+ intr_event_handle(event, NULL);
+
+ return (FILTER_SCHEDULE_THREAD);
+}
+
+static void
+gpiobus_intr_ithread(void *arg)
+{
+ int pin;
+ struct gpiobus_pin_arg *intr;
+ struct gpiobus_softc *sc;
+
+ intr = (struct gpiobus_pin_arg *)arg;
+ sc = intr->sc;
+ pin = intr->pin;
+
+ /* Add 1 to GPIO pin, allowing the use of pin 0. */
+ KNOTE_UNLOCKED(&sc->sc_pins[pin].sel.si_note, pin + 1);
+}
+
+static int
+gpiobus_setup_intr(device_t dev, device_t child, struct resource *ires,
+ int flags, driver_filter_t *filt, driver_intr_t *handler,
+ void *arg, void **cookiep)
+{
+ struct gpiobus_softc *sc;
+ struct intr_event *event;
+ int pin, error;
+
+ sc = GPIOBUS_SOFTC(dev);
+ pin = rman_get_start(ires);
+
+ if (pin > sc->sc_npins)
+ panic("%s: bad pin %d (max: %d)", __func__, pin, sc->sc_npins);
+
+ /* Setup the child interrupt filter/handler. */
+ event = sc->sc_pins[pin].event;
+ if (event == NULL) {
+ error = intr_event_create(&event, (void *)(uintptr_t)pin, 0,
+ pin, NULL, NULL, NULL, NULL, "gpiobus%d pin%d:",
+ device_get_unit(dev), pin);
+
+ if (error != 0)
+ return (error);
+
+ sc->sc_pins[pin].event = event;
+ }
+
+ intr_event_add_handler(event, device_get_nameunit(child), filt,
+ handler, arg, intr_priority(flags), flags, cookiep);
+
+ sc->sc_pin_arg[pin].sc = sc;
+ sc->sc_pin_arg[pin].pin = pin;
+
+ /* And now register our interrupt filter. */
+ return (BUS_SETUP_INTR(device_get_parent(dev), child, ires, flags,
+ gpiobus_intr_filter, gpiobus_intr_ithread, &sc->sc_pin_arg[pin],
+ &sc->sc_pins[pin].irq_hdl));
+}
+
+static int
+gpiobus_teardown_intr(device_t dev, device_t child, struct resource *ires,
+ void *cookie)
+{
+ struct gpiobus_softc *sc;
+ int pin, err;
+
+ sc = GPIOBUS_SOFTC(dev);
+ pin = rman_get_start(ires);
+
+ if (pin > sc->sc_npins)
+ panic("%s: bad pin %d (max %d)", __func__, pin, sc->sc_npins);
+
+ if (sc->sc_pins[pin].event == NULL)
+ panic("Trying to teardown unoccupied IRQ");
+
+ err = intr_event_remove_handler(cookie);
+ if (err)
+ return (err);
+ sc->sc_pins[pin].event = NULL;
+
+ return (BUS_TEARDOWN_INTR(device_get_parent(dev), child, ires,
+ &sc->sc_pins[pin].irq_hdl));
+}
+
+static struct resource *
+gpiobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct gpiobus_softc *sc;
+ struct resource *rv;
+ struct resource_list *rl;
+ struct resource_list_entry *rle;
+ int isdefault;
+
+ if (type != SYS_RES_IRQ)
+ return (NULL);
+
+ isdefault = (start == 0UL && end == ~0UL && count == 1);
+ rle = NULL;
+
+ if (isdefault) {
+ rl = BUS_GET_RESOURCE_LIST(bus, child);
+ if (rl == NULL)
+ return (NULL);
+ rle = resource_list_find(rl, type, *rid);
+ if (rle == NULL)
+ return (NULL);
+ if (rle->res != NULL)
+ panic("%s: resource entry is busy", __func__);
+ start = rle->start;
+ count = rle->count;
+ end = rle->end;
+ }
+
+ sc = device_get_softc(bus);
+ rv = rman_reserve_resource(&sc->sc_intr_rman, start, end, count, flags,
+ child);
+ if (rv == NULL)
+ return (NULL);
+ rman_set_rid(rv, *rid);
+
+ if ((flags & RF_ACTIVE) != 0 &&
+ bus_activate_resource(child, type, *rid, rv) != 0) {
+ rman_release_resource(rv);
+ return (NULL);
+ }
+
+ return (rv);
+}
+
+static int
+gpiobus_release_resource(device_t bus __unused, device_t child, int type,
+ int rid, struct resource *r)
+{
+ int error;
+
+ if ((rman_get_flags(r) & RF_ACTIVE) != 0) {
+ error = bus_deactivate_resource(child, type, rid, r);
+ if (error)
+ return (error);
+ }
+
+ return (rman_release_resource(r));
+}
+
+static struct resource_list *
+gpiobus_get_resource_list(device_t bus __unused, device_t child)
+{
+ struct gpiobus_ivar *ivar;
+
+ ivar = GPIOBUS_IVAR(child);
+
+ return (&ivar->rl);
+}
+
static device_method_t gpiobus_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, gpiobus_probe),
@@ -453,6 +851,15 @@
DEVMETHOD(device_resume, gpiobus_resume),
/* Bus interface */
+ DEVMETHOD(bus_setup_intr, gpiobus_setup_intr),
+ DEVMETHOD(bus_config_intr, bus_generic_config_intr),
+ DEVMETHOD(bus_teardown_intr, gpiobus_teardown_intr),
+ DEVMETHOD(bus_set_resource, gpiobus_set_resource),
+ DEVMETHOD(bus_alloc_resource, gpiobus_alloc_resource),
+ DEVMETHOD(bus_release_resource, gpiobus_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
+ DEVMETHOD(bus_get_resource_list, gpiobus_get_resource_list),
DEVMETHOD(bus_add_child, gpiobus_add_child),
DEVMETHOD(bus_print_child, gpiobus_print_child),
DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str),
Index: sys/dev/gpio/gpiobusvar.h
===================================================================
--- sys/dev/gpio/gpiobusvar.h (revision 265817)
+++ sys/dev/gpio/gpiobusvar.h (working copy)
@@ -32,8 +32,11 @@
#include "opt_platform.h"
+#include <sys/interrupt.h>
#include <sys/lock.h>
#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/selinfo.h>
#ifdef FDT
#include <dev/ofw/ofw_bus_subr.h>
@@ -41,7 +44,12 @@
#include "gpio_if.h"
+#ifdef FDT
+#define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) \
+ &((struct ofw_gpiobus_devinfo *)device_get_ivars(d))->opd_dinfo
+#else
#define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) device_get_ivars(d)
+#endif
#define GPIOBUS_SOFTC(d) (struct gpiobus_softc *) device_get_softc(d)
#define GPIOBUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
#define GPIOBUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
@@ -51,18 +59,38 @@
#define GPIOBUS_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED)
#define GPIOBUS_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED)
+struct gpiobus_softc;
+
+struct gpiobus_pin_arg
+{
+ struct gpiobus_softc *sc;
+ int pin;
+};
+
+struct gpiobus_pin
+{
+ struct intr_event *event; /* isr event */
+ struct selinfo sel; /* knlist data */
+ int mapped; /* pin is mapped ? */
+ void *irq_hdl; /* isr handler cookie */
+};
+
struct gpiobus_softc
{
+ struct cdev *sc_ctl_dev; /* controller device */
struct mtx sc_mtx; /* bus mutex */
+ struct gpiobus_pin *sc_pins; /* gpiobus pins */
+ struct gpiobus_pin_arg *sc_pin_arg; /* isr arg */
+ struct rman sc_intr_rman; /* isr resources */
device_t sc_busdev; /* bus device */
device_t sc_owner; /* bus owner */
device_t sc_dev; /* driver device */
int sc_npins; /* total pins on bus */
- int *sc_pins_mapped; /* mark mapped pins */
};
struct gpiobus_ivar
{
+ struct resource_list rl; /* isr resource list */
uint32_t npins; /* pins total */
uint32_t *flags; /* pins flags */
uint32_t *pins; /* pins map */
@@ -84,6 +112,7 @@
device_t ofw_gpiobus_add_fdt_child(device_t, phandle_t);
#endif
void gpiobus_print_pins(struct gpiobus_ivar *);
+int gpiobus_init_softc(device_t);
extern driver_t gpiobus_driver;
Index: sys/dev/gpio/ofw_gpiobus.c
===================================================================
--- sys/dev/gpio/ofw_gpiobus.c (revision 265817)
+++ sys/dev/gpio/ofw_gpiobus.c (working copy)
@@ -103,6 +103,7 @@
int cells, i, j, len;
pcell_t *gpios;
phandle_t gpio;
+ struct gpiobus_pin *pin;
/* Retrieve the gpios property. */
if ((len = OF_getproplen(child, "gpios")) < 0)
@@ -195,10 +196,12 @@
return (EINVAL);
}
+ pin = &sc->sc_pins[dinfo->pins[j]];
+
/*
* Mark pin as mapped and give warning if it's already mapped.
*/
- if (sc->sc_pins_mapped[dinfo->pins[j]]) {
+ if (pin->mapped) {
device_printf(sc->sc_busdev,
"warning: pin %d is already mapped\n",
dinfo->pins[j]);
@@ -206,7 +209,7 @@
free(gpios, M_DEVBUF);
return (EINVAL);
}
- sc->sc_pins_mapped[dinfo->pins[j]] = 1;
+ pin->mapped = 1;
i += cells + 1;
j++;
@@ -222,6 +225,9 @@
{
struct gpiobus_softc *sc;
struct ofw_gpiobus_devinfo *dinfo;
+ uint32_t *intr, icells;
+ phandle_t iparent;
+ int i, nintr;
sc = device_get_softc(dev);
dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_NOWAIT | M_ZERO);
@@ -239,6 +245,24 @@
return (NULL);
}
+ resource_list_init(&dinfo->opd_dinfo.rl);
+ nintr = OF_getencprop_alloc(node, "interrupts", sizeof(*intr),
+ (void **)&intr);
+ if (nintr > 0) {
+ iparent = 0;
+ OF_searchencprop(node, "interrupt-parent", &iparent,
+ sizeof(iparent));
+ OF_searchencprop(OF_xref_phandle(iparent), "#interrupt-cells",
+ &icells, sizeof(icells));
+ for (i = 0; i < nintr; i+= icells) {
+ intr[i] = ofw_bus_map_intr(dev, iparent, icells,
+ &intr[i]);
+ resource_list_add(&dinfo->opd_dinfo.rl, SYS_RES_IRQ,
+ i, intr[i], intr[i], 1);
+ }
+ free(intr, M_OFWPROP);
+ }
+
return (dinfo);
}
@@ -246,6 +270,7 @@
ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *dinfo)
{
+ resource_list_free(&dinfo->opd_dinfo.rl);
ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo);
free(dinfo, M_DEVBUF);
}
@@ -264,33 +289,13 @@
static int
ofw_gpiobus_attach(device_t dev)
{
- struct gpiobus_softc *sc;
+ int err;
phandle_t child;
- sc = GPIOBUS_SOFTC(dev);
- sc->sc_busdev = dev;
- sc->sc_dev = device_get_parent(dev);
+ err = gpiobus_init_softc(dev);
+ if (err != 0)
+ return (err);
- /* Read the pin max. value */
- if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0)
- return (ENXIO);
-
- KASSERT(sc->sc_npins != 0, ("GPIO device with no pins"));
-
- /*
- * Increase to get number of pins.
- */
- sc->sc_npins++;
-
- sc->sc_pins_mapped = malloc(sizeof(int) * sc->sc_npins, M_DEVBUF,
- M_NOWAIT | M_ZERO);
-
- if (!sc->sc_pins_mapped)
- return (ENOMEM);
-
- /* Init the bus lock. */
- GPIOBUS_LOCK_INIT(sc);
-
bus_generic_probe(dev);
bus_enumerate_hinted_children(dev);
@@ -346,6 +351,8 @@
retval += bus_print_child_header(dev, child);
retval += printf(" at pin(s) ");
gpiobus_print_pins(&devi->opd_dinfo);
+ resource_list_print_type(&devi->opd_dinfo.rl, "irq", SYS_RES_IRQ,
+ "%ld");
retval += bus_print_child_footer(dev, child);
return (retval);
-------------- next part --------------
Index: sys/arm/ti/ti_gpio.c
===================================================================
--- sys/arm/ti/ti_gpio.c (revision 265842)
+++ sys/arm/ti/ti_gpio.c (working copy)
@@ -52,6 +52,7 @@
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/gpio.h>
+#include <sys/interrupt.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -148,6 +149,7 @@
#endif
#define PINS_PER_BANK 32
#define MAX_GPIO_INTRS MAX_GPIO_BANKS * INTR_PER_BANK
+#define MAX_GPIO_PINS MAX_GPIO_BANKS * PINS_PER_BANK
/**
* ti_gpio_mem_spec - Resource specification used when allocating resources
@@ -198,6 +200,10 @@
struct ti_gpio_softc {
device_t sc_dev;
+ /* Interrupt trigger type and level. */
+ enum intr_trigger sc_irq_trigger[MAX_GPIO_PINS];
+ enum intr_polarity sc_irq_polarity[MAX_GPIO_PINS];
+
/*
* The memory resource(s) for the PRCM register set, when the device is
* created the caller can assign up to 6 memory regions depending on
@@ -206,6 +212,9 @@
struct resource *sc_mem_res[MAX_GPIO_BANKS];
struct resource *sc_irq_res[MAX_GPIO_INTRS];
+ /* Interrupt events. */
+ struct intr_event *sc_events[MAX_GPIO_PINS];
+
/* The handle for the register IRQ handlers. */
void *sc_irq_hdl[MAX_GPIO_INTRS];
@@ -220,15 +229,17 @@
/**
* Macros for driver mutex locking
*/
-#define TI_GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
-#define TI_GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
+#define TI_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx)
+#define TI_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx)
#define TI_GPIO_LOCK_INIT(_sc) \
mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
- "ti_gpio", MTX_DEF)
+ "ti_gpio", MTX_SPIN)
#define TI_GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx)
#define TI_GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED)
#define TI_GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED)
+static struct ti_gpio_softc *ti_gpio_sc = NULL;
+
/**
* ti_gpio_read_4 - reads a 16-bit value from one of the PADCONFS registers
* @sc: GPIO device context
@@ -276,6 +287,50 @@
#endif
}
+static inline void
+ti_gpio_intr_set(struct ti_gpio_softc *sc, unsigned int bank, uint32_t mask)
+{
+
+ /*
+ * On OMAP3 and OMAP4 we unmask only the MPU interrupt and on AM335x
+ * we also activate only the first interrupt.
+ */
+#if defined(SOC_OMAP4) || defined(SOC_TI_AM335X)
+ ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_SET_0, mask);
+#else
+ ti_gpio_write_4(sc, bank, TI_GPIO_CLEARIRQENABLE1, mask);
+#endif
+}
+
+static inline void
+ti_gpio_intr_ack(struct ti_gpio_softc *sc, unsigned int bank, uint32_t mask)
+{
+
+#if defined(SOC_OMAP4) || defined(SOC_TI_AM335X)
+ ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_0, mask);
+ ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_1, mask);
+#else
+ ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS1, mask);
+ ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS2, mask);
+#endif
+}
+
+static inline uint32_t
+ti_gpio_intr_status(struct ti_gpio_softc *sc, unsigned int bank)
+{
+ uint32_t reg;
+
+#if defined(SOC_OMAP4) || defined(SOC_TI_AM335X)
+ reg = ti_gpio_read_4(sc, bank, TI_GPIO_IRQSTATUS_0);
+ reg |= ti_gpio_read_4(sc, bank, TI_GPIO_IRQSTATUS_1);
+#else
+ reg = ti_gpio_read_4(sc, bank, TI_GPIO_IRQSTATUS1);
+ reg |= ti_gpio_read_4(sc, bank, TI_GPIO_IRQSTATUS2);
+#endif
+
+ return (reg);
+}
+
/**
* ti_gpio_pin_max - Returns the maximum number of GPIO pins
* @dev: gpio device handle
@@ -625,14 +680,42 @@
* Internally locks the context
*
*/
-static void
+static int
ti_gpio_intr(void *arg)
{
- struct ti_gpio_softc *sc = arg;
+ int bank, bank_last, irq;
+ struct intr_event *event;
+ struct ti_gpio_softc *sc;
+ uint32_t mask, reg;
- TI_GPIO_LOCK(sc);
- /* TODO: something useful */
- TI_GPIO_UNLOCK(sc);
+ sc = (struct ti_gpio_softc *)arg;
+
+ bank_last = -1;
+ for (irq = 0; irq < MAX_GPIO_PINS; irq++) {
+
+ bank = irq / PINS_PER_BANK;
+ mask = (1UL << (irq % PINS_PER_BANK));
+
+ if (bank != bank_last) {
+ reg = ti_gpio_intr_status(sc, bank);
+ bank_last = bank;
+ }
+
+ if (reg & mask) {
+ event = sc->sc_events[irq];
+ if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers))
+ intr_event_handle(event, NULL);
+ else {
+ device_printf(sc->sc_dev, "Stray IRQ %d\n",
+ irq);
+ }
+
+ /* Ack the IRQ Status bit. */
+ ti_gpio_intr_ack(sc, bank, mask);
+ }
+ }
+
+ return (FILTER_HANDLED);
}
/**
@@ -674,13 +757,13 @@
break;
/*
- * Register our interrupt handler for each of the IRQ resources.
+ * Register our interrupt filter for each of the IRQ resources.
*/
if (bus_setup_intr(dev, sc->sc_irq_res[i],
- INTR_TYPE_MISC | INTR_MPSAFE, NULL, ti_gpio_intr, sc,
+ INTR_TYPE_MISC | INTR_MPSAFE, ti_gpio_intr, NULL, sc,
&sc->sc_irq_hdl[i]) != 0) {
device_printf(dev,
- "WARNING: unable to register interrupt handler\n");
+ "WARNING: unable to register interrupt filter\n");
return (-1);
}
}
@@ -694,7 +777,7 @@
int i;
struct ti_gpio_softc *sc;
- /* Teardown our interrupt handlers. */
+ /* Teardown our interrupt filters. */
sc = device_get_softc(dev);
for (i = 0; i < MAX_GPIO_INTRS; i++) {
if (sc->sc_irq_res[i] == NULL)
@@ -772,7 +855,10 @@
unsigned int i;
int err;
- sc = device_get_softc(dev);
+ if (ti_gpio_sc != NULL)
+ return (ENXIO);
+
+ ti_gpio_sc = sc = device_get_softc(dev);
sc->sc_dev = dev;
TI_GPIO_LOCK_INIT(sc);
@@ -821,6 +907,12 @@
}
}
+ /* The default is active-low interrupts. */
+ for (i = 0; i < MAX_GPIO_PINS; i++) {
+ sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL;
+ sc->sc_irq_polarity[i] = INTR_POLARITY_LOW;
+ }
+
/* Finish of the probe call */
device_add_child(dev, "gpioc", device_get_unit(dev));
device_add_child(dev, "gpiobus", device_get_unit(dev));
@@ -867,6 +959,211 @@
return (0);
}
+static uint32_t
+ti_gpio_intr_reg(struct ti_gpio_softc *sc, int irq)
+{
+
+ if (irq > MAX_GPIO_PINS)
+ return (0);
+
+ if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) {
+ if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
+ return (TI_GPIO_LEVELDETECT0);
+ else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
+ return (TI_GPIO_LEVELDETECT1);
+ } else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) {
+ if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW)
+ return (TI_GPIO_FALLINGDETECT);
+ else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH)
+ return (TI_GPIO_RISINGDETECT);
+ }
+
+ return (0);
+}
+
+static void
+ti_gpio_mask_irq(void *source)
+{
+ int bank, irq;
+ uint32_t mask, reg, val;
+
+ irq = (int)source;
+ if (irq > MAX_GPIO_PINS)
+ return;
+
+ bank = irq / PINS_PER_BANK;
+ mask = (1UL << (irq % PINS_PER_BANK));
+
+ TI_GPIO_LOCK(ti_gpio_sc);
+ ti_gpio_intr_clr(ti_gpio_sc, bank, mask);
+ reg = ti_gpio_intr_reg(ti_gpio_sc, irq);
+ if (reg != 0) {
+ val = ti_gpio_read_4(ti_gpio_sc, bank, reg);
+ ti_gpio_write_4(ti_gpio_sc, bank, reg, val & ~mask);
+ }
+ TI_GPIO_UNLOCK(ti_gpio_sc);
+}
+
+static void
+ti_gpio_unmask_irq(void *source)
+{
+ int bank, irq;
+ uint32_t mask, reg, val;
+
+ irq = (int)source;
+ if (irq > MAX_GPIO_PINS)
+ return;
+
+ bank = irq / PINS_PER_BANK;
+ mask = (1UL << (irq % PINS_PER_BANK));
+
+ TI_GPIO_LOCK(ti_gpio_sc);
+ reg = ti_gpio_intr_reg(ti_gpio_sc, irq);
+ if (reg != 0) {
+ val = ti_gpio_read_4(ti_gpio_sc, bank, reg);
+ ti_gpio_write_4(ti_gpio_sc, bank, reg, val | mask);
+ }
+ ti_gpio_intr_set(ti_gpio_sc, bank, mask);
+ TI_GPIO_UNLOCK(ti_gpio_sc);
+}
+
+static int
+ti_gpio_activate_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *res)
+{
+ int pin;
+ struct ti_gpio_softc *sc;
+
+ if (type != SYS_RES_IRQ)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+
+ /* Unmask the interrupt. */
+ pin = rman_get_start(res);
+ ti_gpio_unmask_irq((void *)(uintptr_t)pin);
+
+ return (0);
+}
+
+static int
+ti_gpio_deactivate_resource(device_t dev, device_t child, int type, int rid,
+ struct resource *res)
+{
+ int pin;
+ struct ti_gpio_softc *sc;
+
+ if (type != SYS_RES_IRQ)
+ return (ENXIO);
+
+ sc = device_get_softc(dev);
+
+ /* Mask the interrupt. */
+ pin = rman_get_start(res);
+ ti_gpio_mask_irq((void *)(uintptr_t)pin);
+
+ return (0);
+}
+
+static int
+ti_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ int bank;
+ struct ti_gpio_softc *sc;
+ uint32_t mask, oldreg, reg, val;
+
+ if (irq > MAX_GPIO_PINS)
+ return (EINVAL);
+
+ /* There is no standard trigger or polarity. */
+ if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM)
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ bank = irq / PINS_PER_BANK;
+ mask = (1UL << (irq % PINS_PER_BANK));
+
+ TI_GPIO_LOCK(ti_gpio_sc);
+ /*
+ * TRM recommends add the new event before remove the old one to
+ * avoid losing interrupts.
+ */
+ oldreg = ti_gpio_intr_reg(sc, irq);
+ sc->sc_irq_trigger[irq] = trig;
+ sc->sc_irq_polarity[irq] = pol;
+ reg = ti_gpio_intr_reg(sc, irq);
+ if (reg != 0) {
+ /* Apply the new settings. */
+ val = ti_gpio_read_4(sc, bank, reg);
+ ti_gpio_write_4(sc, bank, reg, val | mask);
+ }
+ if (oldreg != 0) {
+ /* Remove the old settings. */
+ val = ti_gpio_read_4(sc, bank, oldreg);
+ ti_gpio_write_4(sc, bank, oldreg, val & ~mask);
+ }
+ TI_GPIO_UNLOCK(ti_gpio_sc);
+
+ return (0);
+}
+
+static int
+ti_gpio_setup_intr(device_t dev, device_t child, struct resource *ires,
+ int flags, driver_filter_t *filt, driver_intr_t *handler,
+ void *arg, void **cookiep)
+{
+ struct ti_gpio_softc *sc;
+ struct intr_event *event;
+ int pin, error;
+
+ sc = device_get_softc(dev);
+ pin = rman_get_start(ires);
+
+ if (pin > MAX_GPIO_PINS)
+ panic("%s: bad pin %d", __func__, pin);
+
+ event = sc->sc_events[pin];
+ if (event == NULL) {
+ error = intr_event_create(&event, (void *)(uintptr_t)pin, 0,
+ pin, ti_gpio_mask_irq, ti_gpio_unmask_irq, NULL, NULL,
+ "gpio%d pin%d:", device_get_unit(dev), pin);
+
+ if (error != 0)
+ return (error);
+
+ sc->sc_events[pin] = event;
+ }
+
+ intr_event_add_handler(event, device_get_nameunit(child), filt,
+ handler, arg, intr_priority(flags), flags, cookiep);
+
+ return (0);
+}
+
+static int
+ti_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires,
+ void *cookie)
+{
+ struct ti_gpio_softc *sc;
+ int pin, err;
+
+ sc = device_get_softc(dev);
+ pin = rman_get_start(ires);
+
+ if (pin > MAX_GPIO_PINS)
+ panic("%s: bad pin %d", __func__, pin);
+
+ if (sc->sc_events[pin] == NULL)
+ panic("Trying to teardown unoccupied IRQ");
+
+ err = intr_event_remove_handler(cookie);
+ if (!err)
+ sc->sc_events[pin] = NULL;
+
+ return (err);
+}
+
static phandle_t
ti_gpio_get_node(device_t bus, device_t dev)
{
@@ -890,6 +1187,13 @@
DEVMETHOD(gpio_pin_set, ti_gpio_pin_set),
DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle),
+ /* Bus interface */
+ DEVMETHOD(bus_activate_resource, ti_gpio_activate_resource),
+ DEVMETHOD(bus_deactivate_resource, ti_gpio_deactivate_resource),
+ DEVMETHOD(bus_config_intr, ti_gpio_config_intr),
+ DEVMETHOD(bus_setup_intr, ti_gpio_setup_intr),
+ DEVMETHOD(bus_teardown_intr, ti_gpio_teardown_intr),
+
/* ofw_bus interface */
DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node),
More information about the freebsd-embedded
mailing list