]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 3 Nov 2015 22:40:01 +0000 (14:40 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 3 Nov 2015 22:40:01 +0000 (14:40 -0800)
Pull irq updates from Thomas Gleixner:
 "The irq departement delivers:

   - Rework the irqdomain core infrastructure to accomodate ACPI based
     systems.  This is required to support ARM64 without creating
     artificial device tree nodes.

   - Sanitize the ACPI based ARM GIC initialization by making use of the
     new firmware independent irqdomain core

   - Further improvements to the generic MSI management

   - Generalize the irq migration on CPU hotplug

   - Improvements to the threaded interrupt infrastructure

   - Allow the migration of "chained" low level interrupt handlers

   - Allow optional force masking of interrupts in disable_irq[_nosysnc]

   - Support for two new interrupt chips - Sigh!

   - A larger set of errata fixes for ARM gicv3

   - The usual pile of fixes, updates, improvements and cleanups all
     over the place"

* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (71 commits)
  Document that IRQ_NONE should be returned when IRQ not actually handled
  PCI/MSI: Allow the MSI domain to be device-specific
  PCI: Add per-device MSI domain hook
  of/irq: Use the msi-map property to provide device-specific MSI domain
  of/irq: Split of_msi_map_rid to reuse msi-map lookup
  irqchip/gic-v3-its: Parse new version of msi-parent property
  PCI/MSI: Use of_msi_get_domain instead of open-coded "msi-parent" parsing
  of/irq: Use of_msi_get_domain instead of open-coded "msi-parent" parsing
  of/irq: Add support code for multi-parent version of "msi-parent"
  irqchip/gic-v3-its: Add handling of PCI requester id.
  PCI/MSI: Add helper function pci_msi_domain_get_msi_rid().
  of/irq: Add new function of_msi_map_rid()
  Docs: dt: Add PCI MSI map bindings
  irqchip/gic-v2m: Add support for multiple MSI frames
  irqchip/gic-v3: Fix translation of LPIs after conversion to irq_fwspec
  irqchip/mxs: Add Alphascale ASM9260 support
  irqchip/mxs: Prepare driver for hardware with different offsets
  irqchip/mxs: Panic if ioremap or domain creation fails
  irqdomain: Documentation updates
  irqdomain/msi: Use fwnode instead of of_node
  ...

90 files changed:
Documentation/IRQ-domain.txt
Documentation/arm64/booting.txt
Documentation/devicetree/bindings/arm/gic.txt
Documentation/devicetree/bindings/interrupt-controller/renesas,irqc.txt
Documentation/devicetree/bindings/pci/pci-msi.txt [new file with mode: 0644]
arch/arm/Kconfig
arch/arm/include/asm/arch_gicv3.h [new file with mode: 0644]
arch/arm/mach-exynos/suspend.c
arch/arm/mach-imx/gpc.c
arch/arm/mach-omap2/omap-wakeupgen.c
arch/arm64/Kconfig
arch/arm64/include/asm/arch_gicv3.h [new file with mode: 0644]
arch/arm64/include/asm/cpufeature.h
arch/arm64/include/asm/cputype.h
arch/arm64/kernel/cpu_errata.c
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/head.S
arch/arm64/kvm/Kconfig
arch/c6x/platforms/megamod-pic.c
arch/mips/cavium-octeon/octeon-irq.c
arch/powerpc/platforms/cell/axon_msi.c
arch/powerpc/platforms/cell/spider-pic.c
arch/powerpc/platforms/pasemi/msi.c
arch/powerpc/platforms/powernv/opal-irqchip.c
arch/powerpc/sysdev/ehv_pic.c
arch/powerpc/sysdev/fsl_msi.c
arch/powerpc/sysdev/i8259.c
arch/powerpc/sysdev/ipic.c
arch/powerpc/sysdev/mpic.c
arch/powerpc/sysdev/mpic_msi.c
arch/powerpc/sysdev/qe_lib/qe_ic.c
drivers/acpi/gsi.c
drivers/base/platform-msi.c
drivers/gpio/gpio-sodaville.c
drivers/irqchip/Kconfig
drivers/irqchip/Makefile
drivers/irqchip/alphascale_asm9260-icoll.h [new file with mode: 0644]
drivers/irqchip/exynos-combiner.c
drivers/irqchip/irq-atmel-aic-common.c
drivers/irqchip/irq-atmel-aic5.c
drivers/irqchip/irq-crossbar.c
drivers/irqchip/irq-gic-common.c
drivers/irqchip/irq-gic-common.h
drivers/irqchip/irq-gic-v2m.c
drivers/irqchip/irq-gic-v3-its-pci-msi.c
drivers/irqchip/irq-gic-v3-its-platform-msi.c
drivers/irqchip/irq-gic-v3-its.c
drivers/irqchip/irq-gic-v3.c
drivers/irqchip/irq-gic.c
drivers/irqchip/irq-hip04.c
drivers/irqchip/irq-i8259.c
drivers/irqchip/irq-imx-gpcv2.c
drivers/irqchip/irq-mtk-sysirq.c
drivers/irqchip/irq-mxs.c
drivers/irqchip/irq-nvic.c
drivers/irqchip/irq-renesas-intc-irqpin.c
drivers/irqchip/irq-renesas-irqc.c
drivers/irqchip/irq-s3c24xx.c
drivers/irqchip/irq-sunxi-nmi.c
drivers/irqchip/irq-tegra.c
drivers/irqchip/irq-vf610-mscm-ir.c
drivers/of/irq.c
drivers/pci/host/pci-xgene-msi.c
drivers/pci/msi.c
drivers/pci/of.c
drivers/pci/probe.c
drivers/spmi/spmi-pmic-arb.c
include/kvm/arm_vgic.h
include/linux/acpi.h
include/linux/fwnode.h
include/linux/interrupt.h
include/linux/irq.h
include/linux/irqchip/arm-gic-v3.h
include/linux/irqchip/arm-gic.h
include/linux/irqdomain.h
include/linux/irqreturn.h
include/linux/msi.h
include/linux/of_irq.h
kernel/irq/Kconfig
kernel/irq/Makefile
kernel/irq/chip.c
kernel/irq/cpuhotplug.c [new file with mode: 0644]
kernel/irq/handle.c
kernel/irq/internals.h
kernel/irq/irqdomain.c
kernel/irq/manage.c
kernel/irq/msi.c
kernel/irq/proc.c
kernel/irq/settings.h
virt/kvm/arm/vgic.c

index 3a8e15cba816a4ea16fb0208518046214ebff1e6..8d990bde8693fdb85d4f4e67b5d9aaa155418990 100644 (file)
@@ -32,9 +32,9 @@ top of the irq_alloc_desc*() API.  An irq_domain to manage mapping is
 preferred over interrupt controller drivers open coding their own
 reverse mapping scheme.
 
-irq_domain also implements translation from Device Tree interrupt
-specifiers to hwirq numbers, and can be easily extended to support
-other IRQ topology data sources.
+irq_domain also implements translation from an abstract irq_fwspec
+structure to hwirq numbers (Device Tree and ACPI GSI so far), and can
+be easily extended to support other IRQ topology data sources.
 
 === irq_domain usage ===
 An interrupt controller driver creates and registers an irq_domain by
@@ -184,7 +184,7 @@ There are four major interfaces to use hierarchy irq_domain:
    related resources associated with these interrupts.
 3) irq_domain_activate_irq(): activate interrupt controller hardware to
    deliver the interrupt.
-3) irq_domain_deactivate_irq(): deactivate interrupt controller hardware
+4) irq_domain_deactivate_irq(): deactivate interrupt controller hardware
    to stop delivering the interrupt.
 
 Following changes are needed to support hierarchy irq_domain.
index 7d9d3c2286b282d96f03cd1545be5780e998002e..369a4f48eb0dac3c80d5f22054fc4b02f1006f28 100644 (file)
@@ -173,13 +173,22 @@ Before jumping into the kernel, the following conditions must be met:
   the kernel image will be entered must be initialised by software at a
   higher exception level to prevent execution in an UNKNOWN state.
 
-  For systems with a GICv3 interrupt controller:
+  For systems with a GICv3 interrupt controller to be used in v3 mode:
   - If EL3 is present:
     ICC_SRE_EL3.Enable (bit 3) must be initialiased to 0b1.
     ICC_SRE_EL3.SRE (bit 0) must be initialised to 0b1.
   - If the kernel is entered at EL1:
     ICC.SRE_EL2.Enable (bit 3) must be initialised to 0b1
     ICC_SRE_EL2.SRE (bit 0) must be initialised to 0b1.
+  - The DT or ACPI tables must describe a GICv3 interrupt controller.
+
+  For systems with a GICv3 interrupt controller to be used in
+  compatibility (v2) mode:
+  - If EL3 is present:
+    ICC_SRE_EL3.SRE (bit 0) must be initialised to 0b0.
+  - If the kernel is entered at EL1:
+    ICC_SRE_EL2.SRE (bit 0) must be initialised to 0b0.
+  - The DT or ACPI tables must describe a GICv2 interrupt controller.
 
 The requirements described above for CPU mode, caches, MMUs, architected
 timers, coherency and system registers apply to all CPUs.  All CPUs must
index 2da059a4790cb3c62365bedae458c5565589b225..cc56021eb60babea6c580bfbe594aca3c17dfce5 100644 (file)
@@ -11,13 +11,14 @@ have PPIs or SGIs.
 Main node required properties:
 
 - compatible : should be one of:
-       "arm,gic-400"
+       "arm,arm1176jzf-devchip-gic"
+       "arm,arm11mp-gic"
        "arm,cortex-a15-gic"
-       "arm,cortex-a9-gic"
        "arm,cortex-a7-gic"
-       "arm,arm11mp-gic"
+       "arm,cortex-a9-gic"
+       "arm,gic-400"
+       "arm,pl390"
        "brcm,brahma-b15-gic"
-       "arm,arm1176jzf-devchip-gic"
        "qcom,msm-8660-qgic"
        "qcom,msm-qgic2"
 - interrupt-controller : Identifies the node as an interrupt controller
@@ -58,6 +59,21 @@ Optional
   regions, used when the GIC doesn't have banked registers. The offset is
   cpu-offset * cpu-nr.
 
+- clocks        : List of phandle and clock-specific pairs, one for each entry
+  in clock-names.
+- clock-names   : List of names for the GIC clock input(s). Valid clock names
+  depend on the GIC variant:
+       "ic_clk" (for "arm,arm11mp-gic")
+       "PERIPHCLKEN" (for "arm,cortex-a15-gic")
+       "PERIPHCLK", "PERIPHCLKEN" (for "arm,cortex-a9-gic")
+       "clk" (for "arm,gic-400")
+       "gclk" (for "arm,pl390")
+
+- power-domains : A phandle and PM domain specifier as defined by bindings of
+                 the power controller specified by phandle, used when the GIC
+                 is part of a Power or Clock Domain.
+
+
 Example:
 
        intc: interrupt-controller@fff11000 {
index 63633bdea7e40ea84aca7954a2d671a5d9163ad8..ae5054c27c996564b9675fac7d245bd6af28b0bc 100644 (file)
@@ -10,6 +10,7 @@ Required properties:
     - "renesas,irqc-r8a7792" (R-Car V2H)
     - "renesas,irqc-r8a7793" (R-Car M2-N)
     - "renesas,irqc-r8a7794" (R-Car E2)
+    - "renesas,intc-ex-r8a7795" (R-Car H3)
 - #interrupt-cells: has to be <2>: an interrupt index and flags, as defined in
   interrupts.txt in this directory
 - clocks: Must contain a reference to the functional clock.
diff --git a/Documentation/devicetree/bindings/pci/pci-msi.txt b/Documentation/devicetree/bindings/pci/pci-msi.txt
new file mode 100644 (file)
index 0000000..9b3cc81
--- /dev/null
@@ -0,0 +1,220 @@
+This document describes the generic device tree binding for describing the
+relationship between PCI devices and MSI controllers.
+
+Each PCI device under a root complex is uniquely identified by its Requester ID
+(AKA RID). A Requester ID is a triplet of a Bus number, Device number, and
+Function number.
+
+For the purpose of this document, when treated as a numeric value, a RID is
+formatted such that:
+
+* Bits [15:8] are the Bus number.
+* Bits [7:3] are the Device number.
+* Bits [2:0] are the Function number.
+* Any other bits required for padding must be zero.
+
+MSIs may be distinguished in part through the use of sideband data accompanying
+writes. In the case of PCI devices, this sideband data may be derived from the
+Requester ID. A mechanism is required to associate a device with both the MSI
+controllers it can address, and the sideband data that will be associated with
+its writes to those controllers.
+
+For generic MSI bindings, see
+Documentation/devicetree/bindings/interrupt-controller/msi.txt.
+
+
+PCI root complex
+================
+
+Optional properties
+-------------------
+
+- msi-map: Maps a Requester ID to an MSI controller and associated
+  msi-specifier data. The property is an arbitrary number of tuples of
+  (rid-base,msi-controller,msi-base,length), where:
+
+  * rid-base is a single cell describing the first RID matched by the entry.
+
+  * msi-controller is a single phandle to an MSI controller
+
+  * msi-base is an msi-specifier describing the msi-specifier produced for the
+    first RID matched by the entry.
+
+  * length is a single cell describing how many consecutive RIDs are matched
+    following the rid-base.
+
+  Any RID r in the interval [rid-base, rid-base + length) is associated with
+  the listed msi-controller, with the msi-specifier (r - rid-base + msi-base).
+
+- msi-map-mask: A mask to be applied to each Requester ID prior to being mapped
+  to an msi-specifier per the msi-map property.
+
+- msi-parent: Describes the MSI parent of the root complex itself. Where
+  the root complex and MSI controller do not pass sideband data with MSI
+  writes, this property may be used to describe the MSI controller(s)
+  used by PCI devices under the root complex, if defined as such in the
+  binding for the root complex.
+
+
+Example (1)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@f {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to the MSI controller is
+                * the RID, identity-mapped.
+                */
+               msi-map = <0x0 &msi_a 0x0 0x10000>,
+       };
+};
+
+
+Example (2)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@f {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to the MSI controller is
+                * the RID, masked to only the device and function bits.
+                */
+               msi-map = <0x0 &msi_a 0x0 0x100>,
+               msi-map-mask = <0xff>
+       };
+};
+
+
+Example (3)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@f {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to the MSI controller is
+                * the RID, but the high bit of the bus number is
+                * ignored.
+                */
+               msi-map = <0x0000 &msi 0x0000 0x8000>,
+                         <0x8000 &msi 0x0000 0x8000>;
+       };
+};
+
+
+Example (4)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@f {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to the MSI controller is
+                * the RID, but the high bit of the bus number is
+                * negated.
+                */
+               msi-map = <0x0000 &msi 0x8000 0x8000>,
+                         <0x8000 &msi 0x0000 0x8000>;
+       };
+};
+
+
+Example (5)
+===========
+
+/ {
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       msi_a: msi-controller@a {
+               reg = <0xa 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       msi_b: msi-controller@b {
+               reg = <0xb 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       msi_c: msi-controller@c {
+               reg = <0xc 0x1>;
+               compatible = "vendor,some-controller";
+               msi-controller;
+               #msi-cells = <1>;
+       };
+
+       pci: pci@c {
+               reg = <0xf 0x1>;
+               compatible = "vendor,pcie-root-complex";
+               device_type = "pci";
+
+               /*
+                * The sideband data provided to MSI controller a is the
+                * RID, but the high bit of the bus number is negated.
+                * The sideband data provided to MSI controller b is the
+                * RID, identity-mapped.
+                * MSI controller c is not addressable.
+                */
+               msi-map = <0x0000 &msi_a 0x8000 0x08000>,
+                         <0x8000 &msi_a 0x0000 0x08000>,
+                         <0x0000 &msi_b 0x0000 0x10000>;
+       };
+};
index 823f90ea65c4458aa9e0f4096399e6c436cd8873..f1ed1109f4889e006e9df4c6110be001841c0f82 100644 (file)
@@ -820,6 +820,7 @@ config ARCH_VIRT
        bool "Dummy Virtual Machine" if ARCH_MULTI_V7
        select ARM_AMBA
        select ARM_GIC
+       select ARM_GIC_V3
        select ARM_PSCI
        select HAVE_ARM_ARCH_TIMER
 
diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h
new file mode 100644 (file)
index 0000000..6607d97
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * arch/arm/include/asm/arch_gicv3.h
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ *
+ * 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/>.
+ */
+#ifndef __ASM_ARCH_GICV3_H
+#define __ASM_ARCH_GICV3_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/io.h>
+
+#define __ACCESS_CP15(CRn, Op1, CRm, Op2)      p15, Op1, %0, CRn, CRm, Op2
+#define __ACCESS_CP15_64(Op1, CRm)             p15, Op1, %Q0, %R0, CRm
+
+#define ICC_EOIR1                      __ACCESS_CP15(c12, 0, c12, 1)
+#define ICC_DIR                                __ACCESS_CP15(c12, 0, c11, 1)
+#define ICC_IAR1                       __ACCESS_CP15(c12, 0, c12, 0)
+#define ICC_SGI1R                      __ACCESS_CP15_64(0, c12)
+#define ICC_PMR                                __ACCESS_CP15(c4, 0, c6, 0)
+#define ICC_CTLR                       __ACCESS_CP15(c12, 0, c12, 4)
+#define ICC_SRE                                __ACCESS_CP15(c12, 0, c12, 5)
+#define ICC_IGRPEN1                    __ACCESS_CP15(c12, 0, c12, 7)
+
+#define ICC_HSRE                       __ACCESS_CP15(c12, 4, c9, 5)
+
+#define ICH_VSEIR                      __ACCESS_CP15(c12, 4, c9, 4)
+#define ICH_HCR                                __ACCESS_CP15(c12, 4, c11, 0)
+#define ICH_VTR                                __ACCESS_CP15(c12, 4, c11, 1)
+#define ICH_MISR                       __ACCESS_CP15(c12, 4, c11, 2)
+#define ICH_EISR                       __ACCESS_CP15(c12, 4, c11, 3)
+#define ICH_ELSR                       __ACCESS_CP15(c12, 4, c11, 5)
+#define ICH_VMCR                       __ACCESS_CP15(c12, 4, c11, 7)
+
+#define __LR0(x)                       __ACCESS_CP15(c12, 4, c12, x)
+#define __LR8(x)                       __ACCESS_CP15(c12, 4, c13, x)
+
+#define ICH_LR0                                __LR0(0)
+#define ICH_LR1                                __LR0(1)
+#define ICH_LR2                                __LR0(2)
+#define ICH_LR3                                __LR0(3)
+#define ICH_LR4                                __LR0(4)
+#define ICH_LR5                                __LR0(5)
+#define ICH_LR6                                __LR0(6)
+#define ICH_LR7                                __LR0(7)
+#define ICH_LR8                                __LR8(0)
+#define ICH_LR9                                __LR8(1)
+#define ICH_LR10                       __LR8(2)
+#define ICH_LR11                       __LR8(3)
+#define ICH_LR12                       __LR8(4)
+#define ICH_LR13                       __LR8(5)
+#define ICH_LR14                       __LR8(6)
+#define ICH_LR15                       __LR8(7)
+
+/* LR top half */
+#define __LRC0(x)                      __ACCESS_CP15(c12, 4, c14, x)
+#define __LRC8(x)                      __ACCESS_CP15(c12, 4, c15, x)
+
+#define ICH_LRC0                       __LRC0(0)
+#define ICH_LRC1                       __LRC0(1)
+#define ICH_LRC2                       __LRC0(2)
+#define ICH_LRC3                       __LRC0(3)
+#define ICH_LRC4                       __LRC0(4)
+#define ICH_LRC5                       __LRC0(5)
+#define ICH_LRC6                       __LRC0(6)
+#define ICH_LRC7                       __LRC0(7)
+#define ICH_LRC8                       __LRC8(0)
+#define ICH_LRC9                       __LRC8(1)
+#define ICH_LRC10                      __LRC8(2)
+#define ICH_LRC11                      __LRC8(3)
+#define ICH_LRC12                      __LRC8(4)
+#define ICH_LRC13                      __LRC8(5)
+#define ICH_LRC14                      __LRC8(6)
+#define ICH_LRC15                      __LRC8(7)
+
+#define __AP0Rx(x)                     __ACCESS_CP15(c12, 4, c8, x)
+#define ICH_AP0R0                      __AP0Rx(0)
+#define ICH_AP0R1                      __AP0Rx(1)
+#define ICH_AP0R2                      __AP0Rx(2)
+#define ICH_AP0R3                      __AP0Rx(3)
+
+#define __AP1Rx(x)                     __ACCESS_CP15(c12, 4, c9, x)
+#define ICH_AP1R0                      __AP1Rx(0)
+#define ICH_AP1R1                      __AP1Rx(1)
+#define ICH_AP1R2                      __AP1Rx(2)
+#define ICH_AP1R3                      __AP1Rx(3)
+
+/* Low-level accessors */
+
+static inline void gic_write_eoir(u32 irq)
+{
+       asm volatile("mcr " __stringify(ICC_EOIR1) : : "r" (irq));
+       isb();
+}
+
+static inline void gic_write_dir(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_DIR) : : "r" (val));
+       isb();
+}
+
+static inline u32 gic_read_iar(void)
+{
+       u32 irqstat;
+
+       asm volatile("mrc " __stringify(ICC_IAR1) : "=r" (irqstat));
+       return irqstat;
+}
+
+static inline void gic_write_pmr(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_PMR) : : "r" (val));
+}
+
+static inline void gic_write_ctlr(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_CTLR) : : "r" (val));
+       isb();
+}
+
+static inline void gic_write_grpen1(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_IGRPEN1) : : "r" (val));
+       isb();
+}
+
+static inline void gic_write_sgi1r(u64 val)
+{
+       asm volatile("mcrr " __stringify(ICC_SGI1R) : : "r" (val));
+}
+
+static inline u32 gic_read_sre(void)
+{
+       u32 val;
+
+       asm volatile("mrc " __stringify(ICC_SRE) : "=r" (val));
+       return val;
+}
+
+static inline void gic_write_sre(u32 val)
+{
+       asm volatile("mcr " __stringify(ICC_SRE) : : "r" (val));
+       isb();
+}
+
+/*
+ * Even in 32bit systems that use LPAE, there is no guarantee that the I/O
+ * interface provides true 64bit atomic accesses, so using strd/ldrd doesn't
+ * make much sense.
+ * Moreover, 64bit I/O emulation is extremely difficult to implement on
+ * AArch32, since the syndrome register doesn't provide any information for
+ * them.
+ * Consequently, the following IO helpers use 32bit accesses.
+ *
+ * There are only two registers that need 64bit accesses in this driver:
+ * - GICD_IROUTERn, contain the affinity values associated to each interrupt.
+ *   The upper-word (aff3) will always be 0, so there is no need for a lock.
+ * - GICR_TYPER is an ID register and doesn't need atomicity.
+ */
+static inline void gic_write_irouter(u64 val, volatile void __iomem *addr)
+{
+       writel_relaxed((u32)val, addr);
+       writel_relaxed((u32)(val >> 32), addr + 4);
+}
+
+static inline u64 gic_read_typer(const volatile void __iomem *addr)
+{
+       u64 val;
+
+       val = readl_relaxed(addr);
+       val |= (u64)readl_relaxed(addr + 4) << 32;
+       return val;
+}
+
+#endif /* !__ASSEMBLY__ */
+#endif /* !__ASM_ARCH_GICV3_H */
index e00eb39453a41ff3cf0090a6db53ee80a2afc7dc..5a7e47ceec91f7a6486821cc0a4b93e7d51257a9 100644 (file)
@@ -177,54 +177,57 @@ static struct irq_chip exynos_pmu_chip = {
 #endif
 };
 
