Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 17 May 2016 17:27:29 +0000 (10:27 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 17 May 2016 17:27:29 +0000 (10:27 -0700)
Pull irq updates from Thomas Gleixner:
 "This update delivers:

   - Yet another interrupt chip diver (LPC32xx)
   - Core functions to handle partitioned per-cpu interrupts
   - Enhancements to the IPI core
   - Proper handling of irq type configuration
   - A large set of ARM GIC enhancements
   - The usual pile of small fixes, cleanups and enhancements"

* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (31 commits)
  irqchip/bcm2836: Use a more generic memory barrier call
  irqchip/bcm2836: Fix compiler warning on 64-bit build
  irqchip/bcm2836: Drop smp_set_ops on arm64 builds
  irqchip/gic: Add helper functions for GIC setup and teardown
  irqchip/gic: Store GIC configuration parameters
  irqchip/gic: Pass GIC pointer to save/restore functions
  irqchip/gic: Return an error if GIC initialisation fails
  irqchip/gic: Remove static irq_chip definition for eoimode1
  irqchip/gic: Don't initialise chip if mapping IO space fails
  irqchip/gic: WARN if setting the interrupt type for a PPI fails
  irqchip/gic: Don't unnecessarily write the IRQ configuration
  irqchip: Mask the non-type/sense bits when translating an IRQ
  genirq: Ensure IRQ descriptor is valid when setting-up the IRQ
  irqchip/gic-v3: Configure all interrupts as non-secure Group-1
  irqchip/gic-v2m: Add workaround for Broadcom NS2 GICv2m erratum
  irqchip/irq-alpine-msi: Don't use <asm-generic/msi.h>
  irqchip/mbigen: Checking for IS_ERR() instead of NULL
  irqchip/gic-v3: Remove inexistant register definition
  irqchip/gicv3-its: Don't allow devices whose ID is outside range
  irqchip: Add LPC32xx interrupt controller driver
  ...

28 files changed:
Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt
Documentation/devicetree/bindings/interrupt-controller/fsl,ls-scfg-msi.txt [new file with mode: 0644]
arch/arm/Kconfig
arch/arm/mach-lpc32xx/phy3250.c
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/irq-alpine-msi.c
drivers/irqchip/irq-bcm2836.c
drivers/irqchip/irq-crossbar.c
drivers/irqchip/irq-gic-common.c
drivers/irqchip/irq-gic-v2m.c
drivers/irqchip/irq-gic-v3-its.c
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-gic.c
drivers/irqchip/irq-lpc32xx.c [new file with mode: 0644]
drivers/irqchip/irq-ls-scfg-msi.c [new file with mode: 0644]
drivers/irqchip/irq-mbigen.c
drivers/irqchip/irq-partition-percpu.c [new file with mode: 0644]
drivers/irqchip/irq-tegra.c
include/linux/irq.h
include/linux/irqchip/arm-gic-v3.h
include/linux/irqchip/irq-partition-percpu.h [new file with mode: 0644]
include/linux/irqdesc.h
include/linux/irqdomain.h
kernel/irq/ipi.c
kernel/irq/irqdesc.c
kernel/irq/irqdomain.c
kernel/irq/manage.c

index 007a5b46256ad57effb087c5beb2eae8c7e3d1e7..4c29cdab0ea5b72985168100891e2422e8dbb252 100644 (file)
@@ -11,6 +11,8 @@ Main node required properties:
 - interrupt-controller : Identifies the node as an interrupt controller
 - #interrupt-cells : Specifies the number of cells needed to encode an
   interrupt source. Must be a single cell with a value of at least 3.
+  If the system requires describing PPI affinity, then the value must
+  be at least 4.
 
   The 1st cell is the interrupt type; 0 for SPI interrupts, 1 for PPI
   interrupts. Other values are reserved for future use.
@@ -24,7 +26,14 @@ Main node required properties:
                1 = edge triggered
                4 = level triggered
 
-  Cells 4 and beyond are reserved for future use and must have a value
+  The 4th cell is a phandle to a node describing a set of CPUs this
+  interrupt is affine to. The interrupt must be a PPI, and the node
+  pointed must be a subnode of the "ppi-partitions" subnode. For
+  interrupt types other than PPI or PPIs that are not partitionned,
+  this cell must be zero. See the "ppi-partitions" node description
+  below.
+
+  Cells 5 and beyond are reserved for future use and must have a value
   of 0 if present.
 
 - reg : Specifies base physical address(s) and size of the GIC
@@ -50,6 +59,11 @@ Optional
 
 Sub-nodes:
 
+PPI affinity can be expressed as a single "ppi-partitions" node,
+containing a set of sub-nodes, each with the following property:
+- affinity: Should be a list of phandles to CPU nodes (as described in
+Documentation/devicetree/bindings/arm/cpus.txt).
+
 GICv3 has one or more Interrupt Translation Services (ITS) that are
 used to route Message Signalled Interrupts (MSI) to the CPUs.
 
@@ -91,7 +105,7 @@ Examples:
 
        gic: interrupt-controller@2c010000 {
                compatible = "arm,gic-v3";
-               #interrupt-cells = <3>;
+               #interrupt-cells = <4>;
                #address-cells = <2>;
                #size-cells = <2>;
                ranges;
@@ -119,4 +133,20 @@ Examples:
                        #msi-cells = <1>;
                        reg = <0x0 0x2c400000 0 0x200000>;
                };
+
+               ppi-partitions {
+                       part0: interrupt-partition-0 {
+                               affinity = <&cpu0 &cpu2>;
+                       };
+
+                       part1: interrupt-partition-1 {
+                               affinity = <&cpu1 &cpu3>;
+                       };
+               };
+       };
+
+
+       device@0 {
+               reg = <0 0 0 4>;
+               interrupts = <1 1 4 &part0>;
        };
diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,ls-scfg-msi.txt b/Documentation/devicetree/bindings/interrupt-controller/fsl,ls-scfg-msi.txt
new file mode 100644 (file)
index 0000000..9e38949
--- /dev/null
@@ -0,0 +1,30 @@
+* Freescale Layerscape SCFG PCIe MSI controller
+
+Required properties:
+
+- compatible: should be "fsl,<soc-name>-msi" to identify
+             Layerscape PCIe MSI controller block such as:
+              "fsl,1s1021a-msi"
+              "fsl,1s1043a-msi"
+- msi-controller: indicates that this is a PCIe MSI controller node
+- reg: physical base address of the controller and length of memory mapped.
+- interrupts: an interrupt to the parent interrupt controller.
+
+Optional properties:
+- interrupt-parent: the phandle to the parent interrupt controller.
+
+This interrupt controller hardware is a second level interrupt controller that
+is hooked to a parent interrupt controller: e.g: ARM GIC for ARM-based
+platforms. If interrupt-parent is not provided, the default parent interrupt
+controller will be used.
+Each PCIe node needs to have property msi-parent that points to
+MSI controller node
+
+Examples:
+
+       msi1: msi-controller@1571000 {
+               compatible = "fsl,1s1043a-msi";
+               reg = <0x0 0x1571000 0x0 0x8>,
+               msi-controller;
+               interrupts = <0 116 0x4>;
+       };
index cdfa6c2b7626f3a761364ac072fc359b5f02592a..6684af91cb73ece6b50758130132adb9f56913a7 100644 (file)
@@ -531,6 +531,8 @@ config ARCH_LPC32XX
        select COMMON_CLK
        select CPU_ARM926T
        select GENERIC_CLOCKEVENTS
+       select MULTI_IRQ_HANDLER
+       select SPARSE_IRQ
        select USE_OF
        help
          Support for the NXP LPC32XX family of processors
index b2f9e226febeee970b0594c8be030a4838735e7c..f9209d091c4bf3f7b9bd187a4fb291bf3ae572d4 100644 (file)
@@ -206,7 +206,6 @@ static const char *const lpc32xx_dt_compat[] __initconst = {
 DT_MACHINE_START(LPC32XX_DT, "LPC32XX SoC (Flattened Device Tree)")
        .atag_offset    = 0x100,
        .map_io         = lpc32xx_map_io,
-       .init_irq       = lpc32xx_init_irq,
        .init_machine   = lpc3250_machine_init,
        .dt_compat      = lpc32xx_dt_compat,
 MACHINE_END
index 3e124793e224f8008cbfafa8c7377c7afc4bd9fd..81f88ada3a612831b04aa9fba96e8fa3215a3022 100644 (file)
@@ -27,6 +27,7 @@ config ARM_GIC_V3
        select IRQ_DOMAIN
        select MULTI_IRQ_HANDLER
        select IRQ_DOMAIN_HIERARCHY
+       select PARTITION_PERCPU
 
 config ARM_GIC_V3_ITS
        bool
@@ -244,3 +245,11 @@ config IRQ_MXS
 config MVEBU_ODMI
        bool
        select GENERIC_MSI_IRQ_DOMAIN
+
+config LS_SCFG_MSI
+       def_bool y if SOC_LS1021A || ARCH_LAYERSCAPE
+       depends on PCI && PCI_MSI
+       select PCI_MSI_IRQ_DOMAIN
+
+config PARTITION_PERCPU
+       bool
index b03cfcbbac6b949b69c8b2f96ac7d307dd5b54e8..f828244b44c25bfadfbf44f3d70103cd4a1690f4 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_BCM2835)              += irq-bcm2835.o
 obj-$(CONFIG_ARCH_BCM2835)             += irq-bcm2836.o
 obj-$(CONFIG_ARCH_EXYNOS)              += exynos-combiner.o
 obj-$(CONFIG_ARCH_HIP04)               += irq-hip04.o
+obj-$(CONFIG_ARCH_LPC32XX)             += irq-lpc32xx.o
 obj-$(CONFIG_ARCH_MMP)                 += irq-mmp.o
 obj-$(CONFIG_IRQ_MXS)                  += irq-mxs.o
 obj-$(CONFIG_ARCH_TEGRA)               += irq-tegra.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_REALVIEW_DT)             += irq-gic-realview.o
 obj-$(CONFIG_ARM_GIC_V2M)              += irq-gic-v2m.o
 obj-$(CONFIG_ARM_GIC_V3)               += irq-gic-v3.o irq-gic-common.o
 obj-$(CONFIG_ARM_GIC_V3_ITS)           += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o
+obj-$(CONFIG_PARTITION_PERCPU)         += irq-partition-percpu.o
 obj-$(CONFIG_HISILICON_IRQ_MBIGEN)     += irq-mbigen.o
 obj-$(CONFIG_ARM_NVIC)                 += irq-nvic.o
 obj-$(CONFIG_ARM_VIC)                  += irq-vic.o
@@ -65,3 +67,4 @@ 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_LS_SCFG_MSI)              += irq-ls-scfg-msi.o
index 25384255b30f5017eb3e2287a942aebede72c37e..63d980995d17d0751194f4c64fdbfeaab78be3db 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/slab.h>
 
 #include <asm/irq.h>
