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),
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to