-static int exynos_pmu_domain_xlate(struct irq_domain *domain,
-                                  struct device_node *controller,
-                                  const u32 *intspec,
-                                  unsigned int intsize,
-                                  unsigned long *out_hwirq,
-                                  unsigned int *out_type)
+static int exynos_pmu_domain_translate(struct irq_domain *d,
+                                      struct irq_fwspec *fwspec,
+                                      unsigned long *hwirq,
+                                      unsigned int *type)
 {
-       if (domain->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != 0)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int exynos_pmu_domain_alloc(struct irq_domain *domain,
                                   unsigned int virq,
                                   unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        irq_hw_number_t hwirq;
        int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != 0)
+       if (fwspec->param[0] != 0)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
 
        for (i = 0; i < nr_irqs; i++)
                irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
                                              &exynos_pmu_chip, NULL);
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static const struct irq_domain_ops exynos_pmu_domain_ops = {
-       .xlate  = exynos_pmu_domain_xlate,
-       .alloc  = exynos_pmu_domain_alloc,
-       .free   = irq_domain_free_irqs_common,
+       .translate      = exynos_pmu_domain_translate,
+       .alloc          = exynos_pmu_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 static int __init exynos_pmu_irq_init(struct device_node *node,
index 8c4467fad8370c73374ff104209b779e580852e1..10bf7159b27def3adf90403541fed2214b6bac4e 100644 (file)
@@ -181,40 +181,42 @@ static struct irq_chip imx_gpc_chip = {
 #endif
 };
 
-static int imx_gpc_domain_xlate(struct irq_domain *domain,
-                               struct device_node *controller,
-                               const u32 *intspec,
-                               unsigned int intsize,
-                               unsigned long *out_hwirq,
-                               unsigned int *out_type)
+static int imx_gpc_domain_translate(struct irq_domain *d,
+                                   struct irq_fwspec *fwspec,
+                                   unsigned long *hwirq,
+                                   unsigned int *type)
 {
-       if (domain->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != 0)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int imx_gpc_domain_alloc(struct irq_domain *domain,
                                  unsigned int irq,
                                  unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        irq_hw_number_t hwirq;
        int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != 0)
+       if (fwspec->param[0] != 0)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
        if (hwirq >= GPC_MAX_IRQS)
                return -EINVAL; /* Can't deal with this */
 
@@ -222,15 +224,16 @@ static int imx_gpc_domain_alloc(struct irq_domain *domain,
                irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,
                                              &imx_gpc_chip, NULL);
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static const struct irq_domain_ops imx_gpc_domain_ops = {
-       .xlate  = imx_gpc_domain_xlate,
-       .alloc  = imx_gpc_domain_alloc,
-       .free   = irq_domain_free_irqs_common,
+       .translate      = imx_gpc_domain_translate,
+       .alloc          = imx_gpc_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 static int __init imx_gpc_init(struct device_node *node,
index e1d2e991d17a31fc15f1c44616c9b4b7f9de3a84..db7e0bab3587cb975ea1ecb5c89e10956fcc7214 100644 (file)
@@ -399,40 +399,42 @@ static struct irq_chip wakeupgen_chip = {
 #endif
 };
 
-static int wakeupgen_domain_xlate(struct irq_domain *domain,
-                                 struct device_node *controller,
-                                 const u32 *intspec,
-                                 unsigned int intsize,
-                                 unsigned long *out_hwirq,
-                                 unsigned int *out_type)
+static int wakeupgen_domain_translate(struct irq_domain *d,
+                                     struct irq_fwspec *fwspec,
+                                     unsigned long *hwirq,
+                                     unsigned int *type)
 {
-       if (domain->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != 0)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int wakeupgen_domain_alloc(struct irq_domain *domain,
                                  unsigned int virq,
                                  unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        irq_hw_number_t hwirq;
        int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != 0)
+       if (fwspec->param[0] != 0)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
        if (hwirq >= MAX_IRQS)
                return -EINVAL; /* Can't deal with this */
 
@@ -440,15 +442,16 @@ static int wakeupgen_domain_alloc(struct irq_domain *domain,
                irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
                                              &wakeupgen_chip, NULL);
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static const struct irq_domain_ops wakeupgen_domain_ops = {
-       .xlate  = wakeupgen_domain_xlate,
-       .alloc  = wakeupgen_domain_alloc,
-       .free   = irq_domain_free_irqs_common,
+       .translate      = wakeupgen_domain_translate,
+       .alloc          = wakeupgen_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 /*
index 07d1811aa03fcd1ecd5ee7c260688a59f7ab97e4..440d906429deab9c1daf0de497ead020378891af 100644 (file)
@@ -348,6 +348,33 @@ config ARM64_ERRATUM_843419
 
          If unsure, say Y.
 
+config CAVIUM_ERRATUM_22375
+       bool "Cavium erratum 22375, 24313"
+       default y
+       help
+         Enable workaround for erratum 22375, 24313.
+
+         This implements two gicv3-its errata workarounds for ThunderX. Both
+         with small impact affecting only ITS table allocation.
+
+           erratum 22375: only alloc 8MB table size
+           erratum 24313: ignore memory access type
+
+         The fixes are in ITS initialization and basically ignore memory access
+         type and table size provided by the TYPER and BASER registers.
+
+         If unsure, say Y.
+
+config CAVIUM_ERRATUM_23154
+       bool "Cavium erratum 23154: Access to ICC_IAR1_EL1 is not sync'ed"
+       default y
+       help
+         The gicv3 of ThunderX requires a modified version for
+         reading the IAR status to ensure data synchronization
+         (access to icc_iar1_el1 is not sync'ed before and after).
+
+         If unsure, say Y.
+
 endmenu
 
 
diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
new file mode 100644 (file)
index 0000000..030cdcb
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * arch/arm64/include/asm/arch_gicv3.h
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ *
+ * 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/>.
+ */
+#ifndef __ASM_ARCH_GICV3_H
+#define __ASM_ARCH_GICV3_H
+
+#include <asm/sysreg.h>
+
+#define ICC_EOIR1_EL1                  sys_reg(3, 0, 12, 12, 1)
+#define ICC_DIR_EL1                    sys_reg(3, 0, 12, 11, 1)
+#define ICC_IAR1_EL1                   sys_reg(3, 0, 12, 12, 0)
+#define ICC_SGI1R_EL1                  sys_reg(3, 0, 12, 11, 5)
+#define ICC_PMR_EL1                    sys_reg(3, 0, 4, 6, 0)
+#define ICC_CTLR_EL1                   sys_reg(3, 0, 12, 12, 4)
+#define ICC_SRE_EL1                    sys_reg(3, 0, 12, 12, 5)
+#define ICC_GRPEN1_EL1                 sys_reg(3, 0, 12, 12, 7)
+
+#define ICC_SRE_EL2                    sys_reg(3, 4, 12, 9, 5)
+
+/*
+ * System register definitions
+ */
+#define ICH_VSEIR_EL2                  sys_reg(3, 4, 12, 9, 4)
+#define ICH_HCR_EL2                    sys_reg(3, 4, 12, 11, 0)
+#define ICH_VTR_EL2                    sys_reg(3, 4, 12, 11, 1)
+#define ICH_MISR_EL2                   sys_reg(3, 4, 12, 11, 2)
+#define ICH_EISR_EL2                   sys_reg(3, 4, 12, 11, 3)
+#define ICH_ELSR_EL2                   sys_reg(3, 4, 12, 11, 5)
+#define ICH_VMCR_EL2                   sys_reg(3, 4, 12, 11, 7)
+
+#define __LR0_EL2(x)                   sys_reg(3, 4, 12, 12, x)
+#define __LR8_EL2(x)                   sys_reg(3, 4, 12, 13, x)
+
+#define ICH_LR0_EL2                    __LR0_EL2(0)
+#define ICH_LR1_EL2                    __LR0_EL2(1)
+#define ICH_LR2_EL2                    __LR0_EL2(2)
+#define ICH_LR3_EL2                    __LR0_EL2(3)
+#define ICH_LR4_EL2                    __LR0_EL2(4)
+#define ICH_LR5_EL2                    __LR0_EL2(5)
+#define ICH_LR6_EL2                    __LR0_EL2(6)
+#define ICH_LR7_EL2                    __LR0_EL2(7)
+#define ICH_LR8_EL2                    __LR8_EL2(0)
+#define ICH_LR9_EL2                    __LR8_EL2(1)
+#define ICH_LR10_EL2                   __LR8_EL2(2)
+#define ICH_LR11_EL2                   __LR8_EL2(3)
+#define ICH_LR12_EL2                   __LR8_EL2(4)
+#define ICH_LR13_EL2                   __LR8_EL2(5)
+#define ICH_LR14_EL2                   __LR8_EL2(6)
+#define ICH_LR15_EL2                   __LR8_EL2(7)
+
+#define __AP0Rx_EL2(x)                 sys_reg(3, 4, 12, 8, x)
+#define ICH_AP0R0_EL2                  __AP0Rx_EL2(0)
+#define ICH_AP0R1_EL2                  __AP0Rx_EL2(1)
+#define ICH_AP0R2_EL2                  __AP0Rx_EL2(2)
+#define ICH_AP0R3_EL2                  __AP0Rx_EL2(3)
+
+#define __AP1Rx_EL2(x)                 sys_reg(3, 4, 12, 9, x)
+#define ICH_AP1R0_EL2                  __AP1Rx_EL2(0)
+#define ICH_AP1R1_EL2                  __AP1Rx_EL2(1)
+#define ICH_AP1R2_EL2                  __AP1Rx_EL2(2)
+#define ICH_AP1R3_EL2                  __AP1Rx_EL2(3)
+
+#ifndef __ASSEMBLY__
+
+#include <linux/stringify.h>
+
+/*
+ * Low-level accessors
+ *
+ * These system registers are 32 bits, but we make sure that the compiler
+ * sets the GP register's most significant bits to 0 with an explicit cast.
+ */
+
+static inline void gic_write_eoir(u32 irq)
+{
+       asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" ((u64)irq));
+       isb();
+}
+
+static inline void gic_write_dir(u32 irq)
+{
+       asm volatile("msr_s " __stringify(ICC_DIR_EL1) ", %0" : : "r" ((u64)irq));
+       isb();
+}
+
+static inline u64 gic_read_iar_common(void)
+{
+       u64 irqstat;
+
+       asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
+       return irqstat;
+}
+
+/*
+ * Cavium ThunderX erratum 23154
+ *
+ * The gicv3 of ThunderX requires a modified version for reading the
+ * IAR status to ensure data synchronization (access to icc_iar1_el1
+ * is not sync'ed before and after).
+ */
+static inline u64 gic_read_iar_cavium_thunderx(void)
+{
+       u64 irqstat;
+
+       asm volatile(
+               "nop;nop;nop;nop\n\t"
+               "nop;nop;nop;nop\n\t"
+               "mrs_s %0, " __stringify(ICC_IAR1_EL1) "\n\t"
+               "nop;nop;nop;nop"
+               : "=r" (irqstat));
+       mb();
+
+       return irqstat;
+}
+
+static inline void gic_write_pmr(u32 val)
+{
+       asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" ((u64)val));
+}
+
+static inline void gic_write_ctlr(u32 val)
+{
+       asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" ((u64)val));
+       isb();
+}
+
+static inline void gic_write_grpen1(u32 val)
+{
+       asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" ((u64)val));
+       isb();
+}
+
+static inline void gic_write_sgi1r(u64 val)
+{
+       asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
+}
+
+static inline u32 gic_read_sre(void)
+{
+       u64 val;
+
+       asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
+       return val;
+}
+
+static inline void gic_write_sre(u32 val)
+{
+       asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" ((u64)val));
+       isb();
+}
+
+#define gic_read_typer(c)              readq_relaxed(c)
+#define gic_write_irouter(v, c)                writeq_relaxed(v, c)
+
+#endif /* __ASSEMBLY__ */
+#endif /* __ASM_ARCH_GICV3_H */
index 171570702bb801431f141f46238eb0d9e1895fe5..dbc78d2b8cc6bbd43af2bf4781b6fffaa54a4fe1 100644 (file)
@@ -27,8 +27,9 @@
 #define ARM64_HAS_SYSREG_GIC_CPUIF             3
 #define ARM64_HAS_PAN                          4
 #define ARM64_HAS_LSE_ATOMICS                  5
+#define ARM64_WORKAROUND_CAVIUM_23154          6
 
-#define ARM64_NCAPS                            6
+#define ARM64_NCAPS                            7
 
 #ifndef __ASSEMBLY__
 
index ee6403df9fe4c1f32e9a7b30172a7a141056ec40..100a3d1b17c854d6c1a2c465b56ad8f101ab5efa 100644 (file)
        (0xf                    << MIDR_ARCHITECTURE_SHIFT) | \
        ((partnum)              << MIDR_PARTNUM_SHIFT))
 
-#define ARM_CPU_IMP_ARM                0x41
-#define ARM_CPU_IMP_APM                0x50
+#define ARM_CPU_IMP_ARM                        0x41
+#define ARM_CPU_IMP_APM                        0x50
+#define ARM_CPU_IMP_CAVIUM             0x43
 
-#define ARM_CPU_PART_AEM_V8    0xD0F
-#define ARM_CPU_PART_FOUNDATION        0xD00
-#define ARM_CPU_PART_CORTEX_A57        0xD07
-#define ARM_CPU_PART_CORTEX_A53        0xD03
+#define ARM_CPU_PART_AEM_V8            0xD0F
+#define ARM_CPU_PART_FOUNDATION                0xD00
+#define ARM_CPU_PART_CORTEX_A57                0xD07
+#define ARM_CPU_PART_CORTEX_A53                0xD03
 
-#define APM_CPU_PART_POTENZA   0x000
+#define APM_CPU_PART_POTENZA           0x000
+
+#define CAVIUM_CPU_PART_THUNDERX       0x0A1
 
 #define ID_AA64MMFR0_BIGENDEL0_SHIFT   16
 #define ID_AA64MMFR0_BIGENDEL0_MASK    (0xf << ID_AA64MMFR0_BIGENDEL0_SHIFT)
index 6ffd914385609d0aab875813e4e9e8b690976421..574450c257a4d038e17d4eb8a47b954b20b2b9bb 100644 (file)
@@ -23,6 +23,7 @@
 
 #define MIDR_CORTEX_A53 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53)
 #define MIDR_CORTEX_A57 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57)
+#define MIDR_THUNDERX  MIDR_CPU_PART(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX)
 
 #define CPU_MODEL_MASK (MIDR_IMPLEMENTOR_MASK | MIDR_PARTNUM_MASK | \
                        MIDR_ARCHITECTURE_MASK)
@@ -81,6 +82,14 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
                .capability = ARM64_WORKAROUND_845719,
                MIDR_RANGE(MIDR_CORTEX_A53, 0x00, 0x04),
        },
+#endif
+#ifdef CONFIG_CAVIUM_ERRATUM_23154
+       {
+       /* Cavium ThunderX, pass 1.x */
+               .desc = "Cavium erratum 23154",
+               .capability = ARM64_WORKAROUND_CAVIUM_23154,
+               MIDR_RANGE(MIDR_THUNDERX, 0x00, 0x01),
+       },
 #endif
        {
        }
index 3c9aed32f70b2113773f671053a0f6ac87632b4a..305f30dc9e633fe86947621e54d802744e59df52 100644 (file)
@@ -23,6 +23,8 @@
 #include <asm/cpufeature.h>
 #include <asm/processor.h>
 
+#include <linux/irqchip/arm-gic-v3.h>
+
 static bool
 feature_matches(u64 reg, const struct arm64_cpu_capabilities *entry)
 {
@@ -45,11 +47,26 @@ __ID_FEAT_CHK(id_aa64pfr0);
 __ID_FEAT_CHK(id_aa64mmfr1);
 __ID_FEAT_CHK(id_aa64isar0);
 
+static bool has_useable_gicv3_cpuif(const struct arm64_cpu_capabilities *entry)
+{
+       bool has_sre;
+
+       if (!has_id_aa64pfr0_feature(entry))
+               return false;
+
+       has_sre = gic_enable_sre();
+       if (!has_sre)
+               pr_warn_once("%s present but disabled by higher exception level\n",
+                            entry->desc);
+
+       return has_sre;
+}
+
 static const struct arm64_cpu_capabilities arm64_features[] = {
        {
                .desc = "GIC system register CPU interface",
                .capability = ARM64_HAS_SYSREG_GIC_CPUIF,
-               .matches = has_id_aa64pfr0_feature,
+               .matches = has_useable_gicv3_cpuif,
                .field_pos = 24,
                .min_field_value = 1,
        },
index 90d09eddd5b27368e358efd44ab552db8330d39c..351a4de1b1e26e07e3b1920d06b199e778042811 100644 (file)
@@ -498,6 +498,8 @@ CPU_LE(     bic     x0, x0, #(3 << 24)      )       // Clear the EE and E0E bits for EL1
        orr     x0, x0, #ICC_SRE_EL2_ENABLE     // Set ICC_SRE_EL2.Enable==1
        msr_s   ICC_SRE_EL2, x0
        isb                                     // Make sure SRE is now set
+       mrs_s   x0, ICC_SRE_EL2                 // Read SRE back,
+       tbz     x0, #0, 3f                      // and check that it sticks
        msr_s   ICH_HCR_EL2, xzr                // Reset ICC_HCR_EL2 to defaults
 
 3:
index 5c7e920e486132c5257255ff843d81a6616670b1..ff5292c6277c4764734a1a1769af06347ccb8c4b 100644 (file)
@@ -16,6 +16,9 @@ menuconfig VIRTUALIZATION
 
 if VIRTUALIZATION
 
+config KVM_ARM_VGIC_V3
+       bool
+
 config KVM
        bool "Kernel-based Virtual Machine (KVM) support"
        depends on OF
@@ -31,6 +34,7 @@ config KVM
        select KVM_VFIO
        select HAVE_KVM_EVENTFD
        select HAVE_KVM_IRQFD
+       select KVM_ARM_VGIC_V3
        ---help---
          Support hosting virtualized guest machines.
 
index ddcb45d7dfa7dcb4f16408ec8eaca6eeae03566d..43afc03e41251d45c2ccd0f0a00656ad5e368fe9 100644 (file)
@@ -178,7 +178,7 @@ static void __init set_megamod_mux(struct megamod_pic *pic, int src, int output)
 static void __init parse_priority_map(struct megamod_pic *pic,
                                      int *mapping, int size)
 {
-       struct device_node *np = pic->irqhost->of_node;
+       struct device_node *np = irq_domain_get_of_node(pic->irqhost);
        const __be32 *map;
        int i, maplen;
        u32 val;
index 0352bc8d56b316588dbca209462700c4ffa03aa3..4f9eb05768840f93006384959f0a7dfffdb7caf2 100644 (file)
@@ -1094,7 +1094,7 @@ static int octeon_irq_gpio_xlat(struct irq_domain *d,
        unsigned int pin;
        unsigned int trigger;
 
-       if (d->of_node != node)
+       if (irq_domain_get_of_node(d) != node)
                return -EINVAL;
 
        if (intsize < 2)
@@ -2163,7 +2163,7 @@ static int octeon_irq_cib_map(struct irq_domain *d,
 
        if (hw >= host_data->max_bits) {
                pr_err("ERROR: %s mapping %u is to big!\n",
-                      d->of_node->name, (unsigned)hw);
+                      irq_domain_get_of_node(d)->name, (unsigned)hw);
                return -EINVAL;
        }
 
index e0e68a1c0d3c20dd5d618e62cdccba2e429af475..aed7714495c10fd7fa319675f12f4c7baacdd7ed 100644 (file)
@@ -327,7 +327,7 @@ static void axon_msi_shutdown(struct platform_device *device)
        u32 tmp;
 
        pr_devel("axon_msi: disabling %s\n",
-                 msic->irq_domain->of_node->full_name);
+                irq_domain_get_of_node(msic->irq_domain)->full_name);
        tmp  = dcr_read(msic->dcr_host, MSIC_CTRL_REG);
        tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
        msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
index 9d27de62dc62755c4800d46bcc8b4409a81a5115..54ee5743cb724c9b2a28e77607d095da38065d05 100644 (file)
@@ -231,20 +231,23 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic)
        const u32 *imap, *tmp;
        int imaplen, intsize, unit;
        struct device_node *iic;
+       struct device_node *of_node;
+
+       of_node = irq_domain_get_of_node(pic->host);
 
        /* First, we check whether we have a real "interrupts" in the device
         * tree in case the device-tree is ever fixed
         */
-       virq = irq_of_parse_and_map(pic->host->of_node, 0);
+       virq = irq_of_parse_and_map(of_node, 0);
        if (virq)
                return virq;
 
        /* Now do the horrible hacks */
-       tmp = of_get_property(pic->host->of_node, "#interrupt-cells", NULL);
+       tmp = of_get_property(of_node, "#interrupt-cells", NULL);
        if (tmp == NULL)
                return NO_IRQ;
        intsize = *tmp;
-       imap = of_get_property(pic->host->of_node, "interrupt-map", &imaplen);
+       imap = of_get_property(of_node, "interrupt-map", &imaplen);
        if (imap == NULL || imaplen < (intsize + 1))
                return NO_IRQ;
        iic = of_find_node_by_phandle(imap[intsize]);
index b304a9fe55cc410e2bcbd9693a84bce1d2bf5365..d9af76342d9980791a6dce16db4c2ce394d9c386 100644 (file)
@@ -144,9 +144,11 @@ int mpic_pasemi_msi_init(struct mpic *mpic)
 {
        int rc;
        struct pci_controller *phb;
+       struct device_node *of_node;
 
-       if (!mpic->irqhost->of_node ||
-           !of_device_is_compatible(mpic->irqhost->of_node,
+       of_node = irq_domain_get_of_node(mpic->irqhost);
+       if (!of_node ||
+           !of_device_is_compatible(of_node,
                                     "pasemi,pwrficient-openpic"))
                return -ENODEV;
 
index 2c91ee7800b90e09edb93a35aa162da74071ba8c..6ccfb6c1c707b40e9fd6ba4cb202d70c1faf57cb 100644 (file)
@@ -137,7 +137,7 @@ static void opal_handle_irq_work(struct irq_work *work)
 static int opal_event_match(struct irq_domain *h, struct device_node *node,
                            enum irq_domain_bus_token bus_token)
 {
-       return h->of_node == node;
+       return irq_domain_get_of_node(h) == node;
 }
 
 static int opal_event_xlate(struct irq_domain *h, struct device_node *np,
index eca0b00794fa567edb3edc2d12650328b4dec486..bffcc7a486a11c9821ded7f18178c336aa58b9b7 100644 (file)
@@ -181,7 +181,8 @@ static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node,
                              enum irq_domain_bus_token bus_token)
 {
        /* Exact match, unless ehv_pic node is NULL */
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int ehv_pic_host_map(struct irq_domain *h, unsigned int virq,
index 48a576aa47b92455e97a3f437b5cc18b6007f91a..3a2be3676f4353eb6b4e3b237334cd0b3903d37f 100644 (file)
@@ -110,7 +110,7 @@ static int fsl_msi_init_allocator(struct fsl_msi *msi_data)
        int rc, hwirq;
 
        rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS_MAX,
-                             msi_data->irqhost->of_node);
+                             irq_domain_get_of_node(msi_data->irqhost));
        if (rc)
                return rc;
 
index e1a9c2c2d5d357c7e88083d60a6f89c0d04219c2..6f99ed3967fdef3ad382a03758d361e1ee3223db 100644 (file)
@@ -165,7 +165,8 @@ static struct resource pic_edgectrl_iores = {
 static int i8259_host_match(struct irq_domain *h, struct device_node *node,
                            enum irq_domain_bus_token bus_token)
 {
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int i8259_host_map(struct irq_domain *h, unsigned int virq,
index b1297ab1599b46ef24457ec890b28b69ba4dc0bf..f76ee39cb3377a0a6788ad4fcd31fe3c4614743f 100644 (file)
@@ -675,7 +675,8 @@ static int ipic_host_match(struct irq_domain *h, struct device_node *node,
                           enum irq_domain_bus_token bus_token)
 {
        /* Exact match, unless ipic node is NULL */
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int ipic_host_map(struct irq_domain *h, unsigned int virq,
index 537e5db85a060518928ee812cdab8dd00a311b06..cecd1156c1852a71b6f0432c77fd1410eda767d1 100644 (file)
@@ -1011,7 +1011,8 @@ static int mpic_host_match(struct irq_domain *h, struct device_node *node,
                           enum irq_domain_bus_token bus_token)
 {
        /* Exact match, unless mpic node is NULL */
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int mpic_host_map(struct irq_domain *h, unsigned int virq,
index 7dc39f35a4ccdb0d9f213515ccb5b850de3e1b4e..1d48a5385905a8ce96290f10064546a39efed403 100644 (file)
@@ -84,7 +84,7 @@ int mpic_msi_init_allocator(struct mpic *mpic)
        int rc;
 
        rc = msi_bitmap_alloc(&mpic->msi_bitmap, mpic->num_sources,
-                             mpic->irqhost->of_node);
+                             irq_domain_get_of_node(mpic->irqhost));
        if (rc)
                return rc;
 
index fbcc1f855a7f2ea15937d02f0b0cb4305af5034a..ef36f16f9f6fbc9bdfd0c02e6df29e77fc83817c 100644 (file)
@@ -248,7 +248,8 @@ static int qe_ic_host_match(struct irq_domain *h, struct device_node *node,
                            enum irq_domain_bus_token bus_token)
 {
        /* Exact match, unless qe_ic node is NULL */
-       return h->of_node == NULL || h->of_node == node;
+       struct device_node *of_node = irq_domain_get_of_node(h);
+       return of_node == NULL || of_node == node;
 }
 
 static int qe_ic_host_map(struct irq_domain *h, unsigned int virq,
index 38208f2d0e69264f5ad71c66ac65be9719fa70ec..fa4585a6914e912a402ec339d4bd241dd0fadac1 100644 (file)
 #include <linux/acpi.h>
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
+#include <linux/of.h>
 
 enum acpi_irq_model_id acpi_irq_model;
 
+static struct fwnode_handle *acpi_gsi_domain_id;
+
 static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity)
 {
        switch (polarity) {
@@ -45,12 +48,10 @@ static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity)
  */
 int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
 {
-       /*
-        * Only default domain is supported at present, always find
-        * the mapping corresponding to default domain by passing NULL
-        * as irq_domain parameter
-        */
-       *irq = irq_find_mapping(NULL, gsi);
+       struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
+                                                       DOMAIN_BUS_ANY);
+
+       *irq = irq_find_mapping(d, gsi);
        /*
         * *irq == 0 means no mapping, that should
         * be reported as a failure
@@ -72,23 +73,19 @@ EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
 int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
                      int polarity)
 {
-       unsigned int irq;
-       unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity);
+       struct irq_fwspec fwspec;
 
-       /*
-        * There is no way at present to look-up the IRQ domain on ACPI,
-        * hence always create mapping referring to the default domain
-        * by passing NULL as irq_domain parameter
-        */
-       irq = irq_create_mapping(NULL, gsi);
-       if (!irq)
+       if (WARN_ON(!acpi_gsi_domain_id)) {
+               pr_warn("GSI: No registered irqchip, giving up\n");
                return -EINVAL;
+       }
 
-       /* Set irq type if specified and different than the current one */
-       if (irq_type != IRQ_TYPE_NONE &&
-               irq_type != irq_get_trigger_type(irq))
-               irq_set_irq_type(irq, irq_type);
-       return irq;
+       fwspec.fwnode = acpi_gsi_domain_id;
+       fwspec.param[0] = gsi;
+       fwspec.param[1] = acpi_gsi_get_irq_type(trigger, polarity);
+       fwspec.param_count = 2;
+
+       return irq_create_fwspec_mapping(&fwspec);
 }
 EXPORT_SYMBOL_GPL(acpi_register_gsi);
 
@@ -98,8 +95,23 @@ EXPORT_SYMBOL_GPL(acpi_register_gsi);
  */
 void acpi_unregister_gsi(u32 gsi)
 {
-       int irq = irq_find_mapping(NULL, gsi);
+       struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
+                                                       DOMAIN_BUS_ANY);
+       int irq = irq_find_mapping(d, gsi);
 
        irq_dispose_mapping(irq);
 }
 EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
+
+/**
+ * acpi_set_irq_model - Setup the GSI irqdomain information
+ * @model: the value assigned to acpi_irq_model
+ * @fwnode: the irq_domain identifier for mapping and looking up
+ *          GSI interrupts
+ */
+void __init acpi_set_irq_model(enum acpi_irq_model_id model,
+                              struct fwnode_handle *fwnode)
+{
+       acpi_irq_model = model;
+       acpi_gsi_domain_id = fwnode;
+}
index 134483daac25e6627234a5dac90a60687d4810dd..5df4575b5ba765de181a02701c706a6802d38b13 100644 (file)
@@ -152,7 +152,7 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
 
 /**
  * platform_msi_create_irq_domain - Create a platform MSI interrupt domain
- * @np:                Optional device-tree node of the interrupt controller
+ * @fwnode:            Optional fwnode of the interrupt controller
  * @info:      MSI domain info
  * @parent:    Parent irq domain
  *
@@ -162,7 +162,7 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec,
  * Returns:
  * A domain pointer or NULL in case of failure.
  */
-struct irq_domain *platform_msi_create_irq_domain(struct device_node *np,
+struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
                                                  struct msi_domain_info *info,
                                                  struct irq_domain *parent)
 {
@@ -173,7 +173,7 @@ struct irq_domain *platform_msi_create_irq_domain(struct device_node *np,
        if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
                platform_msi_update_chip_ops(info);
 
-       domain = msi_create_irq_domain(np, info, parent);
+       domain = msi_create_irq_domain(fwnode, info, parent);
        if (domain)
                domain->bus_token = DOMAIN_BUS_PLATFORM_MSI;
 
index 65bc9f47a68ed98c89889431a5ece6cf1f8a01b7..34b02b42ab9e5bbfd2bc2f7bd16ba31c704718ec 100644 (file)
@@ -102,7 +102,7 @@ static int sdv_xlate(struct irq_domain *h, struct device_node *node,
 {
        u32 line, type;
 
-       if (node != h->of_node)
+       if (node != irq_domain_get_of_node(h))
                return -EINVAL;
 
        if (intsize < 2)
index 27b52c8729cd1e1a6f17e9945a14e894a78ee81f..4d7294e5d98271493e0ef6d2d90d10108178c83f 100644 (file)
@@ -123,6 +123,7 @@ config RENESAS_INTC_IRQPIN
 
 config RENESAS_IRQC
        bool
+       select GENERIC_IRQ_CHIP
        select IRQ_DOMAIN
 
 config ST_IRQCHIP
@@ -187,3 +188,8 @@ config IMX_GPCV2
        select IRQ_DOMAIN
        help
          Enables the wakeup IRQs for IMX platforms with GPCv2 block
+
+config IRQ_MXS
+       def_bool y if MACH_ASM9260 || ARCH_MXS
+       select IRQ_DOMAIN
+       select STMP_DEVICE
index bb3048f00e647d5c8c0665a7fb31cedc593f4e46..177f78f6e6d6313fd9fd16595fe36089296ce25f 100644 (file)
@@ -6,7 +6,7 @@ obj-$(CONFIG_ARCH_EXYNOS)               += exynos-combiner.o
 obj-$(CONFIG_ARCH_HIP04)               += irq-hip04.o
 obj-$(CONFIG_ARCH_MMP)                 += irq-mmp.o
 obj-$(CONFIG_ARCH_MVEBU)               += irq-armada-370-xp.o
-obj-$(CONFIG_ARCH_MXS)                 += irq-mxs.o
+obj-$(CONFIG_IRQ_MXS)                  += irq-mxs.o
 obj-$(CONFIG_ARCH_TEGRA)               += irq-tegra.o
 obj-$(CONFIG_ARCH_S3C24XX)             += irq-s3c24xx.o
 obj-$(CONFIG_DW_APB_ICTL)              += irq-dw-apb-ictl.o
diff --git a/drivers/irqchip/alphascale_asm9260-icoll.h b/drivers/irqchip/alphascale_asm9260-icoll.h
new file mode 100644 (file)
index 0000000..5cec108
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _ALPHASCALE_ASM9260_ICOLL_H
+#define _ALPHASCALE_ASM9260_ICOLL_H
+
+#define ASM9260_NUM_IRQS               64
+/*
+ * this device provide 4 offsets for each register:
+ * 0x0 - plain read write mode
+ * 0x4 - set mode, OR logic.
+ * 0x8 - clr mode, XOR logic.
+ * 0xc - togle mode.
+ */
+
+#define ASM9260_HW_ICOLL_VECTOR                                0x0000
+/*
+ * bits 31:2
+ * This register presents the vector address for the interrupt currently
+ * active on the CPU IRQ input. Writing to this register notifies the
+ * interrupt collector that the interrupt service routine for the current
+ * interrupt has been entered.
+ * The exception trap should have a LDPC instruction from this address:
+ * LDPC ASM9260_HW_ICOLL_VECTOR_ADDR; IRQ exception at 0xffff0018
+ */
+
+/*
+ * The Interrupt Collector Level Acknowledge Register is used by software to
+ * indicate the completion of an interrupt on a specific level.
+ * This register is written at the very end of an interrupt service routine. If
+ * nesting is used then the CPU irq must be turned on before writing to this
+ * register to avoid a race condition in the CPU interrupt hardware.
+ */
+#define ASM9260_HW_ICOLL_LEVELACK                      0x0010
+#define ASM9260_BM_LEVELn(nr)                          BIT(nr)
+
+#define ASM9260_HW_ICOLL_CTRL                          0x0020
+/*
+ * ASM9260_BM_CTRL_SFTRST and ASM9260_BM_CTRL_CLKGATE are not available on
+ * asm9260.
+ */
+#define ASM9260_BM_CTRL_SFTRST                         BIT(31)
+#define ASM9260_BM_CTRL_CLKGATE                                BIT(30)
+/* disable interrupt level nesting */
+#define ASM9260_BM_CTRL_NO_NESTING                     BIT(19)
+/*
+ * Set this bit to one enable the RISC32-style read side effect associated with
+ * the vector address register. In this mode, interrupt in-service is signaled
+ * by the read of the ASM9260_HW_ICOLL_VECTOR register to acquire the interrupt
+ * vector address. Set this bit to zero for normal operation, in which the ISR
+ * signals in-service explicitly by means of a write to the
+ * ASM9260_HW_ICOLL_VECTOR register.
+ * 0 - Must Write to Vector register to go in-service.
+ * 1 - Go in-service as a read side effect
+ */
+#define ASM9260_BM_CTRL_ARM_RSE_MODE                   BIT(18)
+#define ASM9260_BM_CTRL_IRQ_ENABLE                     BIT(16)
+
+#define ASM9260_HW_ICOLL_STAT_OFFSET                   0x0030
+/*
+ * bits 5:0
+ * Vector number of current interrupt. Multiply by 4 and add to vector base
+ * address to obtain the value in ASM9260_HW_ICOLL_VECTOR.
+ */
+
+/*
+ * RAW0 and RAW1 provides a read-only view of the raw interrupt request lines
+ * coming from various parts of the chip. Its purpose is to improve diagnostic
+ * observability.
+ */
+#define ASM9260_HW_ICOLL_RAW0                          0x0040
+#define ASM9260_HW_ICOLL_RAW1                          0x0050
+
+#define ASM9260_HW_ICOLL_INTERRUPT0                    0x0060
+#define ASM9260_HW_ICOLL_INTERRUPTn(n)         (0x0060 + ((n) >> 2) * 0x10)
+/*
+ * WARNING: Modifying the priority of an enabled interrupt may result in
+ * undefined behavior.
+ */
+#define ASM9260_BM_INT_PRIORITY_MASK                   0x3
+#define ASM9260_BM_INT_ENABLE                          BIT(2)
+#define ASM9260_BM_INT_SOFTIRQ                         BIT(3)
+
+#define ASM9260_BM_ICOLL_INTERRUPTn_SHIFT(n)           (((n) & 0x3) << 3)
+#define ASM9260_BM_ICOLL_INTERRUPTn_ENABLE(n)          (1 << (2 + \
+                       ASM9260_BM_ICOLL_INTERRUPTn_SHIFT(n)))
+
+#define ASM9260_HW_ICOLL_VBASE                         0x0160
+/*
+ * bits 31:2
+ * This bitfield holds the upper 30 bits of the base address of the vector
+ * table.
+ */
+
+#define ASM9260_HW_ICOLL_CLEAR0                                0x01d0
+#define ASM9260_HW_ICOLL_CLEAR1                                0x01e0
+#define ASM9260_HW_ICOLL_CLEARn(n)                     (((n >> 5) * 0x10) \
+                                                       + SET_REG)
+#define ASM9260_BM_CLEAR_BIT(n)                                BIT(n & 0x1f)
+
+/* Scratchpad */
+#define ASM9260_HW_ICOLL_UNDEF_VECTOR                  0x01f0
+#endif
index cd7d3bc78e345c49b733b9120c5a5bed2540583b..ead15be2d20ab12c476157fd75be87d0064c8c41 100644 (file)
@@ -144,7 +144,7 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
                                     unsigned long *out_hwirq,
                                     unsigned int *out_type)
 {
-       if (d->of_node != controller)
+       if (irq_domain_get_of_node(d) != controller)
                return -EINVAL;
 
        if (intsize < 2)
index 63cd031b2c28d40c9c1296ae466005a5d48843d3..b12a5d58546f922b460d6aac34c073ff363ef1b8 100644 (file)
@@ -114,7 +114,7 @@ int aic_common_irq_domain_xlate(struct irq_domain *d,
 
 static void __init aic_common_ext_irq_of_init(struct irq_domain *domain)
 {
-       struct device_node *node = domain->of_node;
+       struct device_node *node = irq_domain_get_of_node(domain);
        struct irq_chip_generic *gc;
        struct aic_chip_data *aic;
        struct property *prop;
index f6d680485beecaf0ec870a0d7492146e3fb72090..62bb840c613f2a660966333f858426af5ab3d93e 100644 (file)
@@ -70,16 +70,15 @@ static struct irq_domain *aic5_domain;
 static asmlinkage void __exception_irq_entry
 aic5_handle(struct pt_regs *regs)
 {
-       struct irq_domain_chip_generic *dgc = aic5_domain->gc;
-       struct irq_chip_generic *gc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(aic5_domain, 0);
        u32 irqnr;
        u32 irqstat;
 
-       irqnr = irq_reg_readl(gc, AT91_AIC5_IVR);
-       irqstat = irq_reg_readl(gc, AT91_AIC5_ISR);
+       irqnr = irq_reg_readl(bgc, AT91_AIC5_IVR);
+       irqstat = irq_reg_readl(bgc, AT91_AIC5_ISR);
 
        if (!irqstat)
-               irq_reg_writel(gc, 0, AT91_AIC5_EOICR);
+               irq_reg_writel(bgc, 0, AT91_AIC5_EOICR);
        else
                handle_domain_irq(aic5_domain, irqnr, regs);
 }
@@ -87,8 +86,7 @@ aic5_handle(struct pt_regs *regs)
 static void aic5_mask(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
-       struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 
        /*
@@ -105,8 +103,7 @@ static void aic5_mask(struct irq_data *d)
 static void aic5_unmask(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
-       struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
 
        /*
@@ -123,14 +120,13 @@ static void aic5_unmask(struct irq_data *d)
 static int aic5_retrigger(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
-       struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *gc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
 
        /* Enable interrupt on AIC5 */
-       irq_gc_lock(gc);
-       irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
-       irq_reg_writel(gc, 1, AT91_AIC5_ISCR);
-       irq_gc_unlock(gc);
+       irq_gc_lock(bgc);
+       irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
+       irq_reg_writel(bgc, 1, AT91_AIC5_ISCR);
+       irq_gc_unlock(bgc);
 
        return 0;
 }
@@ -138,18 +134,17 @@ static int aic5_retrigger(struct irq_data *d)
 static int aic5_set_type(struct irq_data *d, unsigned type)
 {
        struct irq_domain *domain = d->domain;
-       struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *gc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        unsigned int smr;
        int ret;
 
-       irq_gc_lock(gc);
-       irq_reg_writel(gc, d->hwirq, AT91_AIC5_SSR);
-       smr = irq_reg_readl(gc, AT91_AIC5_SMR);
+       irq_gc_lock(bgc);
+       irq_reg_writel(bgc, d->hwirq, AT91_AIC5_SSR);
+       smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
        ret = aic_common_set_type(d, type, &smr);
        if (!ret)
-               irq_reg_writel(gc, smr, AT91_AIC5_SMR);
-       irq_gc_unlock(gc);
+               irq_reg_writel(bgc, smr, AT91_AIC5_SMR);
+       irq_gc_unlock(bgc);
 
        return ret;
 }
@@ -159,7 +154,7 @@ static void aic5_suspend(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
        struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        int i;
        u32 mask;
@@ -183,7 +178,7 @@ static void aic5_resume(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
        struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        int i;
        u32 mask;
@@ -207,7 +202,7 @@ static void aic5_pm_shutdown(struct irq_data *d)
 {
        struct irq_domain *domain = d->domain;
        struct irq_domain_chip_generic *dgc = domain->gc;
-       struct irq_chip_generic *bgc = dgc->gc[0];
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(domain, 0);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        int i;
 
@@ -262,12 +257,11 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
                                 irq_hw_number_t *out_hwirq,
                                 unsigned int *out_type)
 {
-       struct irq_domain_chip_generic *dgc = d->gc;
-       struct irq_chip_generic *gc;
+       struct irq_chip_generic *bgc = irq_get_domain_generic_chip(d, 0);
        unsigned smr;
        int ret;
 
-       if (!dgc)
+       if (!bgc)
                return -EINVAL;
 
        ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize,
@@ -275,15 +269,13 @@ static int aic5_irq_domain_xlate(struct irq_domain *d,
        if (ret)
                return ret;
 
-       gc = dgc->gc[0];
-
-       irq_gc_lock(gc);
-       irq_reg_writel(gc, *out_hwirq, AT91_AIC5_SSR);
-       smr = irq_reg_readl(gc, AT91_AIC5_SMR);
+       irq_gc_lock(bgc);
+       irq_reg_writel(bgc, *out_hwirq, AT91_AIC5_SSR);
+       smr = irq_reg_readl(bgc, AT91_AIC5_SMR);
        ret = aic_common_set_priority(intspec[2], &smr);
        if (!ret)
-               irq_reg_writel(gc, intspec[2] | smr, AT91_AIC5_SMR);
-       irq_gc_unlock(gc);
+               irq_reg_writel(bgc, intspec[2] | smr, AT91_AIC5_SMR);
+       irq_gc_unlock(bgc);
 
        return ret;
 }
index a7f5626930f506289c6eaea34529c20b2051c750..75573fa431ba8552e21afcc19edc8190c329d131 100644 (file)
@@ -78,10 +78,13 @@ static struct irq_chip crossbar_chip = {
 static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
                            irq_hw_number_t hwirq)
 {
-       struct of_phandle_args args;
+       struct irq_fwspec fwspec;
        int i;
        int err;
 
+       if (!irq_domain_get_of_node(domain->parent))
+               return -EINVAL;
+
        raw_spin_lock(&cb->lock);
        for (i = cb->int_max - 1; i >= 0; i--) {
                if (cb->irq_map[i] == IRQ_FREE) {
@@ -94,13 +97,13 @@ static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
        if (i < 0)
                return -ENODEV;
 
-       args.np = domain->parent->of_node;
-       args.args_count = 3;
-       args.args[0] = 0;       /* SPI */
-       args.args[1] = i;
-       args.args[2] = IRQ_TYPE_LEVEL_HIGH;
+       fwspec.fwnode = domain->parent->fwnode;
+       fwspec.param_count = 3;
+       fwspec.param[0] = 0;    /* SPI */
+       fwspec.param[1] = i;
+       fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
 
-       err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+       err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
        if (err)
                cb->irq_map[i] = IRQ_FREE;
        else
@@ -112,16 +115,16 @@ static int allocate_gic_irq(struct irq_domain *domain, unsigned virq,
 static int crossbar_domain_alloc(struct irq_domain *d, unsigned int virq,
                                 unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
+       struct irq_fwspec *fwspec = data;
        irq_hw_number_t hwirq;
        int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != 0)
+       if (fwspec->param[0] != 0)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
        if ((hwirq + nr_irqs) > cb->max_crossbar_sources)
                return -EINVAL; /* Can't deal with this */
 
@@ -166,28 +169,31 @@ static void crossbar_domain_free(struct irq_domain *domain, unsigned int virq,
        raw_spin_unlock(&cb->lock);
 }
 
-static int crossbar_domain_xlate(struct irq_domain *d,
-                                struct device_node *controller,
-                                const u32 *intspec, unsigned int intsize,
-                                unsigned long *out_hwirq,
-                                unsigned int *out_type)
+static int crossbar_domain_translate(struct irq_domain *d,
+                                    struct irq_fwspec *fwspec,
+                                    unsigned long *hwirq,
+                                    unsigned int *type)
 {
-       if (d->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != 0)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static const struct irq_domain_ops crossbar_domain_ops = {
-       .alloc  = crossbar_domain_alloc,
-       .free   = crossbar_domain_free,
-       .xlate  = crossbar_domain_xlate,
+       .alloc          = crossbar_domain_alloc,
+       .free           = crossbar_domain_free,
+       .translate      = crossbar_domain_translate,
 };
 
 static int __init crossbar_of_init(struct device_node *node)
index 9448e391cb710363d18df4c3079ae0504b0cbd44..44a077f3a4a2627c03953d9650a66e7607dd91e2 100644 (file)
 
 #include "irq-gic-common.h"
 
+void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
+               void *data)
+{
+       for (; quirks->desc; quirks++) {
+               if (quirks->iidr != (quirks->mask & iidr))
+                       continue;
+               quirks->init(data);
+               pr_info("GIC: enabling workaround for %s\n", quirks->desc);
+       }
+}
+
 int gic_configure_irq(unsigned int irq, unsigned int type,
                       void __iomem *base, void (*sync_access)(void))
 {
index 35a9884778bd5f337f43041f9a24b88b0371734c..fff697db8e2277be577f7f348917875f9066302e 100644 (file)
 #include <linux/of.h>
 #include <linux/irqdomain.h>
 
+struct gic_quirk {
+       const char *desc;
+       void (*init)(void *data);
+       u32 iidr;
+       u32 mask;
+};
+
 int gic_configure_irq(unsigned int irq, unsigned int type,
                        void __iomem *base, void (*sync_access)(void));
 void gic_dist_config(void __iomem *base, int gic_irqs,
                     void (*sync_access)(void));
 void gic_cpu_config(void __iomem *base, void (*sync_access)(void));
+void gic_enable_quirks(u32 iidr, const struct gic_quirk *quirks,
+               void *data);
 
 #endif /* _IRQ_GIC_COMMON_H */
index 12985daa66ab31c9c7981e65ea0926938e5662fa..87f8d104acab37970b834ded3b74a93ba8b692f6 100644 (file)
 #define V2M_MSI_SETSPI_NS             0x040
 #define V2M_MIN_SPI                   32
 #define V2M_MAX_SPI                   1019
+#define V2M_MSI_IIDR                  0xFCC
 
 #define V2M_MSI_TYPER_BASE_SPI(x)      \
               (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK)
 
 #define V2M_MSI_TYPER_NUM_SPI(x)       ((x) & V2M_MSI_TYPER_NUM_MASK)
 
+/* APM X-Gene with GICv2m MSI_IIDR register value */
+#define XGENE_GICV2M_MSI_IIDR          0x06000170
+
+/* List of flags for specific v2m implementation */
+#define GICV2M_NEEDS_SPI_OFFSET                0x00000001
+
+static LIST_HEAD(v2m_nodes);
+static DEFINE_SPINLOCK(v2m_lock);
+
 struct v2m_data {
-       spinlock_t msi_cnt_lock;
+       struct list_head entry;
+       struct device_node *node;
        struct resource res;    /* GICv2m resource */
        void __iomem *base;     /* GICv2m virt address */
        u32 spi_start;          /* The SPI number that MSIs start */
        u32 nr_spis;            /* The number of SPIs for MSIs */
        unsigned long *bm;      /* MSI vector bitmap */
+       u32 flags;              /* v2m flags for specific implementation */
 };
 
 static void gicv2m_mask_msi_irq(struct irq_data *d)
@@ -98,6 +110,9 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
        msg->address_hi = upper_32_bits(addr);
        msg->address_lo = lower_32_bits(addr);
        msg->data = data->hwirq;
+
+       if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
+               msg->data -= v2m->spi_start;
 }
 
 static struct irq_chip gicv2m_irq_chip = {
@@ -113,17 +128,21 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain,
                                       unsigned int virq,
                                       irq_hw_number_t hwirq)
 {
-       struct of_phandle_args args;
+       struct irq_fwspec fwspec;
        struct irq_data *d;
        int err;
 
-       args.np = domain->parent->of_node;
-       args.args_count = 3;
-       args.args[0] = 0;
-       args.args[1] = hwirq - 32;
-       args.args[2] = IRQ_TYPE_EDGE_RISING;
+       if (is_of_node(domain->parent->fwnode)) {
+               fwspec.fwnode = domain->parent->fwnode;
+               fwspec.param_count = 3;
+               fwspec.param[0] = 0;
+               fwspec.param[1] = hwirq - 32;
+               fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+       } else {
+               return -EINVAL;
+       }
 
-       err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+       err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
        if (err)
                return err;
 
@@ -143,27 +162,30 @@ static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq)
                return;
        }
 
-       spin_lock(&v2m->msi_cnt_lock);
+       spin_lock(&v2m_lock);
        __clear_bit(pos, v2m->bm);
-       spin_unlock(&v2m->msi_cnt_lock);
+       spin_unlock(&v2m_lock);
 }
 
 static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
                                   unsigned int nr_irqs, void *args)
 {
-       struct v2m_data *v2m = domain->host_data;
+       struct v2m_data *v2m = NULL, *tmp;
        int hwirq, offset, err = 0;
 
-       spin_lock(&v2m->msi_cnt_lock);
-       offset = find_first_zero_bit(v2m->bm, v2m->nr_spis);
-       if (offset < v2m->nr_spis)
-               __set_bit(offset, v2m->bm);
-       else
-               err = -ENOSPC;
-       spin_unlock(&v2m->msi_cnt_lock);
+       spin_lock(&v2m_lock);
+       list_for_each_entry(tmp, &v2m_nodes, entry) {
+               offset = find_first_zero_bit(tmp->bm, tmp->nr_spis);
+               if (offset < tmp->nr_spis) {
+                       __set_bit(offset, tmp->bm);
+                       v2m = tmp;
+                       break;
+               }
+       }
+       spin_unlock(&v2m_lock);
 
-       if (err)
-               return err;
+       if (!v2m)
+               return -ENOSPC;
 
        hwirq = v2m->spi_start + offset;
 
@@ -224,12 +246,61 @@ static struct msi_domain_info gicv2m_pmsi_domain_info = {
        .chip   = &gicv2m_pmsi_irq_chip,
 };
 
+static void gicv2m_teardown(void)
+{
+       struct v2m_data *v2m, *tmp;
+
+       list_for_each_entry_safe(v2m, tmp, &v2m_nodes, entry) {
+               list_del(&v2m->entry);
+               kfree(v2m->bm);
+               iounmap(v2m->base);
+               of_node_put(v2m->node);
+               kfree(v2m);
+       }
+}
+
+static int gicv2m_allocate_domains(struct irq_domain *parent)
+{
+       struct irq_domain *inner_domain, *pci_domain, *plat_domain;
+       struct v2m_data *v2m;
+
+       v2m = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
+       if (!v2m)
+               return 0;
+
+       inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node),
+                                             &gicv2m_domain_ops, v2m);
+       if (!inner_domain) {
+               pr_err("Failed to create GICv2m domain\n");
+               return -ENOMEM;
+       }
+
+       inner_domain->bus_token = DOMAIN_BUS_NEXUS;
+       inner_domain->parent = parent;
+       pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+                                              &gicv2m_msi_domain_info,
+                                              inner_domain);
+       plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node),
+                                                    &gicv2m_pmsi_domain_info,
+                                                    inner_domain);
+       if (!pci_domain || !plat_domain) {
+               pr_err("Failed to create MSI domains\n");
+               if (plat_domain)
+                       irq_domain_remove(plat_domain);
+               if (pci_domain)
+                       irq_domain_remove(pci_domain);
+               irq_domain_remove(inner_domain);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
 static int __init gicv2m_init_one(struct device_node *node,
                                  struct irq_domain *parent)
 {
        int ret;
        struct v2m_data *v2m;
-       struct irq_domain *inner_domain, *pci_domain, *plat_domain;
 
        v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL);
        if (!v2m) {
@@ -237,6 +308,9 @@ static int __init gicv2m_init_one(struct device_node *node,
                return -ENOMEM;
        }
 
+       INIT_LIST_HEAD(&v2m->entry);
+       v2m->node = node;
+
        ret = of_address_to_resource(node, 0, &v2m->res);
        if (ret) {
                pr_err("Failed to allocate v2m resource.\n");
@@ -266,6 +340,17 @@ static int __init gicv2m_init_one(struct device_node *node,
                goto err_iounmap;
        }
 
+       /*
+        * APM X-Gene GICv2m implementation has an erratum where
+        * the MSI data needs to be the offset from the spi_start
+        * in order to trigger the correct MSI interrupt. This is
+        * 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).
+        */
+       if (readl_relaxed(v2m->base + V2M_MSI_IIDR) == XGENE_GICV2M_MSI_IIDR)
+               v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
+
        v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis),
                          GFP_KERNEL);
        if (!v2m->bm) {
@@ -273,43 +358,13 @@ static int __init gicv2m_init_one(struct device_node *node,
                goto err_iounmap;
        }
 
-       inner_domain = irq_domain_add_tree(node, &gicv2m_domain_ops, v2m);
-       if (!inner_domain) {
-               pr_err("Failed to create GICv2m domain\n");
-               ret = -ENOMEM;
-               goto err_free_bm;
-       }
-
-       inner_domain->bus_token = DOMAIN_BUS_NEXUS;
-       inner_domain->parent = parent;
-       pci_domain = pci_msi_create_irq_domain(node, &gicv2m_msi_domain_info,
-                                              inner_domain);
-       plat_domain = platform_msi_create_irq_domain(node,
-                                                    &gicv2m_pmsi_domain_info,
-                                                    inner_domain);
-       if (!pci_domain || !plat_domain) {
-               pr_err("Failed to create MSI domains\n");
-               ret = -ENOMEM;
-               goto err_free_domains;
-       }
-
-       spin_lock_init(&v2m->msi_cnt_lock);
-
+       list_add_tail(&v2m->entry, &v2m_nodes);
        pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name,
                (unsigned long)v2m->res.start, (unsigned long)v2m->res.end,
                v2m->spi_start, (v2m->spi_start + v2m->nr_spis));
 
        return 0;
 
-err_free_domains:
-       if (plat_domain)
-               irq_domain_remove(plat_domain);
-       if (pci_domain)
-               irq_domain_remove(pci_domain);
-       if (inner_domain)
-               irq_domain_remove(inner_domain);
-err_free_bm:
-       kfree(v2m->bm);
 err_iounmap:
        iounmap(v2m->base);
 err_free_v2m:
@@ -339,5 +394,9 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
                }
        }
 
+       if (!ret)
+               ret = gicv2m_allocate_domains(parent);
+       if (ret)
+               gicv2m_teardown();
        return ret;
 }
index a7c8c9ffbafd3503228a6c4e63c6870b5d9c784c..aee60ed025dccfab0c6f6b81dbe4e1678a91279c 100644 (file)
@@ -42,7 +42,6 @@ static struct irq_chip its_msi_irq_chip = {
 
 struct its_pci_alias {
        struct pci_dev  *pdev;
-       u32             dev_id;
        u32             count;
 };
 
@@ -60,7 +59,6 @@ static int its_get_pci_alias(struct pci_dev *pdev, u16 alias, void *data)
 {
        struct its_pci_alias *dev_alias = data;
 
-       dev_alias->dev_id = alias;
        if (pdev != dev_alias->pdev)
                dev_alias->count += its_pci_msi_vec_count(pdev);
 
@@ -86,7 +84,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
        pci_for_each_dma_alias(pdev, its_get_pci_alias, &dev_alias);
 
        /* ITS specific DeviceID, as the core ITS ignores dev. */
-       info->scratchpad[0].ul = dev_alias.dev_id;
+       info->scratchpad[0].ul = pci_msi_domain_get_msi_rid(domain, pdev);
 
        return msi_info->ops->msi_prepare(domain->parent,
                                          dev, dev_alias.count, info);
@@ -125,7 +123,8 @@ static int __init its_pci_msi_init(void)
                        continue;
                }
 
-               if (!pci_msi_create_irq_domain(np, &its_pci_msi_domain_info,
+               if (!pci_msi_create_irq_domain(of_node_to_fwnode(np),
+                                              &its_pci_msi_domain_info,
                                               parent)) {
                        pr_err("%s: unable to create PCI domain\n",
                               np->full_name);
index a8655056277995417af6759591e6695ef71cc159..470b4aa7d62c2816015b57420a81f12b37ef4649 100644 (file)
@@ -29,13 +29,25 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
 {
        struct msi_domain_info *msi_info;
        u32 dev_id;
-       int ret;
+       int ret, index = 0;
 
        msi_info = msi_get_domain_info(domain->parent);
 
        /* Suck the DeviceID out of the msi-parent property */
-       ret = of_property_read_u32_index(dev->of_node, "msi-parent",
-                                        1, &dev_id);
+       do {
+               struct of_phandle_args args;
+
+               ret = of_parse_phandle_with_args(dev->of_node,
+                                                "msi-parent", "#msi-cells",
+                                                index, &args);
+               if (args.np == irq_domain_get_of_node(domain)) {
+                       if (WARN_ON(args.args_count != 1))
+                               return -EINVAL;
+                       dev_id = args.args[0];
+                       break;
+               }
+       } while (!ret);
+
        if (ret)
                return ret;
 
@@ -78,7 +90,8 @@ static int __init its_pmsi_init(void)
                        continue;
                }
 
-               if (!platform_msi_create_irq_domain(np, &its_pmsi_domain_info,
+               if (!platform_msi_create_irq_domain(of_node_to_fwnode(np),
+                                                   &its_pmsi_domain_info,
                                                    parent)) {
                        pr_err("%s: unable to create platform domain\n",
                               np->full_name);
index 25ceae9f7348b3208bdd6dec0048a88d4ce34f73..e23d1d18f9d6a39731bdd34633e2bbc9bc473525 100644 (file)
 #include <asm/cputype.h>
 #include <asm/exception.h>
 
-#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING          (1 << 0)
+#include "irq-gic-common.h"
+
+#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING          (1ULL << 0)
+#define ITS_FLAGS_WORKAROUND_CAVIUM_22375      (1ULL << 1)
 
 #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING    (1 << 0)
 
@@ -817,7 +820,22 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
        int i;
        int psz = SZ_64K;
        u64 shr = GITS_BASER_InnerShareable;
-       u64 cache = GITS_BASER_WaWb;
+       u64 cache;
+       u64 typer;
+       u32 ids;
+
+       if (its->flags & ITS_FLAGS_WORKAROUND_CAVIUM_22375) {
+               /*
+                * erratum 22375: only alloc 8MB table size
+                * erratum 24313: ignore memory access type
+                */
+               cache   = 0;
+               ids     = 0x14;                 /* 20 bits, 8MB */
+       } else {
+               cache   = GITS_BASER_WaWb;
+               typer   = readq_relaxed(its->base + GITS_TYPER);
+               ids     = GITS_TYPER_DEVBITS(typer);
+       }
 
        for (i = 0; i < GITS_BASER_NR_REGS; i++) {
                u64 val = readq_relaxed(its->base + GITS_BASER + i * 8);
@@ -825,6 +843,7 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
                u64 entry_size = GITS_BASER_ENTRY_SIZE(val);
                int order = get_order(psz);
                int alloc_size;
+               int alloc_pages;
                u64 tmp;
                void *base;
 
@@ -840,9 +859,6 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
                 * For other tables, only allocate a single page.
                 */
                if (type == GITS_BASER_TYPE_DEVICE) {
-                       u64 typer = readq_relaxed(its->base + GITS_TYPER);
-                       u32 ids = GITS_TYPER_DEVBITS(typer);
-
                        /*
                         * 'order' was initialized earlier to the default page
                         * granule of the the ITS.  We can't have an allocation
@@ -859,6 +875,14 @@ static int its_alloc_tables(const char *node_name, struct its_node *its)
                }
 
                alloc_size = (1 << order) * PAGE_SIZE;
+               alloc_pages = (alloc_size / psz);
+               if (alloc_pages > GITS_BASER_PAGES_MAX) {
+                       alloc_pages = GITS_BASER_PAGES_MAX;
+                       order = get_order(GITS_BASER_PAGES_MAX * psz);
+                       pr_warn("%s: Device Table too large, reduce its page order to %u (%u pages)\n",
+                               node_name, order, alloc_pages);
+               }
+
                base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
                if (!base) {
                        err = -ENOMEM;
@@ -887,7 +911,7 @@ retry_baser:
                        break;
                }
 
-               val |= (alloc_size / psz) - 1;
+               val |= alloc_pages - 1;
 
                writeq_relaxed(val, its->base + GITS_BASER + i * 8);
                tmp = readq_relaxed(its->base + GITS_BASER + i * 8);
@@ -1241,15 +1265,19 @@ static int its_irq_gic_domain_alloc(struct irq_domain *domain,
                                    unsigned int virq,
                                    irq_hw_number_t hwirq)
 {
-       struct of_phandle_args args;
+       struct irq_fwspec fwspec;
 
-       args.np = domain->parent->of_node;
-       args.args_count = 3;
-       args.args[0] = GIC_IRQ_TYPE_LPI;
-       args.args[1] = hwirq;
-       args.args[2] = IRQ_TYPE_EDGE_RISING;
+       if (irq_domain_get_of_node(domain->parent)) {
+               fwspec.fwnode = domain->parent->fwnode;
+               fwspec.param_count = 3;
+               fwspec.param[0] = GIC_IRQ_TYPE_LPI;
+               fwspec.param[1] = hwirq;
+               fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
+       } else {
+               return -EINVAL;
+       }
 
-       return irq_domain_alloc_irqs_parent(domain, virq, 1, &args);
+       return irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
 }
 
 static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
@@ -1370,6 +1398,33 @@ static int its_force_quiescent(void __iomem *base)
        }
 }
 
+static void __maybe_unused its_enable_quirk_cavium_22375(void *data)
+{
+       struct its_node *its = data;
+
+       its->flags |= ITS_FLAGS_WORKAROUND_CAVIUM_22375;
+}
+
+static const struct gic_quirk its_quirks[] = {
+#ifdef CONFIG_CAVIUM_ERRATUM_22375
+       {
+               .desc   = "ITS: Cavium errata 22375, 24313",
+               .iidr   = 0xa100034c,   /* ThunderX pass 1.x */
+               .mask   = 0xffff0fff,
+               .init   = its_enable_quirk_cavium_22375,
+       },
+#endif
+       {
+       }
+};
+
+static void its_enable_quirks(struct its_node *its)
+{
+       u32 iidr = readl_relaxed(its->base + GITS_IIDR);
+
+       gic_enable_quirks(iidr, its_quirks, its);
+}
+
 static int its_probe(struct device_node *node, struct irq_domain *parent)
 {
        struct resource res;
@@ -1428,6 +1483,8 @@ static int its_probe(struct device_node *node, struct irq_domain *parent)
        }
        its->cmd_write = its->cmd_base;
 
+       its_enable_quirks(its);
+
        err = its_alloc_tables(node->full_name, its);
        if (err)
                goto out_free_cmd;
index 36ecfc870e5a6cb86698cb218ac00cccc491e299..d7be6ddc34f685a47dad313274f209b59dff6bf5 100644 (file)
@@ -108,57 +108,17 @@ static void gic_redist_wait_for_rwp(void)
        gic_do_wait_for_rwp(gic_data_rdist_rd_base());
 }
 
-/* Low level accessors */
-static u64 __maybe_unused gic_read_iar(void)
-{
-       u64 irqstat;
-
-       asm volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
-       return irqstat;
-}
-
-static void __maybe_unused gic_write_pmr(u64 val)
-{
-       asm volatile("msr_s " __stringify(ICC_PMR_EL1) ", %0" : : "r" (val));
-}
-
-static void __maybe_unused gic_write_ctlr(u64 val)
-{
-       asm volatile("msr_s " __stringify(ICC_CTLR_EL1) ", %0" : : "r" (val));
-       isb();
-}
-
-static void __maybe_unused gic_write_grpen1(u64 val)
-{
-       asm volatile("msr_s " __stringify(ICC_GRPEN1_EL1) ", %0" : : "r" (val));
-       isb();
-}
+#ifdef CONFIG_ARM64
+static DEFINE_STATIC_KEY_FALSE(is_cavium_thunderx);
 
-static void __maybe_unused gic_write_sgi1r(u64 val)
-{
-       asm volatile("msr_s " __stringify(ICC_SGI1R_EL1) ", %0" : : "r" (val));
-}
-
-static void gic_enable_sre(void)
+static u64 __maybe_unused gic_read_iar(void)
 {
-       u64 val;
-
-       asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
-       val |= ICC_SRE_EL1_SRE;
-       asm volatile("msr_s " __stringify(ICC_SRE_EL1) ", %0" : : "r" (val));
-       isb();
-
-       /*
-        * Need to check that the SRE bit has actually been set. If
-        * not, it means that SRE is disabled at EL2. We're going to
-        * die painfully, and there is nothing we can do about it.
-        *
-        * Kindly inform the luser.
-        */
-       asm volatile("mrs_s %0, " __stringify(ICC_SRE_EL1) : "=r" (val));
-       if (!(val & ICC_SRE_EL1_SRE))
-               pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
+       if (static_branch_unlikely(&is_cavium_thunderx))
+               return gic_read_iar_cavium_thunderx();
+       else
+               return gic_read_iar_common();
 }
+#endif
 
 static void gic_enable_redist(bool enable)
 {
@@ -359,11 +319,11 @@ static int gic_irq_set_vcpu_affinity(struct irq_data *d, void *vcpu)
        return 0;
 }
 
-static u64 gic_mpidr_to_affinity(u64 mpidr)
+static u64 gic_mpidr_to_affinity(unsigned long mpidr)
 {
        u64 aff;
 
-       aff = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
+       aff = ((u64)MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
               MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
               MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8  |
               MPIDR_AFFINITY_LEVEL(mpidr, 0));
@@ -373,7 +333,7 @@ static u64 gic_mpidr_to_affinity(u64 mpidr)
 
 static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
 {
-       u64 irqnr;
+       u32 irqnr;
 
        do {
                irqnr = gic_read_iar();
@@ -432,12 +392,12 @@ static void __init gic_dist_init(void)
         */
        affinity = gic_mpidr_to_affinity(cpu_logical_map(smp_processor_id()));
        for (i = 32; i < gic_data.irq_nr; i++)
-               writeq_relaxed(affinity, base + GICD_IROUTER + i * 8);
+               gic_write_irouter(affinity, base + GICD_IROUTER + i * 8);
 }
 
 static int gic_populate_rdist(void)
 {
-       u64 mpidr = cpu_logical_map(smp_processor_id());
+       unsigned long mpidr = cpu_logical_map(smp_processor_id());
        u64 typer;
        u32 aff;
        int i;
@@ -463,15 +423,14 @@ static int gic_populate_rdist(void)
                }
 
                do {
-                       typer = readq_relaxed(ptr + GICR_TYPER);
+                       typer = gic_read_typer(ptr + GICR_TYPER);
                        if ((typer >> 32) == aff) {
                                u64 offset = ptr - gic_data.redist_regions[i].redist_base;
                                gic_data_rdist_rd_base() = ptr;
                                gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset;
-                               pr_info("CPU%d: found redistributor %llx region %d:%pa\n",
-                                       smp_processor_id(),
-                                       (unsigned long long)mpidr,
-                                       i, &gic_data_rdist()->phys_base);
+                               pr_info("CPU%d: found redistributor %lx region %d:%pa\n",
+                                       smp_processor_id(), mpidr, i,
+                                       &gic_data_rdist()->phys_base);
                                return 0;
                        }
 
@@ -486,15 +445,22 @@ static int gic_populate_rdist(void)
        }
 
        /* We couldn't even deal with ourselves... */
-       WARN(true, "CPU%d: mpidr %llx has no re-distributor!\n",
-            smp_processor_id(), (unsigned long long)mpidr);
+       WARN(true, "CPU%d: mpidr %lx has no re-distributor!\n",
+            smp_processor_id(), mpidr);
        return -ENODEV;
 }
 
 static void gic_cpu_sys_reg_init(void)
 {
-       /* Enable system registers */
-       gic_enable_sre();
+       /*
+        * Need to check that the SRE bit has actually been set. If
+        * not, it means that SRE is disabled at EL2. We're going to
+        * die painfully, and there is nothing we can do about it.
+        *
+        * Kindly inform the luser.
+        */
+       if (!gic_enable_sre())
+               pr_err("GIC: unable to set SRE (disabled at EL2), panic ahead\n");
 
        /* Set priority mask register */
        gic_write_pmr(DEFAULT_PMR_VALUE);
@@ -557,10 +523,10 @@ static struct notifier_block gic_cpu_notifier = {
 };
 
 static u16 gic_compute_target_list(int *base_cpu, const struct cpumask *mask,
-                                  u64 cluster_id)
+                                  unsigned long cluster_id)
 {
        int cpu = *base_cpu;
-       u64 mpidr = cpu_logical_map(cpu);
+       unsigned long mpidr = cpu_logical_map(cpu);
        u16 tlist = 0;
 
        while (cpu < nr_cpu_ids) {
@@ -621,7 +587,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
        smp_wmb();
 
        for_each_cpu(cpu, mask) {
-               u64 cluster_id = cpu_logical_map(cpu) & ~0xffUL;
+               unsigned long cluster_id = cpu_logical_map(cpu) & ~0xffUL;
                u16 tlist;
 
                tlist = gic_compute_target_list(&cpu, mask, cluster_id);
@@ -657,7 +623,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
        reg = gic_dist_base(d) + GICD_IROUTER + (gic_irq(d) * 8);
        val = gic_mpidr_to_affinity(cpu_logical_map(cpu));
 
-       writeq_relaxed(val, reg);
+       gic_write_irouter(val, reg);
 
        /*
         * If the interrupt was enabled, enabled it again. Otherwise,
@@ -771,32 +737,34 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
        return 0;
 }
 
-static int gic_irq_domain_xlate(struct irq_domain *d,
-                               struct device_node *controller,
-                               const u32 *intspec, unsigned int intsize,
-                               unsigned long *out_hwirq, unsigned int *out_type)
+static int gic_irq_domain_translate(struct irq_domain *d,
+                                   struct irq_fwspec *fwspec,
+                                   unsigned long *hwirq,
+                                   unsigned int *type)
 {
-       if (d->of_node != controller)
-               return -EINVAL;
-       if (intsize < 3)
-               return -EINVAL;
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count < 3)
+                       return -EINVAL;
 
-       switch(intspec[0]) {
-       case 0:                 /* SPI */
-               *out_hwirq = intspec[1] + 32;
-               break;
-       case 1:                 /* PPI */
-               *out_hwirq = intspec[1] + 16;
-               break;
-       case GIC_IRQ_TYPE_LPI:  /* LPI */
-               *out_hwirq = intspec[1];
-               break;
-       default:
-               return -EINVAL;
+               switch (fwspec->param[0]) {
+               case 0:                 /* SPI */
+                       *hwirq = fwspec->param[1] + 32;
+                       break;
+               case 1:                 /* PPI */
+                       *hwirq = fwspec->param[1] + 16;
+                       break;
+               case GIC_IRQ_TYPE_LPI:  /* LPI */
+                       *hwirq = fwspec->param[1];
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+               return 0;
        }
 
-       *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
-       return 0;
+       return -EINVAL;
 }
 
 static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
@@ -805,10 +773,9 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
        int i, ret;
        irq_hw_number_t hwirq;
        unsigned int type = IRQ_TYPE_NONE;
-       struct of_phandle_args *irq_data = arg;
+       struct irq_fwspec *fwspec = arg;
 
-       ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,
-                                  irq_data->args_count, &hwirq, &type);
+       ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type);
        if (ret)
                return ret;
 
@@ -831,11 +798,19 @@ static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
 }
 
 static const struct irq_domain_ops gic_irq_domain_ops = {
-       .xlate = gic_irq_domain_xlate,
+       .translate = gic_irq_domain_translate,
        .alloc = gic_irq_domain_alloc,
        .free = gic_irq_domain_free,
 };
 
+static void gicv3_enable_quirks(void)
+{
+#ifdef CONFIG_ARM64
+       if (cpus_have_cap(ARM64_WORKAROUND_CAVIUM_23154))
+               static_branch_enable(&is_cavium_thunderx);
+#endif
+}
+
 static int __init gic_of_init(struct device_node *node, struct device_node *parent)
 {
        void __iomem *dist_base;
@@ -901,6 +876,8 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare
        gic_data.nr_redist_regions = nr_redist_regions;
        gic_data.redist_stride = redist_stride;
 
+       gicv3_enable_quirks();
+
        /*
         * Find out how many interrupts are supported.
         * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
index 982c09c2d79171d21355c75153b4de87f4a1bee2..1d0e76855106cf946627dcc5c5aaa728a38c6bc8 100644 (file)
 
 #include "irq-gic-common.h"
 
+#ifdef CONFIG_ARM64
+#include <asm/cpufeature.h>
+
+static void gic_check_cpu_features(void)
+{
+       WARN_TAINT_ONCE(cpus_have_cap(ARM64_HAS_SYSREG_GIC_CPUIF),
+                       TAINT_CPU_OUT_OF_SPEC,
+                       "GICv3 system registers enabled, broken firmware!\n");
+}
+#else
+#define gic_check_cpu_features()       do { } while(0)
+#endif
+
 union gic_base {
        void __iomem *common_base;
        void __percpu * __iomem *percpu_base;
@@ -903,28 +916,39 @@ static void gic_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
 {
 }
 
-static int gic_irq_domain_xlate(struct irq_domain *d,
-                               struct device_node *controller,
-                               const u32 *intspec, unsigned int intsize,
-                               unsigned long *out_hwirq, unsigned int *out_type)
+static int gic_irq_domain_translate(struct irq_domain *d,
+                                   struct irq_fwspec *fwspec,
+                                   unsigned long *hwirq,
+                                   unsigned int *type)
 {
-       unsigned long ret = 0;
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count < 3)
+                       return -EINVAL;
 
-       if (d->of_node != controller)
-               return -EINVAL;
-       if (intsize < 3)
-               return -EINVAL;
+               /* Get the interrupt number and add 16 to skip over SGIs */
+               *hwirq = fwspec->param[1] + 16;
 
-       /* Get the interrupt number and add 16 to skip over SGIs */
-       *out_hwirq = intspec[1] + 16;
+               /*
+                * For SPIs, we need to add 16 more to get the GIC irq
+                * ID number
+                */
+               if (!fwspec->param[0])
+                       *hwirq += 16;
 
-       /* For SPIs, we need to add 16 more to get the GIC irq ID number */
-       if (!intspec[0])
-               *out_hwirq += 16;
+               *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+               return 0;
+       }
 
-       *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+       if (fwspec->fwnode->type == FWNODE_IRQCHIP) {
+               if(fwspec->param_count != 2)
+                       return -EINVAL;
 
-       return ret;
+               *hwirq = fwspec->param[0];
+               *type = fwspec->param[1];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 #ifdef CONFIG_SMP
@@ -952,10 +976,9 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
        int i, ret;
        irq_hw_number_t hwirq;
        unsigned int type = IRQ_TYPE_NONE;
-       struct of_phandle_args *irq_data = arg;
+       struct irq_fwspec *fwspec = arg;
 
-       ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args,
-                                  irq_data->args_count, &hwirq, &type);
+       ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type);
        if (ret)
                return ret;
 
@@ -966,7 +989,7 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 }
 
 static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
-       .xlate = gic_irq_domain_xlate,
+       .translate = gic_irq_domain_translate,
        .alloc = gic_irq_domain_alloc,
        .free = irq_domain_free_irqs_top,
 };
@@ -974,12 +997,11 @@ static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
 static const struct irq_domain_ops gic_irq_domain_ops = {
        .map = gic_irq_domain_map,
        .unmap = gic_irq_domain_unmap,
-       .xlate = gic_irq_domain_xlate,
 };
 
 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 device_node *node)
+                          u32 percpu_offset, struct fwnode_handle *handle)
 {
        irq_hw_number_t hwirq_base;
        struct gic_chip_data *gic;
@@ -987,6 +1009,8 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
 
        BUG_ON(gic_nr >= MAX_GIC_NR);
 
+       gic_check_cpu_features();
+
        gic = &gic_data[gic_nr];
 #ifdef CONFIG_GIC_NON_BANKED
        if (percpu_offset) { /* Frankein-GIC without banked registers... */
@@ -1031,11 +1055,11 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
                gic_irqs = 1020;
        gic->gic_irqs = gic_irqs;
 
-       if (node) {             /* DT case */
-               gic->domain = irq_domain_add_linear(node, gic_irqs,
-                                                   &gic_irq_domain_hierarchy_ops,
-                                                   gic);
-       } else {                /* Non-DT case */
+       if (handle) {           /* DT/ACPI */
+               gic->domain = irq_domain_create_linear(handle, gic_irqs,
+                                                      &gic_irq_domain_hierarchy_ops,
+                                                      gic);
+       } else {                /* Legacy support */
                /*
                 * For primary GICs, skip over SGIs.
                 * For secondary GICs, skip over PPIs, too.
@@ -1058,7 +1082,7 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
                        irq_base = irq_start;
                }
 
-               gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
+               gic->domain = irq_domain_add_legacy(NULL, gic_irqs, irq_base,
                                        hwirq_base, &gic_irq_domain_ops, gic);
        }
 
@@ -1087,17 +1111,15 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
        gic_pm_init(gic);
 }
 
-void __init gic_init_bases(unsigned int gic_nr, int irq_start,
-                          void __iomem *dist_base, void __iomem *cpu_base,
-                          u32 percpu_offset, struct device_node *node)
+void __init gic_init(unsigned int gic_nr, int irq_start,
+                    void __iomem *dist_base, void __iomem *cpu_base)
 {
        /*
         * 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,
-                        percpu_offset, node);
+       __gic_init_bases(gic_nr, irq_start, dist_base, cpu_base, 0, NULL);
 }
 
 #ifdef CONFIG_OF
@@ -1168,7 +1190,8 @@ gic_of_init(struct device_node *node, struct device_node *parent)
        if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
                percpu_offset = 0;
 
-       __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);
+       __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset,
+                        &node->fwnode);
        if (!gic_cnt)
                gic_init_physaddr(node);
 
@@ -1191,6 +1214,7 @@ IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
 IRQCHIP_DECLARE(cortex_a7_gic, "arm,cortex-a7-gic", gic_of_init);
 IRQCHIP_DECLARE(msm_8660_qgic, "qcom,msm-8660-qgic", gic_of_init);
 IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
+IRQCHIP_DECLARE(pl390, "arm,pl390", gic_of_init);
 
 #endif
 
@@ -1242,6 +1266,7 @@ int __init
 gic_v2_acpi_init(struct acpi_table_header *table)
 {
        void __iomem *cpu_base, *dist_base;
+       struct fwnode_handle *domain_handle;
        int count;
 
        /* Collect CPU base addresses */
@@ -1292,14 +1317,19 @@ gic_v2_acpi_init(struct acpi_table_header *table)
                static_key_slow_dec(&supports_deactivate);
 
        /*
-        * Initialize zero GIC instance (no multi-GIC support). Also, set GIC
-        * as default IRQ domain to allow for GSI registration and GSI to IRQ
-        * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()).
+        * Initialize GIC instance zero (no multi-GIC support).
         */
-       __gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL);
-       irq_set_default_host(gic_data[0].domain);
+       domain_handle = irq_domain_alloc_fwnode(dist_base);
+       if (!domain_handle) {
+               pr_err("Unable to allocate domain handle\n");
+               iounmap(cpu_base);
+               iounmap(dist_base);
+               return -ENOMEM;
+       }
+
+       __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
 
-       acpi_irq_model = ACPI_IRQ_MODEL_GIC;
+       acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
        return 0;
 }
 #endif
index 8f3ca8f3a62b615b2d670851dd8afe1d75904bee..9688d2e2a6364d2accbe77f323dd3b9f1d735dfa 100644 (file)
@@ -325,7 +325,7 @@ static int hip04_irq_domain_xlate(struct irq_domain *d,
 {
        unsigned long ret = 0;
 
-       if (d->of_node != controller)
+       if (irq_domain_get_of_node(d) != controller)
                return -EINVAL;
        if (intsize < 3)
                return -EINVAL;
index e484fd2553210f55fd0eb72375f4ada1511c7fec..6b304eb39bd22c96b64c6a99629ef4195db721b5 100644 (file)
@@ -377,8 +377,8 @@ int __init i8259_of_init(struct device_node *node, struct device_node *parent)
        }
 
        domain = __init_i8259_irqs(node);
-       irq_set_handler_data(parent_irq, domain);
-       irq_set_chained_handler(parent_irq, i8259_irq_dispatch);
+       irq_set_chained_handler_and_data(parent_irq, i8259_irq_dispatch,
+                                        domain);
        return 0;
 }
 IRQCHIP_DECLARE(i8259, "intel,i8259", i8259_of_init);
index e48d3305456fe859e890413144066ff1fbb43875..15af9a9753e582b797a58de3c9713226800999f8 100644 (file)
@@ -150,49 +150,42 @@ static struct irq_chip gpcv2_irqchip_data_chip = {
 #endif
 };
 
-static int imx_gpcv2_domain_xlate(struct irq_domain *domain,
-                               struct device_node *controller,
-                               const u32 *intspec,
-                               unsigned int intsize,
-                               unsigned long *out_hwirq,
-                               unsigned int *out_type)
+static int imx_gpcv2_domain_translate(struct irq_domain *d,
+                                     struct irq_fwspec *fwspec,
+                                     unsigned long *hwirq,
+                                     unsigned int *type)
 {
-       /* Shouldn't happen, really... */
-       if (domain->of_node != controller)
-               return -EINVAL;
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       /* Not GIC compliant */
-       if (intsize != 3)
-               return -EINVAL;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
 
-       /* No PPI should point to this domain */
-       if (intspec[0] != 0)
-               return -EINVAL;
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+       return -EINVAL;
 }
 
 static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
                                  unsigned int irq, unsigned int nr_irqs,
                                  void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        irq_hw_number_t hwirq;
+       unsigned int type;
+       int err;
        int i;
 
-       /* Not GIC compliant */
-       if (args->args_count != 3)
-               return -EINVAL;
-
-       /* No PPI should point to this domain */
-       if (args->args[0] != 0)
-               return -EINVAL;
+       err = imx_gpcv2_domain_translate(domain, fwspec, &hwirq, &type);
+       if (err)
+               return err;
 
-       /* Can't deal with this */
-       hwirq = args->args[1];
        if (hwirq >= GPC_MAX_IRQS)
                return -EINVAL;
 
@@ -201,15 +194,16 @@ static int imx_gpcv2_domain_alloc(struct irq_domain *domain,
                                &gpcv2_irqchip_data_chip, domain->host_data);
        }
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static struct irq_domain_ops gpcv2_irqchip_data_domain_ops = {
-       .xlate  = imx_gpcv2_domain_xlate,
-       .alloc  = imx_gpcv2_domain_alloc,
-       .free   = irq_domain_free_irqs_common,
+       .translate      = imx_gpcv2_domain_translate,
+       .alloc          = imx_gpcv2_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 static int __init imx_gpcv2_irqchip_init(struct device_node *node,
index c8753da4c156ee9e2fa1b3b5ddfde63561733d30..63ac73b1d9c849345adfc0b1e0984ea21cb5f03c 100644 (file)
@@ -67,22 +67,25 @@ static struct irq_chip mtk_sysirq_chip = {
        .irq_set_affinity       = irq_chip_set_affinity_parent,
 };
 
-static int mtk_sysirq_domain_xlate(struct irq_domain *d,
-                                  struct device_node *controller,
-                                  const u32 *intspec, unsigned int intsize,
-                                  unsigned long *out_hwirq,
-                                  unsigned int *out_type)
+static int mtk_sysirq_domain_translate(struct irq_domain *d,
+                                      struct irq_fwspec *fwspec,
+                                      unsigned long *hwirq,
+                                      unsigned int *type)
 {
-       if (intsize != 3)
-               return -EINVAL;
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       /* sysirq doesn't support PPI */
-       if (intspec[0])
-               return -EINVAL;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
-       return 0;
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
@@ -90,30 +93,30 @@ static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 {
        int i;
        irq_hw_number_t hwirq;
-       struct of_phandle_args *irq_data = arg;
-       struct of_phandle_args gic_data = *irq_data;
+       struct irq_fwspec *fwspec = arg;
+       struct irq_fwspec gic_fwspec = *fwspec;
 
-       if (irq_data->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL;
 
        /* sysirq doesn't support PPI */
-       if (irq_data->args[0])
+       if (fwspec->param[0])
                return -EINVAL;
 
-       hwirq = irq_data->args[1];
+       hwirq = fwspec->param[1];
        for (i = 0; i < nr_irqs; i++)
                irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
                                              &mtk_sysirq_chip,
                                              domain->host_data);
 
-       gic_data.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
+       gic_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_fwspec);
 }
 
 static const struct irq_domain_ops sysirq_domain_ops = {
-       .xlate = mtk_sysirq_domain_xlate,
-       .alloc = mtk_sysirq_domain_alloc,
-       .free = irq_domain_free_irqs_common,
+       .translate      = mtk_sysirq_domain_translate,
+       .alloc          = mtk_sysirq_domain_alloc,
+       .free           = irq_domain_free_irqs_common,
 };
 
 static int __init mtk_sysirq_of_init(struct device_node *node,
index 604df63e2edf6f9706db28d08bf559a0347e8bd8..c22e2d40cb302452624c29efd44c483e577f5333 100644 (file)
@@ -1,5 +1,7 @@
 /*
  * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2014 Oleksij Rempel <linux@rempel-privat.de>
+ *     Add Alphascale ASM9260 support.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <linux/stmp_device.h>
 #include <asm/exception.h>
 
+#include "alphascale_asm9260-icoll.h"
+
+/*
+ * this device provide 4 offsets for each register:
+ * 0x0 - plain read write mode
+ * 0x4 - set mode, OR logic.
+ * 0x8 - clr mode, XOR logic.
+ * 0xc - togle mode.
+ */
+#define SET_REG 4
+#define CLR_REG 8
+
 #define HW_ICOLL_VECTOR                                0x0000
 #define HW_ICOLL_LEVELACK                      0x0010
 #define HW_ICOLL_CTRL                          0x0020
 #define HW_ICOLL_STAT_OFFSET                   0x0070
-#define HW_ICOLL_INTERRUPTn_SET(n)             (0x0124 + (n) * 0x10)
-#define HW_ICOLL_INTERRUPTn_CLR(n)             (0x0128 + (n) * 0x10)
-#define BM_ICOLL_INTERRUPTn_ENABLE             0x00000004
+#define HW_ICOLL_INTERRUPT0                    0x0120
+#define HW_ICOLL_INTERRUPTn(n)                 ((n) * 0x10)
+#define BM_ICOLL_INTR_ENABLE                   BIT(2)
 #define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0  0x1
 
 #define ICOLL_NUM_IRQS         128
 
-static void __iomem *icoll_base;
+enum icoll_type {
+       ICOLL,
+       ASM9260_ICOLL,
+};
+
+struct icoll_priv {
+       void __iomem *vector;
+       void __iomem *levelack;
+       void __iomem *ctrl;
+       void __iomem *stat;
+       void __iomem *intr;
+       void __iomem *clear;
+       enum icoll_type type;
+};
+
+static struct icoll_priv icoll_priv;
 static struct irq_domain *icoll_domain;
 
+/* calculate bit offset depending on number of intterupt per register */
+static u32 icoll_intr_bitshift(struct irq_data *d, u32 bit)
+{
+       /*
+        * mask lower part of hwirq to convert it
+        * in 0, 1, 2 or 3 and then multiply it by 8 (or shift by 3)
+        */
+       return bit << ((d->hwirq & 3) << 3);
+}
+
+/* calculate mem offset depending on number of intterupt per register */
+static void __iomem *icoll_intr_reg(struct irq_data *d)
+{
+       /* offset = hwirq / intr_per_reg * 0x10 */
+       return icoll_priv.intr + ((d->hwirq >> 2) * 0x10);
+}
+
 static void icoll_ack_irq(struct irq_data *d)
 {
        /*
@@ -50,19 +96,35 @@ static void icoll_ack_irq(struct irq_data *d)
         * BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 unconditionally.
         */
        __raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0,
-                       icoll_base + HW_ICOLL_LEVELACK);
+                       icoll_priv.levelack);
 }
 
 static void icoll_mask_irq(struct irq_data *d)
 {
-       __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
-                       icoll_base + HW_ICOLL_INTERRUPTn_CLR(d->hwirq));
+       __raw_writel(BM_ICOLL_INTR_ENABLE,
+                       icoll_priv.intr + CLR_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
 }
 
 static void icoll_unmask_irq(struct irq_data *d)
 {
-       __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
-                       icoll_base + HW_ICOLL_INTERRUPTn_SET(d->hwirq));
+       __raw_writel(BM_ICOLL_INTR_ENABLE,
+                       icoll_priv.intr + SET_REG + HW_ICOLL_INTERRUPTn(d->hwirq));
+}
+
+static void asm9260_mask_irq(struct irq_data *d)
+{
+       __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
+                       icoll_intr_reg(d) + CLR_REG);
+}
+
+static void asm9260_unmask_irq(struct irq_data *d)
+{
+       __raw_writel(ASM9260_BM_CLEAR_BIT(d->hwirq),
+                    icoll_priv.clear +
+                    ASM9260_HW_ICOLL_CLEARn(d->hwirq));
+
+       __raw_writel(icoll_intr_bitshift(d, BM_ICOLL_INTR_ENABLE),
+                       icoll_intr_reg(d) + SET_REG);
 }
 
 static struct irq_chip mxs_icoll_chip = {
@@ -71,19 +133,32 @@ static struct irq_chip mxs_icoll_chip = {
        .irq_unmask = icoll_unmask_irq,
 };
 
+static struct irq_chip asm9260_icoll_chip = {
+       .irq_ack = icoll_ack_irq,
+       .irq_mask = asm9260_mask_irq,
+       .irq_unmask = asm9260_unmask_irq,
+};
+
 asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
 {
        u32 irqnr;
 
-       irqnr = __raw_readl(icoll_base + HW_ICOLL_STAT_OFFSET);
-       __raw_writel(irqnr, icoll_base + HW_ICOLL_VECTOR);
+       irqnr = __raw_readl(icoll_priv.stat);
+       __raw_writel(irqnr, icoll_priv.vector);
        handle_domain_irq(icoll_domain, irqnr, regs);
 }
 
 static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
                                irq_hw_number_t hw)
 {
-       irq_set_chip_and_handler(virq, &mxs_icoll_chip, handle_level_irq);
+       struct irq_chip *chip;
+
+       if (icoll_priv.type == ICOLL)
+               chip = &mxs_icoll_chip;
+       else
+               chip = &asm9260_icoll_chip;
+
+       irq_set_chip_and_handler(virq, chip, handle_level_irq);
 
        return 0;
 }
@@ -93,20 +168,80 @@ static const struct irq_domain_ops icoll_irq_domain_ops = {
        .xlate = irq_domain_xlate_onecell,
 };
 
+static void __init icoll_add_domain(struct device_node *np,
+                         int num)
+{
+       icoll_domain = irq_domain_add_linear(np, num,
+                                            &icoll_irq_domain_ops, NULL);
+
+       if (!icoll_domain)
+               panic("%s: unable to create irq domain", np->full_name);
+}
+
+static void __iomem * __init icoll_init_iobase(struct device_node *np)
+{
+       void __iomem *icoll_base;
+
+       icoll_base = of_io_request_and_map(np, 0, np->name);
+       if (!icoll_base)
+               panic("%s: unable to map resource", np->full_name);
+       return icoll_base;
+}
+
 static int __init icoll_of_init(struct device_node *np,
                          struct device_node *interrupt_parent)
 {
-       icoll_base = of_iomap(np, 0);
-       WARN_ON(!icoll_base);
+       void __iomem *icoll_base;
+
+       icoll_priv.type = ICOLL;
+
+       icoll_base              = icoll_init_iobase(np);
+       icoll_priv.vector       = icoll_base + HW_ICOLL_VECTOR;
+       icoll_priv.levelack     = icoll_base + HW_ICOLL_LEVELACK;
+       icoll_priv.ctrl         = icoll_base + HW_ICOLL_CTRL;
+       icoll_priv.stat         = icoll_base + HW_ICOLL_STAT_OFFSET;
+       icoll_priv.intr         = icoll_base + HW_ICOLL_INTERRUPT0;
+       icoll_priv.clear        = NULL;
 
        /*
         * Interrupt Collector reset, which initializes the priority
         * for each irq to level 0.
         */
-       stmp_reset_block(icoll_base + HW_ICOLL_CTRL);
+       stmp_reset_block(icoll_priv.ctrl);
 
-       icoll_domain = irq_domain_add_linear(np, ICOLL_NUM_IRQS,
-                                            &icoll_irq_domain_ops, NULL);
-       return icoll_domain ? 0 : -ENODEV;
+       icoll_add_domain(np, ICOLL_NUM_IRQS);
+
+       return 0;
 }
 IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init);
+
+static int __init asm9260_of_init(struct device_node *np,
+                         struct device_node *interrupt_parent)
+{
+       void __iomem *icoll_base;
+       int i;
+
+       icoll_priv.type = ASM9260_ICOLL;
+
+       icoll_base = icoll_init_iobase(np);
+       icoll_priv.vector       = icoll_base + ASM9260_HW_ICOLL_VECTOR;
+       icoll_priv.levelack     = icoll_base + ASM9260_HW_ICOLL_LEVELACK;
+       icoll_priv.ctrl         = icoll_base + ASM9260_HW_ICOLL_CTRL;
+       icoll_priv.stat         = icoll_base + ASM9260_HW_ICOLL_STAT_OFFSET;
+       icoll_priv.intr         = icoll_base + ASM9260_HW_ICOLL_INTERRUPT0;
+       icoll_priv.clear        = icoll_base + ASM9260_HW_ICOLL_CLEAR0;
+
+       writel_relaxed(ASM9260_BM_CTRL_IRQ_ENABLE,
+                       icoll_priv.ctrl);
+       /*
+        * ASM9260 don't provide reset bit. So, we need to set level 0
+        * manually.
+        */
+       for (i = 0; i < 16 * 0x10; i += 0x10)
+               writel(0, icoll_priv.intr + i);
+
+       icoll_add_domain(np, ASM9260_NUM_IRQS);
+
+       return 0;
+}
+IRQCHIP_DECLARE(asm9260, "alphascale,asm9260-icoll", asm9260_of_init);
index a878b8d03868823a222af0ee4d0f296793673a7d..b1777104fd9feca9c739db29caa1d94cc36d0edd 100644 (file)
@@ -48,16 +48,26 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs)
        handle_IRQ(irq, regs);
 }
 
+static int nvic_irq_domain_translate(struct irq_domain *d,
+                                    struct irq_fwspec *fwspec,
+                                    unsigned long *hwirq, unsigned int *type)
+{
+       if (WARN_ON(fwspec->param_count < 1))
+               return -EINVAL;
+       *hwirq = fwspec->param[0];
+       *type = IRQ_TYPE_NONE;
+       return 0;
+}
+
 static int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
                                unsigned int nr_irqs, void *arg)
 {
        int i, ret;
        irq_hw_number_t hwirq;
        unsigned int type = IRQ_TYPE_NONE;
-       struct of_phandle_args *irq_data = arg;
+       struct irq_fwspec *fwspec = arg;
 
-       ret = irq_domain_xlate_onecell(domain, irq_data->np, irq_data->args,
-                                  irq_data->args_count, &hwirq, &type);
+       ret = nvic_irq_domain_translate(domain, fwspec, &hwirq, &type);
        if (ret)
                return ret;
 
@@ -68,7 +78,7 @@ static int nvic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 }
 
 static const struct irq_domain_ops nvic_irq_domain_ops = {
-       .xlate = irq_domain_xlate_onecell,
+       .translate = nvic_irq_domain_translate,
        .alloc = nvic_irq_domain_alloc,
        .free = irq_domain_free_irqs_top,
 };
index 9525335723f68f7e4c408d81a6538dc8e72a2b5f..c325806561bedd932db973470d4bedfaf3950886 100644 (file)
@@ -361,14 +361,16 @@ static const struct irq_domain_ops intc_irqpin_irq_domain_ops = {
        .xlate  = irq_domain_xlate_twocell,
 };
 
-static const struct intc_irqpin_irlm_config intc_irqpin_irlm_r8a7779 = {
+static const struct intc_irqpin_irlm_config intc_irqpin_irlm_r8a777x = {
        .irlm_bit = 23, /* ICR0.IRLM0 */
 };
 
 static const struct of_device_id intc_irqpin_dt_ids[] = {
        { .compatible = "renesas,intc-irqpin", },
+       { .compatible = "renesas,intc-irqpin-r8a7778",
+         .data = &intc_irqpin_irlm_r8a777x },
        { .compatible = "renesas,intc-irqpin-r8a7779",
-         .data = &intc_irqpin_irlm_r8a7779 },
+         .data = &intc_irqpin_irlm_r8a777x },
        {},
 };
 MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids);
index 35bf97ba4a3d196db74ff5835dc9c59fb056c2af..52304b139aa46a60966d03922197b060f4693cf1 100644 (file)
@@ -62,33 +62,20 @@ struct irqc_priv {
        struct irqc_irq irq[IRQC_IRQ_MAX];
        unsigned int number_of_irqs;
        struct platform_device *pdev;
-       struct irq_chip irq_chip;
+       struct irq_chip_generic *gc;
        struct irq_domain *irq_domain;
        struct clk *clk;
 };
 
-static void irqc_dbg(struct irqc_irq *i, char *str)
-{
-       dev_dbg(&i->p->pdev->dev, "%s (%d:%d)\n",
-               str, i->requested_irq, i->hw_irq);
-}
-
-static void irqc_irq_enable(struct irq_data *d)
+static struct irqc_priv *irq_data_to_priv(struct irq_data *data)
 {
-       struct irqc_priv *p = irq_data_get_irq_chip_data(d);
-       int hw_irq = irqd_to_hwirq(d);
-
-       irqc_dbg(&p->irq[hw_irq], "enable");
-       iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_SET);
+       return data->domain->host_data;
 }
 
-static void irqc_irq_disable(struct irq_data *d)
+static void irqc_dbg(struct irqc_irq *i, char *str)
 {
-       struct irqc_priv *p = irq_data_get_irq_chip_data(d);
-       int hw_irq = irqd_to_hwirq(d);
-
-       irqc_dbg(&p->irq[hw_irq], "disable");
-       iowrite32(BIT(hw_irq), p->cpu_int_base + IRQC_EN_STS);
+       dev_dbg(&i->p->pdev->dev, "%s (%d:%d)\n",
+               str, i->requested_irq, i->hw_irq);
 }
 
 static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
@@ -101,7 +88,7 @@ static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
 
 static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
 {
-       struct irqc_priv *p = irq_data_get_irq_chip_data(d);
+       struct irqc_priv *p = irq_data_to_priv(d);
        int hw_irq = irqd_to_hwirq(d);
        unsigned char value = irqc_sense[type & IRQ_TYPE_SENSE_MASK];
        u32 tmp;
@@ -120,7 +107,7 @@ static int irqc_irq_set_type(struct irq_data *d, unsigned int type)
 
 static int irqc_irq_set_wake(struct irq_data *d, unsigned int on)
 {
-       struct irqc_priv *p = irq_data_get_irq_chip_data(d);
+       struct irqc_priv *p = irq_data_to_priv(d);
        int hw_irq = irqd_to_hwirq(d);
 
        irq_set_irq_wake(p->irq[hw_irq].requested_irq, on);
@@ -153,35 +140,11 @@ static irqreturn_t irqc_irq_handler(int irq, void *dev_id)
        return IRQ_NONE;
 }
 
-/*
- * This lock class tells lockdep that IRQC irqs are in a different
- * category than their parents, so it won't report false recursion.
- */
-static struct lock_class_key irqc_irq_lock_class;
-
-static int irqc_irq_domain_map(struct irq_domain *h, unsigned int virq,
-                              irq_hw_number_t hw)
-{
-       struct irqc_priv *p = h->host_data;
-
-       irqc_dbg(&p->irq[hw], "map");
-       irq_set_chip_data(virq, h->host_data);
-       irq_set_lockdep_class(virq, &irqc_irq_lock_class);
-       irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq);
-       return 0;
-}
-
-static const struct irq_domain_ops irqc_irq_domain_ops = {
-       .map    = irqc_irq_domain_map,
-       .xlate  = irq_domain_xlate_twocell,
-};
-
 static int irqc_probe(struct platform_device *pdev)
 {
        struct irqc_priv *p;
        struct resource *io;
        struct resource *irq;
-       struct irq_chip *irq_chip;
        const char *name = dev_name(&pdev->dev);
        int ret;
        int k;
@@ -241,40 +204,51 @@ static int irqc_probe(struct platform_device *pdev)
 
        p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */
 
-       irq_chip = &p->irq_chip;
-       irq_chip->name = name;
-       irq_chip->irq_mask = irqc_irq_disable;
-       irq_chip->irq_unmask = irqc_irq_enable;
-       irq_chip->irq_set_type = irqc_irq_set_type;
-       irq_chip->irq_set_wake = irqc_irq_set_wake;
-       irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND;
-
        p->irq_domain = irq_domain_add_linear(pdev->dev.of_node,
                                              p->number_of_irqs,
-                                             &irqc_irq_domain_ops, p);
+                                             &irq_generic_chip_ops, p);
        if (!p->irq_domain) {
                ret = -ENXIO;
                dev_err(&pdev->dev, "cannot initialize irq domain\n");
                goto err2;
        }
 
+       ret = irq_alloc_domain_generic_chips(p->irq_domain, p->number_of_irqs,
+                                            1, name, handle_level_irq,
+                                            0, 0, IRQ_GC_INIT_NESTED_LOCK);
+       if (ret) {
+               dev_err(&pdev->dev, "cannot allocate generic chip\n");
+               goto err3;
+       }
+
+       p->gc = irq_get_domain_generic_chip(p->irq_domain, 0);
+       p->gc->reg_base = p->cpu_int_base;
+       p->gc->chip_types[0].regs.enable = IRQC_EN_SET;
+       p->gc->chip_types[0].regs.disable = IRQC_EN_STS;
+       p->gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg;
+       p->gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg;
+       p->gc->chip_types[0].chip.irq_set_type  = irqc_irq_set_type;
+       p->gc->chip_types[0].chip.irq_set_wake  = irqc_irq_set_wake;
+       p->gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
+
        /* request interrupts one by one */
        for (k = 0; k < p->number_of_irqs; k++) {
                if (request_irq(p->irq[k].requested_irq, irqc_irq_handler,
                                0, name, &p->irq[k])) {
                        dev_err(&pdev->dev, "failed to request IRQ\n");
                        ret = -ENOENT;
-                       goto err3;
+                       goto err4;
                }
        }
 
        dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs);
 
        return 0;
-err3:
+err4:
        while (--k >= 0)
                free_irq(p->irq[k].requested_irq, &p->irq[k]);
 
+err3:
        irq_domain_remove(p->irq_domain);
 err2:
        iounmap(p->iomem);
index 7154b011ddd2f65a65f7079bfe88783c50d0d373..c71914e8f596c3700d8c5a4da2448b32acf8c41f 100644 (file)
@@ -311,7 +311,7 @@ static void s3c_irq_demux(struct irq_desc *desc)
         * and one big domain for the dt case where the subintc
         * starts at hwirq number 32.
         */
-       offset = (intc->domain->of_node) ? 32 : 0;
+       offset = irq_domain_get_of_node(intc->domain) ? 32 : 0;
 
        chained_irq_enter(chip, desc);
 
@@ -342,7 +342,7 @@ static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
                return false;
 
        /* non-dt machines use individual domains */
-       if (!intc->domain->of_node)
+       if (!irq_domain_get_of_node(intc->domain))
                intc_offset = 0;
 
        /* We have a problem that the INTOFFSET register does not always
index c143dd58410c64cc97d5afcc5442a45d5b60ebf9..4ef178078e5bd7ddf388e215eddc1bde3151b0fb 100644 (file)
@@ -8,6 +8,9 @@
  * warranty of any kind, whether express or implied.
  */
 
+#define DRV_NAME       "sunxi-nmi"
+#define pr_fmt(fmt)    DRV_NAME ": " fmt
+
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/io.h>
@@ -96,8 +99,8 @@ static int sunxi_sc_nmi_set_type(struct irq_data *data, unsigned int flow_type)
                break;
        default:
                irq_gc_unlock(gc);
-               pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n",
-                       __func__, data->irq);
+               pr_err("Cannot assign multiple trigger modes to IRQ %d.\n",
+                       data->irq);
                return -EBADR;
        }
 
@@ -130,30 +133,29 @@ static int __init sunxi_sc_nmi_irq_init(struct device_node *node,
 
        domain = irq_domain_add_linear(node, 1, &irq_generic_chip_ops, NULL);
        if (!domain) {
-               pr_err("%s: Could not register interrupt domain.\n", node->name);
+               pr_err("Could not register interrupt domain.\n");
                return -ENOMEM;
        }
 
-       ret = irq_alloc_domain_generic_chips(domain, 1, 2, node->name,
+       ret = irq_alloc_domain_generic_chips(domain, 1, 2, DRV_NAME,
                                             handle_fasteoi_irq, clr, 0,
                                             IRQ_GC_INIT_MASK_CACHE);
        if (ret) {
-                pr_err("%s: Could not allocate generic interrupt chip.\n",
-                        node->name);
-                goto fail_irqd_remove;
+               pr_err("Could not allocate generic interrupt chip.\n");
+               goto fail_irqd_remove;
        }
 
        irq = irq_of_parse_and_map(node, 0);
        if (irq <= 0) {
-               pr_err("%s: unable to parse irq\n", node->name);
+               pr_err("unable to parse irq\n");
                ret = -EINVAL;
                goto fail_irqd_remove;
        }
 
        gc = irq_get_domain_generic_chip(domain, 0);
-       gc->reg_base = of_iomap(node, 0);
+       gc->reg_base = of_io_request_and_map(node, 0, of_node_full_name(node));
        if (!gc->reg_base) {
-               pr_err("%s: unable to map resource\n", node->name);
+               pr_err("unable to map resource\n");
                ret = -ENOMEM;
                goto fail_irqd_remove;
        }
index fd88e687791aa8cc5a4249f550b2caa161eef46b..121ec301372e69cbeca0bff90f41fc0653f0012a 100644 (file)
@@ -221,41 +221,43 @@ static struct irq_chip tegra_ictlr_chip = {
 #endif
 };
 
-static int tegra_ictlr_domain_xlate(struct irq_domain *domain,
-                                   struct device_node *controller,
-                                   const u32 *intspec,
-                                   unsigned int intsize,
-                                   unsigned long *out_hwirq,
-                                   unsigned int *out_type)
+static int tegra_ictlr_domain_translate(struct irq_domain *d,
+                                       struct irq_fwspec *fwspec,
+                                       unsigned long *hwirq,
+                                       unsigned int *type)
 {
-       if (domain->of_node != controller)
-               return -EINVAL; /* Shouldn't happen, really... */
-       if (intsize != 3)
-               return -EINVAL; /* Not GIC compliant */
-       if (intspec[0] != GIC_SPI)
-               return -EINVAL; /* No PPI should point to this domain */
+       if (is_of_node(fwspec->fwnode)) {
+               if (fwspec->param_count != 3)
+                       return -EINVAL;
 
-       *out_hwirq = intspec[1];
-       *out_type = intspec[2];
-       return 0;
+               /* No PPI should point to this domain */
+               if (fwspec->param[0] != 0)
+                       return -EINVAL;
+
+               *hwirq = fwspec->param[1];
+               *type = fwspec->param[2];
+               return 0;
+       }
+
+       return -EINVAL;
 }
 
 static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
                                    unsigned int virq,
                                    unsigned int nr_irqs, void *data)
 {
-       struct of_phandle_args *args = data;
-       struct of_phandle_args parent_args;
+       struct irq_fwspec *fwspec = data;
+       struct irq_fwspec parent_fwspec;
        struct tegra_ictlr_info *info = domain->host_data;
        irq_hw_number_t hwirq;
        unsigned int i;
 
-       if (args->args_count != 3)
+       if (fwspec->param_count != 3)
                return -EINVAL; /* Not GIC compliant */
-       if (args->args[0] != GIC_SPI)
+       if (fwspec->param[0] != GIC_SPI)
                return -EINVAL; /* No PPI should point to this domain */
 
-       hwirq = args->args[1];
+       hwirq = fwspec->param[1];
        if (hwirq >= (num_ictlrs * 32))
                return -EINVAL;
 
@@ -267,9 +269,10 @@ static int tegra_ictlr_domain_alloc(struct irq_domain *domain,
                                              info->base[ictlr]);
        }
 
-       parent_args = *args;
-       parent_args.np = domain->parent->of_node;
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args);
+       parent_fwspec = *fwspec;
+       parent_fwspec.fwnode = domain->parent->fwnode;
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+                                           &parent_fwspec);
 }
 
 static void tegra_ictlr_domain_free(struct irq_domain *domain,
@@ -285,9 +288,9 @@ static void tegra_ictlr_domain_free(struct irq_domain *domain,
 }
 
 static const struct irq_domain_ops tegra_ictlr_domain_ops = {
-       .xlate  = tegra_ictlr_domain_xlate,
-       .alloc  = tegra_ictlr_domain_alloc,
-       .free   = tegra_ictlr_domain_free,
+       .translate      = tegra_ictlr_domain_translate,
+       .alloc          = tegra_ictlr_domain_alloc,
+       .free           = tegra_ictlr_domain_free,
 };
 
 static int __init tegra_ictlr_init(struct device_node *node,
index 2c22558864012efe6425ab1f058ea0e07a9f0728..56b5e3cb9de2c4f2c050fcad42b9f91419e92b81 100644 (file)
@@ -130,35 +130,51 @@ static int vf610_mscm_ir_domain_alloc(struct irq_domain *domain, unsigned int vi
 {
        int i;
        irq_hw_number_t hwirq;
-       struct of_phandle_args *irq_data = arg;
-       struct of_phandle_args gic_data;
+       struct irq_fwspec *fwspec = arg;
+       struct irq_fwspec parent_fwspec;
 
-       if (irq_data->args_count != 2)
+       if (!irq_domain_get_of_node(domain->parent))
                return -EINVAL;
 
-       hwirq = irq_data->args[0];
+       if (fwspec->param_count != 2)
+               return -EINVAL;
+
+       hwirq = fwspec->param[0];
        for (i = 0; i < nr_irqs; i++)
                irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
                                              &vf610_mscm_ir_irq_chip,
                                              domain->host_data);
 
-       gic_data.np = domain->parent->of_node;
+       parent_fwspec.fwnode = domain->parent->fwnode;
 
        if (mscm_ir_data->is_nvic) {
-               gic_data.args_count = 1;
-               gic_data.args[0] = irq_data->args[0];
+               parent_fwspec.param_count = 1;
+               parent_fwspec.param[0] = fwspec->param[0];
        } else {
-               gic_data.args_count = 3;
-               gic_data.args[0] = GIC_SPI;
-               gic_data.args[1] = irq_data->args[0];
-               gic_data.args[2] = irq_data->args[1];
+               parent_fwspec.param_count = 3;
+               parent_fwspec.param[0] = GIC_SPI;
+               parent_fwspec.param[1] = fwspec->param[0];
+               parent_fwspec.param[2] = fwspec->param[1];
        }
 
-       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data);
+       return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs,
+                                           &parent_fwspec);
+}
+
+static int vf610_mscm_ir_domain_translate(struct irq_domain *d,
+                                         struct irq_fwspec *fwspec,
+                                         unsigned long *hwirq,
+                                         unsigned int *type)
+{
+       if (WARN_ON(fwspec->param_count < 2))
+               return -EINVAL;
+       *hwirq = fwspec->param[0];
+       *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+       return 0;
 }
 
 static const struct irq_domain_ops mscm_irq_domain_ops = {
-       .xlate = irq_domain_xlate_twocell,
+       .translate = vf610_mscm_ir_domain_translate,
        .alloc = vf610_mscm_ir_domain_alloc,
        .free = irq_domain_free_irqs_common,
 };
@@ -205,7 +221,8 @@ static int __init vf610_mscm_ir_of_init(struct device_node *node,
                goto out_unmap;
        }
 
-       if (of_device_is_compatible(domain->parent->of_node, "arm,armv7m-nvic"))
+       if (of_device_is_compatible(irq_domain_get_of_node(domain->parent),
+                                   "arm,armv7m-nvic"))
                mscm_ir_data->is_nvic = true;
 
        cpu_pm_register_notifier(&mscm_ir_notifier_block);
index 55317fa9c9dca32557c40b1735ac2c2bec5d4714..0baf626da56ac5b142d75367a7cfdb52747c24ce 100644 (file)
@@ -579,22 +579,187 @@ err:
        }
 }
 
+static u32 __of_msi_map_rid(struct device *dev, struct device_node **np,
+                           u32 rid_in)
+{
+       struct device *parent_dev;
+       struct device_node *msi_controller_node;
+       struct device_node *msi_np = *np;
+       u32 map_mask, masked_rid, rid_base, msi_base, rid_len, phandle;
+       int msi_map_len;
+       bool matched;
+       u32 rid_out = rid_in;
+       const __be32 *msi_map = NULL;
+
+       /*
+        * Walk up the device parent links looking for one with a
+        * "msi-map" property.
+        */
+       for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
+               if (!parent_dev->of_node)
+                       continue;
+
+               msi_map = of_get_property(parent_dev->of_node,
+                                         "msi-map", &msi_map_len);
+               if (!msi_map)
+                       continue;
+
+               if (msi_map_len % (4 * sizeof(__be32))) {
+                       dev_err(parent_dev, "Error: Bad msi-map length: %d\n",
+                               msi_map_len);
+                       return rid_out;
+               }
+               /* We have a good parent_dev and msi_map, let's use them. */
+               break;
+       }
+       if (!msi_map)
+               return rid_out;
+
+       /* The default is to select all bits. */
+       map_mask = 0xffffffff;
+
+       /*
+        * Can be overridden by "msi-map-mask" property.  If
+        * of_property_read_u32() fails, the default is used.
+        */
+       of_property_read_u32(parent_dev->of_node, "msi-map-mask", &map_mask);
+
+       masked_rid = map_mask & rid_in;
+       matched = false;
+       while (!matched && msi_map_len >= 4 * sizeof(__be32)) {
+               rid_base = be32_to_cpup(msi_map + 0);
+               phandle = be32_to_cpup(msi_map + 1);
+               msi_base = be32_to_cpup(msi_map + 2);
+               rid_len = be32_to_cpup(msi_map + 3);
+
+               msi_controller_node = of_find_node_by_phandle(phandle);
+
+               matched = (masked_rid >= rid_base &&
+                          masked_rid < rid_base + rid_len);
+               if (msi_np)
+                       matched &= msi_np == msi_controller_node;
+
+               if (matched && !msi_np) {
+                       *np = msi_np = msi_controller_node;
+                       break;
+               }
+
+               of_node_put(msi_controller_node);
+               msi_map_len -= 4 * sizeof(__be32);
+               msi_map += 4;
+       }
+       if (!matched)
+               return rid_out;
+
+       rid_out = masked_rid + msi_base;
+       dev_dbg(dev,
+               "msi-map at: %s, using mask %08x, rid-base: %08x, msi-base: %08x, length: %08x, rid: %08x -> %08x\n",
+               dev_name(parent_dev), map_mask, rid_base, msi_base,
+               rid_len, rid_in, rid_out);
+
+       return rid_out;
+}
+
 /**
- * of_msi_configure - Set the msi_domain field of a device
- * @dev: device structure to associate with an MSI irq domain
- * @np: device node for that device
+ * of_msi_map_rid - Map a MSI requester ID for a device.
+ * @dev: device for which the mapping is to be done.
+ * @msi_np: device node of the expected msi controller.
+ * @rid_in: unmapped MSI requester ID for the device.
+ *
+ * Walk up the device hierarchy looking for devices with a "msi-map"
+ * property.  If found, apply the mapping to @rid_in.
+ *
+ * Returns the mapped MSI requester ID.
  */
-void of_msi_configure(struct device *dev, struct device_node *np)
+u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in)
+{
+       return __of_msi_map_rid(dev, &msi_np, rid_in);
+}
+
+static struct irq_domain *__of_get_msi_domain(struct device_node *np,
+                                             enum irq_domain_bus_token token)
+{
+       struct irq_domain *d;
+
+       d = irq_find_matching_host(np, token);
+       if (!d)
+               d = irq_find_host(np);
+
+       return d;
+}
+
+/**
+ * of_msi_map_get_device_domain - Use msi-map to find the relevant MSI domain
+ * @dev: device for which the mapping is to be done.
+ * @rid: Requester ID for the device.
+ *
+ * Walk up the device hierarchy looking for devices with a "msi-map"
+ * property.
+ *
+ * Returns: the MSI domain for this device (or NULL on failure)
+ */
+struct irq_domain *of_msi_map_get_device_domain(struct device *dev, u32 rid)
+{
+       struct device_node *np = NULL;
+
+       __of_msi_map_rid(dev, &np, rid);
+       return __of_get_msi_domain(np, DOMAIN_BUS_PCI_MSI);
+}
+
+/**
+ * of_msi_get_domain - Use msi-parent to find the relevant MSI domain
+ * @dev: device for which the domain is requested
+ * @np: device node for @dev
+ * @token: bus type for this domain
+ *
+ * Parse the msi-parent property (both the simple and the complex
+ * versions), and returns the corresponding MSI domain.
+ *
+ * Returns: the MSI domain for this device (or NULL on failure).
+ */
+struct irq_domain *of_msi_get_domain(struct device *dev,
+                                    struct device_node *np,
+                                    enum irq_domain_bus_token token)
 {
        struct device_node *msi_np;
        struct irq_domain *d;
 
+       /* Check for a single msi-parent property */
        msi_np = of_parse_phandle(np, "msi-parent", 0);
-       if (!msi_np)
-               return;
+       if (msi_np && !of_property_read_bool(msi_np, "#msi-cells")) {
+               d = __of_get_msi_domain(msi_np, token);
+               if (!d)
+                       of_node_put(msi_np);
+               return d;
+       }
 
-       d = irq_find_matching_host(msi_np, DOMAIN_BUS_PLATFORM_MSI);
-       if (!d)
-               d = irq_find_host(msi_np);
-       dev_set_msi_domain(dev, d);
+       if (token == DOMAIN_BUS_PLATFORM_MSI) {
+               /* Check for the complex msi-parent version */
+               struct of_phandle_args args;
+               int index = 0;
+
+               while (!of_parse_phandle_with_args(np, "msi-parent",
+                                                  "#msi-cells",
+                                                  index, &args)) {
+                       d = __of_get_msi_domain(args.np, token);
+                       if (d)
+                               return d;
+
+                       of_node_put(args.np);
+                       index++;
+               }
+       }
+
+       return NULL;
+}
+
+/**
+ * of_msi_configure - Set the msi_domain field of a device
+ * @dev: device structure to associate with an MSI irq domain
+ * @np: device node for that device
+ */
+void of_msi_configure(struct device *dev, struct device_node *np)
+{
+       dev_set_msi_domain(dev,
+                          of_msi_get_domain(dev, np, DOMAIN_BUS_PLATFORM_MSI));
 }
index e491681daf22681c7bd541ad75d078edfa32ea1c..a6456b5782692a5d5559b9ef59226d2fbce99e3f 100644 (file)
@@ -256,7 +256,7 @@ static int xgene_allocate_domains(struct xgene_msi *msi)
        if (!msi->inner_domain)
                return -ENOMEM;
 
-       msi->msi_domain = pci_msi_create_irq_domain(msi->node,
+       msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(msi->node),
                                                    &xgene_msi_domain_info,
                                                    msi->inner_domain);
 
index 4a7da3c3e0353c3c746e9b10be9a093c8d085920..45a51486d080a54fa987eaa230106df5895b3b09 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/irqdomain.h>
+#include <linux/of_irq.h>
 
 #include "pci.h"
 
@@ -1250,8 +1251,8 @@ static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info)
 }
 
 /**
- * pci_msi_create_irq_domain - Creat a MSI interrupt domain
- * @node:      Optional device-tree node of the interrupt controller
+ * pci_msi_create_irq_domain - Create a MSI interrupt domain
+ * @fwnode:    Optional fwnode of the interrupt controller
  * @info:      MSI domain info
  * @parent:    Parent irq domain
  *
@@ -1260,7 +1261,7 @@ static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info)
  * Returns:
  * A domain pointer or NULL in case of failure.
  */
-struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
+struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
                                             struct msi_domain_info *info,
                                             struct irq_domain *parent)
 {
@@ -1271,7 +1272,7 @@ struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
        if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
                pci_msi_domain_update_chip_ops(info);
 
-       domain = msi_create_irq_domain(node, info, parent);
+       domain = msi_create_irq_domain(fwnode, info, parent);
        if (!domain)
                return NULL;
 
@@ -1307,14 +1308,14 @@ void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev)
 
 /**
  * pci_msi_create_default_irq_domain - Create a default MSI interrupt domain
- * @node:      Optional device-tree node of the interrupt controller
+ * @fwnode:    Optional fwnode of the interrupt controller
  * @info:      MSI domain info
  * @parent:    Parent irq domain
  *
  * Returns: A domain pointer or NULL in case of failure. If successful
  * the default PCI/MSI irqdomain pointer is updated.
  */
-struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
+struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnode,
                struct msi_domain_info *info, struct irq_domain *parent)
 {
        struct irq_domain *domain;
@@ -1324,11 +1325,59 @@ struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
                pr_err("PCI: default irq domain for PCI MSI has already been created.\n");
                domain = NULL;
        } else {
-               domain = pci_msi_create_irq_domain(node, info, parent);
+               domain = pci_msi_create_irq_domain(fwnode, info, parent);
                pci_msi_default_domain = domain;
        }
        mutex_unlock(&pci_msi_domain_lock);
 
        return domain;
 }
+
+static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data)
+{
+       u32 *pa = data;
+
+       *pa = alias;
+       return 0;
+}
+/**
+ * pci_msi_domain_get_msi_rid - Get the MSI requester id (RID)
+ * @domain:    The interrupt domain
+ * @pdev:      The PCI device.
+ *
+ * The RID for a device is formed from the alias, with a firmware
+ * supplied mapping applied
+ *
+ * Returns: The RID.
+ */
+u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
+{
+       struct device_node *of_node;
+       u32 rid = 0;
+
+       pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
+
+       of_node = irq_domain_get_of_node(domain);
+       if (of_node)
+               rid = of_msi_map_rid(&pdev->dev, of_node, rid);
+
+       return rid;
+}
+
+/**
+ * pci_msi_get_device_domain - Get the MSI domain for a given PCI device
+ * @pdev:      The PCI device
+ *
+ * Use the firmware data to find a device-specific MSI domain
+ * (i.e. not one that is ste as a default).
+ *
+ * Returns: The coresponding MSI domain or NULL if none has been found.
+ */
+struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
+{
+       u32 rid = 0;
+
+       pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
+       return of_msi_map_get_device_domain(&pdev->dev, rid);
+}
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
index 2e99a500cb83fe48ac065333fad036186935aa4f..e112da11630ee46803fc14d02f979c079c70ba79 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/of.h>
+#include <linux/of_irq.h>
 #include <linux/of_pci.h>
 #include "pci.h"
 