-#include <asm-generic/msi.h>
+#include <asm/msi.h>
 
 /* MSIX message address format: local GIC target */
 #define ALPINE_MSIX_SPI_TARGET_CLUSTER0                BIT(16)
index b6e950d4782a1e020acddb50705efb180fe758b3..72ff1d5c5de67058f4ef22bb233c41b7c737d6e3 100644 (file)
@@ -195,7 +195,7 @@ static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
         * Ensure that stores to normal memory are visible to the
         * other CPUs before issuing the IPI.
         */
-       dsb();
+       smp_wmb();
 
        for_each_cpu(cpu, mask) {
                writel(1 << ipi, mailbox0_base + 16 * cpu);
@@ -223,6 +223,7 @@ static struct notifier_block bcm2836_arm_irqchip_cpu_notifier = {
        .priority = 100,
 };
 
+#ifdef CONFIG_ARM
 int __init bcm2836_smp_boot_secondary(unsigned int cpu,
                                      struct task_struct *idle)
 {
@@ -238,7 +239,7 @@ int __init bcm2836_smp_boot_secondary(unsigned int cpu,
 static const struct smp_operations bcm2836_smp_ops __initconst = {
        .smp_boot_secondary     = bcm2836_smp_boot_secondary,
 };
-
+#endif
 #endif
 
 static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
@@ -252,12 +253,15 @@ bcm2836_arm_irqchip_smp_init(void)
        /* Unmask IPIs to the boot CPU. */
        bcm2836_arm_irqchip_cpu_notify(&bcm2836_arm_irqchip_cpu_notifier,
                                       CPU_STARTING,
-                                      (void *)smp_processor_id());
+                                      (void *)(uintptr_t)smp_processor_id());
        register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier);
 
        set_smp_cross_call(bcm2836_arm_irqchip_send_ipi);
+
+#ifdef CONFIG_ARM
        smp_set_ops(&bcm2836_smp_ops);
 #endif
+#endif
 }
 
 /*
index 75573fa431ba8552e21afcc19edc8190c329d131..1eef56a89b1fbff1ee348f08b4623f6fcf4f6851 100644 (file)
@@ -183,7 +183,7 @@ static int crossbar_domain_translate(struct irq_domain *d,
                        return -EINVAL;
 
                *hwirq = fwspec->param[1];
-               *type = fwspec->param[2];
+               *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
                return 0;
        }
 
index f174ce0ca36115dc36feec5f047af7bf402e180a..97c0028e83889303e49be5a0abc52fd76d2b5dca 100644 (file)
@@ -50,14 +50,26 @@ int gic_configure_irq(unsigned int irq, unsigned int type,
        else if (type & IRQ_TYPE_EDGE_BOTH)
                val |= confmask;
 
+       /* If the current configuration is the same, then we are done */
+       if (val == oldval)
+               return 0;
+
        /*
         * Write back the new configuration, and possibly re-enable
-        * the interrupt. If we tried to write a new configuration and failed,
-        * return an error.
+        * the interrupt. If we fail to write a new configuration for
+        * an SPI then WARN and return an error. If we fail to write the
+        * configuration for a PPI this is most likely because the GIC
+        * does not allow us to set the configuration or we are in a
+        * non-secure mode, and hence it may not be catastrophic.
         */
        writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
-       if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val && val != oldval)
-               ret = -EINVAL;
+       if (readl_relaxed(base + GIC_DIST_CONFIG + confoff) != val) {
+               if (WARN_ON(irq >= 32))
+                       ret = -EINVAL;
+               else
+                       pr_warn("GIC: PPI%d is secure or misconfigured\n",
+                               irq - 16);
+       }
 
        if (sync_access)
                sync_access();
index 28f047c61baae62ffed6a07c9ef2f55cbd4de05b..ad0d2960b6644d0f71df271e81802d647e7905b7 100644 (file)
@@ -49,6 +49,9 @@
 /* APM X-Gene with GICv2m MSI_IIDR register value */
 #define XGENE_GICV2M_MSI_IIDR          0x06000170
 
+/* Broadcom NS2 GICv2m MSI_IIDR register value */
+#define BCM_NS2_GICV2M_MSI_IIDR                0x0000013f
+
 /* List of flags for specific v2m implementation */
 #define GICV2M_NEEDS_SPI_OFFSET                0x00000001
 
@@ -62,6 +65,7 @@ struct v2m_data {
        void __iomem *base;     /* GICv2m virt address */
        u32 spi_start;          /* The SPI number that MSIs start */
        u32 nr_spis;            /* The number of SPIs for MSIs */
+       u32 spi_offset;         /* offset to be subtracted from SPI number */
        unsigned long *bm;      /* MSI vector bitmap */
        u32 flags;              /* v2m flags for specific implementation */
 };
@@ -102,7 +106,7 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
        msg->data = data->hwirq;
 
        if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
-               msg->data -= v2m->spi_start;
+               msg->data -= v2m->spi_offset;
 }
 
 static struct irq_chip gicv2m_irq_chip = {
@@ -340,9 +344,20 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
         * different from the standard GICv2m implementation where
         * the MSI data is the absolute value within the range from
         * spi_start to (spi_start + num_spis).
+        *
+        * Broadom NS2 GICv2m implementation has an erratum where the MSI data
+        * is 'spi_number - 32'
         */
-       if (readl_relaxed(v2m->base + V2M_MSI_IIDR) == XGENE_GICV2M_MSI_IIDR)
+       switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
+       case XGENE_GICV2M_MSI_IIDR:
+               v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
+               v2m->spi_offset = v2m->spi_start;
+               break;
+       case BCM_NS2_GICV2M_MSI_IIDR:
                v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
+               v2m->spi_offset = 32;
+               break;
+       }
 
        v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis),
                          GFP_KERNEL);
index 39261798c59f9d42f5a94d85d5a19d97c8a80d98..6bd881be24eafe2316f9cf137497c2f2dbef9109 100644 (file)
@@ -54,6 +54,16 @@ struct its_collection {
        u16                     col_id;
 };
 
+/*
+ * The ITS_BASER structure - contains memory information and cached
+ * value of BASER register configuration.
+ */
+struct its_baser {
+       void            *base;
+       u64             val;
+       u32             order;
+};
+
 /*
  * The ITS structure - contains most of the infrastructure, with the
  * top-level MSI domain, the command queue, the collections, and the
@@ -66,14 +76,12 @@ struct its_node {
        unsigned long           phys_base;
        struct its_cmd_block    *cmd_base;
        struct its_cmd_block    *cmd_write;
-       struct {
-               void            *base;
-               u32             order;
-       } tables[GITS_BASER_NR_REGS];
+       struct its_baser        tables[GITS_BASER_NR_REGS];
        struct its_collection   *collections;
        struct list_head        its_device_list;
        u64                     flags;
        u32                     ite_size;
+       u32                     device_ids;
 };
 
 #define ITS_ITT_ALIGN          SZ_256
@@ -838,6 +846,8 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
                ids     = GITS_TYPER_DEVBITS(typer);
        }
 
+       its->device_ids = ids;
+
        for (i = 0; i < GITS_BASER_NR_REGS; i++) {
                u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
                u64 type = GITS_BASER_TYPE(val);
@@ -913,6 +923,7 @@ retry_baser:
                }
 
                val |= alloc_pages - 1;
+               its->tables[i].val = val;
 
                writeq_relaxed(val, its->base + GITS_BASER + i * 8);
                tmp = readq_relaxed(its->base + GITS_BASER + i * 8);
@@ -1138,9 +1149,22 @@ static struct its_device *its_find_device(struct its_node *its, u32 dev_id)
        return its_dev;
 }
 
+static struct its_baser *its_get_baser(struct its_node *its, u32 type)
+{
+       int i;
+
+       for (i = 0; i < GITS_BASER_NR_REGS; i++) {
+               if (GITS_BASER_TYPE(its->tables[i].val) == type)
+                       return &its->tables[i];
+       }
+
+       return NULL;
+}
+
 static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
                                            int nvecs)
 {
+       struct its_baser *baser;
        struct its_device *dev;
        unsigned long *lpi_map;
        unsigned long flags;
@@ -1151,6 +1175,16 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
        int nr_ites;
        int sz;
 
+       baser = its_get_baser(its, GITS_BASER_TYPE_DEVICE);
+
+       /* Don't allow 'dev_id' that exceeds single, flat table limit */
+       if (baser) {
+               if (dev_id >= (PAGE_ORDER_TO_SIZE(baser->order) /
+                             GITS_BASER_ENTRY_SIZE(baser->val)))
+                       return NULL;
+       } else if (ilog2(dev_id) >= its->device_ids)
+               return NULL;
+
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        /*
         * At least one bit of EventID is being used, hence a minimum
index 5b7d3c2129d8776a3e087b9861f64161142a815a..1a1ea4f733c1f6d3b1f71097135f96e5bc706afb 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <linux/irqchip.h>
 #include <linux/irqchip/arm-gic-v3.h>
+#include <linux/irqchip/irq-partition-percpu.h>
 
 #include <asm/cputype.h>
 #include <asm/exception.h>
@@ -44,6 +45,7 @@ struct redist_region {
 };
 
 struct gic_chip_data {
+       struct fwnode_handle    *fwnode;
        void __iomem            *dist_base;
        struct redist_region    *redist_regions;
        struct rdists           rdists;
@@ -51,6 +53,7 @@ struct gic_chip_data {
        u64                     redist_stride;
        u32                     nr_redist_regions;
        unsigned int            irq_nr;
+       struct partition_desc   *ppi_descs[16];
 };
 
 static struct gic_chip_data gic_data __read_mostly;
@@ -364,6 +367,13 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
                        if (static_key_true(&supports_deactivate))
                                gic_write_dir(irqnr);
 #ifdef CONFIG_SMP
+                       /*
+                        * Unlike GICv2, we don't need an smp_rmb() here.
+                        * The control dependency from gic_read_iar to
+                        * the ISB in gic_write_eoir is enough to ensure
+                        * that any shared data read by handle_IPI will
+                        * be read after the ACK.
+                        */
                        handle_IPI(irqnr, regs);
 #else
                        WARN_ONCE(true, "Unexpected SGI received!\n");
@@ -383,6 +393,15 @@ static void __init gic_dist_init(void)
        writel_relaxed(0, base + GICD_CTLR);
        gic_dist_wait_for_rwp();
 
+       /*
+        * Configure SPIs as non-secure Group-1. This will only matter
+        * if the GIC only has a single security state. This will not
+        * do the right thing if the kernel is running in secure mode,
+        * but that's not the intended use case anyway.
+        */
+       for (i = 32; i < gic_data.irq_nr; i += 32)
+               writel_relaxed(~0, base + GICD_IGROUPR + i / 8);
+
        gic_dist_config(base, gic_data.irq_nr, gic_dist_wait_for_rwp);
 
        /* Enable distributor with ARE, Group1 */
