select NEED_MACH_IO_H
        select NEED_MACH_MEMORY_H
        select SPARSE_IRQ
+       select MULTI_IRQ_HANDLER
        help
          Support for ARM's Integrator platform.
 
 
+++ /dev/null
-/*
- * arch/arm/mach-integrator/include/mach/entry-macro.S
- *
- * Low-level IRQ helper macros for Integrator platforms
- *
- * This file is licensed under  the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-#include <mach/hardware.h>
-#include <mach/platform.h>
-#include <mach/irqs.h>
-
-               .macro  get_irqnr_preamble, base, tmp
-               .endm
-
-               .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
-/* FIXME: should not be using soo many LDRs here */
-               ldr     \base, =IO_ADDRESS(INTEGRATOR_IC_BASE)
-               mov     \irqnr, #IRQ_PIC_START
-               ldr     \irqstat, [\base, #IRQ_STATUS]          @ get masked status
-               ldr     \base, =IO_ADDRESS(INTEGRATOR_HDR_BASE)
-               teq     \irqstat, #0
-               ldreq   \irqstat, [\base, #(INTEGRATOR_HDR_IC_OFFSET+IRQ_STATUS)]
-               moveq   \irqnr, #IRQ_CIC_START
-
-1001:          tst     \irqstat, #15
-               bne     1002f
-               add     \irqnr, \irqnr, #4
-               movs    \irqstat, \irqstat, lsr #4
-               bne     1001b
-1002:          tst     \irqstat, #1
-               bne     1003f
-               add     \irqnr, \irqnr, #1
-               movs    \irqstat, \irqstat, lsr #1
-               bne     1002b
-1003:          /* EQ will be set if no irqs pending */
-               .endm
-
 
 /* 
  *  Interrupt numbers
  */
-#define IRQ_PIC_START                  0
-#define IRQ_SOFTINT                    0
-#define IRQ_UARTINT0                   1
-#define IRQ_UARTINT1                   2
-#define IRQ_KMIINT0                    3
-#define IRQ_KMIINT1                    4
-#define IRQ_TIMERINT0                  5
-#define IRQ_TIMERINT1                  6
-#define IRQ_TIMERINT2                  7
-#define IRQ_RTCINT                     8
-#define IRQ_AP_EXPINT0                 9
-#define IRQ_AP_EXPINT1                 10
-#define IRQ_AP_EXPINT2                 11
-#define IRQ_AP_EXPINT3                 12
-#define IRQ_AP_PCIINT0                 13
-#define IRQ_AP_PCIINT1                 14
-#define IRQ_AP_PCIINT2                 15
-#define IRQ_AP_PCIINT3                 16
-#define IRQ_AP_V3INT                   17
-#define IRQ_AP_CPINT0                  18
-#define IRQ_AP_CPINT1                  19
-#define IRQ_AP_LBUSTIMEOUT             20
-#define IRQ_AP_APCINT                  21
-#define IRQ_CP_CLCDCINT                        22
-#define IRQ_CP_MMCIINT0                        23
-#define IRQ_CP_MMCIINT1                        24
-#define IRQ_CP_AACIINT                 25
-#define IRQ_CP_CPPLDINT                        26
-#define IRQ_CP_ETHINT                  27
-#define IRQ_CP_TSPENINT                        28
-#define IRQ_PIC_END                    31
+#define IRQ_PIC_START                  1
+#define IRQ_SOFTINT                    1
+#define IRQ_UARTINT0                   2
+#define IRQ_UARTINT1                   3
+#define IRQ_KMIINT0                    4
+#define IRQ_KMIINT1                    5
+#define IRQ_TIMERINT0                  6
+#define IRQ_TIMERINT1                  7
+#define IRQ_TIMERINT2                  8
+#define IRQ_RTCINT                     9
+#define IRQ_AP_EXPINT0                 10
+#define IRQ_AP_EXPINT1                 11
+#define IRQ_AP_EXPINT2                 12
+#define IRQ_AP_EXPINT3                 13
+#define IRQ_AP_PCIINT0                 14
+#define IRQ_AP_PCIINT1                 15
+#define IRQ_AP_PCIINT2                 16
+#define IRQ_AP_PCIINT3                 17
+#define IRQ_AP_V3INT                   18
+#define IRQ_AP_CPINT0                  19
+#define IRQ_AP_CPINT1                  20
+#define IRQ_AP_LBUSTIMEOUT             21
+#define IRQ_AP_APCINT                  22
+#define IRQ_CP_CLCDCINT                        23
+#define IRQ_CP_MMCIINT0                        24
+#define IRQ_CP_MMCIINT1                        25
+#define IRQ_CP_AACIINT                 26
+#define IRQ_CP_CPPLDINT                        27
+#define IRQ_CP_ETHINT                  28
+#define IRQ_CP_TSPENINT                        29
+#define IRQ_PIC_END                    29
 
 #define IRQ_CIC_START                  32
 #define IRQ_CM_SOFTINT                 32
 
 #define NR_IRQS_INTEGRATOR_AP          34
 #define NR_IRQS_INTEGRATOR_CP          47
-
 
 
 #define INTEGRATOR_SC_VALID_INT        0x003fffff
 
-static struct fpga_irq_data sc_irq_data = {
-       .base           = VA_IC_BASE,
-       .irq_start      = 0,
-       .chip.name      = "SC",
-};
-
 static void __init ap_init_irq(void)
 {
        /* Disable all interrupts initially. */
        writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR);
        writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR);
 
-       fpga_irq_init(-1, INTEGRATOR_SC_VALID_INT, &sc_irq_data);
+       fpga_irq_init(VA_IC_BASE, "SC", IRQ_PIC_START,
+               -1, INTEGRATOR_SC_VALID_INT, NULL);
 }
 
 #ifdef CONFIG_PM
        .nr_irqs        = NR_IRQS_INTEGRATOR_AP,
        .init_early     = integrator_init_early,
        .init_irq       = ap_init_irq,