@@ -64,27 +65,25 @@ struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus)
 struct irq_domain *pci_host_bridge_of_msi_domain(struct pci_bus *bus)
 {
 #ifdef CONFIG_IRQ_DOMAIN
-       struct device_node *np;
        struct irq_domain *d;
 
        if (!bus->dev.of_node)
                return NULL;
 
        /* Start looking for a phandle to an MSI controller. */
-       np = of_parse_phandle(bus->dev.of_node, "msi-parent", 0);
+       d = of_msi_get_domain(&bus->dev, bus->dev.of_node, DOMAIN_BUS_PCI_MSI);
+       if (d)
+               return d;
 
        /*
         * If we don't have an msi-parent property, look for a domain
         * directly attached to the host bridge.
         */
-       if (!np)
-               np = bus->dev.of_node;
-
-       d = irq_find_matching_host(np, DOMAIN_BUS_PCI_MSI);
+       d = irq_find_matching_host(bus->dev.of_node, DOMAIN_BUS_PCI_MSI);
        if (d)
                return d;
 
-       return irq_find_host(np);
+       return irq_find_host(bus->dev.of_node);
 #else
        return NULL;
 #endif
index 8361d27e5ecad82ff9767c3d55a019a66f884046..f14a970b61fa59bf00b6e97474da3ec024ba315e 100644 (file)
@@ -1622,15 +1622,48 @@ static void pci_init_capabilities(struct pci_dev *dev)
        pci_enable_acs(dev);
 }
 
