irqchip: armada-370-xp: fix invalid cast of signed value into unsigned variable
[firefly-linux-kernel-4.4.55.git] / drivers / irqchip / irq-armada-370-xp.c
index 540956465ed2db759ca72eae11a26b16a4c17047..3c8d89b62a211dfa4d5953df47cfa511f0b1a5be 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/init.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
+#include <linux/irqchip/chained_irq.h>
 #include <linux/io.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
@@ -42,6 +43,7 @@
 #define ARMADA_370_XP_INT_SOURCE_CTL(irq)      (0x100 + irq*4)
 
 #define ARMADA_370_XP_CPU_INTACK_OFFS          (0x44)
+#define ARMADA_375_PPI_CAUSE                   (0x10)
 
 #define ARMADA_370_XP_SW_TRIG_INT_OFFS           (0x4)
 #define ARMADA_370_XP_IN_DRBEL_MSK_OFFS          (0xc)
@@ -130,8 +132,7 @@ static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
                                       struct msi_desc *desc)
 {
        struct msi_msg msg;
-       irq_hw_number_t hwirq;
-       int virq;
+       int virq, hwirq;
 
        hwirq = armada_370_xp_alloc_msi();
        if (hwirq < 0)
@@ -352,7 +353,63 @@ static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
        .xlate = irq_domain_xlate_onecell,
 };
 
-static asmlinkage void __exception_irq_entry
+#ifdef CONFIG_PCI_MSI
+static void armada_370_xp_handle_msi_irq(struct pt_regs *regs, bool is_chained)
+{
+       u32 msimask, msinr;
+
+       msimask = readl_relaxed(per_cpu_int_base +
+                               ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
+               & PCI_MSI_DOORBELL_MASK;
+
+       writel(~msimask, per_cpu_int_base +
+              ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
+
+       for (msinr = PCI_MSI_DOORBELL_START;
+            msinr < PCI_MSI_DOORBELL_END; msinr++) {
+               int irq;
+
+               if (!(msimask & BIT(msinr)))
+                       continue;
+
+               irq = irq_find_mapping(armada_370_xp_msi_domain,
+                                      msinr - 16);
+
+               if (is_chained)
+                       generic_handle_irq(irq);
+               else
+                       handle_IRQ(irq, regs);
+       }
+}
+#else
+static void armada_370_xp_handle_msi_irq(struct pt_regs *r, bool b) {}
+#endif
+
+static void armada_370_xp_mpic_handle_cascade_irq(unsigned int irq,
+                                                 struct irq_desc *desc)
+{
+       struct irq_chip *chip = irq_get_chip(irq);
+       unsigned long irqmap, irqn;
+       unsigned int cascade_irq;
+
+       chained_irq_enter(chip, desc);
+
+       irqmap = readl_relaxed(per_cpu_int_base + ARMADA_375_PPI_CAUSE);
+
+       if (irqmap & BIT(0)) {
+               armada_370_xp_handle_msi_irq(NULL, true);
+               irqmap &= ~BIT(0);
+       }
+
+       for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) {
+               cascade_irq = irq_find_mapping(armada_370_xp_mpic_domain, irqn);
+               generic_handle_irq(cascade_irq);
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static void __exception_irq_entry
 armada_370_xp_handle_irq(struct pt_regs *regs)
 {
        u32 irqstat, irqnr;
@@ -372,31 +429,9 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
                        continue;
                }
 
-#ifdef CONFIG_PCI_MSI
                /* MSI handling */
-               if (irqnr == 1) {
-                       u32 msimask, msinr;
-
-                       msimask = readl_relaxed(per_cpu_int_base +
-                                               ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
-                               & PCI_MSI_DOORBELL_MASK;
-
-                       writel(~msimask, per_cpu_int_base +
-                              ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
-
-                       for (msinr = PCI_MSI_DOORBELL_START;
-                            msinr < PCI_MSI_DOORBELL_END; msinr++) {
-                               int irq;
-
-                               if (!(msimask & BIT(msinr)))
-                                       continue;
-
-                               irq = irq_find_mapping(armada_370_xp_msi_domain,
-                                                      msinr - 16);
-                               handle_IRQ(irq, regs);
-                       }
-               }
-#endif
+               if (irqnr == 1)
+                       armada_370_xp_handle_msi_irq(regs, false);
 
 #ifdef CONFIG_SMP
                /* IPI Handling */
@@ -427,6 +462,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
                                             struct device_node *parent)
 {
        struct resource main_int_res, per_cpu_int_res;
+       int parent_irq;
        u32 control;
 
        BUG_ON(of_address_to_resource(node, 0, &main_int_res));
@@ -455,8 +491,6 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
 
        BUG_ON(!armada_370_xp_mpic_domain);
 
-       irq_set_default_host(armada_370_xp_mpic_domain);
-
 #ifdef CONFIG_SMP
        armada_xp_mpic_smp_cpu_init();
 
@@ -472,7 +506,14 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
 
        armada_370_xp_msi_init(node, main_int_res.start);
 
-       set_handle_irq(armada_370_xp_handle_irq);
+       parent_irq = irq_of_parse_and_map(node, 0);
+       if (parent_irq <= 0) {
+               irq_set_default_host(armada_370_xp_mpic_domain);
+               set_handle_irq(armada_370_xp_handle_irq);
+       } else {
+               irq_set_chained_handler(parent_irq,
+                                       armada_370_xp_mpic_handle_cascade_irq);
+       }
 
        return 0;
 }