svn commit: r298742 - head/sys/arm/nvidia

Michal Meloun mmel at FreeBSD.org
Thu Apr 28 13:00:41 UTC 2016


Author: mmel
Date: Thu Apr 28 13:00:40 2016
New Revision: 298742
URL: https://svnweb.freebsd.org/changeset/base/298742

Log:
  TEGRA: Add interrupt support for Tegra GPIO controller.

Modified:
  head/sys/arm/nvidia/tegra_gpio.c

Modified: head/sys/arm/nvidia/tegra_gpio.c
==============================================================================
--- head/sys/arm/nvidia/tegra_gpio.c	Thu Apr 28 12:24:58 2016	(r298741)
+++ head/sys/arm/nvidia/tegra_gpio.c	Thu Apr 28 13:00:40 2016	(r298742)
@@ -30,21 +30,20 @@ __FBSDID("$FreeBSD$");
 /*
  * Tegra GPIO driver.
  */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
+#include "opt_platform.h"
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
-
+#include <sys/gpio.h>
 #include <sys/kernel.h>
-#include <sys/module.h>
+#include <sys/proc.h>
 #include <sys/rman.h>
 #include <sys/lock.h>
+#include <sys/module.h>
 #include <sys/mutex.h>
-#include <sys/gpio.h>
 
 #include <machine/bus.h>
+#include <machine/intr.h>
 #include <machine/resource.h>
 
 #include <dev/fdt/fdt_common.h>
@@ -53,14 +52,15 @@ __FBSDID("$FreeBSD$");
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
+#include "pic_if.h"
 
-#define	GPIO_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
-#define	GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
-#define	GPIO_LOCK_INIT(_sc)	mtx_init(&_sc->sc_mtx, 			\
-	    device_get_nameunit(_sc->sc_dev), "tegra_gpio", MTX_DEF)
-#define	GPIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
-#define	GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
-#define	GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
+#define	GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
+#define	GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
+#define	GPIO_LOCK_INIT(_sc)	mtx_init(&_sc->mtx, 			\
+	    device_get_nameunit(_sc->dev), "tegra_gpio", MTX_DEF)
+#define	GPIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->mtx);
+#define	GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->mtx, MA_OWNED);
+#define	GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED);
 
 #define	WR4(_sc, _r, _v)	bus_write_4((_sc)->mem_res, (_r), (_v))
 #define	RD4(_sc, _r)		bus_read_4((_sc)->mem_res, (_r))
@@ -87,6 +87,11 @@ __FBSDID("$FreeBSD$");
 #define	GPIO_INT_STA		0x40
 #define	GPIO_INT_ENB		0x50
 #define	GPIO_INT_LVL		0x60
+#define  GPIO_INT_LVL_DELTA		(1 << 16)
+#define  GPIO_INT_LVL_EDGE		(1 << 8)
+#define  GPIO_INT_LVL_HIGH		(1 << 0)
+#define  GPIO_INT_LVL_MASK		(GPIO_INT_LVL_DELTA |		\
+					 GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH)
 #define	GPIO_INT_CLR		0x70
 #define	GPIO_MSK_CNF		0x80
 #define	GPIO_MSK_OE		0x90
