svn commit: r300786 - head/sys/arm/freescale/imx

Ian Lepore ian at FreeBSD.org
Thu May 26 22:34:27 UTC 2016


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);
 }
 


More information about the svn-src-all mailing list