@@ -500,6 +519,9 @@ static void gic_cpu_init(void)
 
        rbase = gic_data_rdist_sgi_base();
 
+       /* Configure SGIs/PPIs as non-secure Group-1 */
+       writel_relaxed(~0, rbase + GICR_IGROUPR0);
+
        gic_cpu_config(rbase, gic_redist_wait_for_rwp);
 
        /* Give LPIs a spin */
@@ -812,10 +834,62 @@ static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
        }
 }
 
+static int gic_irq_domain_select(struct irq_domain *d,
+                                struct irq_fwspec *fwspec,
+                                enum irq_domain_bus_token bus_token)
+{
+       /* Not for us */
+        if (fwspec->fwnode != d->fwnode)
+               return 0;
+
+       /* If this is not DT, then we have a single domain */
+       if (!is_of_node(fwspec->fwnode))
+               return 1;
+
+       /*
+        * If this is a PPI and we have a 4th (non-null) parameter,
+        * then we need to match the partition domain.
+        */
+       if (fwspec->param_count >= 4 &&
+           fwspec->param[0] == 1 && fwspec->param[3] != 0)
+               return d == partition_get_domain(gic_data.ppi_descs[fwspec->param[1]]);
+
+       return d == gic_data.domain;
+}
+
 static const struct irq_domain_ops gic_irq_domain_ops = {
        .translate = gic_irq_domain_translate,
        .alloc = gic_irq_domain_alloc,
        .free = gic_irq_domain_free,
+       .select = gic_irq_domain_select,
+};
+
+static int partition_domain_translate(struct irq_domain *d,
+                                     struct irq_fwspec *fwspec,
+                                     unsigned long *hwirq,
+                                     unsigned int *type)
+{
+       struct device_node *np;
+       int ret;
+
+       np = of_find_node_by_phandle(fwspec->param[3]);
+       if (WARN_ON(!np))
+               return -EINVAL;
+
+       ret = partition_translate_id(gic_data.ppi_descs[fwspec->param[1]],
+                                    of_node_to_fwnode(np));
+       if (ret < 0)
+               return ret;
+
+       *hwirq = ret;
+       *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+
+       return 0;
+}
+
+static const struct irq_domain_ops partition_domain_ops = {
+       .translate = partition_domain_translate,
+       .select = gic_irq_domain_select,
 };
 
 static void gicv3_enable_quirks(void)
@@ -843,6 +917,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
        if (static_key_true(&supports_deactivate))
                pr_info("GIC: Using split EOI/Deactivate mode\n");
 
+       gic_data.fwnode = handle;
        gic_data.dist_base = dist_base;
        gic_data.redist_regions = rdist_regs;
        gic_data.nr_redist_regions = nr_redist_regions;
@@ -901,6 +976,119 @@ static int __init gic_validate_dist_version(void __iomem *dist_base)
        return 0;
 }
 
+static int get_cpu_number(struct device_node *dn)
+{
+       const __be32 *cell;
+       u64 hwid;
+       int i;
+
+       cell = of_get_property(dn, "reg", NULL);
+       if (!cell)
+               return -1;
+
+       hwid = of_read_number(cell, of_n_addr_cells(dn));
+
+       /*
+        * Non affinity bits must be set to 0 in the DT
+        */
+       if (hwid & ~MPIDR_HWID_BITMASK)
+               return -1;
+
+       for (i = 0; i < num_possible_cpus(); i++)
+               if (cpu_logical_map(i) == hwid)
+                       return i;
+
+       return -1;
+}
+
+/* Create all possible partitions at boot time */
+static void gic_populate_ppi_partitions(struct device_node *gic_node)
+{
+       struct device_node *parts_node, *child_part;
+       int part_idx = 0, i;
+       int nr_parts;
+       struct partition_affinity *parts;
+
+       parts_node = of_find_node_by_name(gic_node, "ppi-partitions");
+       if (!parts_node)
+               return;
+
+       nr_parts = of_get_child_count(parts_node);
+
+       if (!nr_parts)
+               return;
+
+       parts = kzalloc(sizeof(*parts) * nr_parts, GFP_KERNEL);
+       if (WARN_ON(!parts))
+               return;
+
+       for_each_child_of_node(parts_node, child_part) {
+               struct partition_affinity *part;
+               int n;
+
+               part = &parts[part_idx];
+
+               part->partition_id = of_node_to_fwnode(child_part);
+
+               pr_info("GIC: PPI partition %s[%d] { ",
+                       child_part->name, part_idx);
+
+               n = of_property_count_elems_of_size(child_part, "affinity",
+                                                   sizeof(u32));
+               WARN_ON(n <= 0);
+
+               for (i = 0; i < n; i++) {
+                       int err, cpu;
+                       u32 cpu_phandle;
+                       struct device_node *cpu_node;
+
+                       err = of_property_read_u32_index(child_part, "affinity",
+                                                        i, &cpu_phandle);
+                       if (WARN_ON(err))
+                               continue;
+
+                       cpu_node = of_find_node_by_phandle(cpu_phandle);
+                       if (WARN_ON(!cpu_node))
+                               continue;
+
+                       cpu = get_cpu_number(cpu_node);
+                       if (WARN_ON(cpu == -1))
+                               continue;
+
+                       pr_cont("%s[%d] ", cpu_node->full_name, cpu);
+
+                       cpumask_set_cpu(cpu, &part->mask);
+               }
+
+               pr_cont("}\n");
+               part_idx++;
+       }
+
+       for (i = 0; i < 16; i++) {
+               unsigned int irq;
+               struct partition_desc *desc;
+               struct irq_fwspec ppi_fwspec = {
+                       .fwnode         = gic_data.fwnode,
+                       .param_count    = 3,
+                       .param          = {
+                               [0]     = 1,
+                               [1]     = i,
+                               [2]     = IRQ_TYPE_NONE,
+                       },
+               };
+
+               irq = irq_create_fwspec_mapping(&ppi_fwspec);
+               if (WARN_ON(!irq))
+                       continue;
+               desc = partition_create_desc(gic_data.fwnode, parts, nr_parts,
+                                            irq, &partition_domain_ops);
+               if (WARN_ON(!desc))
+                       continue;
+
+               gic_data.ppi_descs[i] = desc;
+       }
+}
+
 static int __init gic_of_init(struct device_node *node, struct device_node *parent)
 {
        void __iomem *dist_base;
@@ -952,8 +1140,11 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
 
        err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,
                             redist_stride, &node->fwnode);
-       if (!err)
-               return 0;
+       if (err)
+               goto out_unmap_rdist;
+
+       gic_populate_ppi_partitions(node);
+       return 0;
 
 out_unmap_rdist:
        for (i = 0; i < nr_redist_regions; i++)
index 095bb5b5c3f2bee0c45a7a763fb33dbb1b023112..1de20e14a721111737bdab39c575ab7a45ddc63b 100644 (file)
@@ -72,6 +72,9 @@ struct gic_chip_data {
        struct irq_chip chip;
        union gic_base dist_base;
        union gic_base cpu_base;
+       void __iomem *raw_dist_base;
+       void __iomem *raw_cpu_base;
+       u32 percpu_offset;
 #ifdef CONFIG_CPU_PM
        u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
        u32 saved_spi_active[DIV_ROUND_UP(1020, 32)];
@@ -344,6 +347,14 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
                        if (static_key_true(&supports_deactivate))
                                writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
 #ifdef CONFIG_SMP
+                       /*
+                        * Ensure any shared data written by the CPU sending
+                        * the IPI is read after we've read the ACK register
+                        * on the GIC.
+                        *
+                        * Pairs with the write barrier in gic_raise_softirq
+                        */
+                       smp_rmb();
                        handle_IPI(irqnr, regs);
 #endif
                        continue;
@@ -391,20 +402,6 @@ static struct irq_chip gic_chip = {
                                  IRQCHIP_MASK_ON_SUSPEND,
 };
 
-static struct irq_chip gic_eoimode1_chip = {
-       .name                   = "GICv2",
-       .irq_mask               = gic_eoimode1_mask_irq,
-       .irq_unmask             = gic_unmask_irq,
-       .irq_eoi                = gic_eoimode1_eoi_irq,
-       .irq_set_type           = gic_set_type,
-       .irq_get_irqchip_state  = gic_irq_get_irqchip_state,
-       .irq_set_irqchip_state  = gic_irq_set_irqchip_state,
-       .irq_set_vcpu_affinity  = gic_irq_set_vcpu_affinity,
-       .flags                  = IRQCHIP_SET_TYPE_MASKED |
-                                 IRQCHIP_SKIP_SET_WAKE |
-                                 IRQCHIP_MASK_ON_SUSPEND,
-};
-
 void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)
 {
        BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
@@ -473,7 +470,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic)
        writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL);
 }
 
-static void gic_cpu_init(struct gic_chip_data *gic)
+static int gic_cpu_init(struct gic_chip_data *gic)
 {
        void __iomem *dist_base = gic_data_dist_base(gic);
        void __iomem *base = gic_data_cpu_base(gic);
@@ -489,7 +486,9 @@ static void gic_cpu_init(struct gic_chip_data *gic)
                /*
                 * Get what the GIC says our CPU mask is.
                 */
-               BUG_ON(cpu >= NR_GIC_CPU_IF);
+               if (WARN_ON(cpu >= NR_GIC_CPU_IF))
+                       return -EINVAL;
+
                gic_check_cpu_features();
                cpu_mask = gic_get_cpumask(gic);
                gic_cpu_map[cpu] = cpu_mask;
@@ -507,6 +506,8 @@ static void gic_cpu_init(struct gic_chip_data *gic)
 
        writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK);
        gic_cpu_if_up(gic);
+
+       return 0;
 }
 
 int gic_cpu_if_down(unsigned int gic_nr)
@@ -532,34 +533,35 @@ int gic_cpu_if_down(unsigned int gic_nr)
  * this function, no interrupts will be delivered by the GIC, and another
  * platform-specific wakeup source must be enabled.
  */
-static void gic_dist_save(unsigned int gic_nr)
+static void gic_dist_save(struct gic_chip_data *gic)
 {
        unsigned int gic_irqs;
        void __iomem *dist_base;
        int i;
 
-       BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
+       if (WARN_ON(!gic))
+               return;
 
-       gic_irqs = gic_data[gic_nr].gic_irqs;
-       dist_base = gic_data_dist_base(&gic_data[gic_nr]);
+       gic_irqs = gic->gic_irqs;
+       dist_base = gic_data_dist_base(gic);
 
        if (!dist_base)
                return;
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
-               gic_data[gic_nr].saved_spi_conf[i] =
+               gic->saved_spi_conf[i] =
                        readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
-               gic_data[gic_nr].saved_spi_target[i] =
+               gic->saved_spi_target[i] =
                        readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
-               gic_data[gic_nr].saved_spi_enable[i] =
+               gic->saved_spi_enable[i] =
                        readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
-               gic_data[gic_nr].saved_spi_active[i] =
+               gic->saved_spi_active[i] =
                        readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4);
 }
 