+       .handle_irq     = fpga_handle_irq,
        .timer          = &ap_timer,
        .init_machine   = ap_init,
        .restart        = integrator_restart,
 
        iotable_init(intcp_io_desc, ARRAY_SIZE(intcp_io_desc));
 }
 
-static struct fpga_irq_data cic_irq_data = {
-       .base           = INTCP_VA_CIC_BASE,
-       .irq_start      = IRQ_CIC_START,
-       .chip.name      = "CIC",
-};
-
-static struct fpga_irq_data pic_irq_data = {
-       .base           = INTCP_VA_PIC_BASE,
-       .irq_start      = IRQ_PIC_START,
-       .chip.name      = "PIC",
-};
-
-static struct fpga_irq_data sic_irq_data = {
-       .base           = INTCP_VA_SIC_BASE,
-       .irq_start      = IRQ_SIC_START,
-       .chip.name      = "SIC",
-};
-
 static void __init intcp_init_irq(void)
 {
-       u32 pic_mask, sic_mask;
+       u32 pic_mask, cic_mask, sic_mask;
 
+       /* These masks are for the HW IRQ registers */
        pic_mask = ~((~0u) << (11 - IRQ_PIC_START));
        pic_mask |= (~((~0u) << (29 - 22))) << 22;
+       cic_mask = ~((~0u) << (1 + IRQ_CIC_END - IRQ_CIC_START));
        sic_mask = ~((~0u) << (1 + IRQ_SIC_END - IRQ_SIC_START));
 
        /*
        writel(sic_mask, INTCP_VA_SIC_BASE + IRQ_ENABLE_CLEAR);
        writel(sic_mask, INTCP_VA_SIC_BASE + FIQ_ENABLE_CLEAR);
 
-       fpga_irq_init(-1, pic_mask, &pic_irq_data);
+       fpga_irq_init(INTCP_VA_PIC_BASE, "PIC", IRQ_PIC_START,
+                     -1, pic_mask, NULL);
 
-       fpga_irq_init(-1, ~((~0u) << (1 + IRQ_CIC_END - IRQ_CIC_START)),
-               &cic_irq_data);
+       fpga_irq_init(INTCP_VA_CIC_BASE, "CIC", IRQ_CIC_START,
+                     -1, cic_mask, NULL);
 
-       fpga_irq_init(IRQ_CP_CPPLDINT, sic_mask, &sic_irq_data);
+       fpga_irq_init(INTCP_VA_SIC_BASE, "SIC", IRQ_SIC_START,
+                     IRQ_CP_CPPLDINT, sic_mask, NULL);
 }
 
 /*
        .nr_irqs        = NR_IRQS_INTEGRATOR_CP,
        .init_early     = intcp_init_early,
        .init_irq       = intcp_init_irq,
+       .handle_irq     = fpga_handle_irq,
        .timer          = &cp_timer,
        .init_machine   = intcp_init,
        .restart        = integrator_restart,
 
 #define VA_VIC_BASE            __io_address(VERSATILE_VIC_BASE)
 #define VA_SIC_BASE            __io_address(VERSATILE_SIC_BASE)
 
-static struct fpga_irq_data sic_irq = {
-       .base           = VA_SIC_BASE,
-       .irq_start      = IRQ_SIC_START,
-       .chip.name      = "SIC",
-};
-
 #if 1
 #define IRQ_MMCI0A     IRQ_VICSOURCE22
 #define IRQ_AACI       IRQ_VICSOURCE24
 
        writel(~0, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR);
 
-       fpga_irq_init(IRQ_VICSOURCE31, ~PIC_MASK, &sic_irq);
-       irq_domain_generate_simple(sic_of_match, VERSATILE_SIC_BASE, IRQ_SIC_START);
+       np = of_find_matching_node_by_address(NULL, sic_of_match,
+                                             VERSATILE_SIC_BASE);
+
+       fpga_irq_init(VA_SIC_BASE, "SIC", IRQ_SIC_START,
+               IRQ_VICSOURCE31, ~PIC_MASK, np);
 
        /*
         * Interrupts on secondary controller from 0 to 8 are routed to
 
 
 config PLAT_VERSATILE_FPGA_IRQ
        bool
+       select IRQ_DOMAIN
+
+config PLAT_VERSATILE_FPGA_IRQ_NR
+       int
+       default 4
+       depends on PLAT_VERSATILE_FPGA_IRQ
 
 config PLAT_VERSATILE_LEDS
        def_bool y if LEDS_CLASS
 
  */
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
 
+#include <asm/exception.h>
 #include <asm/mach/irq.h>
 #include <plat/fpga-irq.h>
 
 #define IRQ_ENABLE_SET         0x08
 #define IRQ_ENABLE_CLEAR       0x0c
 
+/**
+ * struct fpga_irq_data - irq data container for the FPGA IRQ controller
+ * @base: memory offset in virtual memory
+ * @irq_start: first IRQ number handled by this instance
+ * @chip: chip container for this instance
+ * @domain: IRQ domain for this instance
+ * @valid: mask for valid IRQs on this controller
+ * @used_irqs: number of active IRQs on this controller
+ */
+struct fpga_irq_data {
+       void __iomem *base;
+       unsigned int irq_start;
+       struct irq_chip chip;
+       u32 valid;
+       struct irq_domain *domain;
+       u8 used_irqs;
+};
+
+/* we cannot allocate memory when the controllers are initially registered */
+static struct fpga_irq_data fpga_irq_devices[CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR];
+static int fpga_irq_id;
+
 static void fpga_irq_mask(struct irq_data *d)
 {
        struct fpga_irq_data *f = irq_data_get_irq_chip_data(d);
-       u32 mask = 1 << (d->irq - f->irq_start);
+       u32 mask = 1 << d->hwirq;
 
        writel(mask, f->base + IRQ_ENABLE_CLEAR);
 }
 static void fpga_irq_unmask(struct irq_data *d)
 {
        struct fpga_irq_data *f = irq_data_get_irq_chip_data(d);
-       u32 mask = 1 << (d->irq - f->irq_start);
+       u32 mask = 1 << d->hwirq;
 
        writel(mask, f->base + IRQ_ENABLE_SET);
 }
        do {
                irq = ffs(status) - 1;
                status &= ~(1 << irq);
-
-               generic_handle_irq(irq + f->irq_start);
+               generic_handle_irq(irq_find_mapping(f->domain, irq));
        } while (status);
 }
 
