From: Ken Ma <m...@marvell.com>

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 <m...@marvell.com>
Signed-off-by: Gregory CLEMENT <gregory.clem...@free-electrons.com>
---
 drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 64 +++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c 
b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
index 71b944748304..4e8d836a8c6f 100644
--- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
+++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c
@@ -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);
 
-- 
2.14.2

Reply via email to