summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpio/gpio-pl061.c55
1 files changed, 47 insertions, 8 deletions
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 229ef653e0f8..5b1461f6aeed 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -158,24 +158,63 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
 	gpiois = readb(chip->base + GPIOIS);
 	gpioibe = readb(chip->base + GPIOIBE);
 
+	if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
+	    (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
+	{
+		dev_err(gc->dev,
+			"trying to configure line %d for both level and edge "
+			"detection, choose one!\n",
+			offset);
+		return -EINVAL;
+	}
+
 	if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
+		bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
+
+		/* Disable edge detection */
+		gpioibe &= ~bit;
+		/* Enable level detection */
 		gpiois |= bit;
-		if (trigger & IRQ_TYPE_LEVEL_HIGH)
+		/* Select polarity */
+		if (polarity)
 			gpioiev |= bit;
 		else
 			gpioiev &= ~bit;
-	} else
-		gpiois &= ~bit;
 
-	if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
-		/* Setting this makes GPIOEV be ignored */
+		dev_dbg(gc->dev, "line %d: IRQ on %s level\n",
+			offset,
+			polarity ? "HIGH" : "LOW");
+	} else if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
+		/* Disable level detection */
+		gpiois &= ~bit;
+		/* Select both edges, setting this makes GPIOEV be ignored */
 		gpioibe |= bit;
-	else {
+
+		dev_dbg(gc->dev, "line %d: IRQ on both edges\n", offset);
+	} else if ((trigger & IRQ_TYPE_EDGE_RISING) ||
+		   (trigger & IRQ_TYPE_EDGE_FALLING)) {
+		bool rising = trigger & IRQ_TYPE_EDGE_RISING;
+
+		/* Disable level detection */
+		gpiois &= ~bit;
+		/* Clear detection on both edges */
 		gpioibe &= ~bit;
-		if (trigger & IRQ_TYPE_EDGE_RISING)
+		/* Select edge */
+		if (rising)
 			gpioiev |= bit;
-		else if (trigger & IRQ_TYPE_EDGE_FALLING)
+		else
 			gpioiev &= ~bit;
+
+		dev_dbg(gc->dev, "line %d: IRQ on %s edge\n",
+			offset,
+			rising ? "RISING" : "FALLING");
+	} else {
+		/* No trigger: disable everything */
+		gpiois &= ~bit;
+		gpioibe &= ~bit;
+		gpioiev &= ~bit;
+		dev_warn(gc->dev, "no trigger selected for line %d\n",
+			 offset);
 	}
 
 	writeb(gpiois, chip->base + GPIOIS);