The interrupt handling method is changed from old-style cascade to
chained_irq which is more appropriate. Also, it can process the
corner situation that more than one irq is coming to a single
chip at the same time.

Signed-off-by: Zhou Yanjie <[email protected]>
---
 drivers/irqchip/irq-ingenic.c | 48 +++++++++++++++++++++----------------------
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/drivers/irqchip/irq-ingenic.c b/drivers/irqchip/irq-ingenic.c
index 2ff0898..5f775a1 100644
--- a/drivers/irqchip/irq-ingenic.c
+++ b/drivers/irqchip/irq-ingenic.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  *  Copyright (C) 2009-2010, Lars-Peter Clausen <[email protected]>
- *  JZ4740 platform IRQ support
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under  the terms of the GNU General         Public License as published by 
the
- *  Free Software Foundation;  either version 2 of the License, or (at your
- *  option) any later version.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  675 Mass Ave, Cambridge, MA 02139, USA.
- *
+ *  Ingenic XBurst platform IRQ support
  */
 
 #include <linux/errno.h>
@@ -19,6 +10,7 @@
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
 #include <linux/irqchip/ingenic.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
@@ -41,22 +33,34 @@ struct ingenic_intc_data {
 #define JZ_REG_INTC_PENDING    0x10
 #define CHIP_SIZE              0x20
 
-static irqreturn_t intc_cascade(int irq, void *data)
+static void ingenic_chained_handle_irq(struct irq_desc *desc)
 {
-       struct ingenic_intc_data *intc = irq_get_handler_data(irq);
-       uint32_t irq_reg;
+       struct ingenic_intc_data *intc = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       bool have_irq = false;
+       uint32_t pending;
        unsigned i;
 
+       chained_irq_enter(chip, desc);
        for (i = 0; i < intc->num_chips; i++) {
-               irq_reg = readl(intc->base + (i * CHIP_SIZE) +
+               pending = readl(intc->base + (i * CHIP_SIZE) +
                                JZ_REG_INTC_PENDING);
-               if (!irq_reg)
+               if (!pending)
                        continue;
 
-               generic_handle_irq(__fls(irq_reg) + (i * 32) + JZ4740_IRQ_BASE);
+               have_irq = true;
+               while (pending) {
+                       int bit = __fls(pending);
+
+                       generic_handle_irq(bit + (i * 32) + JZ4740_IRQ_BASE);
+                       pending &= ~BIT(bit);
+               }
        }
 
-       return IRQ_HANDLED;
+       if (!have_irq)
+               spurious_interrupt();
+
+       chained_irq_exit(chip, desc);
 }
 
 static void intc_irq_set_mask(struct irq_chip_generic *gc, uint32_t mask)
@@ -79,11 +83,6 @@ void ingenic_intc_irq_resume(struct irq_data *data)
        intc_irq_set_mask(gc, gc->mask_cache);
 }
 
-static struct irqaction intc_cascade_action = {
-       .handler = intc_cascade,
-       .name = "SoC intc cascade interrupt",
-};
-
 static int __init ingenic_intc_of_init(struct device_node *node,
                                       unsigned num_chips)
 {
@@ -148,7 +147,8 @@ static int __init ingenic_intc_of_init(struct device_node 
*node,
        if (!domain)
                pr_warn("unable to register IRQ domain\n");
 
-       setup_irq(parent_irq, &intc_cascade_action);
+       irq_set_chained_handler_and_data(parent_irq,
+                                       ingenic_chained_handle_irq, intc);
        return 0;
 
 out_unmap_irq:
-- 
2.7.4


Reply via email to