@@ -570,16 +572,17 @@ static void gic_dist_save(unsigned int gic_nr)
  * handled normally, but any edge interrupts that occured will not be seen by
  * the GIC and need to be handled by the platform-specific wakeup source.
  */
-static void gic_dist_restore(unsigned int gic_nr)
+static void gic_dist_restore(struct gic_chip_data *gic)
 {
        unsigned int gic_irqs;
        unsigned int i;
        void __iomem *dist_base;
 
-       BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
+       if (WARN_ON(!gic))
+               return;
 
-       gic_irqs = gic_data[gic_nr].gic_irqs;
-       dist_base = gic_data_dist_base(&gic_data[gic_nr]);
+       gic_irqs = gic->gic_irqs;
+       dist_base = gic_data_dist_base(gic);
 
        if (!dist_base)
                return;
@@ -587,7 +590,7 @@ static void gic_dist_restore(unsigned int gic_nr)
        writel_relaxed(GICD_DISABLE, dist_base + GIC_DIST_CTRL);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
-               writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
+               writel_relaxed(gic->saved_spi_conf[i],
                        dist_base + GIC_DIST_CONFIG + i * 4);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
@@ -595,85 +598,87 @@ static void gic_dist_restore(unsigned int gic_nr)
                        dist_base + GIC_DIST_PRI + i * 4);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
-               writel_relaxed(gic_data[gic_nr].saved_spi_target[i],
+               writel_relaxed(gic->saved_spi_target[i],
                        dist_base + GIC_DIST_TARGET + i * 4);
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) {
                writel_relaxed(GICD_INT_EN_CLR_X32,
                        dist_base + GIC_DIST_ENABLE_CLEAR + i * 4);
-               writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
+               writel_relaxed(gic->saved_spi_enable[i],
                        dist_base + GIC_DIST_ENABLE_SET + i * 4);
        }
 
        for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++) {
                writel_relaxed(GICD_INT_EN_CLR_X32,
                        dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4);
-               writel_relaxed(gic_data[gic_nr].saved_spi_active[i],
+               writel_relaxed(gic->saved_spi_active[i],
                        dist_base + GIC_DIST_ACTIVE_SET + i * 4);
        }
 
        writel_relaxed(GICD_ENABLE, dist_base + GIC_DIST_CTRL);
 }
 
-static void gic_cpu_save(unsigned int gic_nr)
+static void gic_cpu_save(struct gic_chip_data *gic)
 {
        int i;
        u32 *ptr;
        void __iomem *dist_base;
        void __iomem *cpu_base;
 
-       BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
+       if (WARN_ON(!gic))
+               return;
 
-       dist_base = gic_data_dist_base(&gic_data[gic_nr]);
-       cpu_base = gic_data_cpu_base(&gic_data[gic_nr]);
+       dist_base = gic_data_dist_base(gic);
+       cpu_base = gic_data_cpu_base(gic);
 
        if (!dist_base || !cpu_base)
                return;
 
-       ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+       ptr = raw_cpu_ptr(gic->saved_ppi_enable);
        for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
                ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
 
-       ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_active);
+       ptr = raw_cpu_ptr(gic->saved_ppi_active);
        for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
                ptr[i] = readl_relaxed(dist_base + GIC_DIST_ACTIVE_SET + i * 4);
 
-       ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+       ptr = raw_cpu_ptr(gic->saved_ppi_conf);
        for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
                ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
 
 }
 
-static void gic_cpu_restore(unsigned int gic_nr)
+static void gic_cpu_restore(struct gic_chip_data *gic)
 {
        int i;
        u32 *ptr;
        void __iomem *dist_base;
        void __iomem *cpu_base;
 
-       BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
+       if (WARN_ON(!gic))
+               return;
 
-       dist_base = gic_data_dist_base(&gic_data[gic_nr]);
-       cpu_base = gic_data_cpu_base(&gic_data[gic_nr]);
+       dist_base = gic_data_dist_base(gic);
+       cpu_base = gic_data_cpu_base(gic);
 
        if (!dist_base || !cpu_base)
                return;
 
-       ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+       ptr = raw_cpu_ptr(gic->saved_ppi_enable);
        for (i = 0; i < DIV_ROUND_UP(32, 32); i++) {
                writel_relaxed(GICD_INT_EN_CLR_X32,
                               dist_base + GIC_DIST_ENABLE_CLEAR + i * 4);
                writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);
        }
 
-       ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_active);
+       ptr = raw_cpu_ptr(gic->saved_ppi_active);
        for (i = 0; i < DIV_ROUND_UP(32, 32); i++) {
                writel_relaxed(GICD_INT_EN_CLR_X32,
                               dist_base + GIC_DIST_ACTIVE_CLEAR + i * 4);
                writel_relaxed(ptr[i], dist_base + GIC_DIST_ACTIVE_SET + i * 4);
        }
 
-       ptr = raw_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+       ptr = raw_cpu_ptr(gic->saved_ppi_conf);
        for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
                writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
 
@@ -682,7 +687,7 @@ static void gic_cpu_restore(unsigned int gic_nr)
                                        dist_base + GIC_DIST_PRI + i * 4);
 
        writel_relaxed(GICC_INT_PRI_THRESHOLD, cpu_base + GIC_CPU_PRIMASK);
-       gic_cpu_if_up(&gic_data[gic_nr]);
+       gic_cpu_if_up(gic);
 }
 
 static int gic_notifier(struct notifier_block *self, unsigned long cmd,        void *v)
@@ -697,18 +702,18 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd,   void *v)
 #endif
                switch (cmd) {
                case CPU_PM_ENTER:
-                       gic_cpu_save(i);
+                       gic_cpu_save(&gic_data[i]);
                        break;
                case CPU_PM_ENTER_FAILED:
                case CPU_PM_EXIT:
-                       gic_cpu_restore(i);
+                       gic_cpu_restore(&gic_data[i]);
                        break;
                case CPU_CLUSTER_PM_ENTER:
-                       gic_dist_save(i);
+                       gic_dist_save(&gic_data[i]);
                        break;
                case CPU_CLUSTER_PM_ENTER_FAILED:
                case CPU_CLUSTER_PM_EXIT:
-                       gic_dist_restore(i);
+                       gic_dist_restore(&gic_data[i]);
                        break;
                }
        }
@@ -720,26 +725,39 @@ static struct notifier_block gic_notifier_block = {
        .notifier_call = gic_notifier,
 };
 
-static void __init gic_pm_init(struct gic_chip_data *gic)
+static int __init gic_pm_init(struct gic_chip_data *gic)
 {
        gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
                sizeof(u32));
-       BUG_ON(!gic->saved_ppi_enable);
+       if (WARN_ON(!gic->saved_ppi_enable))
+               return -ENOMEM;
 
        gic->saved_ppi_active = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
                sizeof(u32));
-       BUG_ON(!gic->saved_ppi_active);
+       if (WARN_ON(!gic->saved_ppi_active))
+               goto free_ppi_enable;
 
        gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
                sizeof(u32));
-       BUG_ON(!gic->saved_ppi_conf);
+       if (WARN_ON(!gic->saved_ppi_conf))
+               goto free_ppi_active;
 
        if (gic == &gic_data[0])
                cpu_pm_register_notifier(&gic_notifier_block);
+
+       return 0;
+
+free_ppi_active:
+       free_percpu(gic->saved_ppi_active);
+free_ppi_enable:
+       free_percpu(gic->saved_ppi_enable);
+
+       return -ENOMEM;
 }
 #else
-static void __init gic_pm_init(struct gic_chip_data *gic)
+static int __init gic_pm_init(struct gic_chip_data *gic)
 {
+       return 0;
 }
 #endif
 
@@ -1012,61 +1030,63 @@ static const struct irq_domain_ops gic_irq_domain_ops = {
        .unmap = gic_irq_domain_unmap,
 };
 
-static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
-                          void __iomem *dist_base, void __iomem *cpu_base,
-                          u32 percpu_offset, struct fwnode_handle *handle)
+static int __init __gic_init_bases(struct gic_chip_data *gic, int irq_start,
+                                  struct fwnode_handle *handle)
 {
        irq_hw_number_t hwirq_base;
-       struct gic_chip_data *gic;
-       int gic_irqs, irq_base, i;
-
-       BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR);
+       int gic_irqs, irq_base, i, ret;
 
-       gic = &gic_data[gic_nr];
+       if (WARN_ON(!gic || gic->domain))
+               return -EINVAL;
 
        /* Initialize irq_chip */
-       if (static_key_true(&supports_deactivate) && gic_nr == 0) {
-               gic->chip = gic_eoimode1_chip;
+       gic->chip = gic_chip;
+
+       if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) {
+               gic->chip.irq_mask = gic_eoimode1_mask_irq;
+               gic->chip.irq_eoi = gic_eoimode1_eoi_irq;
+               gic->chip.irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity;
+               gic->chip.name = kasprintf(GFP_KERNEL, "GICv2");
        } else {
-               gic->chip = gic_chip;
-               gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr);
+               gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d",
+                                          (int)(gic - &gic_data[0]));
        }
 
 #ifdef CONFIG_SMP
-       if (gic_nr == 0)
+       if (gic == &gic_data[0])
                gic->chip.irq_set_affinity = gic_set_affinity;
 #endif
 
-#ifdef CONFIG_GIC_NON_BANKED
-       if (percpu_offset) { /* Frankein-GIC without banked registers... */
+       if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) {
+               /* Frankein-GIC without banked registers... */
                unsigned int cpu;
 
                gic->dist_base.percpu_base = alloc_percpu(void __iomem *);
                gic->cpu_base.percpu_base = alloc_percpu(void __iomem *);
                if (WARN_ON(!gic->dist_base.percpu_base ||
                            !gic->cpu_base.percpu_base)) {
-                       free_percpu(gic->dist_base.percpu_base);
-                       free_percpu(gic->cpu_base.percpu_base);
-                       return;
+                       ret = -ENOMEM;
+                       goto error;
                }
 
                for_each_possible_cpu(cpu) {
                        u32 mpidr = cpu_logical_map(cpu);
                        u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
-                       unsigned long offset = percpu_offset * core_id;
-                       *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset;
-                       *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset;
+                       unsigned long offset = gic->percpu_offset * core_id;
+                       *per_cpu_ptr(gic->dist_base.percpu_base, cpu) =
+                               gic->raw_dist_base + offset;
+                       *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) =
+                               gic->raw_cpu_base + offset;
                }
 
                gic_set_base_accessor(gic, gic_get_percpu_base);
