summaryrefslogtreecommitdiffstats
path: root/sys/arm/freescale
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2016-05-26 22:34:25 +0000
committerian <ian@FreeBSD.org>2016-05-26 22:34:25 +0000
commita8f815c9b35219b3441f723f1e2f2c5b7b50b25f (patch)
treeb9b3d747440bc36f7826dedc259f07f27d1cfc13 /sys/arm/freescale
parent761a966fdf9b5793e3d3cc43d695cdcd89b26b30 (diff)
downloadFreeBSD-src-a8f815c9b35219b3441f723f1e2f2c5b7b50b25f.zip
FreeBSD-src-a8f815c9b35219b3441f723f1e2f2c5b7b50b25f.tar.gz
Add support for triggering interrupts on both rising and falling edges.
Also, EOI a gpio interrupt in the post_ithread routine before re-enabling.
Diffstat (limited to 'sys/arm/freescale')
-rw-r--r--sys/arm/freescale/imx/imx_gpio.c106
1 files changed, 67 insertions, 39 deletions
diff --git a/sys/arm/freescale/imx/imx_gpio.c b/sys/arm/freescale/imx/imx_gpio.c
index 1f17f5c..922548e 100644
--- a/sys/arm/freescale/imx/imx_gpio.c
+++ b/sys/arm/freescale/imx/imx_gpio.c
@@ -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 *sc, struct intr_map_data_fdt *daf,
* 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 *sc, struct intr_map_data_fdt *daf,
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_softc *sc, struct intr_map_data_gpio *dag,
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_softc *sc, struct intr_map_data_gpio *dag,
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 intr_irqsrc *isrc,
{
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 intr_irqsrc *isrc,
/* 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, struct intr_irqsrc *isrc)
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);
}
OpenPOWER on IntegriCloud