sunxi: initial 3.14 patchset
[openwrt/openwrt.git] / target / linux / sunxi / patches-3.14 / 136-1-irqchip-sun4i-fixes.patch
diff --git a/target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch b/target/linux/sunxi/patches-3.14/136-1-irqchip-sun4i-fixes.patch
new file mode 100644 (file)
index 0000000..5a8c83c
--- /dev/null
@@ -0,0 +1,415 @@
+From 843da234cfc0e7014f9e2da82786a485e0820665 Mon Sep 17 00:00:00 2001
+From: Thomas Gleixner <tglx@linutronix.de>
+Date: Thu, 13 Mar 2014 15:32:05 +0100
+Subject: [PATCH] irq: Add a new IRQCHIP_EOI_THREADED flag
+
+This flag must be used in combination with handle_fasteoi_irq, when set
+handle_fasteoi_irq will delay the calling of chip->irq_eoi until the threaded
+handler has run.
+
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Tested-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ include/linux/irq.h    |  3 +++
+ kernel/irq/chip.c      | 48 ++++++++++++++++++++++++++++++++++++++++--------
+ kernel/irq/internals.h |  1 +
+ kernel/irq/manage.c    |  2 +-
+ 4 files changed, 45 insertions(+), 9 deletions(-)
+
+diff --git a/include/linux/irq.h b/include/linux/irq.h
+index 7dc1003..0f036fb 100644
+--- a/include/linux/irq.h
++++ b/include/linux/irq.h
+@@ -349,6 +349,8 @@ struct irq_chip {
+  * IRQCHIP_ONOFFLINE_ENABLED: Only call irq_on/off_line callbacks
+  *                            when irq enabled
+  * IRQCHIP_SKIP_SET_WAKE:     Skip chip.irq_set_wake(), for this irq chip
++ * IRQCHIP_ONESHOT_SAFE:      One shot does not require mask/unmask
++ * IRQCHIP_EOI_THREADED:      Chip requires eoi() on unmask in threaded mode
+  */
+ enum {
+       IRQCHIP_SET_TYPE_MASKED         = (1 <<  0),
+@@ -357,6 +359,7 @@ enum {
+       IRQCHIP_ONOFFLINE_ENABLED       = (1 <<  3),
+       IRQCHIP_SKIP_SET_WAKE           = (1 <<  4),
+       IRQCHIP_ONESHOT_SAFE            = (1 <<  5),
++      IRQCHIP_EOI_THREADED            = (1 <<  6),
+ };
+ /* This include will go away once we isolated irq_desc usage to core code */
+diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
+index dc04c16..6397df2 100644
+--- a/kernel/irq/chip.c
++++ b/kernel/irq/chip.c
+@@ -281,6 +281,19 @@ void unmask_irq(struct irq_desc *desc)
+       }
+ }
++void unmask_threaded_irq(struct irq_desc *desc)
++{
++      struct irq_chip *chip = desc->irq_data.chip;
++
++      if (chip->flags & IRQCHIP_EOI_THREADED)
++              chip->irq_eoi(&desc->irq_data);
++
++      if (chip->irq_unmask) {
++              chip->irq_unmask(&desc->irq_data);
++              irq_state_clr_masked(desc);
++      }
++}
++
+ /*
+  *    handle_nested_irq - Handle a nested irq from a irq thread
+  *    @irq:   the interrupt number
+@@ -435,6 +448,27 @@ static inline void preflow_handler(struct irq_desc *desc)
+ static inline void preflow_handler(struct irq_desc *desc) { }
+ #endif
++static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip)
++{
++      if (!(desc->istate & IRQS_ONESHOT)) {
++              chip->irq_eoi(&desc->irq_data);
++              return;
++      }
++      /*
++       * We need to unmask in the following cases:
++       * - Oneshot irq which did not wake the thread (caused by a
++       *   spurious interrupt or a primary handler handling it
++       *   completely).
++       */
++      if (!irqd_irq_disabled(&desc->irq_data) &&
++          irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) {
++              chip->irq_eoi(&desc->irq_data);
++              unmask_irq(desc);
++      } else if (!(chip->flags & IRQCHIP_EOI_THREADED)) {
++              chip->irq_eoi(&desc->irq_data);
++      }
++}
++
+ /**
+  *    handle_fasteoi_irq - irq handler for transparent controllers
+  *    @irq:   the interrupt number
+@@ -448,6 +482,8 @@ static inline void preflow_handler(struct irq_desc *desc) { }
+ void
+ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
+ {
++      struct irq_chip *chip = desc->irq_data.chip;
++
+       raw_spin_lock(&desc->lock);
+       if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
+@@ -473,18 +509,14 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
+       preflow_handler(desc);
+       handle_irq_event(desc);
+-      if (desc->istate & IRQS_ONESHOT)
+-              cond_unmask_irq(desc);
++      cond_unmask_eoi_irq(desc, chip);
+-out_eoi:
+-      desc->irq_data.chip->irq_eoi(&desc->irq_data);
+-out_unlock:
+       raw_spin_unlock(&desc->lock);
+       return;
+ out:
+-      if (!(desc->irq_data.chip->flags & IRQCHIP_EOI_IF_HANDLED))
+-              goto out_eoi;
+-      goto out_unlock;
++      if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
++              chip->irq_eoi(&desc->irq_data);
++      raw_spin_unlock(&desc->lock);
+ }
+ /**
+diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
+index 001fa5b..e98bb56 100644
+--- a/kernel/irq/internals.h
++++ b/kernel/irq/internals.h
+@@ -73,6 +73,7 @@ extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu);
+ extern void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu);
+ extern void mask_irq(struct irq_desc *desc);
+ extern void unmask_irq(struct irq_desc *desc);
++extern void unmask_threaded_irq(struct irq_desc *desc);
+ extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr);
+diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
+index d3bf660..7593958 100644
+--- a/kernel/irq/manage.c
++++ b/kernel/irq/manage.c
+@@ -718,7 +718,7 @@ static void irq_finalize_oneshot(struct irq_desc *desc,
+       if (!desc->threads_oneshot && !irqd_irq_disabled(&desc->irq_data) &&
+           irqd_irq_masked(&desc->irq_data))
+-              unmask_irq(desc);
++              unmask_threaded_irq(desc);
+ out_unlock:
+       raw_spin_unlock_irq(&desc->lock);
+-- 
+2.0.3
+
+From d000f9a5348e6d6c8b620a9c2d0b97c69d6d6153 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 11 Mar 2014 16:47:46 +0100
+Subject: [PATCH] irqchip: sun4i: Fix irq 0 not working
+
+SUN4I_IRQ_VECTOR_REG containing 0 can mean one of 3 things:
+1) no more irqs pending
+2) irq 0 pending
+3) spurious irq
+
+So if we immediately get a reading of 0, check the irq-pending reg
+to differentiate between 2 and 3. We only do this once to avoid
+the extra check in the common case of 1) hapening after having
+read the vector-reg once.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+---
+ drivers/irqchip/irq-sun4i.c | 18 ++++++++++++++++--
+ 1 file changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
+index a5438d8..5c25048 100644
+--- a/drivers/irqchip/irq-sun4i.c
++++ b/drivers/irqchip/irq-sun4i.c
+@@ -140,10 +140,24 @@ static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *re
+ {
+       u32 irq, hwirq;
++      /*
++       * hwirq == 0 can mean one of 3 things:
++       * 1) no more irqs pending
++       * 2) irq 0 pending
++       * 3) spurious irq
++       * So if we immediately get a reading of 0, check the irq-pending reg
++       * to differentiate between 2 and 3. We only do this once to avoid
++       * the extra check in the common case of 1 hapening after having
++       * read the vector-reg once.
++       */
+       hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
+-      while (hwirq != 0) {
++      if (hwirq == 0 &&
++                !(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0)))
++              return;
++
++      do {
+               irq = irq_find_mapping(sun4i_irq_domain, hwirq);
+               handle_IRQ(irq, regs);
+               hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
+-      }
++      } while (hwirq != 0);
+ }
+-- 
+2.0.3
+
+From b37587009473582d9fc080e8b8b99b67b0077a90 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 11 Mar 2014 16:53:23 +0100
+Subject: [PATCH] irqchip: sun4i: Fix a comment about mask register
+ initialization
+
+The comment was claiming that we were masking all irqs, while the code actually
+*un*masks all of them.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+---
+ drivers/irqchip/irq-sun4i.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
+index 5c25048..8a2fbee 100644
+--- a/drivers/irqchip/irq-sun4i.c
++++ b/drivers/irqchip/irq-sun4i.c
+@@ -109,7 +109,7 @@ static int __init sun4i_of_init(struct device_node *node,
+       writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1));
+       writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2));
+-      /* Mask all the interrupts */
++      /* Unmask all the interrupts, ENABLE_REG(x) is used for masking */
+       writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0));
+       writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1));
+       writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2));
+-- 
+2.0.3
+
+From c8865ee82b74b2d95339370972a0d9bfdbac09cf Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 12 Mar 2014 17:43:45 +0100
+Subject: [PATCH] irqchip: sun4i: Don't ack IRQs != 0, fix acking of IRQ 0
+
+All IRQs except for IRQ 0 seem to not need acking, so drop acking for them.
+
+The ENMI needs to have the ack done *after* clearing the interrupt source,
+otherwise we will get a spurious interrupt for each real interrupt.
+
+So use the new IRQCHIP_EOI_THREADED flag for this in combination with
+handle_fasteoi_irq. This uses a separate irq_chip struct for IRQ 0,
+since we only want this behavior for IRQ 0.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ drivers/irqchip/irq-sun4i.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
+index 8a2fbee..a0ed1ea 100644
+--- a/drivers/irqchip/irq-sun4i.c
++++ b/drivers/irqchip/irq-sun4i.c
+@@ -76,16 +76,29 @@ static void sun4i_irq_unmask(struct irq_data *irqd)
+ static struct irq_chip sun4i_irq_chip = {
+       .name           = "sun4i_irq",
+-      .irq_ack        = sun4i_irq_ack,
+       .irq_mask       = sun4i_irq_mask,
+       .irq_unmask     = sun4i_irq_unmask,
+ };
++/* IRQ 0 / the ENMI needs a late eoi call */
++static struct irq_chip sun4i_irq_chip_enmi = {
++      .name           = "sun4i_irq",
++      .irq_eoi        = sun4i_irq_ack,
++      .irq_mask       = sun4i_irq_mask,
++      .irq_unmask     = sun4i_irq_unmask,
++      .flags          = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED,
++};
++
+ static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,
+                        irq_hw_number_t hw)
+ {
+-      irq_set_chip_and_handler(virq, &sun4i_irq_chip,
+-                               handle_level_irq);
++      if (hw == 0)
++              irq_set_chip_and_handler(virq, &sun4i_irq_chip_enmi,
++                                       handle_fasteoi_irq);
++      else
++              irq_set_chip_and_handler(virq, &sun4i_irq_chip,
++                                       handle_level_irq);
++
+       set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+       return 0;
+-- 
+2.0.3
+
+From f8b4347aa12d7a30aa1d3e5bfcdccece52d17af3 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 13 Mar 2014 19:38:26 +0100
+Subject: [PATCH] irqchip: sun4i: Use handle_fasteoi_irq for all interrupts
+
+Since the sun4i irq chip does not require any action and clears the interrupt
+when the level goes back to inactive, we don't need to mask / unmask for
+non oneshot IRQs, to achieve this we make sun4i_irq_ack a nop for all irqs
+except irq 0 and use handle_fasteoi_irq for all interrupts.
+
+Now there might be a case when the device reactivates the interrupt
+before the RETI. But that does not matter as we run the primary
+interrupt handlers with interrupts disabled.
+
+This also allows us to get rid of needing to use 2 irq_chip structs, this
+means that the IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED will now influence
+all interrupts rather then just irq 0, but that does not matter as the eoi
+is now a nop anyways for all interrupts but irq 0.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+---
+ drivers/irqchip/irq-sun4i.c | 18 ++++--------------
+ 1 file changed, 4 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
+index a0ed1ea..6a8c88d 100644
+--- a/drivers/irqchip/irq-sun4i.c
++++ b/drivers/irqchip/irq-sun4i.c
+@@ -45,6 +45,9 @@ static void sun4i_irq_ack(struct irq_data *irqd)
+       int reg = irq / 32;
+       u32 val;
++      if (irq != 0)
++              return; /* Only IRQ 0 / the ENMI needs to be acked */
++
+       val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg));
+       writel(val | (1 << irq_off),
+              sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg));
+@@ -76,13 +79,6 @@ static void sun4i_irq_unmask(struct irq_data *irqd)
+ static struct irq_chip sun4i_irq_chip = {
+       .name           = "sun4i_irq",
+-      .irq_mask       = sun4i_irq_mask,
+-      .irq_unmask     = sun4i_irq_unmask,
+-};
+-
+-/* IRQ 0 / the ENMI needs a late eoi call */
+-static struct irq_chip sun4i_irq_chip_enmi = {
+-      .name           = "sun4i_irq",
+       .irq_eoi        = sun4i_irq_ack,
+       .irq_mask       = sun4i_irq_mask,
+       .irq_unmask     = sun4i_irq_unmask,
+@@ -92,13 +88,7 @@ static struct irq_chip sun4i_irq_chip_enmi = {
+ static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,
+                        irq_hw_number_t hw)
+ {
+-      if (hw == 0)
+-              irq_set_chip_and_handler(virq, &sun4i_irq_chip_enmi,
+-                                       handle_fasteoi_irq);
+-      else
+-              irq_set_chip_and_handler(virq, &sun4i_irq_chip,
+-                                       handle_level_irq);
+-
++      irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq);
+       set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+       return 0;
+-- 
+2.0.3
+
+From de39bc31eaa554bd044e6adefacd3da6da5bf6e3 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 13 Mar 2014 20:41:20 +0100
+Subject: [PATCH] irqchip: sun4i: simplify sun4i_irq_ack
+
+Now that we only ack irq 0 the code can be simplified a lot.
+
+Also switch from read / modify / write to a simple write clear:
+1) This is what the android code does (it has a hack for acking irq 0
+  in its unmask code doing this)
+2) read / modify / write simply does not make sense for an irq status
+  register like this, if the other bits are writeable (and the data sheet says
+  they are not) they should be write 1 to clear, since otherwise a read /
+  modify / write can race with a device raising an interrupt and then clear
+  the pending bit unintentionally
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
+---
+ drivers/irqchip/irq-sun4i.c | 7 +------
+ 1 file changed, 1 insertion(+), 6 deletions(-)
+
+diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c
+index 6a8c88d..75615b5 100644
+--- a/drivers/irqchip/irq-sun4i.c
++++ b/drivers/irqchip/irq-sun4i.c
+@@ -41,16 +41,11 @@ static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *re
+ static void sun4i_irq_ack(struct irq_data *irqd)
+ {
+       unsigned int irq = irqd_to_hwirq(irqd);
+-      unsigned int irq_off = irq % 32;
+-      int reg = irq / 32;
+-      u32 val;
+       if (irq != 0)
+               return; /* Only IRQ 0 / the ENMI needs to be acked */
+-      val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg));
+-      writel(val | (1 << irq_off),
+-             sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg));
++      writel(BIT(0), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0));
+ }
+ static void sun4i_irq_mask(struct irq_data *irqd)
+-- 
+2.0.3
+