-       } else
-#endif
-       {                       /* Normal, sane GIC... */
-               WARN(percpu_offset,
+       } else {
+               /* Normal, sane GIC... */
+               WARN(gic->percpu_offset,
                     "GIC_NON_BANKED not enabled, ignoring %08x offset!",
-                    percpu_offset);
-               gic->dist_base.common_base = dist_base;
-               gic->cpu_base.common_base = cpu_base;
+                    gic->percpu_offset);
+               gic->dist_base.common_base = gic->raw_dist_base;
+               gic->cpu_base.common_base = gic->raw_cpu_base;
                gic_set_base_accessor(gic, gic_get_common_base);
        }
 
@@ -1089,7 +1109,7 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
                 * For primary GICs, skip over SGIs.
                 * For secondary GICs, skip over PPIs, too.
                 */
-               if (gic_nr == 0 && (irq_start & 31) > 0) {
+               if (gic == &gic_data[0] && (irq_start & 31) > 0) {
                        hwirq_base = 16;
                        if (irq_start != -1)
                                irq_start = (irq_start & ~31) + 16;
@@ -1111,10 +1131,12 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
                                        hwirq_base, &gic_irq_domain_ops, gic);
        }
 
-       if (WARN_ON(!gic->domain))
-               return;
+       if (WARN_ON(!gic->domain)) {
+               ret = -ENODEV;
+               goto error;
+       }
 
-       if (gic_nr == 0) {
+       if (gic == &gic_data[0]) {
                /*
                 * Initialize the CPU interface map to all CPUs.
                 * It will be refined as each CPU probes its ID.
@@ -1132,19 +1154,57 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
        }
 
        gic_dist_init(gic);
-       gic_cpu_init(gic);
-       gic_pm_init(gic);
+       ret = gic_cpu_init(gic);
+       if (ret)
+               goto error;
+
+       ret = gic_pm_init(gic);
+       if (ret)
+               goto error;
+
+       return 0;
+
+error:
+       if (IS_ENABLED(CONFIG_GIC_NON_BANKED) && gic->percpu_offset) {
+               free_percpu(gic->dist_base.percpu_base);
+               free_percpu(gic->cpu_base.percpu_base);
+       }
+
+       kfree(gic->chip.name);
+
+       return ret;
 }
 
 void __init gic_init(unsigned int gic_nr, int irq_start,
                     void __iomem *dist_base, void __iomem *cpu_base)
 {
+       struct gic_chip_data *gic;
+
+       if (WARN_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR))
+               return;
+
        /*
         * Non-DT/ACPI systems won't run a hypervisor, so let's not
         * bother with these...
         */
        static_key_slow_dec(&supports_deactivate);
-       __gic_init_bases(gic_nr, irq_start, dist_base, cpu_base, 0, NULL);
+
+       gic = &gic_data[gic_nr];
+       gic->raw_dist_base = dist_base;
+       gic->raw_cpu_base = cpu_base;
+
+       __gic_init_bases(gic, irq_start, NULL);
+}
+
+static void gic_teardown(struct gic_chip_data *gic)
+{
+       if (WARN_ON(!gic))
+               return;
+
+       if (gic->raw_dist_base)
+               iounmap(gic->raw_dist_base);
+       if (gic->raw_cpu_base)
+               iounmap(gic->raw_cpu_base);
 }
 
 #ifdef CONFIG_OF
@@ -1188,35 +1248,61 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base)
        return true;
 }
 
+static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node)
+{
+       if (!gic || !node)
+               return -EINVAL;
+
+       gic->raw_dist_base = of_iomap(node, 0);
+       if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n"))
+               goto error;
+
+       gic->raw_cpu_base = of_iomap(node, 1);
+       if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n"))
+               goto error;
+
+       if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset))
+               gic->percpu_offset = 0;
+
+       return 0;
+
+error:
+       gic_teardown(gic);
+
+       return -ENOMEM;
+}
+
 int __init
 gic_of_init(struct device_node *node, struct device_node *parent)
 {
-       void __iomem *cpu_base;
-       void __iomem *dist_base;
-       u32 percpu_offset;
-       int irq;
+       struct gic_chip_data *gic;
+       int irq, ret;
 
        if (WARN_ON(!node))
                return -ENODEV;
 
-       dist_base = of_iomap(node, 0);
-       WARN(!dist_base, "unable to map gic dist registers\n");
+       if (WARN_ON(gic_cnt >= CONFIG_ARM_GIC_MAX_NR))
+               return -EINVAL;
+
+       gic = &gic_data[gic_cnt];
 
-       cpu_base = of_iomap(node, 1);
-       WARN(!cpu_base, "unable to map gic cpu registers\n");
+       ret = gic_of_setup(gic, node);
+       if (ret)
+               return ret;
 
        /*
         * Disable split EOI/Deactivate if either HYP is not available
         * or the CPU interface is too small.
         */
-       if (gic_cnt == 0 && !gic_check_eoimode(node, &cpu_base))
+       if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base))
                static_key_slow_dec(&supports_deactivate);
 
-       if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
-               percpu_offset = 0;
+       ret = __gic_init_bases(gic, -1, &node->fwnode);
+       if (ret) {
+               gic_teardown(gic);
+               return ret;
+       }
 
-       __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset,
-                        &node->fwnode);
        if (!gic_cnt)
                gic_init_physaddr(node);
 
@@ -1303,9 +1389,9 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
                                   const unsigned long end)
 {
        struct acpi_madt_generic_distributor *dist;
-       void __iomem *cpu_base, *dist_base;
        struct fwnode_handle *domain_handle;
-       int count;
+       struct gic_chip_data *gic = &gic_data[0];
+       int count, ret;
 
        /* Collect CPU base addresses */
        count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
@@ -1315,17 +1401,18 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
                return -EINVAL;
        }
 