+/*
+ * This is the equivalent of pci_host_bridge_msi_domain that acts on
+ * devices. Firmware interfaces that can select the MSI domain on a
+ * per-device basis should be called from here.
+ */
+static struct irq_domain *pci_dev_msi_domain(struct pci_dev *dev)
+{
+       struct irq_domain *d;
+
+       /*
+        * If a domain has been set through the pcibios_add_device
+        * callback, then this is the one (platform code knows best).
+        */
+       d = dev_get_msi_domain(&dev->dev);
+       if (d)
+               return d;
+
+       /*
+        * Let's see if we have a firmware interface able to provide
+        * the domain.
+        */
+       d = pci_msi_get_device_domain(dev);
+       if (d)
+               return d;
+
+       return NULL;
+}
+
 static void pci_set_msi_domain(struct pci_dev *dev)
 {
+       struct irq_domain *d;
+
        /*
-        * If no domain has been set through the pcibios_add_device
-        * callback, inherit the default from the bus device.
+        * If the platform or firmware interfaces cannot supply a
+        * device-specific MSI domain, then inherit the default domain
+        * from the host bridge itself.
         */
-       if (!dev_get_msi_domain(&dev->dev))
-               dev_set_msi_domain(&dev->dev,
-                                  dev_get_msi_domain(&dev->bus->dev));
+       d = pci_dev_msi_domain(dev);
+       if (!d)
+               d = dev_get_msi_domain(&dev->bus->dev);
+
+       dev_set_msi_domain(&dev->dev, d);
 }
 
 void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