-void __init fpga_irq_init(int parent_irq, u32 valid, struct fpga_irq_data *f)
+/*
+ * Handle each interrupt in a single FPGA IRQ controller.  Returns non-zero
+ * if we've handled at least one interrupt.  This does a single read of the
+ * status register and handles all interrupts in order from LSB first.
+ */
+static int handle_one_fpga(struct fpga_irq_data *f, struct pt_regs *regs)
+{
+       int handled = 0;
+       int irq;
+       u32 status;
+
+       while ((status  = readl(f->base + IRQ_STATUS))) {
+               irq = ffs(status) - 1;
+               handle_IRQ(irq_find_mapping(f->domain, irq), regs);
+               handled = 1;
+       }
+
+       return handled;
+}
+
+/*
+ * Keep iterating over all registered FPGA IRQ controllers until there are
+ * no pending interrupts.
+ */
+asmlinkage void __exception_irq_entry fpga_handle_irq(struct pt_regs *regs)
 {
-       unsigned int i;
+       int i, handled;
 
+       do {
+               for (i = 0, handled = 0; i < fpga_irq_id; ++i)
+                       handled |= handle_one_fpga(&fpga_irq_devices[i], regs);
+       } while (handled);
+}
+
+static int fpga_irqdomain_map(struct irq_domain *d, unsigned int irq,
+               irq_hw_number_t hwirq)
+{
+       struct fpga_irq_data *f = d->host_data;
+
+       /* Skip invalid IRQs, only register handlers for the real ones */
+       if (!(f->valid & (1 << hwirq)))
+               return -ENOTSUPP;
+       irq_set_chip_data(irq, f);
+       irq_set_chip_and_handler(irq, &f->chip,
+                               handle_level_irq);
+       set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+       f->used_irqs++;
+       return 0;
+}
+
+static struct irq_domain_ops fpga_irqdomain_ops = {
+       .map = fpga_irqdomain_map,
+       .xlate = irq_domain_xlate_onetwocell,
+};
+
+void __init fpga_irq_init(void __iomem *base, const char *name, int irq_start,
+                         int parent_irq, u32 valid, struct device_node *node)
+{
+       struct fpga_irq_data *f;
+
+       if (fpga_irq_id >= ARRAY_SIZE(fpga_irq_devices)) {
+               printk(KERN_ERR "%s: too few FPGA IRQ controllers, increase CONFIG_PLAT_VERSATILE_FPGA_IRQ_NR\n", __func__);
+               return;
+       }
+
+       f = &fpga_irq_devices[fpga_irq_id];
+       f->base = base;
+       f->irq_start = irq_start;
+       f->chip.name = name;
        f->chip.irq_ack = fpga_irq_mask;
        f->chip.irq_mask = fpga_irq_mask;
        f->chip.irq_unmask = fpga_irq_unmask;
+       f->valid = valid;
 
        if (parent_irq != -1) {
                irq_set_handler_data(parent_irq, f);
                irq_set_chained_handler(parent_irq, fpga_irq_handle);
        }
 
-       for (i = 0; i < 32; i++) {
-               if (valid & (1 << i)) {
-                       unsigned int irq = f->irq_start + i;
+       f->domain = irq_domain_add_legacy(node, fls(valid), f->irq_start, 0,
+                                         &fpga_irqdomain_ops, f);
+       pr_info("FPGA IRQ chip %d \"%s\" @ %p, %u irqs\n",
+               fpga_irq_id, name, base, f->used_irqs);
 
-                       irq_set_chip_data(irq, f);
-                       irq_set_chip_and_handler(irq, &f->chip,
-                                                handle_level_irq);
-                       set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
-               }
-       }
+       fpga_irq_id++;
 }
 
 #ifndef PLAT_FPGA_IRQ_H
 #define PLAT_FPGA_IRQ_H
 
-struct fpga_irq_data {
-       void __iomem *base;
-       unsigned int irq_start;
-       struct irq_chip chip;
-};
+struct device_node;
+struct pt_regs;
 
-void fpga_irq_init(int, u32, struct fpga_irq_data *);
+void fpga_handle_irq(struct pt_regs *regs);
+void fpga_irq_init(void __iomem *, const char *, int, int, u32,
+               struct device_node *node);
 
 #endif