IRQCHIP: bcm7120-l2: Add support for BCM3380-style controllers
authorKevin Cernekee <cernekee@gmail.com>
Thu, 25 Dec 2014 17:49:05 +0000 (09:49 -0800)
committerRalf Baechle <ralf@linux-mips.org>
Wed, 1 Apr 2015 15:21:37 +0000 (17:21 +0200)
These controllers support multiple enable/status pairs (64+ IRQs),
can put the enable/status words at different offsets, and do not
support multiple parent IRQs.

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
Cc: f.fainelli@gmail.com
Cc: jaedon.shin@gmail.com
Cc: abrestic@chromium.org
Cc: tglx@linutronix.de
Cc: jason@lakedaemon.net
Cc: jogo@openwrt.org
Cc: arnd@arndb.de
Cc: computersforpeace@gmail.com
Cc: linux-mips@linux-mips.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/8843/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Documentation/devicetree/bindings/interrupt-controller/brcm,bcm3380-l2-intc.txt [new file with mode: 0644]
drivers/irqchip/irq-bcm7120-l2.c

diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm3380-l2-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm3380-l2-intc.txt
new file mode 100644 (file)
index 0000000..8f48aad
--- /dev/null
@@ -0,0 +1,41 @@
+Broadcom BCM3380-style Level 1 / Level 2 interrupt controller
+
+This interrupt controller shows up in various forms on many BCM338x/BCM63xx
+chipsets.  It has the following properties:
+
+- outputs a single interrupt signal to its interrupt controller parent
+
+- contains one or more enable/status word pairs, which often appear at
+  different offsets in different blocks
+
+- no atomic set/clear operations
+
+Required properties:
+
+- compatible: should be "brcm,bcm3380-l2-intc"
+- reg: specifies one or more enable/status pairs, in the following format:
+  <enable_reg 0x4 status_reg 0x4>...
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: specifies the number of cells needed to encode an interrupt
+  source, should be 1.
+- interrupt-parent: specifies the phandle to the parent interrupt controller
+  this one is cascaded from
+- interrupts: specifies the interrupt line in the interrupt-parent controller
+  node, valid values depend on the type of parent interrupt controller
+
+Optional properties:
+
+- brcm,irq-can-wake: if present, this means the L2 controller can be used as a
+  wakeup source for system suspend/resume.
+
+Example:
+
+irq0_intc: interrupt-controller@10000020 {
+       compatible = "brcm,bcm3380-l2-intc";
+       reg = <0x10000024 0x4 0x1000002c 0x4>,
+             <0x10000020 0x4 0x10000028 0x4>;
+       interrupt-controller;
+       #interrupt-cells = <1>;
+       interrupt-parent = <&cpu_intc>;
+       interrupts = <2>;
+};
index 6a6285897df1d3dbb6691b69f493e170b4672fda..3ba5cc780fcbbba4568db2557df8828b63d5d4d9 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/kconfig.h>
+#include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
@@ -120,10 +121,15 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
        /* For multiple parent IRQs with multiple words, this looks like:
         * <irq0_w0 irq0_w1 irq1_w0 irq1_w1 ...>
         */
-       for (idx = 0; idx < data->n_words; idx++)
-               data->irq_map_mask[idx] |=
-                       be32_to_cpup(data->map_mask_prop +
-                                    irq * data->n_words + idx);
+       for (idx = 0; idx < data->n_words; idx++) {
+               if (data->map_mask_prop) {
+                       data->irq_map_mask[idx] |=
+                               be32_to_cpup(data->map_mask_prop +
+                                            irq * data->n_words + idx);
+               } else {
+                       data->irq_map_mask[idx] = 0xffffffff;
+               }
+       }
 
        irq_set_handler_data(parent_irq, data);
        irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle);
@@ -165,6 +171,37 @@ static int __init bcm7120_l2_intc_iomap_7120(struct device_node *dn,
        return 0;
 }
 
+static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn,
+                                            struct bcm7120_l2_intc_data *data)
+{
+       unsigned int gc_idx;
+
+       for (gc_idx = 0; gc_idx < MAX_WORDS; gc_idx++) {
+               unsigned int map_idx = gc_idx * 2;
+               void __iomem *en = of_iomap(dn, map_idx + 0);
+               void __iomem *stat = of_iomap(dn, map_idx + 1);
+               void __iomem *base = min(en, stat);
+
+               data->map_base[map_idx + 0] = en;
+               data->map_base[map_idx + 1] = stat;
+
+               if (!base)
+                       break;
+
+               data->pair_base[gc_idx] = base;
+               data->en_offset[gc_idx] = en - base;
+               data->stat_offset[gc_idx] = stat - base;
+       }
+
+       if (!gc_idx) {
+               pr_err("unable to map registers\n");
+               return -EINVAL;
+       }
+
+       data->n_words = gc_idx;
+       return 0;
+}
+
 int __init bcm7120_l2_intc_probe(struct device_node *dn,
                                 struct device_node *parent,
                                 int (*iomap_regs_fn)(struct device_node *,
@@ -279,5 +316,15 @@ int __init bcm7120_l2_intc_probe_7120(struct device_node *dn,
                                     "BCM7120 L2");
 }
 
+int __init bcm7120_l2_intc_probe_3380(struct device_node *dn,
+                                     struct device_node *parent)
+{
+       return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380,
+                                    "BCM3380 L2");
+}
+
 IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc",
                bcm7120_l2_intc_probe_7120);
+
+IRQCHIP_DECLARE(bcm3380_l2_intc, "brcm,bcm3380-l2-intc",
+               bcm7120_l2_intc_probe_3380);