Merge tag 'irqchip-core-4.9' of git://git.infradead.org/users/jcooper/linux into...
authorThomas Gleixner <tglx@linutronix.de>
Thu, 22 Sep 2016 20:49:52 +0000 (22:49 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 22 Sep 2016 20:49:52 +0000 (22:49 +0200)
Pull irqchip core changes for v4.9 from Jason Cooper

 - jcore: Add AIC driver
 - mips-gic: Use for_each_set_bit
 - mvebu: Add PIC driver

Documentation/devicetree/bindings/interrupt-controller/jcore,aic.txt [new file with mode: 0644]
Documentation/devicetree/bindings/interrupt-controller/marvell,armada-8k-pic.txt [new file with mode: 0644]
Documentation/devicetree/bindings/interrupt-controller/marvell,odmi-controller.txt
arch/arm64/Kconfig.platforms
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-jcore-aic.c [new file with mode: 0644]
drivers/irqchip/irq-keystone.c
drivers/irqchip/irq-mips-gic.c
drivers/irqchip/irq-mvebu-pic.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/interrupt-controller/jcore,aic.txt b/Documentation/devicetree/bindings/interrupt-controller/jcore,aic.txt
new file mode 100644 (file)
index 0000000..ee2ad36
--- /dev/null
@@ -0,0 +1,26 @@
+J-Core Advanced Interrupt Controller
+
+Required properties:
+
+- compatible: Should be "jcore,aic1" for the (obsolete) first-generation aic
+  with 8 interrupt lines with programmable priorities, or "jcore,aic2" for
+  the "aic2" core with 64 interrupts.
+
+- reg: Memory region(s) for configuration. For SMP, there should be one
+  region per cpu, indexed by the sequential, zero-based hardware cpu
+  number.
+
+- interrupt-controller: Identifies the node as an interrupt controller
+
+- #interrupt-cells: Specifies the number of cells needed to encode an
+  interrupt source. The value shall be 1.
+
+
+Example:
+
+aic: interrupt-controller@200 {
+       compatible = "jcore,aic2";
+       reg = < 0x200 0x30 0x500 0x30 >;
+       interrupt-controller;
+       #interrupt-cells = <1>;
+};
diff --git a/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-8k-pic.txt b/Documentation/devicetree/bindings/interrupt-controller/marvell,armada-8k-pic.txt
new file mode 100644 (file)
index 0000000..86a7b4c
--- /dev/null
@@ -0,0 +1,25 @@
+Marvell Armada 7K/8K PIC Interrupt controller
+---------------------------------------------
+
+This is the Device Tree binding for the PIC, a secondary interrupt
+controller available on the Marvell Armada 7K/8K ARM64 SoCs, and
+typically connected to the GIC as the primary interrupt controller.
+
+Required properties:
+- compatible: should be "marvell,armada-8k-pic"
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: the number of cells to define interrupts on this
+  controller. Should be 1
+- reg: the register area for the PIC interrupt controller
+- interrupts: the interrupt to the primary interrupt controller,
+  typically the GIC
+
+Example:
+
+       pic: interrupt-controller@3f0100 {
+               compatible = "marvell,armada-8k-pic";
+               reg = <0x3f0100 0x10>;
+               #interrupt-cells = <1>;
+               interrupt-controller;
+               interrupts = <GIC_PPI 15 IRQ_TYPE_LEVEL_HIGH>;
+       };
index 8af0a8e..3f6442c 100644 (file)
@@ -31,7 +31,7 @@ Required properties:
 Example:
 
        odmi: odmi@300000 {
-               compatible = "marvell,ap806-odm-controller",
+               compatible = "marvell,ap806-odmi-controller",
                             "marvell,odmi-controller";
                interrupt-controller;
                msi-controller;
index be5d824..169c40a 100644 (file)
@@ -93,6 +93,7 @@ config ARCH_MVEBU
        select ARMADA_CP110_SYSCON
        select ARMADA_37XX_CLK
        select MVEBU_ODMI
+       select MVEBU_PIC
        help
          This enables support for Marvell EBU familly, including:
           - Armada 3700 SoC Family
index 329c941..82b0b5d 100644 (file)
@@ -157,6 +157,13 @@ config PIC32_EVIC
        select GENERIC_IRQ_CHIP
        select IRQ_DOMAIN
 
+config JCORE_AIC
+       bool "J-Core integrated AIC"
+       depends on OF && (SUPERH || COMPILE_TEST)
+       select IRQ_DOMAIN
+       help
+         Support for the J-Core integrated AIC.
+
 config RENESAS_INTC_IRQPIN
        bool
        select IRQ_DOMAIN
@@ -252,6 +259,9 @@ config IRQ_MXS
 config MVEBU_ODMI
        bool
 
+config MVEBU_PIC
+       bool
+
 config LS_SCFG_MSI
        def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE
        depends on PCI && PCI_MSI
index 96383b2..b372e79 100644 (file)
@@ -40,6 +40,7 @@ obj-$(CONFIG_I8259)                   += irq-i8259.o
 obj-$(CONFIG_IMGPDC_IRQ)               += irq-imgpdc.o
 obj-$(CONFIG_IRQ_MIPS_CPU)             += irq-mips-cpu.o
 obj-$(CONFIG_SIRF_IRQ)                 += irq-sirfsoc.o
+obj-$(CONFIG_JCORE_AIC)                        += irq-jcore-aic.o
 obj-$(CONFIG_RENESAS_INTC_IRQPIN)      += irq-renesas-intc-irqpin.o
 obj-$(CONFIG_RENESAS_IRQC)             += irq-renesas-irqc.o
 obj-$(CONFIG_VERSATILE_FPGA_IRQ)       += irq-versatile-fpga.o
@@ -68,6 +69,7 @@ obj-$(CONFIG_INGENIC_IRQ)             += irq-ingenic.o
 obj-$(CONFIG_IMX_GPCV2)                        += irq-imx-gpcv2.o
 obj-$(CONFIG_PIC32_EVIC)               += irq-pic32-evic.o
 obj-$(CONFIG_MVEBU_ODMI)               += irq-mvebu-odmi.o
+obj-$(CONFIG_MVEBU_PIC)                        += irq-mvebu-pic.o
 obj-$(CONFIG_LS_SCFG_MSI)              += irq-ls-scfg-msi.o
 obj-$(CONFIG_EZNPS_GIC)                        += irq-eznps.o
 obj-$(CONFIG_ARCH_ASPEED)              += irq-aspeed-vic.o
diff --git a/drivers/irqchip/irq-jcore-aic.c b/drivers/irqchip/irq-jcore-aic.c
new file mode 100644 (file)
index 0000000..84b01de
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * J-Core SoC AIC driver
+ *
+ * Copyright (C) 2015-2016 Smart Energy Instruments, Inc.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/cpu.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+
+#define JCORE_AIC_MAX_HWIRQ    127
+#define JCORE_AIC1_MIN_HWIRQ   16
+#define JCORE_AIC2_MIN_HWIRQ   64
+
+#define JCORE_AIC1_INTPRI_REG  8
+
+static struct irq_chip jcore_aic;
+
+static int jcore_aic_irqdomain_map(struct irq_domain *d, unsigned int irq,
+                                  irq_hw_number_t hwirq)
+{
+       struct irq_chip *aic = d->host_data;
+
+       irq_set_chip_and_handler(irq, aic, handle_simple_irq);
+
+       return 0;
+}
+
+static const struct irq_domain_ops jcore_aic_irqdomain_ops = {
+       .map = jcore_aic_irqdomain_map,
+       .xlate = irq_domain_xlate_onecell,
+};
+
+static void noop(struct irq_data *data)
+{
+}
+
+static int __init aic_irq_of_init(struct device_node *node,
+                                 struct device_node *parent)
+{
+       unsigned min_irq = JCORE_AIC2_MIN_HWIRQ;
+       unsigned dom_sz = JCORE_AIC_MAX_HWIRQ+1;
+       struct irq_domain *domain;
+
+       pr_info("Initializing J-Core AIC\n");
+
+       /* AIC1 needs priority initialization to receive interrupts. */
+       if (of_device_is_compatible(node, "jcore,aic1")) {
+               unsigned cpu;
+
+               for_each_present_cpu(cpu) {
+                       void __iomem *base = of_iomap(node, cpu);
+
+                       if (!base) {
+                               pr_err("Unable to map AIC for cpu %u\n", cpu);
+                               return -ENOMEM;
+                       }
+                       __raw_writel(0xffffffff, base + JCORE_AIC1_INTPRI_REG);
+                       iounmap(base);
+               }
+               min_irq = JCORE_AIC1_MIN_HWIRQ;
+       }
+
+       /*
+        * The irq chip framework requires either mask/unmask or enable/disable
+        * function pointers to be provided, but the hardware does not have any
+        * such mechanism; the only interrupt masking is at the cpu level and
+        * it affects all interrupts. We provide dummy mask/unmask. The hardware
+        * handles all interrupt control and clears pending status when the cpu
+        * accepts the interrupt.
+        */
+       jcore_aic.irq_mask = noop;
+       jcore_aic.irq_unmask = noop;
+       jcore_aic.name = "AIC";
+
+       domain = irq_domain_add_linear(node, dom_sz, &jcore_aic_irqdomain_ops,
+                                      &jcore_aic);
+       if (!domain)
+               return -ENOMEM;
+       irq_create_strict_mappings(domain, min_irq, min_irq, dom_sz - min_irq);
+
+       return 0;
+}
+
+IRQCHIP_DECLARE(jcore_aic2, "jcore,aic2", aic_irq_of_init);
+IRQCHIP_DECLARE(jcore_aic1, "jcore,aic1", aic_irq_of_init);
index deb89d6..54a5e87 100644 (file)
@@ -109,7 +109,7 @@ static void keystone_irq_handler(struct irq_desc *desc)
                        dev_dbg(kirq->dev, "dispatch bit %d, virq %d\n",
                                src, virq);
                        if (!virq)
-                               dev_warn(kirq->dev, "sporious irq detected hwirq %d, virq %d\n",
+                               dev_warn(kirq->dev, "spurious irq detected hwirq %d, virq %d\n",
                                         src, virq);
                        generic_handle_irq(virq);
                }
index 8f7d38b..c0178a1 100644 (file)
@@ -371,18 +371,13 @@ static void gic_handle_shared_int(bool chained)
        bitmap_and(pending, pending, intrmask, gic_shared_intrs);
        bitmap_and(pending, pending, pcpu_mask, gic_shared_intrs);
 
-       intr = find_first_bit(pending, gic_shared_intrs);
-       while (intr != gic_shared_intrs) {
+       for_each_set_bit(intr, pending, gic_shared_intrs) {
                virq = irq_linear_revmap(gic_irq_domain,
                                         GIC_SHARED_TO_HWIRQ(intr));
                if (chained)
                        generic_handle_irq(virq);
                else
                        do_IRQ(virq);
-
-               /* go to next pending bit */
-               bitmap_clear(pending, intr, 1);
-               intr = find_first_bit(pending, gic_shared_intrs);
        }
 }
 
diff --git a/drivers/irqchip/irq-mvebu-pic.c b/drivers/irqchip/irq-mvebu-pic.c
new file mode 100644 (file)
index 0000000..eec6395
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 Marvell
+ *
+ * Yehuda Yitschak <yehuday@marvell.com>
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * 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 <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#define PIC_CAUSE             0x0
+#define PIC_MASK              0x4
+
+#define PIC_MAX_IRQS           32
+#define PIC_MAX_IRQ_MASK       ((1UL << PIC_MAX_IRQS) - 1)
+
+struct mvebu_pic {
+       void __iomem *base;
+       u32 parent_irq;
+       struct irq_domain *domain;
+       struct irq_chip irq_chip;
+};
+
+static void mvebu_pic_reset(struct mvebu_pic *pic)
+{
+       /* ACK and mask all interrupts */
+       writel(0, pic->base + PIC_MASK);
+       writel(PIC_MAX_IRQ_MASK, pic->base + PIC_CAUSE);
+}
+
+static void mvebu_pic_eoi_irq(struct irq_data *d)
+{
+       struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
+
+       writel(1 << d->hwirq, pic->base + PIC_CAUSE);
+}
+
+static void mvebu_pic_mask_irq(struct irq_data *d)
+{
+       struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
+       u32 reg;
+
+       reg =  readl(pic->base + PIC_MASK);
+       reg |= (1 << d->hwirq);
+       writel(reg, pic->base + PIC_MASK);
+}
+
+static void mvebu_pic_unmask_irq(struct irq_data *d)
+{
+       struct mvebu_pic *pic = irq_data_get_irq_chip_data(d);
+       u32 reg;
+
+       reg = readl(pic->base + PIC_MASK);
+       reg &= ~(1 << d->hwirq);
+       writel(reg, pic->base + PIC_MASK);
+}
+
+static int mvebu_pic_irq_map(struct irq_domain *domain, unsigned int virq,
+                            irq_hw_number_t hwirq)
+{
+       struct mvebu_pic *pic = domain->host_data;
+
+       irq_set_percpu_devid(virq);
+       irq_set_chip_data(virq, pic);
+       irq_set_chip_and_handler(virq, &pic->irq_chip,
+                                handle_percpu_devid_irq);
+       irq_set_status_flags(virq, IRQ_LEVEL);
+       irq_set_probe(virq);
+
+       return 0;
+}
+
+static const struct irq_domain_ops mvebu_pic_domain_ops = {
+       .map = mvebu_pic_irq_map,
+       .xlate = irq_domain_xlate_onecell,
+};
+
+static void mvebu_pic_handle_cascade_irq(struct irq_desc *desc)
+{
+       struct mvebu_pic *pic = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       unsigned long irqmap, irqn;
+       unsigned int cascade_irq;
+
+       irqmap = readl_relaxed(pic->base + PIC_CAUSE);
+       chained_irq_enter(chip, desc);
+
+       for_each_set_bit(irqn, &irqmap, BITS_PER_LONG) {
+               cascade_irq = irq_find_mapping(pic->domain, irqn);
+               generic_handle_irq(cascade_irq);
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static void mvebu_pic_enable_percpu_irq(void *data)
+{
+       struct mvebu_pic *pic = data;
+
+       mvebu_pic_reset(pic);
+       enable_percpu_irq(pic->parent_irq, IRQ_TYPE_NONE);
+}
+
+static void mvebu_pic_disable_percpu_irq(void *data)
+{
+       struct mvebu_pic *pic = data;
+
+       disable_percpu_irq(pic->parent_irq);
+}
+
+static int mvebu_pic_probe(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct mvebu_pic *pic;
+       struct irq_chip *irq_chip;
+       struct resource *res;
+
+       pic = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pic), GFP_KERNEL);
+       if (!pic)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       pic->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(pic->base))
+               return PTR_ERR(pic->base);
+
+       irq_chip = &pic->irq_chip;
+       irq_chip->name = dev_name(&pdev->dev);
+       irq_chip->irq_mask = mvebu_pic_mask_irq;
+       irq_chip->irq_unmask = mvebu_pic_unmask_irq;
+       irq_chip->irq_eoi = mvebu_pic_eoi_irq;
+
+       pic->parent_irq = irq_of_parse_and_map(node, 0);
+       if (pic->parent_irq <= 0) {
+               dev_err(&pdev->dev, "Failed to parse parent interrupt\n");
+               return -EINVAL;
+       }
+
+       pic->domain = irq_domain_add_linear(node, PIC_MAX_IRQS,
+                                           &mvebu_pic_domain_ops, pic);
+       if (!pic->domain) {
+               dev_err(&pdev->dev, "Failed to allocate irq domain\n");
+               return -ENOMEM;
+       }
+
+       irq_set_chained_handler(pic->parent_irq, mvebu_pic_handle_cascade_irq);
+       irq_set_handler_data(pic->parent_irq, pic);
+
+       on_each_cpu(mvebu_pic_enable_percpu_irq, pic, 1);
+
+       platform_set_drvdata(pdev, pic);
+
+       return 0;
+}
+
+static int mvebu_pic_remove(struct platform_device *pdev)
+{
+       struct mvebu_pic *pic = platform_get_drvdata(pdev);
+
+       on_each_cpu(mvebu_pic_disable_percpu_irq, pic, 1);
+       irq_domain_remove(pic->domain);
+
+       return 0;
+}
+
+static const struct of_device_id mvebu_pic_of_match[] = {
+       { .compatible = "marvell,armada-8k-pic", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, mvebu_pic_of_match);
+
+static struct platform_driver mvebu_pic_driver = {
+       .probe  = mvebu_pic_probe,
+       .remove = mvebu_pic_remove,
+       .driver = {
+               .name = "mvebu-pic",
+               .of_match_table = mvebu_pic_of_match,
+       },
+};
+module_platform_driver(mvebu_pic_driver);
+
+MODULE_AUTHOR("Yehuda Yitschak <yehuday@marvell.com>");
+MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mvebu_pic");
+