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