@@ -102,19 +107,33 @@ char *tegra_gpio_port_names[] = {
 	 "M",  "N",  "O",  "P", /* Bank 3 */
 	 "Q",  "R",  "S",  "T", /* Bank 4 */
 	 "U",  "V",  "W",  "X", /* Bank 5 */
-	 "Y",  "Z", "AA", "BB", /* Bank 5 */
-	"CC", "DD", "EE"	/* Bank 5 */
+	 "Y",  "Z", "AA", "BB", /* Bank 6 */
+	"CC", "DD", "EE"	/* Bank 7 */
+};
+
+struct tegra_gpio_irqsrc {
+	struct intr_irqsrc	isrc;
+	u_int			irq;
+	uint32_t		cfgreg;
+};
+
+struct tegra_gpio_softc;
+struct tegra_gpio_irq_cookie {
+	struct tegra_gpio_softc	*sc;
+	int			bank_num;
 };
 
 struct tegra_gpio_softc {
 	device_t		dev;
-	device_t		sc_busdev;
-	struct mtx		sc_mtx;
+	device_t		busdev;
+	struct mtx		mtx;
 	struct resource		*mem_res;
-	struct resource		*irq_res;
-	void			*gpio_ih;
+	struct resource		*irq_res[GPIO_NUM_BANKS];
+	void			*irq_ih[GPIO_NUM_BANKS];
+	struct tegra_gpio_irq_cookie irq_cookies[GPIO_NUM_BANKS];
 	int			gpio_npins;
 	struct gpio_pin		gpio_pins[NGPIO];
+	struct tegra_gpio_irqsrc *isrcs;
 };
 
 static struct ofw_compat_data compat_data[] = {
@@ -122,6 +141,11 @@ static struct ofw_compat_data compat_dat
 	{NULL,			0}
 };
 
+/* --------------------------------------------------------------------------
+ *
+ * GPIO
+ *
+ */
 static inline void
 gpio_write_masked(struct tegra_gpio_softc *sc, bus_size_t reg,
     struct gpio_pin *pin, uint32_t val)
@@ -134,6 +158,7 @@ gpio_write_masked(struct tegra_gpio_soft
 	tmp |= (val & 1) << bit;	/* value */
 	bus_write_4(sc->mem_res, reg + GPIO_REGNUM(pin->gp_pin), tmp);
 }
+
 static inline uint32_t
 gpio_read(struct tegra_gpio_softc *sc, bus_size_t reg, struct gpio_pin *pin)
 {
@@ -170,7 +195,7 @@ tegra_gpio_get_bus(device_t dev)
 	struct tegra_gpio_softc *sc;
 
 	sc = device_get_softc(dev);
-	return (sc->sc_busdev);
+	return (sc->busdev);
 }
 
 static int
@@ -308,32 +333,363 @@ tegra_gpio_pin_toggle(device_t dev, uint
 	return (0);
 }
 