index 4a3cf9ba152f6b8060e3c7709550dfdc7ec9fe17..fb36810ae89a40a1aae2729e5767191c82840c50 100644 (file)
@@ -657,7 +657,7 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
                "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n",
                intspec[0], intspec[1], intspec[2]);
 
-       if (d->of_node != controller)
+       if (irq_domain_get_of_node(d) != controller)
                return -EINVAL;
        if (intsize != 4)
                return -EINVAL;
index 4e14dac282bb6c963440593090bc060afecf8130..6a3538ef72753cd5ef70ac23605cf1484f852774 100644 (file)
@@ -282,7 +282,7 @@ struct vgic_v2_cpu_if {
 };
 
 struct vgic_v3_cpu_if {
-#ifdef CONFIG_ARM_GIC_V3
+#ifdef CONFIG_KVM_ARM_VGIC_V3
        u32             vgic_hcr;
        u32             vgic_vmcr;
        u32             vgic_sre;       /* Restored only, change ignored */
@@ -364,7 +364,7 @@ void kvm_vgic_set_phys_irq_active(struct irq_phys_map *map, bool active);
 int vgic_v2_probe(struct device_node *vgic_node,
                  const struct vgic_ops **ops,
                  const struct vgic_params **params);
-#ifdef CONFIG_ARM_GIC_V3
+#ifdef CONFIG_KVM_ARM_VGIC_V3
 int vgic_v3_probe(struct device_node *vgic_node,
                  const struct vgic_ops **ops,
                  const struct vgic_params **params);
index 43856d19cf4d8abc8942849202485fe76244135d..d863e12bbead6eb6161ffce64b791e73dca7fd27 100644 (file)
@@ -201,6 +201,9 @@ int acpi_register_gsi (struct device *dev, u32 gsi, int triggering, int polarity
 int acpi_gsi_to_irq (u32 gsi, unsigned int *irq);
 int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi);
 
+void acpi_set_irq_model(enum acpi_irq_model_id model,
+                       struct fwnode_handle *fwnode);
+
 #ifdef CONFIG_X86_IO_APIC
 extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity);
 #else
index 0408545bce42403ecd50ecbeed8bffb78b336274..37ec668546ab21897393bcebc1ba548796db7994 100644 (file)
@@ -17,6 +17,7 @@ enum fwnode_type {
        FWNODE_OF,
        FWNODE_ACPI,
        FWNODE_PDATA,
+       FWNODE_IRQCHIP,
 };
 
 struct fwnode_handle {
index be7e75c945e97b07d5f248ded5c1e0d9240756ad..ad16809c85961e16ebc804280e4a78d66cf77634 100644 (file)
@@ -102,6 +102,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
  * @flags:     flags (see IRQF_* above)
  * @thread_fn: interrupt handler function for threaded interrupts
  * @thread:    thread pointer for threaded interrupts
+ * @secondary: pointer to secondary irqaction (force threading)
  * @thread_flags:      flags related to @thread
  * @thread_mask:       bitmask for keeping track of @thread activity
  * @dir:       pointer to the proc/irq/NN/name entry
@@ -113,6 +114,7 @@ struct irqaction {
        struct irqaction        *next;
        irq_handler_t           thread_fn;
        struct task_struct      *thread;
+       struct irqaction        *secondary;
        unsigned int            irq;
        unsigned int            flags;
        unsigned long           thread_flags;
index 11bf09288ddb08ab277b6f213696c1216348155c..3c1c96786248cb02d2d9792cce455892323ef275 100644 (file)
@@ -67,11 +67,12 @@ enum irqchip_irq_state;
  *                               request/setup_irq()
  * IRQ_NO_BALANCING            - Interrupt cannot be balanced (affinity set)
  * IRQ_MOVE_PCNTXT             - Interrupt can be migrated from process context
- * IRQ_NESTED_TRHEAD           - Interrupt nests into another thread
+ * IRQ_NESTED_THREAD           - Interrupt nests into another thread
  * IRQ_PER_CPU_DEVID           - Dev_id is a per-cpu variable
  * IRQ_IS_POLLED               - Always polled by another interrupt. Exclude
  *                               it from the spurious interrupt detection
  *                               mechanism and from core side polling.
+ * IRQ_DISABLE_UNLAZY          - Disable lazy irq disable
  */
 enum {
        IRQ_TYPE_NONE           = 0x00000000,
@@ -97,13 +98,14 @@ enum {
        IRQ_NOTHREAD            = (1 << 16),
        IRQ_PER_CPU_DEVID       = (1 << 17),
        IRQ_IS_POLLED           = (1 << 18),
+       IRQ_DISABLE_UNLAZY      = (1 << 19),
 };
 
 #define IRQF_MODIFY_MASK       \
        (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
         IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
         IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID | \
-        IRQ_IS_POLLED)
+        IRQ_IS_POLLED | IRQ_DISABLE_UNLAZY)
 
 #define IRQ_NO_BALANCING_MASK  (IRQ_PER_CPU | IRQ_NO_BALANCING)
 
@@ -297,21 +299,6 @@ static inline void irqd_clr_forwarded_to_vcpu(struct irq_data *d)
        __irqd_to_state(d) &= ~IRQD_FORWARDED_TO_VCPU;
 }
 
-/*
- * Functions for chained handlers which can be enabled/disabled by the
- * standard disable_irq/enable_irq calls. Must be called with
- * irq_desc->lock held.
- */
-static inline void irqd_set_chained_irq_inprogress(struct irq_data *d)
-{
-       __irqd_to_state(d) |= IRQD_IRQ_INPROGRESS;
-}
-
-static inline void irqd_clr_chained_irq_inprogress(struct irq_data *d)
-{
-       __irqd_to_state(d) &= ~IRQD_IRQ_INPROGRESS;
-}
-
 static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
 {
        return d->hwirq;
@@ -452,6 +439,8 @@ extern int irq_set_affinity_locked(struct irq_data *data,
                                   const struct cpumask *cpumask, bool force);
 extern int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info);
 
+extern void irq_migrate_all_off_this_cpu(void);
+
 #if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_PENDING_IRQ)
 void irq_move_irq(struct irq_data *data);
 void irq_move_masked_irq(struct irq_data *data);
index 9eeeb9589acfc35baed79a89d7d3f0c1be134cf3..c9ae0c6ec050569fc592b6f07b3ce1fa052bcc59 100644 (file)
@@ -18,8 +18,6 @@
 #ifndef __LINUX_IRQCHIP_ARM_GIC_V3_H
 #define __LINUX_IRQCHIP_ARM_GIC_V3_H
 
-#include <asm/sysreg.h>
-
 /*
  * Distributor registers. We assume we're running non-secure, with ARE
  * being set. Secure-only and non-ARE registers are not described.
 #define GITS_BASER_PAGE_SIZE_16K       (1UL << GITS_BASER_PAGE_SIZE_SHIFT)
 #define GITS_BASER_PAGE_SIZE_64K       (2UL << GITS_BASER_PAGE_SIZE_SHIFT)
 #define GITS_BASER_PAGE_SIZE_MASK      (3UL << GITS_BASER_PAGE_SIZE_SHIFT)
+#define GITS_BASER_PAGES_MAX           256
 
 #define GITS_BASER_TYPE_NONE           0
 #define GITS_BASER_TYPE_DEVICE         1
 /*
  * Hypervisor interface registers (SRE only)
  */
-#define ICH_LR_VIRTUAL_ID_MASK         ((1UL << 32) - 1)
-
-#define ICH_LR_EOI                     (1UL << 41)
-#define ICH_LR_GROUP                   (1UL << 60)
-#define ICH_LR_HW                      (1UL << 61)
-#define ICH_LR_STATE                   (3UL << 62)
-#define ICH_LR_PENDING_BIT             (1UL << 62)
-#define ICH_LR_ACTIVE_BIT              (1UL << 63)
+#define ICH_LR_VIRTUAL_ID_MASK         ((1ULL << 32) - 1)
+
+#define ICH_LR_EOI                     (1ULL << 41)
+#define ICH_LR_GROUP                   (1ULL << 60)
+#define ICH_LR_HW                      (1ULL << 61)
+#define ICH_LR_STATE                   (3ULL << 62)
+#define ICH_LR_PENDING_BIT             (1ULL << 62)
+#define ICH_LR_ACTIVE_BIT              (1ULL << 63)
 #define ICH_LR_PHYS_ID_SHIFT           32
-#define ICH_LR_PHYS_ID_MASK            (0x3ffUL << ICH_LR_PHYS_ID_SHIFT)
+#define ICH_LR_PHYS_ID_MASK            (0x3ffULL << ICH_LR_PHYS_ID_SHIFT)
 
 #define ICH_MISR_EOI                   (1 << 0)
 #define ICH_MISR_U                     (1 << 1)
 #define ICH_VMCR_PMR_SHIFT             24
 #define ICH_VMCR_PMR_MASK              (0xffUL << ICH_VMCR_PMR_SHIFT)
 
-#define ICC_EOIR1_EL1                  sys_reg(3, 0, 12, 12, 1)
-#define ICC_DIR_EL1                    sys_reg(3, 0, 12, 11, 1)
-#define ICC_IAR1_EL1                   sys_reg(3, 0, 12, 12, 0)
-#define ICC_SGI1R_EL1                  sys_reg(3, 0, 12, 11, 5)
-#define ICC_PMR_EL1                    sys_reg(3, 0, 4, 6, 0)
-#define ICC_CTLR_EL1                   sys_reg(3, 0, 12, 12, 4)
-#define ICC_SRE_EL1                    sys_reg(3, 0, 12, 12, 5)
-#define ICC_GRPEN1_EL1                 sys_reg(3, 0, 12, 12, 7)
-
 #define ICC_IAR1_EL1_SPURIOUS          0x3ff
 
-#define ICC_SRE_EL2                    sys_reg(3, 4, 12, 9, 5)
-
 #define ICC_SRE_EL2_SRE                        (1 << 0)
 #define ICC_SRE_EL2_ENABLE             (1 << 3)
 
 #define ICC_SGI1R_AFFINITY_3_SHIFT     48
 #define ICC_SGI1R_AFFINITY_3_MASK      (0xffULL << ICC_SGI1R_AFFINITY_1_SHIFT)
 
-/*
- * System register definitions
- */
-#define ICH_VSEIR_EL2                  sys_reg(3, 4, 12, 9, 4)
-#define ICH_HCR_EL2                    sys_reg(3, 4, 12, 11, 0)
-#define ICH_VTR_EL2                    sys_reg(3, 4, 12, 11, 1)
-#define ICH_MISR_EL2                   sys_reg(3, 4, 12, 11, 2)
-#define ICH_EISR_EL2                   sys_reg(3, 4, 12, 11, 3)
-#define ICH_ELSR_EL2                   sys_reg(3, 4, 12, 11, 5)
-#define ICH_VMCR_EL2                   sys_reg(3, 4, 12, 11, 7)
-
-#define __LR0_EL2(x)                   sys_reg(3, 4, 12, 12, x)
-#define __LR8_EL2(x)                   sys_reg(3, 4, 12, 13, x)
-
-#define ICH_LR0_EL2                    __LR0_EL2(0)
-#define ICH_LR1_EL2                    __LR0_EL2(1)
-#define ICH_LR2_EL2                    __LR0_EL2(2)
-#define ICH_LR3_EL2                    __LR0_EL2(3)
-#define ICH_LR4_EL2                    __LR0_EL2(4)
-#define ICH_LR5_EL2                    __LR0_EL2(5)
-#define ICH_LR6_EL2                    __LR0_EL2(6)
-#define ICH_LR7_EL2                    __LR0_EL2(7)
-#define ICH_LR8_EL2                    __LR8_EL2(0)
-#define ICH_LR9_EL2                    __LR8_EL2(1)
-#define ICH_LR10_EL2                   __LR8_EL2(2)
-#define ICH_LR11_EL2                   __LR8_EL2(3)
-#define ICH_LR12_EL2                   __LR8_EL2(4)
-#define ICH_LR13_EL2                   __LR8_EL2(5)
-#define ICH_LR14_EL2                   __LR8_EL2(6)
-#define ICH_LR15_EL2                   __LR8_EL2(7)
-
-#define __AP0Rx_EL2(x)                 sys_reg(3, 4, 12, 8, x)
-#define ICH_AP0R0_EL2                  __AP0Rx_EL2(0)
-#define ICH_AP0R1_EL2                  __AP0Rx_EL2(1)
-#define ICH_AP0R2_EL2                  __AP0Rx_EL2(2)
-#define ICH_AP0R3_EL2                  __AP0Rx_EL2(3)
-
-#define __AP1Rx_EL2(x)                 sys_reg(3, 4, 12, 9, x)
-#define ICH_AP1R0_EL2                  __AP1Rx_EL2(0)
-#define ICH_AP1R1_EL2                  __AP1Rx_EL2(1)
-#define ICH_AP1R2_EL2                  __AP1Rx_EL2(2)
-#define ICH_AP1R3_EL2                  __AP1Rx_EL2(3)
+#include <asm/arch_gicv3.h>
 
 #ifndef __ASSEMBLY__
 
-#include <linux/stringify.h>
-#include <asm/msi.h>
-
 /*
  * We need a value to serve as a irq-type for LPIs. Choose one that will
  * hopefully pique the interest of the reviewer.
@@ -385,23 +329,26 @@ struct rdists {
        u64                     flags;
 };
 
-static inline void gic_write_eoir(u64 irq)
-{
-       asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" (irq));
-       isb();
-}
-
-static inline void gic_write_dir(u64 irq)
-{
-       asm volatile("msr_s " __stringify(ICC_DIR_EL1) ", %0" : : "r" (irq));
-       isb();
-}
-
 struct irq_domain;
 int its_cpu_init(void);
 int its_init(struct device_node *node, struct rdists *rdists,
             struct irq_domain *domain);
 
+static inline bool gic_enable_sre(void)
+{
+       u32 val;
+
+       val = gic_read_sre();
+       if (val & ICC_SRE_EL1_SRE)
+               return true;
+
+       val |= ICC_SRE_EL1_SRE;
+       gic_write_sre(val);
+       val = gic_read_sre();
+
+       return !!(val & ICC_SRE_EL1_SRE);
+}
+
 #endif
 
 #endif
index b8901dfd9e9584ba06de5f585f14c6edc9da8fb5..bae69e5d693c3e60cea1a209fc06d2d33fdfb960 100644 (file)
 
 struct device_node;
 
-void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *,
-                   u32 offset, struct device_node *);
 void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
 int gic_cpu_if_down(unsigned int gic_nr);
 
-static inline void gic_init(unsigned int nr, int start,
-                           void __iomem *dist , void __iomem *cpu)
-{
-       gic_init_bases(nr, start, dist, cpu, 0, NULL);
-}
+void gic_init(unsigned int nr, int start,
+             void __iomem *dist , void __iomem *cpu);
 
 int gicv2m_of_init(struct device_node *node, struct irq_domain *parent);
 
index f644fdb06dd691ba2d218384b1172791100f9aaf..d5e5c5bef28c45e29bff88b175d581c0595b73f9 100644 (file)
@@ -5,9 +5,10 @@
  * helpful for interrupt controllers to implement mapping between hardware
  * irq numbers and the Linux irq number space.
  *
- * irq_domains also have a hook for translating device tree interrupt
- * representation into a hardware irq number that can be mapped back to a
- * Linux irq number without any extra platform support code.
+ * irq_domains also have hooks for translating device tree or other
+ * firmware interrupt representations into a hardware irq number that
+ * can be mapped back to a Linux irq number without any extra platform
+ * support code.
  *
  * Interrupt controller "domain" data structure. This could be defined as a
  * irq domain controller. That is, it handles the mapping between hardware
  * model). It's the domain callbacks that are responsible for setting the
  * irq_chip on a given irq_desc after it's been mapped.
  *
- * The host code and data structures are agnostic to whether or not
- * we use an open firmware device-tree. We do have references to struct
- * device_node in two places: in irq_find_host() to find the host matching
- * a given interrupt controller node, and of course as an argument to its
- * counterpart domain->ops->match() callback. However, those are treated as
- * generic pointers by the core and the fact that it's actually a device-node
- * pointer is purely a convention between callers and implementation. This
- * code could thus be used on other architectures by replacing those two
- * by some sort of arch-specific void * "token" used to identify interrupt
- * controllers.
+ * The host code and data structures use a fwnode_handle pointer to
+ * identify the domain. In some cases, and in order to preserve source
+ * code compatibility, this fwnode pointer is "upgraded" to a DT
+ * device_node. For those firmware infrastructures that do not provide
+ * a unique identifier for an interrupt controller, the irq_domain
+ * code offers a fwnode allocator.
  */
 
 #ifndef _LINUX_IRQDOMAIN_H
@@ -34,6 +31,7 @@
 
 #include <linux/types.h>
 #include <linux/irqhandler.h>
+#include <linux/of.h>
 #include <linux/radix-tree.h>
 
 struct device_node;
@@ -45,6 +43,24 @@ struct irq_data;
 /* Number of irqs reserved for a legacy isa controller */
 #define NUM_ISA_INTERRUPTS     16
 
+#define IRQ_DOMAIN_IRQ_SPEC_PARAMS 16
+
+/**
+ * struct irq_fwspec - generic IRQ specifier structure
+ *
+ * @fwnode:            Pointer to a firmware-specific descriptor
+ * @param_count:       Number of device-specific parameters
+ * @param:             Device-specific parameters
+ *
+ * This structure, directly modeled after of_phandle_args, is used to
+ * pass a device-specific description of an interrupt.
+ */
+struct irq_fwspec {
+       struct fwnode_handle *fwnode;
+       int param_count;
+       u32 param[IRQ_DOMAIN_IRQ_SPEC_PARAMS];
+};
+
 /*
  * Should several domains have the same device node, but serve
  * different purposes (for example one domain is for PCI/MSI, and the
@@ -91,6 +107,8 @@ struct irq_domain_ops {
                     unsigned int nr_irqs);
        void (*activate)(struct irq_domain *d, struct irq_data *irq_data);
        void (*deactivate)(struct irq_domain *d, struct irq_data *irq_data);
+       int (*translate)(struct irq_domain *d, struct irq_fwspec *fwspec,
+                        unsigned long *out_hwirq, unsigned int *out_type);
 #endif
 };
 
@@ -130,7 +148,7 @@ struct irq_domain {
        unsigned int flags;
 
        /* Optional data */
-       struct device_node *of_node;
+       struct fwnode_handle *fwnode;
        enum irq_domain_bus_token bus_token;
        struct irq_domain_chip_generic *gc;
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
@@ -163,11 +181,13 @@ enum {
 
 static inline struct device_node *irq_domain_get_of_node(struct irq_domain *d)
 {
-       return d->of_node;
+       return to_of_node(d->fwnode);
 }
 
 #ifdef CONFIG_IRQ_DOMAIN
-struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
+struct fwnode_handle *irq_domain_alloc_fwnode(void *data);
+void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
+struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    irq_hw_number_t hwirq_max, int direct_max,
                                    const struct irq_domain_ops *ops,
                                    void *host_data);
@@ -182,10 +202,21 @@ 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_host(struct device_node *node,
-                                                enum irq_domain_bus_token bus_token);
+extern struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
+                                                  enum irq_domain_bus_token bus_token);
 extern void irq_set_default_host(struct irq_domain *host);
 
