From 30ac0d3b0702ea3b6ad19b0c1452831526fe03d5 Mon Sep 17 00:00:00 2001 From: Ken Ma Date: Thu, 19 Oct 2017 15:10:03 +0200 Subject: [PATCH] pinctrl: armada-37xx: Add edge both type gpio irq support 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 Signed-off-by: Gregory CLEMENT Signed-off-by: Linus Walleij --- 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);