+/* --------------------------------------------------------------------------
+ *
+ * Interrupts
+ *
+ */
+static inline void
+intr_write_masked(struct tegra_gpio_softc *sc, bus_addr_t reg,
+    struct tegra_gpio_irqsrc *tgi, uint32_t val)
+{
+	uint32_t tmp;
+	int bit;
+
+	bit = GPIO_BIT(tgi->irq);
+	tmp = 0x100 << bit;		/* mask */
+	tmp |= (val & 1) << bit;	/* value */
+	bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp);
+}
+
+static inline void
+intr_write_modify(struct tegra_gpio_softc *sc, bus_addr_t reg,
+    struct tegra_gpio_irqsrc *tgi, uint32_t val, uint32_t mask)
+{
+	uint32_t tmp;
+	int bit;
+
+	bit = GPIO_BIT(tgi->irq);
+	GPIO_LOCK(sc);
+	tmp = bus_read_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq));
+	tmp &= ~(mask << bit);
+	tmp |= val << bit;
+	bus_write_4(sc->mem_res, reg + GPIO_REGNUM(tgi->irq), tmp);
+	GPIO_UNLOCK(sc);
+}
+
+static inline void
+tegra_gpio_isrc_mask(struct tegra_gpio_softc *sc,
+     struct tegra_gpio_irqsrc *tgi, uint32_t val)
+{
+
+	intr_write_masked(sc, GPIO_MSK_INT_ENB, tgi, val);
+}
+
+static inline void
+tegra_gpio_isrc_eoi(struct tegra_gpio_softc *sc,
+     struct tegra_gpio_irqsrc *tgi)
+{
+
+	intr_write_masked(sc, GPIO_INT_CLR, tgi, 1);
+}
+
+static inline bool
+tegra_gpio_isrc_is_level(struct tegra_gpio_irqsrc *tgi)
+{
+
+	return (tgi->cfgreg & GPIO_INT_LVL_EDGE);
+}
+
 static int
 tegra_gpio_intr(void *arg)
 {
+	u_int irq, i, j, val, basepin;
 	struct tegra_gpio_softc *sc;
-	uint32_t val;
-	int i;
-
-	sc = arg;
-	for (i = 0; i < NGPIO; i += GPIO_PINS_IN_REG) {
-		/* Clear interrupt */
-		val = bus_read_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(i));
-		val &= bus_read_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(i));
-		bus_write_4(sc->mem_res, GPIO_INT_CLR + GPIO_REGNUM(i), val);
+	struct trapframe *tf;
+	struct tegra_gpio_irqsrc *tgi;
+	struct tegra_gpio_irq_cookie *cookie;
+
+	cookie = (struct tegra_gpio_irq_cookie *)arg;
+	sc = cookie->sc;
+	tf = curthread->td_intr_frame;
+
+	for (i = 0; i < GPIO_REGS_IN_BANK; i++) {
+		basepin  = cookie->bank_num * GPIO_REGS_IN_BANK *
+		    GPIO_PINS_IN_REG + i * GPIO_PINS_IN_REG;
+
+		val = bus_read_4(sc->mem_res, GPIO_INT_STA +
+		    GPIO_REGNUM(basepin));
+		val &= bus_read_4(sc->mem_res, GPIO_INT_ENB +
+		    GPIO_REGNUM(basepin));
 		/* Interrupt handling */
-#ifdef not_yet
 		for (j = 0; j < GPIO_PINS_IN_REG; j++) {
-			if (val & (1 << j))
-				handle_irq(i + j);
+			if ((val & (1 << j)) == 0)
+				continue;
+			irq = basepin + j;
+			tgi = &sc->isrcs[irq];
+			if (!tegra_gpio_isrc_is_level(tgi))
+				tegra_gpio_isrc_eoi(sc, tgi);
+			if (intr_isrc_dispatch(&tgi->isrc, tf) != 0) {
+				tegra_gpio_isrc_mask(sc, tgi, 0);
+				if (tegra_gpio_isrc_is_level(tgi))
+					tegra_gpio_isrc_eoi(sc, tgi);
+				device_printf(sc->dev,
+				    "Stray irq %u disabled\n", irq);
+			}
+
 		}
-		*/
-#endif
 	}
+
 	return (FILTER_HANDLED);
 }
 
 static int
