[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