-       cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE);
-       if (!cpu_base) {
+       gic->raw_cpu_base = ioremap(cpu_phy_base, ACPI_GIC_CPU_IF_MEM_SIZE);
+       if (!gic->raw_cpu_base) {
                pr_err("Unable to map GICC registers\n");
                return -ENOMEM;
        }
 
        dist = (struct acpi_madt_generic_distributor *)header;
-       dist_base = ioremap(dist->base_address, ACPI_GICV2_DIST_MEM_SIZE);
-       if (!dist_base) {
+       gic->raw_dist_base = ioremap(dist->base_address,
+                                    ACPI_GICV2_DIST_MEM_SIZE);
+       if (!gic->raw_dist_base) {
                pr_err("Unable to map GICD registers\n");
-               iounmap(cpu_base);
+               gic_teardown(gic);
                return -ENOMEM;
        }
 
@@ -1340,15 +1427,20 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
        /*
         * Initialize GIC instance zero (no multi-GIC support).
         */
-       domain_handle = irq_domain_alloc_fwnode(dist_base);
+       domain_handle = irq_domain_alloc_fwnode(gic->raw_dist_base);
        if (!domain_handle) {
                pr_err("Unable to allocate domain handle\n");
-               iounmap(cpu_base);
-               iounmap(dist_base);
+               gic_teardown(gic);
                return -ENOMEM;
        }
 
-       __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
+       ret = __gic_init_bases(gic, -1, domain_handle);
+       if (ret) {
+               pr_err("Failed to initialise GIC\n");
+               irq_domain_free_fwnode(domain_handle);
+               gic_teardown(gic);
+               return ret;
+       }
 
        acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
 
diff --git a/drivers/irqchip/irq-lpc32xx.c b/drivers/irqchip/irq-lpc32xx.c
new file mode 100644 (file)
index 0000000..1034aeb
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2015-2016 Vladimir Zapolskiy <vz@mleia.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/io.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <asm/exception.h>
+
+#define LPC32XX_INTC_MASK              0x00
+#define LPC32XX_INTC_RAW               0x04
+#define LPC32XX_INTC_STAT              0x08
+#define LPC32XX_INTC_POL               0x0C
+#define LPC32XX_INTC_TYPE              0x10
+#define LPC32XX_INTC_FIQ               0x14
+
+#define NR_LPC32XX_IC_IRQS             32
+
+struct lpc32xx_irq_chip {
+       void __iomem *base;
+       struct irq_domain *domain;
+       struct irq_chip chip;
+};
+
+static struct lpc32xx_irq_chip *lpc32xx_mic_irqc;
+
+static inline u32 lpc32xx_ic_read(struct lpc32xx_irq_chip *ic, u32 reg)
+{
+       return readl_relaxed(ic->base + reg);
+}
+
+static inline void lpc32xx_ic_write(struct lpc32xx_irq_chip *ic,
+                                   u32 reg, u32 val)
+{
+       writel_relaxed(val, ic->base + reg);
+}
+
+static void lpc32xx_irq_mask(struct irq_data *d)
+{
+       struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d);
+       u32 val, mask = BIT(d->hwirq);
+
+       val = lpc32xx_ic_read(ic, LPC32XX_INTC_MASK) & ~mask;
+       lpc32xx_ic_write(ic, LPC32XX_INTC_MASK, val);
+}
+
+static void lpc32xx_irq_unmask(struct irq_data *d)
+{
+       struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d);
+       u32 val, mask = BIT(d->hwirq);
+
+       val = lpc32xx_ic_read(ic, LPC32XX_INTC_MASK) | mask;
+       lpc32xx_ic_write(ic, LPC32XX_INTC_MASK, val);
+}
+
+static void lpc32xx_irq_ack(struct irq_data *d)
+{
+       struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d);
+       u32 mask = BIT(d->hwirq);
+
+       lpc32xx_ic_write(ic, LPC32XX_INTC_RAW, mask);
+}
+
+static int lpc32xx_irq_set_type(struct irq_data *d, unsigned int type)
+{
+       struct lpc32xx_irq_chip *ic = irq_data_get_irq_chip_data(d);
+       u32 val, mask = BIT(d->hwirq);
+       bool high, edge;
+
+       switch (type) {
+       case IRQ_TYPE_EDGE_RISING:
+               edge = true;
+               high = true;
+               break;
+       case IRQ_TYPE_EDGE_FALLING:
+               edge = true;
+               high = false;
+               break;
+       case IRQ_TYPE_LEVEL_HIGH:
+               edge = false;
+               high = true;
+               break;
+       case IRQ_TYPE_LEVEL_LOW:
+               edge = false;
+               high = false;
+               break;
+       default:
+               pr_info("unsupported irq type %d\n", type);
+               return -EINVAL;
+       }
+
+       irqd_set_trigger_type(d, type);
+
+       val = lpc32xx_ic_read(ic, LPC32XX_INTC_POL);
+       if (high)
+               val |= mask;
+       else
+               val &= ~mask;
+       lpc32xx_ic_write(ic, LPC32XX_INTC_POL, val);
+
+       val = lpc32xx_ic_read(ic, LPC32XX_INTC_TYPE);
+       if (edge) {
+               val |= mask;
+               irq_set_handler_locked(d, handle_edge_irq);
+       } else {
+               val &= ~mask;
+               irq_set_handler_locked(d, handle_level_irq);
+       }
+       lpc32xx_ic_write(ic, LPC32XX_INTC_TYPE, val);
+
+       return 0;
+}
+
+static void __exception_irq_entry lpc32xx_handle_irq(struct pt_regs *regs)
+{
+       struct lpc32xx_irq_chip *ic = lpc32xx_mic_irqc;
+       u32 hwirq = lpc32xx_ic_read(ic, LPC32XX_INTC_STAT), irq;
+
+       while (hwirq) {
+               irq = __ffs(hwirq);
+               hwirq &= ~BIT(irq);
+               handle_domain_irq(lpc32xx_mic_irqc->domain, irq, regs);
+       }
+}
+
+static void lpc32xx_sic_handler(struct irq_desc *desc)
+{
+       struct lpc32xx_irq_chip *ic = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       u32 hwirq = lpc32xx_ic_read(ic, LPC32XX_INTC_STAT), irq;
+
+       chained_irq_enter(chip, desc);
+
+       while (hwirq) {
+               irq = __ffs(hwirq);
+               hwirq &= ~BIT(irq);
+               generic_handle_irq(irq_find_mapping(ic->domain, irq));
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static int lpc32xx_irq_domain_map(struct irq_domain *id, unsigned int virq,
+                                 irq_hw_number_t hw)
+{
+       struct lpc32xx_irq_chip *ic = id->host_data;
+
+       irq_set_chip_data(virq, ic);
+       irq_set_chip_and_handler(virq, &ic->chip, handle_level_irq);
+       irq_set_status_flags(virq, IRQ_LEVEL);
+       irq_set_noprobe(virq);
+
+       return 0;
+}
+
+static void lpc32xx_irq_domain_unmap(struct irq_domain *id, unsigned int virq)
+{
+       irq_set_chip_and_handler(virq, NULL, NULL);
+}
+
+static const struct irq_domain_ops lpc32xx_irq_domain_ops = {
+       .map    = lpc32xx_irq_domain_map,
+       .unmap  = lpc32xx_irq_domain_unmap,
+       .xlate  = irq_domain_xlate_twocell,
+};
+
+static int __init lpc32xx_of_ic_init(struct device_node *node,
+                                    struct device_node *parent)
+{
+       struct lpc32xx_irq_chip *irqc;
+       bool is_mic = of_device_is_compatible(node, "nxp,lpc3220-mic");
+       const __be32 *reg = of_get_property(node, "reg", NULL);
+       u32 parent_irq, i, addr = reg ? be32_to_cpu(*reg) : 0;
+
+       irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
+       if (!irqc)
+               return -ENOMEM;
+
+       irqc->base = of_iomap(node, 0);
+       if (!irqc->base) {
+               pr_err("%s: unable to map registers\n", node->full_name);
+               kfree(irqc);
+               return -EINVAL;
+       }
+
+       irqc->chip.irq_ack = lpc32xx_irq_ack;
+       irqc->chip.irq_mask = lpc32xx_irq_mask;
+       irqc->chip.irq_unmask = lpc32xx_irq_unmask;
+       irqc->chip.irq_set_type = lpc32xx_irq_set_type;
+       if (is_mic)
+               irqc->chip.name = kasprintf(GFP_KERNEL, "%08x.mic", addr);
+       else
+               irqc->chip.name = kasprintf(GFP_KERNEL, "%08x.sic", addr);
+
+       irqc->domain = irq_domain_add_linear(node, NR_LPC32XX_IC_IRQS,
+                                            &lpc32xx_irq_domain_ops, irqc);
+       if (!irqc->domain) {
+               pr_err("unable to add irq domain\n");
+               iounmap(irqc->base);
+               kfree(irqc->chip.name);
+               kfree(irqc);
+               return -ENODEV;
+       }
+
+       if (is_mic) {
+               lpc32xx_mic_irqc = irqc;
+               set_handle_irq(lpc32xx_handle_irq);
+       } else {
+               for (i = 0; i < of_irq_count(node); i++) {
+                       parent_irq = irq_of_parse_and_map(node, i);
+                       if (parent_irq)
+                               irq_set_chained_handler_and_data(parent_irq,
+                                                lpc32xx_sic_handler, irqc);
+               }
+       }
+
+       lpc32xx_ic_write(irqc, LPC32XX_INTC_MASK, 0x00);
+       lpc32xx_ic_write(irqc, LPC32XX_INTC_POL,  0x00);
+       lpc32xx_ic_write(irqc, LPC32XX_INTC_TYPE, 0x00);
+
+       return 0;
+}
+
+IRQCHIP_DECLARE(nxp_lpc32xx_mic, "nxp,lpc3220-mic", lpc32xx_of_ic_init);
+IRQCHIP_DECLARE(nxp_lpc32xx_sic, "nxp,lpc3220-sic", lpc32xx_of_ic_init);
diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c
new file mode 100644 (file)
index 0000000..02cca74
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Freescale SCFG MSI(-X) support
+ *
+ * Copyright (C) 2016 Freescale Semiconductor.
+ *
+ * Author: Minghuan Lian <Minghuan.Lian@nxp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/spinlock.h>
+
+#define MSI_MAX_IRQS   32
+#define MSI_IBS_SHIFT  3
+#define MSIR           4
+
+struct ls_scfg_msi {
+       spinlock_t              lock;
+       struct platform_device  *pdev;
+       struct irq_domain       *parent;
+       struct irq_domain       *msi_domain;
+       void __iomem            *regs;
+       phys_addr_t             msiir_addr;
+       int                     irq;
+       DECLARE_BITMAP(used, MSI_MAX_IRQS);
+};
+
+static struct irq_chip ls_scfg_msi_irq_chip = {
+       .name = "MSI",
+       .irq_mask       = pci_msi_mask_irq,
+       .irq_unmask     = pci_msi_unmask_irq,
+};
+
+static struct msi_domain_info ls_scfg_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS |
+                  MSI_FLAG_USE_DEF_CHIP_OPS |
+                  MSI_FLAG_PCI_MSIX),
+       .chip   = &ls_scfg_msi_irq_chip,
+};
+
+static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
+{
+       struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data);
+
+       msg->address_hi = upper_32_bits(msi_data->msiir_addr);
+       msg->address_lo = lower_32_bits(msi_data->msiir_addr);
+       msg->data = data->hwirq << MSI_IBS_SHIFT;
+}
+
+static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
+                                   const struct cpumask *mask, bool force)
+{
+       return -EINVAL;
+}
+
+static struct irq_chip ls_scfg_msi_parent_chip = {
+       .name                   = "SCFG",
+       .irq_compose_msi_msg    = ls_scfg_msi_compose_msg,
+       .irq_set_affinity       = ls_scfg_msi_set_affinity,
+};
+
+static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
+                                       unsigned int virq,
+                                       unsigned int nr_irqs,
+                                       void *args)
+{
+       struct ls_scfg_msi *msi_data = domain->host_data;
+       int pos, err = 0;
+
+       WARN_ON(nr_irqs != 1);
+
+       spin_lock(&msi_data->lock);
+       pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS);
+       if (pos < MSI_MAX_IRQS)
+               __set_bit(pos, msi_data->used);
+       else
+               err = -ENOSPC;
+       spin_unlock(&msi_data->lock);
+
+       if (err)
+               return err;
+
+       irq_domain_set_info(domain, virq, pos,
+                           &ls_scfg_msi_parent_chip, msi_data,
+                           handle_simple_irq, NULL, NULL);
+
+       return 0;
+}
+
+static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
+                                  unsigned int virq, unsigned int nr_irqs)
+{
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d);
+       int pos;
+
+       pos = d->hwirq;
+       if (pos < 0 || pos >= MSI_MAX_IRQS) {
+               pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
+               return;
+       }
+
+       spin_lock(&msi_data->lock);
+       __clear_bit(pos, msi_data->used);
+       spin_unlock(&msi_data->lock);
+}
+
+static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
+       .alloc  = ls_scfg_msi_domain_irq_alloc,
+       .free   = ls_scfg_msi_domain_irq_free,
+};
+
+static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
+{
+       struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc);
+       unsigned long val;
+       int pos, virq;
+
+       chained_irq_enter(irq_desc_get_chip(desc), desc);
+
+       val = ioread32be(msi_data->regs + MSIR);
+       for_each_set_bit(pos, &val, MSI_MAX_IRQS) {
+               virq = irq_find_mapping(msi_data->parent, (31 - pos));
+               if (virq)
+                       generic_handle_irq(virq);
+       }
+
+       chained_irq_exit(irq_desc_get_chip(desc), desc);
+}
+
+static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
+{
+       /* Initialize MSI domain parent */
+       msi_data->parent = irq_domain_add_linear(NULL,
+                                                MSI_MAX_IRQS,
+                                                &ls_scfg_msi_domain_ops,
+                                                msi_data);
+       if (!msi_data->parent) {
+               dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n");
+               return -ENOMEM;
+       }
+
+       msi_data->msi_domain = pci_msi_create_irq_domain(
+                               of_node_to_fwnode(msi_data->pdev->dev.of_node),
+                               &ls_scfg_msi_domain_info,
+                               msi_data->parent);
+       if (!msi_data->msi_domain) {
+               dev_err(&msi_data->pdev->dev, "failed to create MSI domain\n");
+               irq_domain_remove(msi_data->parent);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int ls_scfg_msi_probe(struct platform_device *pdev)
+{
+       struct ls_scfg_msi *msi_data;
+       struct resource *res;
+       int ret;
+
+       msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
+       if (!msi_data)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       msi_data->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(msi_data->regs)) {
+               dev_err(&pdev->dev, "failed to initialize 'regs'\n");
+               return PTR_ERR(msi_data->regs);
+       }
+       msi_data->msiir_addr = res->start;
+
+       msi_data->irq = platform_get_irq(pdev, 0);
+       if (msi_data->irq <= 0) {
+               dev_err(&pdev->dev, "failed to get MSI irq\n");
+               return -ENODEV;
+       }
+
+       msi_data->pdev = pdev;
+       spin_lock_init(&msi_data->lock);
+
+       ret = ls_scfg_msi_domains_init(msi_data);
+       if (ret)
+               return ret;
+
+       irq_set_chained_handler_and_data(msi_data->irq,
+                                        ls_scfg_msi_irq_handler,
+                                        msi_data);
+
+       platform_set_drvdata(pdev, msi_data);
+
+       return 0;
+}
+
+static int ls_scfg_msi_remove(struct platform_device *pdev)
+{
+       struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
+
+       irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL);
+
+       irq_domain_remove(msi_data->msi_domain);
+       irq_domain_remove(msi_data->parent);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static const struct of_device_id ls_scfg_msi_id[] = {
+       { .compatible = "fsl,1s1021a-msi", },
+       { .compatible = "fsl,1s1043a-msi", },
+       {},
+};
+
+static struct platform_driver ls_scfg_msi_driver = {
+       .driver = {
+               .name = "ls-scfg-msi",
+               .of_match_table = ls_scfg_msi_id,
+       },
+       .probe = ls_scfg_msi_probe,
+       .remove = ls_scfg_msi_remove,
+};
+
+module_platform_driver(ls_scfg_msi_driver);
+
+MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>");
+MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver");
+MODULE_LICENSE("GPL v2");
index d67baa231c1324af4f970aefd399d8b78fdf79b4..03b79b061d245bc40bd4b9b243d3ef79ac8549b2 100644 (file)
@@ -263,8 +263,8 @@ static int mbigen_device_probe(struct platform_device *pdev)
 
                parent = platform_bus_type.dev_root;
                child = of_platform_device_create(np, NULL, parent);
-               if (IS_ERR(child))
-                       return PTR_ERR(child);
+               if (!child)
+                       return -ENOMEM;
 
                if (of_property_read_u32(child->dev.of_node, "num-pins",
                                         &num_pins) < 0) {
diff --git a/drivers/irqchip/irq-partition-percpu.c b/drivers/irqchip/irq-partition-percpu.c
new file mode 100644 (file)
index 0000000..ccd72c2
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2016 ARM Limited, All Rights Reserved.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip/irq-partition-percpu.h>
+#include <linux/irqdomain.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+struct partition_desc {
+       int                             nr_parts;
+       struct partition_affinity       *parts;
+       struct irq_domain               *domain;
+       struct irq_desc                 *chained_desc;
+       unsigned long                   *bitmap;
+       struct irq_domain_ops           ops;
+};
+
+static bool partition_check_cpu(struct partition_desc *part,
+                               unsigned int cpu, unsigned int hwirq)
+{
+       return cpumask_test_cpu(cpu, &part->parts[hwirq].mask);
+}
+
+static void partition_irq_mask(struct irq_data *d)
+{
+       struct partition_desc *part = irq_data_get_irq_chip_data(d);
+       struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
+       struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
+
+       if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
+           chip->irq_mask)
+               chip->irq_mask(data);
+}
+
+static void partition_irq_unmask(struct irq_data *d)
+{
+       struct partition_desc *part = irq_data_get_irq_chip_data(d);
+       struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
+       struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
+
+       if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
+           chip->irq_unmask)
+               chip->irq_unmask(data);
+}
+
+static int partition_irq_set_irqchip_state(struct irq_data *d,
+                                          enum irqchip_irq_state which,
+                                          bool val)
+{
+       struct partition_desc *part = irq_data_get_irq_chip_data(d);
+       struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
+       struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
+
+       if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
+           chip->irq_set_irqchip_state)
+               return chip->irq_set_irqchip_state(data, which, val);
+
+       return -EINVAL;
+}
+
+static int partition_irq_get_irqchip_state(struct irq_data *d,
+                                          enum irqchip_irq_state which,
+                                          bool *val)
+{
+       struct partition_desc *part = irq_data_get_irq_chip_data(d);
+       struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
+       struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
+
+       if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
+           chip->irq_get_irqchip_state)
+               return chip->irq_get_irqchip_state(data, which, val);
+
+       return -EINVAL;
+}
+
+static int partition_irq_set_type(struct irq_data *d, unsigned int type)
+{
+       struct partition_desc *part = irq_data_get_irq_chip_data(d);
+       struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
+       struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
+
+       if (chip->irq_set_type)
+               return chip->irq_set_type(data, type);
+
+       return -EINVAL;
+}
+
+static void partition_irq_print_chip(struct irq_data *d, struct seq_file *p)
+{
+       struct partition_desc *part = irq_data_get_irq_chip_data(d);
+       struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
+       struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
+
+       seq_printf(p, " %5s-%lu", chip->name, data->hwirq);
+}
+
+static struct irq_chip partition_irq_chip = {
+       .irq_mask               = partition_irq_mask,
+       .irq_unmask             = partition_irq_unmask,
+       .irq_set_type           = partition_irq_set_type,
+       .irq_get_irqchip_state  = partition_irq_get_irqchip_state,
+       .irq_set_irqchip_state  = partition_irq_set_irqchip_state,
+       .irq_print_chip         = partition_irq_print_chip,
+};
+
+static void partition_handle_irq(struct irq_desc *desc)
+{
+       struct partition_desc *part = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       int cpu = smp_processor_id();
+       int hwirq;
+
+       chained_irq_enter(chip, desc);
+
+       for_each_set_bit(hwirq, part->bitmap, part->nr_parts) {
+               if (partition_check_cpu(part, cpu, hwirq))
+                       break;
+       }
+
+       if (unlikely(hwirq == part->nr_parts)) {
+               handle_bad_irq(desc);
+       } else {
+               unsigned int irq;
+               irq = irq_find_mapping(part->domain, hwirq);
+               generic_handle_irq(irq);
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
+static int partition_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs, void *arg)
+{
+       int ret;
+       irq_hw_number_t hwirq;
+       unsigned int type;
+       struct irq_fwspec *fwspec = arg;
+       struct partition_desc *part;
+
+       BUG_ON(nr_irqs != 1);
+       ret = domain->ops->translate(domain, fwspec, &hwirq, &type);
+       if (ret)
+               return ret;
+
+       part = domain->host_data;
+
+       set_bit(hwirq, part->bitmap);
+       irq_set_chained_handler_and_data(irq_desc_get_irq(part->chained_desc),
+                                        partition_handle_irq, part);
+       irq_set_percpu_devid_partition(virq, &part->parts[hwirq].mask);
+       irq_domain_set_info(domain, virq, hwirq, &partition_irq_chip, part,
+                           handle_percpu_devid_irq, NULL, NULL);
+       irq_set_status_flags(virq, IRQ_NOAUTOEN);
+
+       return 0;
+}
+
+static void partition_domain_free(struct irq_domain *domain, unsigned int virq,
+                                 unsigned int nr_irqs)
+{
+       struct irq_data *d;
+
+       BUG_ON(nr_irqs != 1);
+
+       d = irq_domain_get_irq_data(domain, virq);
+       irq_set_handler(virq, NULL);
+       irq_domain_reset_irq_data(d);
+}
+
+int partition_translate_id(struct partition_desc *desc, void *partition_id)
+{
+       struct partition_affinity *part = NULL;
+       int i;
+
+       for (i = 0; i < desc->nr_parts; i++) {
+               if (desc->parts[i].partition_id == partition_id) {
+                       part = &desc->parts[i];
+                       break;
+               }
+       }
+
+       if (WARN_ON(!part)) {
+               pr_err("Failed to find partition\n");
+               return -EINVAL;
+       }
+
+       return i;
+}
+
+struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode,
+                                            struct partition_affinity *parts,
+                                            int nr_parts,
+                                            int chained_irq,
+                                            const struct irq_domain_ops *ops)
+{
+       struct partition_desc *desc;
+       struct irq_domain *d;
+
+       BUG_ON(!ops->select || !ops->translate);
+
+       desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+       if (!desc)
+               return NULL;
+
+       desc->ops = *ops;
+       desc->ops.free = partition_domain_free;
+       desc->ops.alloc = partition_domain_alloc;
+
+       d = irq_domain_create_linear(fwnode, nr_parts, &desc->ops, desc);
+       if (!d)
+               goto out;
+       desc->domain = d;
+
+       desc->bitmap = kzalloc(sizeof(long) * BITS_TO_LONGS(nr_parts),
+                              GFP_KERNEL);
+       if (WARN_ON(!desc->bitmap))
+               goto out;
+
+       desc->chained_desc = irq_to_desc(chained_irq);
+       desc->nr_parts = nr_parts;
+       desc->parts = parts;
+
+       return desc;
+out:
+       if (d)
+               irq_domain_remove(d);
+       kfree(desc);
+
+       return NULL;
+}
+
+struct irq_domain *partition_get_domain(struct partition_desc *dsc)
+{
+       if (dsc)
+               return dsc->domain;
+
+       return NULL;
+}
index 50be9639e27eeda270a6cc75eb144a7e19a418c6..e902f081e16c53d2071fdefc37d8d82f501d9747 100644 (file)
@@ -235,7 +235,7 @@ static int tegra_ictlr_domain_translate(struct irq_domain *d,
                        return -EINVAL;
 
                *hwirq = fwspec->param[1];
-               *type = fwspec->param[2];
+               *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
                return 0;
        }
 