+tegra_gpio_pic_attach(struct tegra_gpio_softc *sc)
+{
+	int error;
+	uint32_t irq;
+	const char *name;
+
+	sc->isrcs = malloc(sizeof(*sc->isrcs) * sc->gpio_npins, M_DEVBUF,
+	    M_WAITOK | M_ZERO);
+
+	name = device_get_nameunit(sc->dev);
+	for (irq = 0; irq < sc->gpio_npins; irq++) {
+		sc->isrcs[irq].irq = irq;
+		sc->isrcs[irq].cfgreg = 0;
+		error = intr_isrc_register(&sc->isrcs[irq].isrc,
+		    sc->dev, 0, "%s,%u", name, irq);
+		if (error != 0)
+			return (error); /* XXX deregister ISRCs */
+	}
+	return (intr_pic_register(sc->dev,
+	    OF_xref_from_node(ofw_bus_get_node(sc->dev))));
+}
+
+static int
+tegra_gpio_pic_detach(struct tegra_gpio_softc *sc)
+{
+
+	/*
+	 *  There has not been established any procedure yet
+	 *  how to detach PIC from living system correctly.
+	 */
+	device_printf(sc->dev, "%s: not implemented yet\n", __func__);
+	return (EBUSY);
+}
+
+
+static void
+tegra_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+	tegra_gpio_isrc_mask(sc, tgi, 0);
+}
+
+static void
+tegra_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+	tegra_gpio_isrc_mask(sc, tgi, 1);
+}
+
+static int
+tegra_gpio_pic_map_fdt(struct tegra_gpio_softc *sc, u_int ncells,
+    pcell_t *cells, u_int *irqp, uint32_t *regp)
+{
+	uint32_t reg;
+
+	/*
+	 * The first cell is the interrupt number.
+	 * The second cell is used to specify flags:
+	 *	bits[3:0] trigger type and level flags:
+	 *		1 = low-to-high edge triggered.
+	 *		2 = high-to-low edge triggered.
+	 *		4 = active high level-sensitive.
+	 *		8 = active low level-sensitive.
+	 */
+	if (ncells != 2 || cells[0] >= sc->gpio_npins)
+		return (EINVAL);
+
+	/*
+	 * All interrupt types could be set for an interrupt at one moment.
+	 * At least, the combination of 'low-to-high' and 'high-to-low' edge
+	 * triggered interrupt types can make a sense.
+	 */
+	if (cells[1] == 1)
+		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH;
+	else if (cells[1] == 2)
+		reg = GPIO_INT_LVL_EDGE;
+	else if (cells[1] == 3)
+		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA;
+	else if (cells[1] == 4)
+		reg = GPIO_INT_LVL_HIGH;
+	else if (cells[1] == 8)
+		reg = 0;
+	else
+		return (EINVAL);
+
+	*irqp = cells[0];
+	if (regp != NULL)
+		*regp = reg;
+	return (0);
+}
+
+
+static int
+tegra_gpio_pic_map_gpio(struct tegra_gpio_softc *sc, u_int gpio_pin_num,
+    u_int gpio_pin_flags, u_int intr_mode, u_int *irqp, uint32_t *regp)
+{
+
+	uint32_t reg;
+
+	if (gpio_pin_num >= sc->gpio_npins)
+		return (EINVAL);
+	switch (intr_mode) {
+	case GPIO_INTR_CONFORM:
+	case GPIO_INTR_LEVEL_LOW:
+		reg = 0;
+		break;
+	case GPIO_INTR_LEVEL_HIGH:
+		reg = GPIO_INT_LVL_HIGH;
+		break;
+	case GPIO_INTR_EDGE_RISING:
+		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_HIGH;
+		break;
+	case GPIO_INTR_EDGE_FALLING:
+		reg = GPIO_INT_LVL_EDGE;
+		break;
+	case GPIO_INTR_EDGE_BOTH:
+		reg = GPIO_INT_LVL_EDGE | GPIO_INT_LVL_DELTA;
+		break;
+	default:
+		return (EINVAL);
+	}
+	*irqp = gpio_pin_num;
+	if (regp != NULL)
+		*regp = reg;
+	return (0);
+}
+
+static int
+tegra_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+    struct intr_irqsrc **isrcp)
+{
+	int rv;
+	u_int irq;
+	struct tegra_gpio_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (data->type == INTR_MAP_DATA_FDT)
+		rv = tegra_gpio_pic_map_fdt(sc, data->fdt.ncells,
+		    data->fdt.cells, &irq, NULL);
+	else if (data->type == INTR_MAP_DATA_GPIO)
+		rv = tegra_gpio_pic_map_gpio(sc, data->gpio.gpio_pin_num,
+		   data->gpio.gpio_pin_flags, data->gpio.gpio_intr_mode,
+		       &irq, NULL);
+	else
+		return (ENOTSUP);
+
+	if (rv == 0)
+		*isrcp = &sc->isrcs[irq].isrc;
+	return (rv);
+}
+
+static void
+tegra_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+	if (tegra_gpio_isrc_is_level(tgi))
+		tegra_gpio_isrc_eoi(sc, tgi);
+}
+
+static void
+tegra_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+	tegra_gpio_isrc_mask(sc, tgi, 1);
+}
+
+static void
+tegra_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+
+	tegra_gpio_isrc_mask(sc, tgi, 0);
+	if (tegra_gpio_isrc_is_level(tgi))
+		tegra_gpio_isrc_eoi(sc, tgi);
+}
+
+static int
+tegra_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	u_int irq;
+	uint32_t cfgreg;
+	int rv;
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+
+	if (data == NULL)
+		return (ENOTSUP);
+
+	/* Get and check config for an interrupt. */
+	if (data->type == INTR_MAP_DATA_FDT)
+		rv = tegra_gpio_pic_map_fdt(sc, data->fdt.ncells,
+		    data->fdt.cells, &irq, &cfgreg);
+	else if (data->type == INTR_MAP_DATA_GPIO)
+		rv = tegra_gpio_pic_map_gpio(sc, data->gpio.gpio_pin_num,
+		   data->gpio.gpio_pin_flags, data->gpio.gpio_intr_mode,
+		       &irq, &cfgreg);
+	else
+		return (ENOTSUP);
+	if (rv != 0)
+		return (EINVAL);
+
+	/*
+	 * If this is a setup for another handler,
+	 * only check that its configuration match.
+	 */
+	if (isrc->isrc_handlers != 0)
+		return (tgi->cfgreg == cfgreg ? 0 : EINVAL);
+
+	tgi->cfgreg = cfgreg;
+	intr_write_modify(sc, GPIO_INT_LVL, tgi, cfgreg, GPIO_INT_LVL_MASK);
+	tegra_gpio_pic_enable_intr(dev, isrc);
+
+	return (0);
+}
+
+static int
+tegra_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	struct tegra_gpio_softc *sc;
+	struct tegra_gpio_irqsrc *tgi;
+
+	sc = device_get_softc(dev);
+	tgi = (struct tegra_gpio_irqsrc *)isrc;
+
+	if (isrc->isrc_handlers == 0)
+		tegra_gpio_isrc_mask(sc, tgi, 0);
+	return (0);
+}
+
+static int
 tegra_gpio_probe(device_t dev)
 {
 
@@ -347,23 +703,39 @@ tegra_gpio_probe(device_t dev)
 	return (ENXIO);
 }
 
