ARM: 7389/2: plat-versatile: modernize FPGA IRQ controller
authorLinus Walleij <linus.walleij@linaro.org>
Sat, 28 Apr 2012 13:33:47 +0000 (14:33 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Fri, 4 May 2012 15:53:37 +0000 (16:53 +0100)
This does two things to the FPGA IRQ controller in the versatile
family:

- Convert to MULTI_IRQ_HANDLER so we can drop the entry macro
  from the Integrator. The C IRQ handler was inspired from
  arch/arm/common/vic.c, recent bug discovered in this handler was
  accounted for.
- Convert to using IRQ domains so we can get rid of the NO_IRQ
  mess and proceed with device tree and such stuff.

As part of the exercise, bump all the low IRQ numbers on the
Integrator PIC to start from 1 rather than 0, since IRQ 0 is
now NO_IRQ. The Linux IRQ numbers are thus entirely decoupled
from the hardware IRQ numbers in this controller.

I was unable to split this patch. The main reason is the half-done
conversion to device tree in Versatile.

Tested on Integrator/AP and Integrator/CP.

Cc: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Rob Herring <rob.herring@calxeda.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/Kconfig
arch/arm/mach-integrator/include/mach/entry-macro.S [deleted file]
arch/arm/mach-integrator/include/mach/irqs.h
arch/arm/mach-integrator/integrator_ap.c
arch/arm/mach-integrator/integrator_cp.c
arch/arm/mach-versatile/core.c
arch/arm/plat-versatile/Kconfig
arch/arm/plat-versatile/fpga-irq.c
arch/arm/plat-versatile/include/plat/fpga-irq.h

index cf006d40342ca4d51a0128ca0ae60dc36452cd6f..2f67f6c62f56399f3db0e06e4617d9b369ab2f16 100644 (file)
@@ -280,6 +280,7 @@ config ARCH_INTEGRATOR
        select NEED_MACH_IO_H
        select NEED_MACH_MEMORY_H
        select SPARSE_IRQ
+       select MULTI_IRQ_HANDLER
        help
          Support for ARM's Integrator platform.
 
diff --git a/arch/arm/mach-integrator/include/mach/entry-macro.S b/arch/arm/mach-integrator/include/mach/entry-macro.S
deleted file mode 100644 (file)
index 5cc7b85..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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
-
index a19a1a2fcf6bece62d342599548286dae08aace4..7371018455d2f56e02ea607a707785e3f8b7d8b9 100644 (file)
 /* 
  *  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
@@ -80,4 +80,3 @@
 
 #define NR_IRQS_INTEGRATOR_AP          34
 #define NR_IRQS_INTEGRATOR_CP          47
-
index 871f148ffd723fdb4389e7fff95f7b0d47494cea..c857501c5783c0ea9967bc5c96182e706bada638 100644 (file)
@@ -162,12 +162,6 @@ static void __init ap_map_io(void)
 
 #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. */
@@ -178,7 +172,8 @@ static void __init ap_init_irq(void)
        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
@@ -478,6 +473,7 @@ MACHINE_START(INTEGRATOR, "ARM-Integrator")
        .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,
index 48a115a91d9d1c3a2bb479dc00bedefe257ceb5c..a56c536089397c2e78cb2f16a1a9291d4d2e920e 100644 (file)
@@ -143,30 +143,14 @@ static void __init intcp_map_io(void)
        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));
 
        /*
@@ -179,12 +163,14 @@ static void __init intcp_init_irq(void)
        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);
 }
 
 /*
@@ -467,6 +453,7 @@ MACHINE_START(CINTEGRATOR, "ARM-IntegratorCP")
        .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,
index 6bbd74e950abd102c22624cd42678b3f598a3d6a..6f5fb466a27325c74fa0ba54137e42f576b661b1 100644 (file)
 #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
@@ -105,8 +99,11 @@ void __init versatile_init_irq(void)
 
        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
index 043f7b02a9e709b239d81d2715ab0258748986d3..81ee7cc344575956a698ad929fe70ca8df1cee33 100644 (file)
@@ -5,6 +5,12 @@ config PLAT_VERSATILE_CLCD
 
 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
index f0cc8e19b0943dcfb96078895b14d1232bf7ab79..6e70d03824a16147da20a53912a94f2cb28a3c29 100644 (file)
@@ -3,7 +3,10 @@
  */
 #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);
 }
@@ -23,7 +48,7 @@ static void fpga_irq_mask(struct irq_data *d)
 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);
 }
@@ -41,32 +66,93 @@ static void fpga_irq_handle(unsigned int irq, struct irq_desc *desc)
        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++;
 }
index 627fafd1e59517d3a0445208761cad2d921aa30d..91bcfb67551dda84254e2d78192c0750cdeb84a7 100644 (file)
@@ -1,12 +1,11 @@
 #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