index c4de62348ff229194c85b133d45f0d220642b082..4d758a7c604a9cfa5689aa3843ff9e4620efb7c0 100644 (file)
@@ -530,6 +530,10 @@ static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *c
 }
 
 extern int irq_set_percpu_devid(unsigned int irq);
+extern int irq_set_percpu_devid_partition(unsigned int irq,
+                                         const struct cpumask *affinity);
+extern int irq_get_percpu_devid_partition(unsigned int irq,
+                                         struct cpumask *affinity);
 
 extern void
 __irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
index d5d798b35c1f6c105c81d0993bce6140bc4fc83b..9e6fdd33bdb2c15d3936fb198fd98cf26b2828cf 100644 (file)
 #define GICR_SYNCR                     0x00C0
 #define GICR_MOVLPIR                   0x0100
 #define GICR_MOVALLR                   0x0110
-#define GICR_ISACTIVER                 GICD_ISACTIVER
-#define GICR_ICACTIVER                 GICD_ICACTIVER
 #define GICR_IDREGS                    GICD_IDREGS
 #define GICR_PIDR2                     GICD_PIDR2
 
diff --git a/include/linux/irqchip/irq-partition-percpu.h b/include/linux/irqchip/irq-partition-percpu.h
new file mode 100644 (file)
index 0000000..87433a5
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 ARM Limited, All Rights Reserved.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/fwnode.h>
+#include <linux/cpumask.h>
+#include <linux/irqdomain.h>
+
+struct partition_affinity {
+       cpumask_t                       mask;
+       void                            *partition_id;
+};
+
+struct partition_desc;
+
+#ifdef CONFIG_PARTITION_PERCPU
+int partition_translate_id(struct partition_desc *desc, void *partition_id);
+struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode,
+                                            struct partition_affinity *parts,
+                                            int nr_parts,
+                                            int chained_irq,
+                                            const struct irq_domain_ops *ops);
+struct irq_domain *partition_get_domain(struct partition_desc *dsc);
+#else
+static inline int partition_translate_id(struct partition_desc *desc,
+                                        void *partition_id)
+{
+       return -EINVAL;
+}
+
+static inline
+struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode,
+                                            struct partition_affinity *parts,
+                                            int nr_parts,
+                                            int chained_irq,
+                                            const struct irq_domain_ops *ops)
+{
+       return NULL;
+}
+
+static inline
+struct irq_domain *partition_get_domain(struct partition_desc *dsc)
+{
+       return NULL;
+}
+#endif
index dcca77c4b9d2dca2790544bbcda2cfdb15fac842..b51beebf9804c1d4f45e5128052886094fb95755 100644 (file)
@@ -66,6 +66,7 @@ struct irq_desc {
        int                     threads_handled_last;
        raw_spinlock_t          lock;
        struct cpumask          *percpu_enabled;
+       const struct cpumask    *percpu_affinity;
 #ifdef CONFIG_SMP
        const struct cpumask    *affinity_hint;
        struct irq_affinity_notify *affinity_notify;
index 2aed0439621056960b5107d32da874a4c61666f4..f1f36e04d885c5e20592476b61318b4d215b598b 100644 (file)
@@ -96,6 +96,8 @@ enum irq_domain_bus_token {
 struct irq_domain_ops {
        int (*match)(struct irq_domain *d, struct device_node *node,
                     enum irq_domain_bus_token bus_token);
+       int (*select)(struct irq_domain *d, struct irq_fwspec *fwspec,
+                     enum irq_domain_bus_token bus_token);
        int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw);
        void (*unmap)(struct irq_domain *d, unsigned int virq);
        int (*xlate)(struct irq_domain *d, struct device_node *node,
@@ -211,7 +213,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
                                         irq_hw_number_t first_hwirq,
                                         const struct irq_domain_ops *ops,
                                         void *host_data);
-extern struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
+extern struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
                                                   enum irq_domain_bus_token bus_token);
 extern void irq_set_default_host(struct irq_domain *host);
 extern int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
@@ -227,6 +229,17 @@ static inline bool is_fwnode_irqchip(struct fwnode_handle *fwnode)
        return fwnode && fwnode->type == FWNODE_IRQCHIP;
 }
 