+/* --------------------------------------------------------------------------
+ *
+ * Bus
+ *
+ */
 static int
 tegra_gpio_detach(device_t dev)
 {
 	struct tegra_gpio_softc *sc;
+	int i;
 
 	sc = device_get_softc(dev);
 
-	KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized"));
+	KASSERT(mtx_initialized(&sc->mtx), ("gpio mutex not initialized"));
+
+	for (i = 0; i < GPIO_NUM_BANKS; i++) {
+		if (sc->irq_ih[i] != NULL)
+			bus_teardown_intr(dev, sc->irq_res[i], sc->irq_ih[i]);
+	}
+
+	if (sc->isrcs != NULL)
+		tegra_gpio_pic_detach(sc);
 
 	gpiobus_detach_bus(dev);
-	if (sc->gpio_ih != NULL)
-		bus_teardown_intr(dev, sc->irq_res, sc->gpio_ih);
-	if (sc->irq_res != NULL)
-		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
+
+	for (i = 0; i < GPIO_NUM_BANKS; i++) {
+		if (sc->irq_res[i] != NULL)
+			bus_release_resource(dev, SYS_RES_IRQ, 0,
+			    sc->irq_res[i]);
+	}
 	if (sc->mem_res != NULL)
 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
-	mtx_destroy(&sc->sc_mtx);
+	GPIO_LOCK_DESTROY(sc);
 
 	return(0);
 }
@@ -375,7 +747,8 @@ tegra_gpio_attach(device_t dev)
 	int i, rid;
 
 	sc = device_get_softc(dev);
-	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+	sc->dev = dev;
+	GPIO_LOCK_INIT(sc);
 
 	/* Allocate bus_space resources. */
 	rid = 0;
@@ -387,28 +760,13 @@ tegra_gpio_attach(device_t dev)
 		return (ENXIO);
 	}
 
-	rid = 0;
-	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
-	if (sc->irq_res == NULL) {
-		device_printf(dev, "Cannot allocate IRQ resources\n");
-		tegra_gpio_detach(dev);
-		return (ENXIO);
-	}
-
-	sc->dev = dev;
 	sc->gpio_npins = NGPIO;
