OSDN Git Service

pinctrl: armada-37xx: Add edge both type gpio irq support
authorKen Ma <make@marvell.com>
Thu, 19 Oct 2017 13:10:03 +0000 (15:10 +0200)
committerLinus Walleij <linus.walleij@linaro.org>
Tue, 31 Oct 2017 12:06:15 +0000 (13:06 +0100)
Current edge both type gpio irqs which need to swap polarity in each
interrupt are not supported, this patch adds edge both type gpio irq
support.

Signed-off-by: Ken Ma <make@marvell.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/pinctrl/mvebu/pinctrl-armada-37xx.c

index 71b9447..4e8d836 100644 (file)
@@ -576,6 +576,19 @@ static int armada_37xx_irq_set_type(struct irq_data *d, unsigned int type)
        case IRQ_TYPE_EDGE_FALLING:
                val |= (BIT(d->hwirq % GPIO_PER_REG));
                break;
+       case IRQ_TYPE_EDGE_BOTH: {
+               u32 in_val, in_reg = INPUT_VAL;
+
+               armada_37xx_irq_update_reg(&in_reg, d);
+               regmap_read(info->regmap, in_reg, &in_val);
+
+               /* Set initial polarity based on current input level. */
+               if (in_val & d->mask)
+                       val |= d->mask;         /* falling */
+               else
+                       val &= ~d->mask;        /* rising */
+               break;
+       }
        default:
                spin_unlock_irqrestore(&info->irq_lock, flags);
                return -EINVAL;
@@ -586,6 +599,40 @@ static int armada_37xx_irq_set_type(struct irq_data *d, unsigned int type)
        return 0;
 }
 
+static int armada_37xx_edge_both_irq_swap_pol(struct armada_37xx_pinctrl *info,
+                                            u32 pin_idx)
+{
+       u32 reg_idx = pin_idx / GPIO_PER_REG;
+       u32 bit_num = pin_idx % GPIO_PER_REG;
+       u32 p, l, ret;
+       unsigned long flags;
+
+       regmap_read(info->regmap, INPUT_VAL + 4*reg_idx, &l);
+
+       spin_lock_irqsave(&info->irq_lock, flags);
+       p = readl(info->base + IRQ_POL + 4 * reg_idx);
+       if ((p ^ l) & (1 << bit_num)) {
+               /*
+                * For the gpios which are used for both-edge irqs, when their
+                * interrupts happen, their input levels are changed,
+                * yet their interrupt polarities are kept in old values, we
+                * should synchronize their interrupt polarities; for example,
+                * at first a gpio's input level is low and its interrupt
+                * polarity control is "Detect rising edge", then the gpio has
+                * a interrupt , its level turns to high, we should change its
+                * polarity control to "Detect falling edge" correspondingly.
+                */
+               p ^= 1 << bit_num;
+               writel(p, info->base + IRQ_POL + 4 * reg_idx);
+               ret = 0;
+       } else {
+               /* Spurious irq */
+               ret = -1;
+       }
+
+       spin_unlock_irqrestore(&info->irq_lock, flags);
+       return ret;
+}
 
 static void armada_37xx_irq_handler(struct irq_desc *desc)
 {
@@ -609,6 +656,23 @@ static void armada_37xx_irq_handler(struct irq_desc *desc)
                        u32 hwirq = ffs(status) - 1;
                        u32 virq = irq_find_mapping(d, hwirq +
                                                     i * GPIO_PER_REG);
+                       u32 t = irq_get_trigger_type(virq);
+
+                       if ((t & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
+                               /* Swap polarity (race with GPIO line) */
+                               if (armada_37xx_edge_both_irq_swap_pol(info,
+                                       hwirq + i * GPIO_PER_REG)) {
+                                       /*
+                                        * For spurious irq, which gpio level
+                                        * is not as expected after incoming
+                                        * edge, just ack the gpio irq.
+                                        */
+                                       writel(1 << hwirq,
+                                              info->base +
+                                              IRQ_STATUS + 4 * i);
+                                       continue;
+                               }
+                       }
 
                        generic_handle_irq(virq);