+static inline
+struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
+                                           enum irq_domain_bus_token bus_token)
+{
+       struct irq_fwspec fwspec = {
+               .fwnode = fwnode,
+       };
+
+       return irq_find_matching_fwspec(&fwspec, bus_token);
+}
+
 static inline struct irq_domain *irq_find_matching_host(struct device_node *node,
                                                        enum irq_domain_bus_token bus_token)
 {
@@ -346,9 +359,8 @@ int irq_domain_xlate_onetwocell(struct irq_domain *d, struct device_node *ctrlr,
                        irq_hw_number_t *out_hwirq, unsigned int *out_type);
 
 /* IPI functions */
-unsigned int irq_reserve_ipi(struct irq_domain *domain,
-                            const struct cpumask *dest);
-void irq_destroy_ipi(unsigned int irq);
+int irq_reserve_ipi(struct irq_domain *domain, const struct cpumask *dest);
+int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest);
 
 /* V2 interfaces to support hierarchy IRQ domains. */
 extern struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain,
index 14777af8e0977aed249e724601a789c7e21ffd0f..c42742208e5eefb028e084398d380cb4fe0e4e64 100644 (file)
@@ -19,9 +19,9 @@
  *
  * Allocate a virq that can be used to send IPI to any CPU in dest mask.
  *
- * On success it'll return linux irq number and 0 on failure
+ * On success it'll return linux irq number and error code on failure
  */
-unsigned int irq_reserve_ipi(struct irq_domain *domain,
+int irq_reserve_ipi(struct irq_domain *domain,
                             const struct cpumask *dest)
 {
        unsigned int nr_irqs, offset;
@@ -30,18 +30,18 @@ unsigned int irq_reserve_ipi(struct irq_domain *domain,
 
        if (!domain ||!irq_domain_is_ipi(domain)) {
                pr_warn("Reservation on a non IPI domain\n");
-               return 0;
+               return -EINVAL;
        }
 
        if (!cpumask_subset(dest, cpu_possible_mask)) {
                pr_warn("Reservation is not in possible_cpu_mask\n");
-               return 0;
+               return -EINVAL;
        }
 
        nr_irqs = cpumask_weight(dest);
        if (!nr_irqs) {
                pr_warn("Reservation for empty destination mask\n");
-               return 0;
+               return -EINVAL;
        }
 
        if (irq_domain_is_ipi_single(domain)) {
@@ -72,14 +72,14 @@ unsigned int irq_reserve_ipi(struct irq_domain *domain,
                        next = cpumask_next(next, dest);
                if (next < nr_cpu_ids) {
                        pr_warn("Destination mask has holes\n");
-                       return 0;
+                       return -EINVAL;
                }
        }
 
        virq = irq_domain_alloc_descs(-1, nr_irqs, 0, NUMA_NO_NODE);
        if (virq <= 0) {
                pr_warn("Can't reserve IPI, failed to alloc descs\n");
-               return 0;
+               return -ENOMEM;
        }
 
        virq = __irq_domain_alloc_irqs(domain, virq, nr_irqs, NUMA_NO_NODE,
@@ -100,17 +100,20 @@ unsigned int irq_reserve_ipi(struct irq_domain *domain,
 
 free_descs:
        irq_free_descs(virq, nr_irqs);
-       return 0;
+       return -EBUSY;
 }
 
 /**
  * irq_destroy_ipi() - unreserve an IPI that was previously allocated
  * @irq:       linux irq number to be destroyed
+ * @dest:      cpumask of cpus which should have the IPI removed
  *
- * Return the IPIs allocated with irq_reserve_ipi() to the system destroying
- * all virqs associated with them.
+ * The IPIs allocated with irq_reserve_ipi() are retuerned to the system
+ * destroying all virqs associated with them.
+ *
+ * Return 0 on success or error code on failure.
  */
-void irq_destroy_ipi(unsigned int irq)
+int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest)
 {
        struct irq_data *data = irq_get_irq_data(irq);
        struct cpumask *ipimask = data ? irq_data_get_affinity_mask(data) : NULL;
@@ -118,7 +121,7 @@ void irq_destroy_ipi(unsigned int irq)
        unsigned int nr_irqs;
 
        if (!irq || !data || !ipimask)
-               return;
+               return -EINVAL;
 
        domain = data->domain;
        if (WARN_ON(domain == NULL))
@@ -126,15 +129,25 @@ void irq_destroy_ipi(unsigned int irq)
 
        if (!irq_domain_is_ipi(domain)) {
                pr_warn("Trying to destroy a non IPI domain!\n");
-               return;
+               return -EINVAL;
        }
 
-       if (irq_domain_is_ipi_per_cpu(domain))
-               nr_irqs = cpumask_weight(ipimask);
-       else
+       if (WARN_ON(!cpumask_subset(dest, ipimask)))
+               /*
+                * Must be destroying a subset of CPUs to which this IPI
+                * was set up to target
+                */
+               return -EINVAL;
+
+       if (irq_domain_is_ipi_per_cpu(domain)) {
+               irq = irq + cpumask_first(dest) - data->common->ipi_offset;
+               nr_irqs = cpumask_weight(dest);
+       } else {
                nr_irqs = 1;
+       }
 
        irq_domain_free_irqs(irq, nr_irqs);
+       return 0;
 }
 
 /**
index 0ccd028817d7136ce7bd1faf10c6630690e2264e..8731e1c5d1e7865a3521597946e25197efbb170e 100644 (file)
@@ -595,7 +595,8 @@ void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus)
                chip_bus_sync_unlock(desc);
 }
 
-int irq_set_percpu_devid(unsigned int irq)
+int irq_set_percpu_devid_partition(unsigned int irq,
+                                  const struct cpumask *affinity)
 {
        struct irq_desc *desc = irq_to_desc(irq);
 
@@ -610,10 +611,33 @@ int irq_set_percpu_devid(unsigned int irq)
        if (!desc->percpu_enabled)
                return -ENOMEM;
 
+       if (affinity)
+               desc->percpu_affinity = affinity;
+       else
+               desc->percpu_affinity = cpu_possible_mask;
+
        irq_set_percpu_devid_flags(irq);
        return 0;
 }
 
+int irq_set_percpu_devid(unsigned int irq)
+{
+       return irq_set_percpu_devid_partition(irq, NULL);
+}
+
+int irq_get_percpu_devid_partition(unsigned int irq, struct cpumask *affinity)
+{
+       struct irq_desc *desc = irq_to_desc(irq);
+
+       if (!desc || !desc->percpu_enabled)
+               return -EINVAL;
+
+       if (affinity)
+               cpumask_copy(affinity, desc->percpu_affinity);
+
+       return 0;
+}
+
 void kstat_incr_irq_this_cpu(unsigned int irq)
 {
        kstat_incr_irqs_this_cpu(irq_to_desc(irq));
index 3a519a01118b0f68d071f8b0a0b368ea56327c20..503c5b9dd030a0eb2fa35c9de0122810f09a6609 100644 (file)
@@ -243,14 +243,15 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
 EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
 
 /**
- * irq_find_matching_fwnode() - Locates a domain for a given fwnode
- * @fwnode: FW descriptor of the interrupt controller
+ * irq_find_matching_fwspec() - Locates a domain for a given fwspec
+ * @fwspec: FW specifier for an interrupt
  * @bus_token: domain-specific data
  */
-struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
+struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
                                            enum irq_domain_bus_token bus_token)
 {
        struct irq_domain *h, *found = NULL;
+       struct fwnode_handle *fwnode = fwspec->fwnode;
        int rc;
 
        /* We might want to match the legacy controller last since
@@ -264,7 +265,9 @@ struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
         */
        mutex_lock(&irq_domain_mutex);
        list_for_each_entry(h, &irq_domain_list, link) {
-               if (h->ops->match)
+               if (h->ops->select && fwspec->param_count)
+                       rc = h->ops->select(h, fwspec, bus_token);
+               else if (h->ops->match)
                        rc = h->ops->match(h, to_of_node(fwnode), bus_token);
                else
                        rc = ((fwnode != NULL) && (h->fwnode == fwnode) &&
@@ -279,7 +282,7 @@ struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
        mutex_unlock(&irq_domain_mutex);
        return found;
 }
-EXPORT_SYMBOL_GPL(irq_find_matching_fwnode);
+EXPORT_SYMBOL_GPL(irq_find_matching_fwspec);
 
 /**
  * irq_set_default_host() - Set a "default" irq domain
@@ -574,11 +577,9 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
        int virq;
 
        if (fwspec->fwnode) {
-               domain = irq_find_matching_fwnode(fwspec->fwnode,
-                                                 DOMAIN_BUS_WIRED);
+               domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_WIRED);
                if (!domain)
-                       domain = irq_find_matching_fwnode(fwspec->fwnode,
-                                                         DOMAIN_BUS_ANY);
+                       domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_ANY);
        } else {
                domain = irq_default_domain;
        }
index cc1cc641d653216614ea9bab5dbe8e0bd7238788..ef0bc02c3a703dac4c3b980fb3ed3fb40ca5fe8d 100644 (file)
@@ -1407,7 +1407,7 @@ int setup_irq(unsigned int irq, struct irqaction *act)
        int retval;
        struct irq_desc *desc = irq_to_desc(irq);
 
-       if (WARN_ON(irq_settings_is_per_cpu_devid(desc)))
+       if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
                return -EINVAL;
        chip_bus_lock(desc);
        retval = __setup_irq(irq, desc, act);