-
-	if ((bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC,
-	    tegra_gpio_intr, NULL, sc, &sc->gpio_ih))) {
-		device_printf(dev,
-		    "WARNING: unable to register interrupt handler\n");
-		tegra_gpio_detach(dev);
-		return (ENXIO);
-	}
-
 	for (i = 0; i < sc->gpio_npins; i++) {
 		sc->gpio_pins[i].gp_pin = i;
-		sc->gpio_pins[i].gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+		sc->gpio_pins[i].gp_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
+		    GPIO_INTR_LEVEL_LOW | GPIO_INTR_LEVEL_HIGH | 
+		    GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING |
+		    GPIO_INTR_EDGE_BOTH;
 		snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "gpio_%s.%d",
 		    tegra_gpio_port_names[ i / GPIO_PINS_IN_REG],
 		    i % GPIO_PINS_IN_REG);
@@ -417,8 +775,43 @@ tegra_gpio_attach(device_t dev)
 		    GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
 	}
 
-	sc->sc_busdev = gpiobus_attach_bus(dev);
-	if (sc->sc_busdev == NULL) {
+	/* Init interrupt related registes. */
+	for (i = 0; i < sc->gpio_npins; i += GPIO_PINS_IN_REG) {
+		bus_write_4(sc->mem_res, GPIO_INT_ENB + GPIO_REGNUM(i), 0);
+		bus_write_4(sc->mem_res, GPIO_INT_STA + GPIO_REGNUM(i), 0xFF);
+		bus_write_4(sc->mem_res, GPIO_INT_CLR + GPIO_REGNUM(i), 0xFF);
+	}
+
+	/* Allocate interrupts. */
+	for (i = 0; i < GPIO_NUM_BANKS; i++) {
+		sc->irq_cookies[i].sc = sc;
+		sc->irq_cookies[i].bank_num = i;
+		rid = i;
+		sc->irq_res[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+		    &rid, RF_ACTIVE);
+		if (sc->irq_res[i] == NULL) {
+			device_printf(dev, "Cannot allocate IRQ resources\n");
+			tegra_gpio_detach(dev);
+			return (ENXIO);
+		}
+		if ((bus_setup_intr(dev, sc->irq_res[i],
+		    INTR_TYPE_MISC | INTR_MPSAFE, tegra_gpio_intr, NULL,
+		    &sc->irq_cookies[i], &sc->irq_ih[i]))) {
+			device_printf(dev,
+			    "WARNING: unable to register interrupt handler\n");
+			tegra_gpio_detach(dev);
+			return (ENXIO);
+		}
+	}
+
+	if (tegra_gpio_pic_attach(sc) != 0) {
+		device_printf(dev, "WARNING: unable to attach PIC\n");
+		tegra_gpio_detach(dev);
+		return (ENXIO);
+	}
+
+	sc->busdev = gpiobus_attach_bus(dev);
+	if (sc->busdev == NULL) {
 		tegra_gpio_detach(dev);
 		return (ENXIO);
 	}
@@ -451,6 +844,16 @@ static device_method_t tegra_gpio_method
 	DEVMETHOD(device_attach,	tegra_gpio_attach),
 	DEVMETHOD(device_detach,	tegra_gpio_detach),
 
+	/* Interrupt controller interface */
+	DEVMETHOD(pic_disable_intr,	tegra_gpio_pic_disable_intr),
+	DEVMETHOD(pic_enable_intr,	tegra_gpio_pic_enable_intr),
+	DEVMETHOD(pic_map_intr,		tegra_gpio_pic_map_intr),
+	DEVMETHOD(pic_setup_intr,	tegra_gpio_pic_setup_intr),
+	DEVMETHOD(pic_teardown_intr,	tegra_gpio_pic_teardown_intr),
+	DEVMETHOD(pic_post_filter,	tegra_gpio_pic_post_filter),
+	DEVMETHOD(pic_post_ithread,	tegra_gpio_pic_post_ithread),
+	DEVMETHOD(pic_pre_ithread,	tegra_gpio_pic_pre_ithread),
+
 	/* GPIO protocol */
 	DEVMETHOD(gpio_get_bus,		tegra_gpio_get_bus),
 	DEVMETHOD(gpio_pin_max,		tegra_gpio_pin_max),


More information about the svn-src-head mailing list