Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
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 ...
This commit is contained in:
commit
6aa2fdb87c
@ -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.
|
||||
|
@ -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
|
||||
|
@ -11,13 +11,14 @@ have PPIs or SGIs.
|
||||
Main node required properties:
|
||||
|
||||
- compatible : should be one of:
|
||||
"arm,gic-400"
|
||||
"arm,cortex-a15-gic"
|
||||
"arm,cortex-a9-gic"
|
||||
"arm,cortex-a7-gic"
|
||||
"arm,arm11mp-gic"
|
||||
"brcm,brahma-b15-gic"
|
||||
"arm,arm1176jzf-devchip-gic"
|
||||
"arm,arm11mp-gic"
|
||||
"arm,cortex-a15-gic"
|
||||
"arm,cortex-a7-gic"
|
||||
"arm,cortex-a9-gic"
|
||||
"arm,gic-400"
|
||||
"arm,pl390"
|
||||
"brcm,brahma-b15-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 {
|
||||
|
@ -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.
|
||||
|
220
Documentation/devicetree/bindings/pci/pci-msi.txt
Normal file
220
Documentation/devicetree/bindings/pci/pci-msi.txt
Normal file
@ -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>;
|
||||
};
|
||||
};
|
@ -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
|
||||
|
||||
|
188
arch/arm/include/asm/arch_gicv3.h
Normal file
188
arch/arm/include/asm/arch_gicv3.h
Normal file
@ -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 */
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
170
arch/arm64/include/asm/arch_gicv3.h
Normal file
170
arch/arm64/include/asm/arch_gicv3.h
Normal file
@ -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 */
|
@ -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__
|
||||
|
||||
|
@ -62,15 +62,18 @@
|
||||
(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)
|
||||
|
@ -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
|
||||
{
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -11,9 +11,12 @@
|
||||
#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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
109
drivers/irqchip/alphascale_asm9260-icoll.h
Normal file
109
drivers/irqchip/alphascale_asm9260-icoll.h
Normal file
@ -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
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -21,6 +21,17 @@
|
||||
|
||||
#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))
|
||||
{
|
||||
|
@ -20,10 +20,19 @@
|
||||
#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 */
|
||||
|
@ -37,19 +37,31 @@
|
||||
#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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -37,7 +37,10 @@
|
||||
#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;
|
||||
|
@ -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 */
|
||||
#ifdef CONFIG_ARM64
|
||||
static DEFINE_STATIC_KEY_FALSE(is_cavium_thunderx);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -51,6 +51,19 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
acpi_irq_model = ACPI_IRQ_MODEL_GIC;
|
||||
__gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
|
||||
|
||||
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
err = imx_gpcv2_domain_translate(domain, fwspec, &hwirq, &type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* No PPI should point to this domain */
|
||||
if (args->args[0] != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* 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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
@ -28,20 +30,64 @@
|
||||
#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);
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -62,35 +62,22 @@ 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 struct irqc_priv *irq_data_to_priv(struct irq_data *data)
|
||||
{
|
||||
return data->domain->host_data;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static void irqc_irq_disable(struct irq_data *d)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = {
|
||||
[IRQ_TYPE_LEVEL_LOW] = 0x01,
|
||||
[IRQ_TYPE_LEVEL_HIGH] = 0x02,
|
||||
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
187
drivers/of/irq.c
187
drivers/of/irq.c
@ -579,6 +579,180 @@ 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_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.
|
||||
*/
|
||||
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 && !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;
|
||||
}
|
||||
|
||||
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
|
||||
@ -586,15 +760,6 @@ err:
|
||||
*/
|
||||
void of_msi_configure(struct device *dev, struct device_node *np)
|
||||
{
|
||||
struct device_node *msi_np;
|
||||
struct irq_domain *d;
|
||||
|
||||
msi_np = of_parse_phandle(np, "msi-parent", 0);
|
||||
if (!msi_np)
|
||||
return;
|
||||
|
||||
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);
|
||||
dev_set_msi_domain(dev,
|
||||
of_msi_get_domain(dev, np, DOMAIN_BUS_PLATFORM_MSI));
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -17,6 +17,7 @@ enum fwnode_type {
|
||||
FWNODE_OF,
|
||||
FWNODE_ACPI,
|
||||
FWNODE_PDATA,
|
||||
FWNODE_IRQCHIP,
|
||||
};
|
||||
|
||||
struct fwnode_handle {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
@ -231,6 +229,7 @@
|
||||
#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
|
||||
@ -266,16 +265,16 @@
|
||||
/*
|
||||
* Hypervisor interface registers (SRE only)
|
||||
*/
|
||||
#define ICH_LR_VIRTUAL_ID_MASK ((1UL << 32) - 1)
|
||||
#define ICH_LR_VIRTUAL_ID_MASK ((1ULL << 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_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)
|
||||
@ -292,19 +291,8 @@
|
||||
#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)
|
||||
|
||||
@ -320,54 +308,10 @@
|
||||
#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
|
||||
|
@ -100,16 +100,11 @@
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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
|
||||
@ -17,16 +18,12 @@
|
||||
* 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);
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -21,6 +21,20 @@
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
82
kernel/irq/cpuhotplug.c
Normal file
82
kernel/irq/cpuhotplug.c
Normal file
@ -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);
|
||||
}
|
@ -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);
|
||||
|
@ -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 :*/
|
||||
|
@ -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;
|
||||
|
@ -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, ¶m);
|
||||
|
||||
/*
|
||||
* 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, ¶m);
|
||||
|
||||
/*
|
||||
* 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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user