Date: Thu, 28 Apr 2016 13:00:40 +0000 (UTC) From: Michal Meloun <mmel@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r298742 - head/sys/arm/nvidia Message-ID: <201604281300.u3SD0eDR026868@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
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),
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201604281300.u3SD0eDR026868>