+static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node)
+{
+       return node ? &node->fwnode : NULL;
+}
+
+static inline struct irq_domain *irq_find_matching_host(struct device_node *node,
+                                                       enum irq_domain_bus_token bus_token)
+{
+       return irq_find_matching_fwnode(of_node_to_fwnode(node), bus_token);
+}
+
 static inline struct irq_domain *irq_find_host(struct device_node *node)
 {
        return irq_find_matching_host(node, DOMAIN_BUS_ANY);
@@ -203,14 +234,14 @@ static inline struct irq_domain *irq_domain_add_linear(struct device_node *of_no
                                         const struct irq_domain_ops *ops,
                                         void *host_data)
 {
-       return __irq_domain_add(of_node, size, size, 0, ops, host_data);
+       return __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data);
 }
 static inline struct irq_domain *irq_domain_add_nomap(struct device_node *of_node,
                                         unsigned int max_irq,
                                         const struct irq_domain_ops *ops,
                                         void *host_data)
 {
-       return __irq_domain_add(of_node, 0, max_irq, max_irq, ops, host_data);
+       return __irq_domain_add(of_node_to_fwnode(of_node), 0, max_irq, max_irq, ops, host_data);
 }
 static inline struct irq_domain *irq_domain_add_legacy_isa(
                                struct device_node *of_node,
@@ -224,7 +255,22 @@ static inline struct irq_domain *irq_domain_add_tree(struct device_node *of_node
                                         const struct irq_domain_ops *ops,
                                         void *host_data)
 {
-       return __irq_domain_add(of_node, 0, ~0, 0, ops, host_data);
+       return __irq_domain_add(of_node_to_fwnode(of_node), 0, ~0, 0, ops, host_data);
+}
+
+static inline struct irq_domain *irq_domain_create_linear(struct fwnode_handle *fwnode,
+                                        unsigned int size,
+                                        const struct irq_domain_ops *ops,
+                                        void *host_data)
+{
+       return __irq_domain_add(fwnode, size, size, 0, ops, host_data);
+}
+
+static inline struct irq_domain *irq_domain_create_tree(struct fwnode_handle *fwnode,
+                                        const struct irq_domain_ops *ops,
+                                        void *host_data)
+{
+       return __irq_domain_add(fwnode, 0, ~0, 0, ops, host_data);
 }
 
 extern void irq_domain_remove(struct irq_domain *host);
@@ -239,6 +285,7 @@ extern void irq_domain_disassociate(struct irq_domain *domain,
 
 extern unsigned int irq_create_mapping(struct irq_domain *host,
                                       irq_hw_number_t hwirq);
+extern unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec);
 extern void irq_dispose_mapping(unsigned int virq);
 
 /**
@@ -290,10 +337,23 @@ extern void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
                                void *chip_data, irq_flow_handler_t handler,
                                void *handler_data, const char *handler_name);
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
-extern struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+extern struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
                        unsigned int flags, unsigned int size,
-                       struct device_node *node,
+                       struct fwnode_handle *fwnode,
                        const struct irq_domain_ops *ops, void *host_data);
+
+static inline struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+                                           unsigned int flags,
+                                           unsigned int size,
+                                           struct device_node *node,
+                                           const struct irq_domain_ops *ops,
+                                           void *host_data)
+{
+       return irq_domain_create_hierarchy(parent, flags, size,
+                                          of_node_to_fwnode(node),
+                                          ops, host_data);
+}
+
 extern int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
                                   unsigned int nr_irqs, int node, void *arg,
                                   bool realloc);
index e374e369fb2f4c9eb5ace48679376550d5ee633c..eb1bdcf95f2e0c931a3a5cb599ab06547ff34f3d 100644 (file)
@@ -3,7 +3,7 @@
 
 /**
  * enum irqreturn
- * @IRQ_NONE           interrupt was not from this device
+ * @IRQ_NONE           interrupt was not from this device or was not handled
  * @IRQ_HANDLED                interrupt was handled by this device
  * @IRQ_WAKE_THREAD    handler requests to wake the handler thread
  */
index ad939d0ba816372b975d5c44d9582617b7b618e0..0b4460374020fd98e40248c990eb6110493a21be 100644 (file)
@@ -174,6 +174,7 @@ struct msi_controller {
 struct irq_domain;
 struct irq_chip;
 struct device_node;
+struct fwnode_handle;
 struct msi_domain_info;
 
 /**
@@ -262,7 +263,7 @@ enum {
 int msi_domain_set_affinity(struct irq_data *data, const struct cpumask *mask,
                            bool force);
 
-struct irq_domain *msi_create_irq_domain(struct device_node *of_node,
+struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
                                         struct msi_domain_info *info,
                                         struct irq_domain *parent);
 int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
@@ -270,7 +271,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
 void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev);
 struct msi_domain_info *msi_get_domain_info(struct irq_domain *domain);
 
-struct irq_domain *platform_msi_create_irq_domain(struct device_node *np,
+struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode,
                                                  struct msi_domain_info *info,
                                                  struct irq_domain *parent);
 int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec,
@@ -280,19 +281,26 @@ void platform_msi_domain_free_irqs(struct device *dev);
 
 #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
 void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg);
-struct irq_domain *pci_msi_create_irq_domain(struct device_node *node,
+struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
                                             struct msi_domain_info *info,
                                             struct irq_domain *parent);
 int pci_msi_domain_alloc_irqs(struct irq_domain *domain, struct pci_dev *dev,
                              int nvec, int type);
 void pci_msi_domain_free_irqs(struct irq_domain *domain, struct pci_dev *dev);
-struct irq_domain *pci_msi_create_default_irq_domain(struct device_node *node,
+struct irq_domain *pci_msi_create_default_irq_domain(struct fwnode_handle *fwnode,
                 struct msi_domain_info *info, struct irq_domain *parent);
 
 irq_hw_number_t pci_msi_domain_calc_hwirq(struct pci_dev *dev,
                                          struct msi_desc *desc);
 int pci_msi_domain_check_cap(struct irq_domain *domain,
                             struct msi_domain_info *info, struct device *dev);
+u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev);
+struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev);
+#else
+static inline struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
+{
+       return NULL;
+}
 #endif /* CONFIG_PCI_MSI_IRQ_DOMAIN */
 
 #endif /* LINUX_MSI_H */
index 4bcbd586a67296081fadb219ee96f32d7fcb65b2..65d969246a4d02e1ee8854451c923d5003941d31 100644 (file)
@@ -46,6 +46,11 @@ extern int of_irq_get(struct device_node *dev, int index);
 extern int of_irq_get_byname(struct device_node *dev, const char *name);
 extern int of_irq_to_resource_table(struct device_node *dev,
                struct resource *res, int nr_irqs);
+extern struct irq_domain *of_msi_get_domain(struct device *dev,
+                                           struct device_node *np,
+                                           enum irq_domain_bus_token token);
+extern struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
+                                                      u32 rid);
 #else
 static inline int of_irq_count(struct device_node *dev)
 {
@@ -64,6 +69,17 @@ static inline int of_irq_to_resource_table(struct device_node *dev,
 {
        return 0;
 }
+static inline struct irq_domain *of_msi_get_domain(struct device *dev,
+                                                  struct device_node *np,
+                                                  enum irq_domain_bus_token token)
+{
+       return NULL;
+}
+static inline struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
+                                                             u32 rid)
+{
+       return NULL;
+}
 #endif
 
 #if defined(CONFIG_OF)
@@ -75,6 +91,7 @@ static inline int of_irq_to_resource_table(struct device_node *dev,
 extern unsigned int irq_of_parse_and_map(struct device_node *node, int index);
 extern struct device_node *of_irq_find_parent(struct device_node *child);
 extern void of_msi_configure(struct device *dev, struct device_node *np);
+u32 of_msi_map_rid(struct device *dev, struct device_node *msi_np, u32 rid_in);
 
 #else /* !CONFIG_OF */
 static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
@@ -87,6 +104,12 @@ static inline void *of_irq_find_parent(struct device_node *child)
 {
        return NULL;
 }
+
+static inline u32 of_msi_map_rid(struct device *dev,
+                                struct device_node *msi_np, u32 rid_in)
+{
+       return rid_in;
+}
 #endif /* !CONFIG_OF */
 
 #endif /* __OF_IRQ_H */
index 9a76e3beda5423b7743f1d67a0945755b771c977..3b48dab801648dc1b7ef9962b2dd8d8067d9adf8 100644 (file)
@@ -30,6 +30,10 @@ config GENERIC_IRQ_LEGACY_ALLOC_HWIRQ
 config GENERIC_PENDING_IRQ
        bool
 
+# Support for generic irq migrating off cpu before the cpu is offline.
+config GENERIC_IRQ_MIGRATION
+       bool
+
 # Alpha specific irq affinity mechanism
 config AUTO_IRQ_AFFINITY
        bool
index d12123526e2b48b07dbf63274aa03f31dcd215e8..2fc9cbdf35b6221385ab1f8393098523cc4c43cd 100644 (file)
@@ -5,5 +5,6 @@ obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
 obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
+obj-$(CONFIG_GENERIC_IRQ_MIGRATION) += cpuhotplug.o
 obj-$(CONFIG_PM_SLEEP) += pm.o
 obj-$(CONFIG_GENERIC_MSI_IRQ) += msi.o
index e28169dd1c36a51f92523208984ec4c7a4b5b251..15206453b12aab09cf96dd09cc3fa2da92fd9c14 100644 (file)
 
 #include "internals.h"
 
+static irqreturn_t bad_chained_irq(int irq, void *dev_id)
+{
+       WARN_ONCE(1, "Chained irq %d should not call an action\n", irq);
+       return IRQ_NONE;
+}
+
+/*
+ * Chained handlers should never call action on their IRQ. This default
+ * action will emit warning if such thing happens.
+ */
+struct irqaction chained_action = {
+       .handler = bad_chained_irq,
+};
+
 /**
  *     irq_set_chip - set the irq chip for an irq
  *     @irq:   irq number
@@ -227,6 +241,13 @@ void irq_enable(struct irq_desc *desc)
  * disabled. If an interrupt happens, then the interrupt flow
  * handler masks the line at the hardware level and marks it
  * pending.
+ *
+ * If the interrupt chip does not implement the irq_disable callback,
+ * a driver can disable the lazy approach for a particular irq line by
+ * calling 'irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY)'. This can
+ * be used for devices which cannot disable the interrupt at the
+ * device level under certain circumstances and have to use
+ * disable_irq[_nosync] instead.
  */
 void irq_disable(struct irq_desc *desc)
 {
@@ -234,6 +255,8 @@ void irq_disable(struct irq_desc *desc)
        if (desc->irq_data.chip->irq_disable) {
                desc->irq_data.chip->irq_disable(&desc->irq_data);
                irq_state_set_masked(desc);
+       } else if (irq_settings_disable_unlazy(desc)) {
+               mask_irq(desc);
        }
 }
 
@@ -669,7 +692,7 @@ void handle_percpu_irq(struct irq_desc *desc)
        if (chip->irq_ack)
                chip->irq_ack(&desc->irq_data);
 
-       handle_irq_event_percpu(desc, desc->action);
+       handle_irq_event_percpu(desc);
 
        if (chip->irq_eoi)
                chip->irq_eoi(&desc->irq_data);
@@ -746,6 +769,8 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
                if (desc->irq_data.chip != &no_irq_chip)
                        mask_ack_irq(desc);
                irq_state_set_disabled(desc);
+               if (is_chained)
+                       desc->action = NULL;
                desc->depth = 1;
        }
        desc->handle_irq = handle;
@@ -755,6 +780,7 @@ __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
                irq_settings_set_noprobe(desc);
                irq_settings_set_norequest(desc);
                irq_settings_set_nothread(desc);
+               desc->action = &chained_action;
                irq_startup(desc, true);
        }
 }
diff --git a/kernel/irq/cpuhotplug.c b/kernel/irq/cpuhotplug.c
new file mode 100644 (file)
index 0000000..80f4f4e
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Generic cpu hotunplug interrupt migration code copied from the
+ * arch/arm implementation
+ *
+ * Copyright (C) Russell King
+ *
+ * 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/interrupt.h>
+#include <linux/ratelimit.h>
+#include <linux/irq.h>
+
+#include "internals.h"
+
+static bool migrate_one_irq(struct irq_desc *desc)
+{
+       struct irq_data *d = irq_desc_get_irq_data(desc);
+       const struct cpumask *affinity = d->common->affinity;
+       struct irq_chip *c;
+       bool ret = false;
+
+       /*
+        * If this is a per-CPU interrupt, or the affinity does not
+        * include this CPU, then we have nothing to do.
+        */
+       if (irqd_is_per_cpu(d) ||
+           !cpumask_test_cpu(smp_processor_id(), affinity))
+               return false;
+
+       if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
+               affinity = cpu_online_mask;
+               ret = true;
+       }
+
+       c = irq_data_get_irq_chip(d);
+       if (!c->irq_set_affinity) {
+               pr_warn_ratelimited("IRQ%u: unable to set affinity\n", d->irq);
+       } else {
+               int r = irq_do_set_affinity(d, affinity, false);
+               if (r)
+                       pr_warn_ratelimited("IRQ%u: set affinity failed(%d).\n",
+                                           d->irq, r);
+       }
+
+       return ret;
+}
+
+/**
+ * irq_migrate_all_off_this_cpu - Migrate irqs away from offline cpu
+ *
+ * The current CPU has been marked offline.  Migrate IRQs off this CPU.
+ * If the affinity settings do not allow other CPUs, force them onto any
+ * available CPU.
+ *
+ * Note: we must iterate over all IRQs, whether they have an attached
+ * action structure or not, as we need to get chained interrupts too.
+ */
+void irq_migrate_all_off_this_cpu(void)
+{
+       unsigned int irq;
+       struct irq_desc *desc;
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       for_each_active_irq(irq) {
+               bool affinity_broken;
+
+               desc = irq_to_desc(irq);
+               raw_spin_lock(&desc->lock);
+               affinity_broken = migrate_one_irq(desc);
+               raw_spin_unlock(&desc->lock);
+
+               if (affinity_broken)
+                       pr_warn_ratelimited("IRQ%u no longer affine to CPU%u\n",
+                                           irq, smp_processor_id());
+       }
+
+       local_irq_restore(flags);
+}
index e25a83b67ccea18568f932636b72bd0e9ba182d0..a302cf9a2126c8a911ff4da1a944c88c6d3b3f8a 100644 (file)
@@ -132,11 +132,11 @@ void __irq_wake_thread(struct irq_desc *desc, struct irqaction *action)
        wake_up_process(action->thread);
 }
 
