Date: Thu, 26 May 2016 22:34:25 +0000 (UTC) From: Ian Lepore <ian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r300786 - head/sys/arm/freescale/imx Message-ID: <201605262234.u4QMYP3T054460@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: ian Date: Thu May 26 22:34:25 2016 New Revision: 300786 URL: https://svnweb.freebsd.org/changeset/base/300786 Log: Add support for triggering interrupts on both rising and falling edges. Also, EOI a gpio interrupt in the post_ithread routine before re-enabling. Modified: head/sys/arm/freescale/imx/imx_gpio.c Modified: head/sys/arm/freescale/imx/imx_gpio.c ============================================================================== --- head/sys/arm/freescale/imx/imx_gpio.c Thu May 26 22:13:40 2016 (r300785) +++ head/sys/arm/freescale/imx/imx_gpio.c Thu May 26 22:34:25 2016 (r300786) @@ -84,6 +84,7 @@ __FBSDID("$FreeBSD$"); #define GPIO_ICR_COND_HIGH 1 #define GPIO_ICR_COND_RISE 2 #define GPIO_ICR_COND_FALL 3 +#define GPIO_ICR_COND_MASK 0x3 #define IMX_GPIO_IMR_REG 0x014 /* Interrupt Mask Register */ #define IMX_GPIO_ISR_REG 0x018 /* Interrupt Status Register */ #define IMX_GPIO_EDGE_REG 0x01C /* Edge Detect Register */ @@ -91,7 +92,7 @@ __FBSDID("$FreeBSD$"); #ifdef INTRNG #define DEFAULT_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_FALLING | GPIO_INTR_EDGE_BOTH) #else #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) #endif @@ -177,7 +178,8 @@ gpio_pic_map_fdt(struct imx51_gpio_softc * 2 = high-to-low edge triggered. * 4 = active high level-sensitive. * 8 = active low level-sensitive. - * We can do any single one of these modes, but nothing in combo. + * We can do any single one of these modes, and also edge low+high + * (i.e., trigger on both edges); other combinations are not supported. */ if (daf->ncells != 2) { @@ -197,6 +199,9 @@ gpio_pic_map_fdt(struct imx51_gpio_softc case 2: mode = GPIO_INTR_EDGE_FALLING; break; + case 3: + mode = GPIO_INTR_EDGE_BOTH; + break; case 4: mode = GPIO_INTR_LEVEL_HIGH; break; @@ -219,7 +224,6 @@ gpio_pic_map_gpio(struct imx51_gpio_soft u_int *irqp, uint32_t *modep) { u_int irq; - uint32_t mode; irq = dag->gpio_pin_num; if (irq >= sc->gpio_npins) { @@ -227,17 +231,22 @@ gpio_pic_map_gpio(struct imx51_gpio_soft return (EINVAL); } - mode = dag->gpio_intr_mode; - if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH && - mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING) { + switch (dag->gpio_intr_mode) { + case GPIO_INTR_LEVEL_LOW: + case GPIO_INTR_LEVEL_HIGH: + case GPIO_INTR_EDGE_RISING: + case GPIO_INTR_EDGE_FALLING: + case GPIO_INTR_EDGE_BOTH: + break; + default: device_printf(sc->dev, "Unsupported interrupt mode 0x%8x\n", - mode); + dag->gpio_intr_mode); return (EINVAL); } *irqp = irq; if (modep != NULL) - *modep = mode; + *modep = dag->gpio_intr_mode; return (0); } @@ -300,8 +309,8 @@ gpio_pic_setup_intr(device_t dev, struct { struct imx51_gpio_softc *sc; struct gpio_irqsrc *gi; - int error, icfg; - u_int irq, reg, shift, wrk; + int error; + u_int icfg, irq, reg, shift, wrk; uint32_t mode; if (data == NULL) @@ -320,39 +329,52 @@ gpio_pic_setup_intr(device_t dev, struct /* Compare config if this is not first setup. */ if (isrc->isrc_handlers != 0) return (gi->gi_mode == mode ? 0 : EINVAL); - gi->gi_mode = mode; - switch (mode) { - case GPIO_INTR_LEVEL_LOW: - icfg = GPIO_ICR_COND_LOW; - break; - case GPIO_INTR_LEVEL_HIGH: - icfg = GPIO_ICR_COND_HIGH; - break; - case GPIO_INTR_EDGE_RISING: - icfg = GPIO_ICR_COND_RISE; - break; - case GPIO_INTR_EDGE_FALLING: - icfg = GPIO_ICR_COND_FALL; - break; - } - if (irq < 16) { - reg = IMX_GPIO_ICR1_REG; - shift = 2 * irq; + /* + * To interrupt on both edges we have to use the EDGE register. The + * manual says it only exists for backwards compatibilty with older imx + * chips, but it's also the only way to configure interrupting on both + * edges. If the EDGE bit is on, the corresponding ICRn bit is ignored. + */ + mtx_lock_spin(&sc->sc_mtx); + if (mode == GPIO_INTR_EDGE_BOTH) { + SET4(sc, IMX_GPIO_EDGE_REG, (1u << irq)); } else { - reg = IMX_GPIO_ICR2_REG; - shift = 2 * (irq - 16); + CLEAR4(sc, IMX_GPIO_EDGE_REG, (1u << irq)); + switch (mode) { + default: + /* silence warnings; default can't actually happen. */ + /* FALLTHROUGH */ + case GPIO_INTR_LEVEL_LOW: + icfg = GPIO_ICR_COND_LOW; + break; + case GPIO_INTR_LEVEL_HIGH: + icfg = GPIO_ICR_COND_HIGH; + break; + case GPIO_INTR_EDGE_RISING: + icfg = GPIO_ICR_COND_RISE; + break; + case GPIO_INTR_EDGE_FALLING: + icfg = GPIO_ICR_COND_FALL; + break; + } + if (irq < 16) { + reg = IMX_GPIO_ICR1_REG; + shift = 2 * irq; + } else { + reg = IMX_GPIO_ICR2_REG; + shift = 2 * (irq - 16); + } + wrk = READ4(sc, reg); + wrk &= ~(GPIO_ICR_COND_MASK << shift); + wrk |= icfg << shift; + WRITE4(sc, reg, wrk); } - - mtx_lock_spin(&sc->sc_mtx); - CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); - WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); - wrk = READ4(sc, reg); - wrk &= ~(0x03 << shift); - wrk |= icfg << shift; - WRITE4(sc, reg, wrk); + WRITE4(sc, IMX_GPIO_ISR_REG, (1u << irq)); + SET4(sc, IMX_GPIO_IMR_REG, (1u << irq)); mtx_unlock_spin(&sc->sc_mtx); + return (0); } @@ -407,15 +429,21 @@ gpio_pic_post_filter(device_t dev, struc static void gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) { + struct imx51_gpio_softc *sc; + u_int irq; + + sc = device_get_softc(dev); + irq = ((struct gpio_irqsrc *)isrc)->gi_irq; arm_irq_memory_barrier(0); + /* EOI. W1C reg so no r-m-w, no locking needed. */ + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); gpio_pic_enable_intr(dev, isrc); } static void gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) { - gpio_pic_disable_intr(dev, isrc); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201605262234.u4QMYP3T054460>