-irqreturn_t
-handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
+irqreturn_t handle_irq_event_percpu(struct irq_desc *desc)
 {
        irqreturn_t retval = IRQ_NONE;
        unsigned int flags = 0, irq = desc->irq_data.irq;
+       struct irqaction *action = desc->action;
 
        do {
                irqreturn_t res;
@@ -184,14 +184,13 @@ handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
 
 irqreturn_t handle_irq_event(struct irq_desc *desc)
 {
-       struct irqaction *action = desc->action;
        irqreturn_t ret;
 
        desc->istate &= ~IRQS_PENDING;
        irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
        raw_spin_unlock(&desc->lock);
 
-       ret = handle_irq_event_percpu(desc, action);
+       ret = handle_irq_event_percpu(desc);
 
        raw_spin_lock(&desc->lock);
        irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
index 5ef0c2dbe9302a846e724d5106c01a8eec248fda..05c2188271b86730323c9bce053adc2d16e8d5a3 100644 (file)
@@ -18,6 +18,8 @@
 
 extern bool noirqdebug;
 
+extern struct irqaction chained_action;
+
 /*
  * Bits used by threaded handlers:
  * IRQTF_RUNTHREAD - signals that the interrupt handler thread should run
@@ -81,7 +83,7 @@ extern void irq_mark_irq(unsigned int irq);
 
 extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr);
 
-irqreturn_t handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action);
+irqreturn_t handle_irq_event_percpu(struct irq_desc *desc);
 irqreturn_t handle_irq_event(struct irq_desc *desc);
 
 /* Resending of interrupts :*/
index dc9d27c0c1589e58bb14a15e3442ed2ff0d091a4..22aa9612ef7ca98cd8796188a7021810cc86d05a 100644 (file)
@@ -27,6 +27,57 @@ static int irq_domain_alloc_descs(int virq, unsigned int nr_irqs,
                                  irq_hw_number_t hwirq, int node);
 static void irq_domain_check_hierarchy(struct irq_domain *domain);
 
+struct irqchip_fwid {
+       struct fwnode_handle fwnode;
+       char *name;
+       void *data;
+};
+
+/**
+ * irq_domain_alloc_fwnode - Allocate a fwnode_handle suitable for
+ *                           identifying an irq domain
+ * @data: optional user-provided data
+ *
+ * Allocate a struct device_node, and return a poiner to the embedded
+ * fwnode_handle (or NULL on failure).
+ */
+struct fwnode_handle *irq_domain_alloc_fwnode(void *data)
+{
+       struct irqchip_fwid *fwid;
+       char *name;
+
+       fwid = kzalloc(sizeof(*fwid), GFP_KERNEL);
+       name = kasprintf(GFP_KERNEL, "irqchip@%p", data);
+
+       if (!fwid || !name) {
+               kfree(fwid);
+               kfree(name);
+               return NULL;
+       }
+
+       fwid->name = name;
+       fwid->data = data;
+       fwid->fwnode.type = FWNODE_IRQCHIP;
+       return &fwid->fwnode;
+}
+
+/**
+ * irq_domain_free_fwnode - Free a non-OF-backed fwnode_handle
+ *
+ * Free a fwnode_handle allocated with irq_domain_alloc_fwnode.
+ */
+void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
+{
+       struct irqchip_fwid *fwid;
+
+       if (WARN_ON(fwnode->type != FWNODE_IRQCHIP))
+               return;
+
+       fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
+       kfree(fwid->name);
+       kfree(fwid);
+}
+
 /**
  * __irq_domain_add() - Allocate a new irq_domain data structure
  * @of_node: optional device-tree node of the interrupt controller
@@ -40,23 +91,28 @@ static void irq_domain_check_hierarchy(struct irq_domain *domain);
  * Allocates and initialize and irq_domain structure.
  * Returns pointer to IRQ domain, or NULL on failure.
  */
-struct irq_domain *__irq_domain_add(struct device_node *of_node, int size,
+struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
                                    irq_hw_number_t hwirq_max, int direct_max,
                                    const struct irq_domain_ops *ops,
                                    void *host_data)
 {
        struct irq_domain *domain;
+       struct device_node *of_node;
+
+       of_node = to_of_node(fwnode);
 
        domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),
                              GFP_KERNEL, of_node_to_nid(of_node));
        if (WARN_ON(!domain))
                return NULL;
 
+       of_node_get(of_node);
+
        /* Fill structure */
        INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
        domain->ops = ops;
        domain->host_data = host_data;
-       domain->of_node = of_node_get(of_node);
+       domain->fwnode = fwnode;
        domain->hwirq_max = hwirq_max;
        domain->revmap_size = size;
        domain->revmap_direct_max_irq = direct_max;
@@ -102,7 +158,7 @@ void irq_domain_remove(struct irq_domain *domain)
 
        pr_debug("Removed domain %s\n", domain->name);
 
-       of_node_put(domain->of_node);
+       of_node_put(irq_domain_get_of_node(domain));
        kfree(domain);
 }
 EXPORT_SYMBOL_GPL(irq_domain_remove);
@@ -133,7 +189,7 @@ struct irq_domain *irq_domain_add_simple(struct device_node *of_node,
 {
        struct irq_domain *domain;
 
-       domain = __irq_domain_add(of_node, size, size, 0, ops, host_data);
+       domain = __irq_domain_add(of_node_to_fwnode(of_node), size, size, 0, ops, host_data);
        if (!domain)
                return NULL;
 
@@ -177,7 +233,7 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
 {
        struct irq_domain *domain;
 
-       domain = __irq_domain_add(of_node, first_hwirq + size,
+       domain = __irq_domain_add(of_node_to_fwnode(of_node), first_hwirq + size,
                                  first_hwirq + size, 0, ops, host_data);
        if (domain)
                irq_domain_associate_many(domain, first_irq, first_hwirq, size);
@@ -187,12 +243,12 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node,
 EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
 
 /**
- * irq_find_matching_host() - Locates a domain for a given device node
- * @node: device-tree node of the interrupt controller
+ * irq_find_matching_fwnode() - Locates a domain for a given fwnode
+ * @fwnode: FW descriptor of the interrupt controller
  * @bus_token: domain-specific data
  */
-struct irq_domain *irq_find_matching_host(struct device_node *node,
-                                         enum irq_domain_bus_token bus_token)
+struct irq_domain *irq_find_matching_fwnode(struct fwnode_handle *fwnode,
+                                           enum irq_domain_bus_token bus_token)
 {
        struct irq_domain *h, *found = NULL;
        int rc;
@@ -209,9 +265,9 @@ struct irq_domain *irq_find_matching_host(struct device_node *node,
        mutex_lock(&irq_domain_mutex);
        list_for_each_entry(h, &irq_domain_list, link) {
                if (h->ops->match)
-                       rc = h->ops->match(h, node, bus_token);
+                       rc = h->ops->match(h, to_of_node(fwnode), bus_token);
                else
-                       rc = ((h->of_node != NULL) && (h->of_node == node) &&
+                       rc = ((fwnode != NULL) && (h->fwnode == fwnode) &&
                              ((bus_token == DOMAIN_BUS_ANY) ||
                               (h->bus_token == bus_token)));
 
@@ -223,7 +279,7 @@ struct irq_domain *irq_find_matching_host(struct device_node *node,
        mutex_unlock(&irq_domain_mutex);
        return found;
 }
-EXPORT_SYMBOL_GPL(irq_find_matching_host);
+EXPORT_SYMBOL_GPL(irq_find_matching_fwnode);
 
 /**
  * irq_set_default_host() - Set a "default" irq domain
@@ -336,10 +392,12 @@ EXPORT_SYMBOL_GPL(irq_domain_associate);
 void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
                               irq_hw_number_t hwirq_base, int count)
 {
+       struct device_node *of_node;
        int i;
 
+       of_node = irq_domain_get_of_node(domain);
        pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
-               of_node_full_name(domain->of_node), irq_base, (int)hwirq_base, count);
+               of_node_full_name(of_node), irq_base, (int)hwirq_base, count);
 
        for (i = 0; i < count; i++) {
                irq_domain_associate(domain, irq_base + i, hwirq_base + i);
@@ -359,12 +417,14 @@ EXPORT_SYMBOL_GPL(irq_domain_associate_many);
  */
 unsigned int irq_create_direct_mapping(struct irq_domain *domain)
 {
+       struct device_node *of_node;
        unsigned int virq;
 
        if (domain == NULL)
                domain = irq_default_domain;
 
-       virq = irq_alloc_desc_from(1, of_node_to_nid(domain->of_node));
+       of_node = irq_domain_get_of_node(domain);
+       virq = irq_alloc_desc_from(1, of_node_to_nid(of_node));
        if (!virq) {
                pr_debug("create_direct virq allocation failed\n");
                return 0;
@@ -399,6 +459,7 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
 unsigned int irq_create_mapping(struct irq_domain *domain,
                                irq_hw_number_t hwirq)
 {
+       struct device_node *of_node;
        int virq;
 
        pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
@@ -412,6 +473,8 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
        }
        pr_debug("-> using domain @%p\n", domain);
 
+       of_node = irq_domain_get_of_node(domain);
+
        /* Check if mapping already exists */
        virq = irq_find_mapping(domain, hwirq);
        if (virq) {
@@ -420,8 +483,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
        }
 
        /* Allocate a virtual interrupt number */
-       virq = irq_domain_alloc_descs(-1, 1, hwirq,
-                                     of_node_to_nid(domain->of_node));
+       virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node));
        if (virq <= 0) {
                pr_debug("-> virq allocation failed\n");
                return 0;
@@ -433,7 +495,7 @@ unsigned int irq_create_mapping(struct irq_domain *domain,
        }
 
        pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
-               hwirq, of_node_full_name(domain->of_node), virq);
+               hwirq, of_node_full_name(of_node), virq);
 
        return virq;
 }
@@ -460,10 +522,12 @@ EXPORT_SYMBOL_GPL(irq_create_mapping);
 int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
                               irq_hw_number_t hwirq_base, int count)
 {
+       struct device_node *of_node;
        int ret;
 
+       of_node = irq_domain_get_of_node(domain);
        ret = irq_alloc_descs(irq_base, irq_base, count,
-                             of_node_to_nid(domain->of_node));
+                             of_node_to_nid(of_node));
        if (unlikely(ret < 0))
                return ret;
 
@@ -472,28 +536,56 @@ int irq_create_strict_mappings(struct irq_domain *domain, unsigned int irq_base,
 }
 EXPORT_SYMBOL_GPL(irq_create_strict_mappings);
 
-unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
+static int irq_domain_translate(struct irq_domain *d,
+                               struct irq_fwspec *fwspec,
+                               irq_hw_number_t *hwirq, unsigned int *type)
+{
+#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
+       if (d->ops->translate)
+               return d->ops->translate(d, fwspec, hwirq, type);
+#endif
+       if (d->ops->xlate)
+               return d->ops->xlate(d, to_of_node(fwspec->fwnode),
+                                    fwspec->param, fwspec->param_count,
+                                    hwirq, type);
+
+       /* If domain has no translation, then we assume interrupt line */
+       *hwirq = fwspec->param[0];
+       return 0;
+}
+
+static void of_phandle_args_to_fwspec(struct of_phandle_args *irq_data,
+                                     struct irq_fwspec *fwspec)
+{
+       int i;
+
+       fwspec->fwnode = irq_data->np ? &irq_data->np->fwnode : NULL;
+       fwspec->param_count = irq_data->args_count;
+
+       for (i = 0; i < irq_data->args_count; i++)
+               fwspec->param[i] = irq_data->args[i];
+}
+
+unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
 {
        struct irq_domain *domain;
        irq_hw_number_t hwirq;
        unsigned int type = IRQ_TYPE_NONE;
        int virq;
 
-       domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain;
+       if (fwspec->fwnode)
+               domain = irq_find_matching_fwnode(fwspec->fwnode, DOMAIN_BUS_ANY);
+       else
+               domain = irq_default_domain;
+
        if (!domain) {
                pr_warn("no irq domain found for %s !\n",
-                       of_node_full_name(irq_data->np));
+                       of_node_full_name(to_of_node(fwspec->fwnode)));
                return 0;
        }
 
-       /* If domain has no translation, then we assume interrupt line */
-       if (domain->ops->xlate == NULL)
-               hwirq = irq_data->args[0];
-       else {
-               if (domain->ops->xlate(domain, irq_data->np, irq_data->args,
-                                       irq_data->args_count, &hwirq, &type))
-                       return 0;
-       }
+       if (irq_domain_translate(domain, fwspec, &hwirq, &type))
+               return 0;
 
        if (irq_domain_is_hierarchy(domain)) {
                /*
@@ -504,7 +596,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
                if (virq)
                        return virq;
 
-               virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data);
+               virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
                if (virq <= 0)
                        return 0;
        } else {
@@ -520,6 +612,15 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
                irq_set_irq_type(virq, type);
        return virq;
 }
+EXPORT_SYMBOL_GPL(irq_create_fwspec_mapping);
+
+unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data)
+{
+       struct irq_fwspec fwspec;
+
+       of_phandle_args_to_fwspec(irq_data, &fwspec);
+       return irq_create_fwspec_mapping(&fwspec);
+}
 EXPORT_SYMBOL_GPL(irq_create_of_mapping);
 
 /**
@@ -590,14 +691,16 @@ static int virq_debug_show(struct seq_file *m, void *private)
                   "name", "mapped", "linear-max", "direct-max", "devtree-node");
        mutex_lock(&irq_domain_mutex);
        list_for_each_entry(domain, &irq_domain_list, link) {
+               struct device_node *of_node;
                int count = 0;
+               of_node = irq_domain_get_of_node(domain);
                radix_tree_for_each_slot(slot, &domain->revmap_tree, &iter, 0)
                        count++;
                seq_printf(m, "%c%-16s  %6u  %10u  %10u  %s\n",
                           domain == irq_default_domain ? '*' : ' ', domain->name,
                           domain->revmap_size + count, domain->revmap_size,
                           domain->revmap_direct_max_irq,
-                          domain->of_node ? of_node_full_name(domain->of_node) : "");
+                          of_node ? of_node_full_name(of_node) : "");
        }
        mutex_unlock(&irq_domain_mutex);
 
@@ -751,11 +854,11 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
 
 #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
 /**
- * irq_domain_add_hierarchy - Add a irqdomain into the hierarchy
+ * irq_domain_create_hierarchy - Add a irqdomain into the hierarchy
  * @parent:    Parent irq domain to associate with the new domain
  * @flags:     Irq domain flags associated to the domain
  * @size:      Size of the domain. See below
- * @node:      Optional device-tree node of the interrupt controller
+ * @fwnode:    Optional fwnode of the interrupt controller
  * @ops:       Pointer to the interrupt domain callbacks
  * @host_data: Controller private data pointer
  *
@@ -765,19 +868,19 @@ static int irq_domain_alloc_descs(int virq, unsigned int cnt,
  * domain flags are set.
  * Returns pointer to IRQ domain, or NULL on failure.
  */
-struct irq_domain *irq_domain_add_hierarchy(struct irq_domain *parent,
+struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
                                            unsigned int flags,
                                            unsigned int size,
-                                           struct device_node *node,
+                                           struct fwnode_handle *fwnode,
                                            const struct irq_domain_ops *ops,
                                            void *host_data)
 {
        struct irq_domain *domain;
 
        if (size)
-               domain = irq_domain_add_linear(node, size, ops, host_data);
+               domain = irq_domain_create_linear(fwnode, size, ops, host_data);
        else
-               domain = irq_domain_add_tree(node, ops, host_data);
+               domain = irq_domain_create_tree(fwnode, ops, host_data);
        if (domain) {
                domain->parent = parent;
                domain->flags |= flags;
index f9a59f6cabd2d3a96f8cd9bee179b6ab4214b83e..a71175ff98d58f7c274786cadd5b6b520678c665 100644 (file)
@@ -258,37 +258,6 @@ int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m)
 }
 EXPORT_SYMBOL_GPL(irq_set_affinity_hint);
 
-/**
- *     irq_set_vcpu_affinity - Set vcpu affinity for the interrupt
- *     @irq: interrupt number to set affinity
- *     @vcpu_info: vCPU specific data
- *
- *     This function uses the vCPU specific data to set the vCPU
- *     affinity for an irq. The vCPU specific data is passed from
- *     outside, such as KVM. One example code path is as below:
- *     KVM -> IOMMU -> irq_set_vcpu_affinity().
- */
-int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info)
-{
-       unsigned long flags;
-       struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
-       struct irq_data *data;
-       struct irq_chip *chip;
-       int ret = -ENOSYS;
-
-       if (!desc)
-               return -EINVAL;
-
-       data = irq_desc_get_irq_data(desc);
-       chip = irq_data_get_irq_chip(data);
-       if (chip && chip->irq_set_vcpu_affinity)
-               ret = chip->irq_set_vcpu_affinity(data, vcpu_info);
-       irq_put_desc_unlock(desc, flags);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(irq_set_vcpu_affinity);
-
 static void irq_affinity_notify(struct work_struct *work)
 {
        struct irq_affinity_notify *notify =
@@ -424,6 +393,37 @@ setup_affinity(struct irq_desc *desc, struct cpumask *mask)
 }
 #endif
 
+/**
+ *     irq_set_vcpu_affinity - Set vcpu affinity for the interrupt
+ *     @irq: interrupt number to set affinity
+ *     @vcpu_info: vCPU specific data
+ *
+ *     This function uses the vCPU specific data to set the vCPU
+ *     affinity for an irq. The vCPU specific data is passed from
+ *     outside, such as KVM. One example code path is as below:
+ *     KVM -> IOMMU -> irq_set_vcpu_affinity().
+ */
+int irq_set_vcpu_affinity(unsigned int irq, void *vcpu_info)
+{
+       unsigned long flags;
+       struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
+       struct irq_data *data;
+       struct irq_chip *chip;
+       int ret = -ENOSYS;
+
+       if (!desc)
+               return -EINVAL;
+
+       data = irq_desc_get_irq_data(desc);
+       chip = irq_data_get_irq_chip(data);
+       if (chip && chip->irq_set_vcpu_affinity)
+               ret = chip->irq_set_vcpu_affinity(data, vcpu_info);
+       irq_put_desc_unlock(desc, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(irq_set_vcpu_affinity);
+
 void __disable_irq(struct irq_desc *desc)
 {
        if (!desc->depth++)
@@ -730,6 +730,12 @@ static irqreturn_t irq_nested_primary_handler(int irq, void *dev_id)
        return IRQ_NONE;
 }
 
+static irqreturn_t irq_forced_secondary_handler(int irq, void *dev_id)
+{
+       WARN(1, "Secondary action handler called for irq %d\n", irq);
+       return IRQ_NONE;
+}
+
 static int irq_wait_for_interrupt(struct irqaction *action)
 {
        set_current_state(TASK_INTERRUPTIBLE);
@@ -756,7 +762,8 @@ static int irq_wait_for_interrupt(struct irqaction *action)
 static void irq_finalize_oneshot(struct irq_desc *desc,
                                 struct irqaction *action)
 {
-       if (!(desc->istate & IRQS_ONESHOT))
+       if (!(desc->istate & IRQS_ONESHOT) ||
+           action->handler == irq_forced_secondary_handler)
                return;
 again:
        chip_bus_lock(desc);
@@ -910,6 +917,18 @@ static void irq_thread_dtor(struct callback_head *unused)
        irq_finalize_oneshot(desc, action);
 }
 
+static void irq_wake_secondary(struct irq_desc *desc, struct irqaction *action)
+{
+       struct irqaction *secondary = action->secondary;
+
+       if (WARN_ON_ONCE(!secondary))
+               return;
+
+       raw_spin_lock_irq(&desc->lock);
+       __irq_wake_thread(desc, secondary);
+       raw_spin_unlock_irq(&desc->lock);
+}
+
 /*
  * Interrupt handler thread
  */
@@ -940,6 +959,8 @@ static int irq_thread(void *data)
                action_ret = handler_fn(desc, action);
                if (action_ret == IRQ_HANDLED)
                        atomic_inc(&desc->threads_handled);
+               if (action_ret == IRQ_WAKE_THREAD)
+                       irq_wake_secondary(desc, action);
 
                wake_threads_waitq(desc);
        }
@@ -984,20 +1005,36 @@ void irq_wake_thread(unsigned int irq, void *dev_id)
 }
 EXPORT_SYMBOL_GPL(irq_wake_thread);
 
-static void irq_setup_forced_threading(struct irqaction *new)
+static int irq_setup_forced_threading(struct irqaction *new)
 {
        if (!force_irqthreads)
-               return;
+               return 0;
        if (new->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT))
-               return;
+               return 0;
 
        new->flags |= IRQF_ONESHOT;
 
-       if (!new->thread_fn) {
-               set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);
-               new->thread_fn = new->handler;
-               new->handler = irq_default_primary_handler;
+       /*
+        * Handle the case where we have a real primary handler and a
+        * thread handler. We force thread them as well by creating a
+        * secondary action.
+        */
+       if (new->handler != irq_default_primary_handler && new->thread_fn) {
+               /* Allocate the secondary action */
+               new->secondary = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
+               if (!new->secondary)
+                       return -ENOMEM;
+               new->secondary->handler = irq_forced_secondary_handler;
+               new->secondary->thread_fn = new->thread_fn;
+               new->secondary->dev_id = new->dev_id;
+               new->secondary->irq = new->irq;
+               new->secondary->name = new->name;
        }
+       /* Deal with the primary handler */
+       set_bit(IRQTF_FORCED_THREAD, &new->thread_flags);
+       new->thread_fn = new->handler;
+       new->handler = irq_default_primary_handler;
+       return 0;
 }
 
 static int irq_request_resources(struct irq_desc *desc)
@@ -1017,6 +1054,48 @@ static void irq_release_resources(struct irq_desc *desc)
                c->irq_release_resources(d);
 }
 
+static int
+setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary)
+{
+       struct task_struct *t;
+       struct sched_param param = {
+               .sched_priority = MAX_USER_RT_PRIO/2,
+       };
+
+       if (!secondary) {
+               t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
+                                  new->name);
+       } else {
+               t = kthread_create(irq_thread, new, "irq/%d-s-%s", irq,
+                                  new->name);
+               param.sched_priority -= 1;
+       }
+
+       if (IS_ERR(t))
+               return PTR_ERR(t);
+
+       sched_setscheduler_nocheck(t, SCHED_FIFO, &param);
+
+       /*
+        * We keep the reference to the task struct even if
+        * the thread dies to avoid that the interrupt code
+        * references an already freed task_struct.
+        */
+       get_task_struct(t);
+       new->thread = t;
+       /*
+        * Tell the thread to set its affinity. This is
+        * important for shared interrupt handlers as we do
+        * not invoke setup_affinity() for the secondary
+        * handlers as everything is already set up. Even for
+        * interrupts marked with IRQF_NO_BALANCE this is
+        * correct as we want the thread to move to the cpu(s)
+        * on which the requesting code placed the interrupt.
+        */
+       set_bit(IRQTF_AFFINITY, &new->thread_flags);
+       return 0;
+}
+
 /*
  * Internal function to register an irqaction - typically used to
  * allocate special interrupts that are part of the architecture.
@@ -1037,6 +1116,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
        if (!try_module_get(desc->owner))
                return -ENODEV;
 
+       new->irq = irq;
+
        /*
         * Check whether the interrupt nests into another interrupt
         * thread.
@@ -1054,8 +1135,11 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                 */
                new->handler = irq_nested_primary_handler;
        } else {
-               if (irq_settings_can_thread(desc))
-                       irq_setup_forced_threading(new);
+               if (irq_settings_can_thread(desc)) {
+                       ret = irq_setup_forced_threading(new);
+                       if (ret)
+                               goto out_mput;
+               }
        }
 
        /*
@@ -1064,37 +1148,14 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
         * thread.
         */
        if (new->thread_fn && !nested) {
-               struct task_struct *t;
-               static const struct sched_param param = {
-                       .sched_priority = MAX_USER_RT_PRIO/2,
-               };
-
-               t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
-                                  new->name);
-               if (IS_ERR(t)) {
-                       ret = PTR_ERR(t);
+               ret = setup_irq_thread(new, irq, false);
+               if (ret)
                        goto out_mput;
+               if (new->secondary) {
+                       ret = setup_irq_thread(new->secondary, irq, true);
+                       if (ret)
+                               goto out_thread;
                }
-
-               sched_setscheduler_nocheck(t, SCHED_FIFO, &param);
-
-               /*
-                * We keep the reference to the task struct even if
-                * the thread dies to avoid that the interrupt code
-                * references an already freed task_struct.
-                */
-               get_task_struct(t);
-               new->thread = t;
-               /*
-                * Tell the thread to set its affinity. This is
-                * important for shared interrupt handlers as we do
-                * not invoke setup_affinity() for the secondary
-                * handlers as everything is already set up. Even for
-                * interrupts marked with IRQF_NO_BALANCE this is
-                * correct as we want the thread to move to the cpu(s)
-                * on which the requesting code placed the interrupt.
-                */
-               set_bit(IRQTF_AFFINITY, &new->thread_flags);
        }
 
        if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
@@ -1267,7 +1328,6 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
                                   irq, nmsk, omsk);
        }
 
-       new->irq = irq;
        *old_ptr = new;
 
        irq_pm_install_action(desc, new);
@@ -1293,6 +1353,8 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
         */
        if (new->thread)
                wake_up_process(new->thread);
+       if (new->secondary)
+               wake_up_process(new->secondary->thread);
 
        register_irq_proc(irq, desc);
        new->dir = NULL;
@@ -1323,6 +1385,13 @@ out_thread:
                kthread_stop(t);
                put_task_struct(t);
        }
+       if (new->secondary && new->secondary->thread) {
+               struct task_struct *t = new->secondary->thread;
+
+               new->secondary->thread = NULL;
+               kthread_stop(t);
+               put_task_struct(t);
+       }
 out_mput:
        module_put(desc->owner);
        return ret;
@@ -1394,6 +1463,7 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
 
        /* If this was the last handler, shut down the IRQ line: */
        if (!desc->action) {
+               irq_settings_clr_disable_unlazy(desc);
                irq_shutdown(desc);
                irq_release_resources(desc);
        }
@@ -1430,9 +1500,14 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id)
        if (action->thread) {
                kthread_stop(action->thread);
                put_task_struct(action->thread);
+               if (action->secondary && action->secondary->thread) {
+                       kthread_stop(action->secondary->thread);
+                       put_task_struct(action->secondary->thread);
+               }
        }
 
        module_put(desc->owner);
+       kfree(action->secondary);
        return action;
 }
 
@@ -1576,8 +1651,10 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler,
        retval = __setup_irq(irq, desc, action);
        chip_bus_sync_unlock(desc);
 
-       if (retval)
+       if (retval) {
+               kfree(action->secondary);
                kfree(action);
+       }
 
 #ifdef CONFIG_DEBUG_SHIRQ_FIXME
        if (!retval && (irqflags & IRQF_SHARED)) {
index be9149f62eb86e63ac06194d4eeaa4065823ea62..6b0c0b74a2a1a88c0d3f81519fea7c520b290967 100644 (file)
@@ -235,11 +235,11 @@ static void msi_domain_update_chip_ops(struct msi_domain_info *info)
 
 /**
  * msi_create_irq_domain - Create a MSI interrupt domain
- * @of_node:   Optional device-tree node of the interrupt controller
+ * @fwnode:    Optional fwnode of the interrupt controller
  * @info:      MSI domain info
  * @parent:    Parent irq domain
  */
-struct irq_domain *msi_create_irq_domain(struct device_node *node,
+struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
                                         struct msi_domain_info *info,
                                         struct irq_domain *parent)
 {
@@ -248,8 +248,8 @@ struct irq_domain *msi_create_irq_domain(struct device_node *node,
        if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
                msi_domain_update_chip_ops(info);
 
-       return irq_domain_add_hierarchy(parent, 0, 0, node, &msi_domain_ops,
-                                       info);
+       return irq_domain_create_hierarchy(parent, 0, 0, fwnode,
+                                          &msi_domain_ops, info);
 }
 
 /**
index a50ddc9417ff5fbdccf837f9f43b716200ceb013..a916cf144b6550caf452fc304a1e848d40246b8a 100644 (file)
@@ -475,7 +475,7 @@ int show_interrupts(struct seq_file *p, void *v)
        for_each_online_cpu(j)
                any_count |= kstat_irqs_cpu(i, j);
        action = desc->action;
-       if (!action && !any_count)
+       if ((!action || action == &chained_action) && !any_count)
                goto out;
 
        seq_printf(p, "%*d: ", prec, i);
index 3320b84cc60f79ac09501d6f4d55acc01b0cebe6..320579d8909100d1ebb181510dd8ce4433ab5456 100644 (file)
@@ -15,6 +15,7 @@ enum {
        _IRQ_NESTED_THREAD      = IRQ_NESTED_THREAD,
        _IRQ_PER_CPU_DEVID      = IRQ_PER_CPU_DEVID,
        _IRQ_IS_POLLED          = IRQ_IS_POLLED,
+       _IRQ_DISABLE_UNLAZY     = IRQ_DISABLE_UNLAZY,
        _IRQF_MODIFY_MASK       = IRQF_MODIFY_MASK,
 };
 
@@ -28,6 +29,7 @@ enum {
 #define IRQ_NESTED_THREAD      GOT_YOU_MORON
 #define IRQ_PER_CPU_DEVID      GOT_YOU_MORON
 #define IRQ_IS_POLLED          GOT_YOU_MORON
+#define IRQ_DISABLE_UNLAZY     GOT_YOU_MORON
 #undef IRQF_MODIFY_MASK
 #define IRQF_MODIFY_MASK       GOT_YOU_MORON
 
@@ -154,3 +156,13 @@ static inline bool irq_settings_is_polled(struct irq_desc *desc)
 {
        return desc->status_use_accessors & _IRQ_IS_POLLED;
 }
+
+static inline bool irq_settings_disable_unlazy(struct irq_desc *desc)
+{
+       return desc->status_use_accessors & _IRQ_DISABLE_UNLAZY;
+}
+
+static inline void irq_settings_clr_disable_unlazy(struct irq_desc *desc)
+{
+       desc->status_use_accessors &= ~_IRQ_DISABLE_UNLAZY;
+}
index 66c66165e712d743ed3da1a501c03a087f442378..30489181922d28d9749feefb552e7b8f0fa97f52 100644 (file)
@@ -2137,7 +2137,7 @@ static int init_vgic_model(struct kvm *kvm, int type)
        case KVM_DEV_TYPE_ARM_VGIC_V2:
                vgic_v2_init_emulation(kvm);
                break;
-#ifdef CONFIG_ARM_GIC_V3
+#ifdef CONFIG_KVM_ARM_VGIC_V3
        case KVM_DEV_TYPE_ARM_VGIC_V3:
                vgic_v3_init_emulation(kvm);
                break;
@@ -2299,7 +2299,7 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write)
                block_size = KVM_VGIC_V2_CPU_SIZE;
                alignment = SZ_4K;
                break;
-#ifdef CONFIG_ARM_GIC_V3
+#ifdef CONFIG_KVM_ARM_VGIC_V3
        case KVM_VGIC_V3_ADDR_TYPE_DIST:
                type_needed = KVM_DEV_TYPE_ARM_VGIC_V3;
                addr_ptr = &vgic->vgic_dist_base;