diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-15 20:30:09 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-15 20:30:09 -0500 |
commit | 60d7ef3fd34dca2eb7ef4c997ccf1ef76a9ba148 (patch) | |
tree | a5d9449b76af277c8f36008535c5d00036e021de | |
parent | 9b8ec916df67ba31614796037caf606b763e2e79 (diff) | |
parent | c848126734e8621e81659d819922b20d93a2aa6d (diff) |
Merge branch 'irq-irqdomain-arm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq domain ARM updates from Thomas Gleixner:
"This set of changes make use of hierarchical irqdomains to provide:
- MSI/ITS support for GICv3
- MSI support for GICv2m
- Interrupt polarity extender for GICv1
Marc has come more cleanups for the existing extension hooks of GIC in
the pipeline, but they are going to be 3.20 material"
* 'irq-irqdomain-arm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (22 commits)
irqchip: gicv3-its: Fix ITT allocation
irqchip: gicv3-its: Move some alloc/free code to activate/deactivate
irqchip: gicv3-its: Fix domain free in multi-MSI case
irqchip: gic: Remove warning by including linux/irqdomain.h
irqchip: gic-v2m: Add DT bindings for GICv2m
irqchip: gic-v2m: Add support for ARM GICv2m MSI(-X) doorbell
irqchip: mtk-sysirq: dt-bindings: Add bindings for mediatek sysirq
irqchip: mtk-sysirq: Add sysirq interrupt polarity support
irqchip: gic: Support hierarchy irq domain.
irqchip: GICv3: Binding updates for ITS
irqchip: GICv3: ITS: enable compilation of the ITS driver
irqchip: GICv3: ITS: plug ITS init into main GICv3 code
irqchip: GICv3: ITS: DT probing and initialization
irqchip: GICv3: ITS: MSI support
irqchip: GICv3: ITS: device allocation and configuration
irqchip: GICv3: ITS: tables allocators
irqchip: GICv3: ITS: LPI allocator
irqchip: GICv3: ITS: irqchip implementation
irqchip: GICv3: ITS command queue
irqchip: GICv3: rework redistributor structure
...
-rw-r--r-- | Documentation/devicetree/bindings/arm/gic-v3.txt | 39 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/arm/gic.txt | 53 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/arm/mediatek/mediatek,sysirq.txt | 28 | ||||
-rw-r--r-- | arch/arm64/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm64/include/asm/Kbuild | 1 | ||||
-rw-r--r-- | drivers/irqchip/Kconfig | 12 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 3 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v2m.c | 333 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 1425 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3.c | 156 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic.c | 81 | ||||
-rw-r--r-- | drivers/irqchip/irq-mtk-sysirq.c | 163 | ||||
-rw-r--r-- | include/linux/irqchip/arm-gic-v3.h | 128 | ||||
-rw-r--r-- | include/linux/irqchip/arm-gic.h | 4 |
14 files changed, 2364 insertions, 64 deletions
diff --git a/Documentation/devicetree/bindings/arm/gic-v3.txt b/Documentation/devicetree/bindings/arm/gic-v3.txt index 33cd05e6c125..ddfade40ac59 100644 --- a/Documentation/devicetree/bindings/arm/gic-v3.txt +++ b/Documentation/devicetree/bindings/arm/gic-v3.txt | |||
@@ -49,11 +49,29 @@ Optional | |||
49 | occupied by the redistributors. Required if more than one such | 49 | occupied by the redistributors. Required if more than one such |
50 | region is present. | 50 | region is present. |
51 | 51 | ||
52 | Sub-nodes: | ||
53 | |||
54 | GICv3 has one or more Interrupt Translation Services (ITS) that are | ||
55 | used to route Message Signalled Interrupts (MSI) to the CPUs. | ||
56 | |||
57 | These nodes must have the following properties: | ||
58 | - compatible : Should at least contain "arm,gic-v3-its". | ||
59 | - msi-controller : Boolean property. Identifies the node as an MSI controller | ||
60 | - reg: Specifies the base physical address and size of the ITS | ||
61 | registers. | ||
62 | |||
63 | The main GIC node must contain the appropriate #address-cells, | ||
64 | #size-cells and ranges properties for the reg property of all ITS | ||
65 | nodes. | ||
66 | |||
52 | Examples: | 67 | Examples: |
53 | 68 | ||
54 | gic: interrupt-controller@2cf00000 { | 69 | gic: interrupt-controller@2cf00000 { |
55 | compatible = "arm,gic-v3"; | 70 | compatible = "arm,gic-v3"; |
56 | #interrupt-cells = <3>; | 71 | #interrupt-cells = <3>; |
72 | #address-cells = <2>; | ||
73 | #size-cells = <2>; | ||
74 | ranges; | ||
57 | interrupt-controller; | 75 | interrupt-controller; |
58 | reg = <0x0 0x2f000000 0 0x10000>, // GICD | 76 | reg = <0x0 0x2f000000 0 0x10000>, // GICD |
59 | <0x0 0x2f100000 0 0x200000>, // GICR | 77 | <0x0 0x2f100000 0 0x200000>, // GICR |
@@ -61,11 +79,20 @@ Examples: | |||
61 | <0x0 0x2c010000 0 0x2000>, // GICH | 79 | <0x0 0x2c010000 0 0x2000>, // GICH |
62 | <0x0 0x2c020000 0 0x2000>; // GICV | 80 | <0x0 0x2c020000 0 0x2000>; // GICV |
63 | interrupts = <1 9 4>; | 81 | interrupts = <1 9 4>; |
82 | |||
83 | gic-its@2c200000 { | ||
84 | compatible = "arm,gic-v3-its"; | ||
85 | msi-controller; | ||
86 | reg = <0x0 0x2c200000 0 0x200000>; | ||
87 | }; | ||
64 | }; | 88 | }; |
65 | 89 | ||
66 | gic: interrupt-controller@2c010000 { | 90 | gic: interrupt-controller@2c010000 { |
67 | compatible = "arm,gic-v3"; | 91 | compatible = "arm,gic-v3"; |
68 | #interrupt-cells = <3>; | 92 | #interrupt-cells = <3>; |
93 | #address-cells = <2>; | ||
94 | #size-cells = <2>; | ||
95 | ranges; | ||
69 | interrupt-controller; | 96 | interrupt-controller; |
70 | redistributor-stride = <0x0 0x40000>; // 256kB stride | 97 | redistributor-stride = <0x0 0x40000>; // 256kB stride |
71 | #redistributor-regions = <2>; | 98 | #redistributor-regions = <2>; |
@@ -76,4 +103,16 @@ Examples: | |||
76 | <0x0 0x2c060000 0 0x2000>, // GICH | 103 | <0x0 0x2c060000 0 0x2000>, // GICH |
77 | <0x0 0x2c080000 0 0x2000>; // GICV | 104 | <0x0 0x2c080000 0 0x2000>; // GICV |
78 | interrupts = <1 9 4>; | 105 | interrupts = <1 9 4>; |
106 | |||
107 | gic-its@2c200000 { | ||
108 | compatible = "arm,gic-v3-its"; | ||
109 | msi-controller; | ||
110 | reg = <0x0 0x2c200000 0 0x200000>; | ||
111 | }; | ||
112 | |||
113 | gic-its@2c400000 { | ||
114 | compatible = "arm,gic-v3-its"; | ||
115 | msi-controller; | ||
116 | reg = <0x0 0x2c400000 0 0x200000>; | ||
117 | }; | ||
79 | }; | 118 | }; |
diff --git a/Documentation/devicetree/bindings/arm/gic.txt b/Documentation/devicetree/bindings/arm/gic.txt index b38608af66db..8112d0c3675a 100644 --- a/Documentation/devicetree/bindings/arm/gic.txt +++ b/Documentation/devicetree/bindings/arm/gic.txt | |||
@@ -97,3 +97,56 @@ Example: | |||
97 | <0x2c006000 0x2000>; | 97 | <0x2c006000 0x2000>; |
98 | interrupts = <1 9 0xf04>; | 98 | interrupts = <1 9 0xf04>; |
99 | }; | 99 | }; |
100 | |||
101 | |||
102 | * GICv2m extension for MSI/MSI-x support (Optional) | ||
103 | |||
104 | Certain revisions of GIC-400 supports MSI/MSI-x via V2M register frame(s). | ||
105 | This is enabled by specifying v2m sub-node(s). | ||
106 | |||
107 | Required properties: | ||
108 | |||
109 | - compatible : The value here should contain "arm,gic-v2m-frame". | ||
110 | |||
111 | - msi-controller : Identifies the node as an MSI controller. | ||
112 | |||
113 | - reg : GICv2m MSI interface register base and size | ||
114 | |||
115 | Optional properties: | ||
116 | |||
117 | - arm,msi-base-spi : When the MSI_TYPER register contains an incorrect | ||
118 | value, this property should contain the SPI base of | ||
119 | the MSI frame, overriding the HW value. | ||
120 | |||
121 | - arm,msi-num-spis : When the MSI_TYPER register contains an incorrect | ||
122 | value, this property should contain the number of | ||
123 | SPIs assigned to the frame, overriding the HW value. | ||
124 | |||
125 | Example: | ||
126 | |||
127 | interrupt-controller@e1101000 { | ||
128 | compatible = "arm,gic-400"; | ||
129 | #interrupt-cells = <3>; | ||
130 | #address-cells = <2>; | ||
131 | #size-cells = <2>; | ||
132 | interrupt-controller; | ||
133 | interrupts = <1 8 0xf04>; | ||
134 | ranges = <0 0 0 0xe1100000 0 0x100000>; | ||
135 | reg = <0x0 0xe1110000 0 0x01000>, | ||
136 | <0x0 0xe112f000 0 0x02000>, | ||
137 | <0x0 0xe1140000 0 0x10000>, | ||
138 | <0x0 0xe1160000 0 0x10000>; | ||
139 | v2m0: v2m@0x8000 { | ||
140 | compatible = "arm,gic-v2m-frame"; | ||
141 | msi-controller; | ||
142 | reg = <0x0 0x80000 0 0x1000>; | ||
143 | }; | ||
144 | |||
145 | .... | ||
146 | |||
147 | v2mN: v2m@0x9000 { | ||
148 | compatible = "arm,gic-v2m-frame"; | ||
149 | msi-controller; | ||
150 | reg = <0x0 0x90000 0 0x1000>; | ||
151 | }; | ||
152 | }; | ||
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,sysirq.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,sysirq.txt new file mode 100644 index 000000000000..d680b07ec6e8 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,sysirq.txt | |||
@@ -0,0 +1,28 @@ | |||
1 | Mediatek 65xx/81xx sysirq | ||
2 | |||
3 | Mediatek SOCs sysirq support controllable irq inverter for each GIC SPI | ||
4 | interrupt. | ||
5 | |||
6 | Required properties: | ||
7 | - compatible: should be one of: | ||
8 | "mediatek,mt8135-sysirq" | ||
9 | "mediatek,mt8127-sysirq" | ||
10 | "mediatek,mt6589-sysirq" | ||
11 | "mediatek,mt6582-sysirq" | ||
12 | "mediatek,mt6577-sysirq" | ||
13 | - interrupt-controller : Identifies the node as an interrupt controller | ||
14 | - #interrupt-cells : Use the same format as specified by GIC in | ||
15 | Documentation/devicetree/bindings/arm/gic.txt | ||
16 | - interrupt-parent: phandle of irq parent for sysirq. The parent must | ||
17 | use the same interrupt-cells format as GIC. | ||
18 | - reg: Physical base address of the intpol registers and length of memory | ||
19 | mapped region. | ||
20 | |||
21 | Example: | ||
22 | sysirq: interrupt-controller@10200100 { | ||
23 | compatible = "mediatek,mt6589-sysirq", "mediatek,mt6577-sysirq"; | ||
24 | interrupt-controller; | ||
25 | #interrupt-cells = <3>; | ||
26 | interrupt-parent = <&gic>; | ||
27 | reg = <0 0x10200100 0 0x1c>; | ||
28 | }; | ||
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 688db03ef5b8..b1f9a20a3677 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig | |||
@@ -14,7 +14,9 @@ config ARM64 | |||
14 | select ARM_ARCH_TIMER | 14 | select ARM_ARCH_TIMER |
15 | select ARM_GIC | 15 | select ARM_GIC |
16 | select AUDIT_ARCH_COMPAT_GENERIC | 16 | select AUDIT_ARCH_COMPAT_GENERIC |
17 | select ARM_GIC_V2M if PCI_MSI | ||
17 | select ARM_GIC_V3 | 18 | select ARM_GIC_V3 |
19 | select ARM_GIC_V3_ITS if PCI_MSI | ||
18 | select BUILDTIME_EXTABLE_SORT | 20 | select BUILDTIME_EXTABLE_SORT |
19 | select CLONE_BACKWARDS | 21 | select CLONE_BACKWARDS |
20 | select COMMON_CLK | 22 | select COMMON_CLK |
diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild index 6b61091c7f4c..55103e50c51b 100644 --- a/arch/arm64/include/asm/Kbuild +++ b/arch/arm64/include/asm/Kbuild | |||
@@ -27,6 +27,7 @@ generic-y += local64.h | |||
27 | generic-y += mcs_spinlock.h | 27 | generic-y += mcs_spinlock.h |
28 | generic-y += mman.h | 28 | generic-y += mman.h |
29 | generic-y += msgbuf.h | 29 | generic-y += msgbuf.h |
30 | generic-y += msi.h | ||
30 | generic-y += mutex.h | 31 | generic-y += mutex.h |
31 | generic-y += pci.h | 32 | generic-y += pci.h |
32 | generic-y += pci-bridge.h | 33 | generic-y += pci-bridge.h |
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index e12cb23d786c..cc79d2a5a8c2 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -5,8 +5,15 @@ config IRQCHIP | |||
5 | config ARM_GIC | 5 | config ARM_GIC |
6 | bool | 6 | bool |
7 | select IRQ_DOMAIN | 7 | select IRQ_DOMAIN |
8 | select IRQ_DOMAIN_HIERARCHY | ||
8 | select MULTI_IRQ_HANDLER | 9 | select MULTI_IRQ_HANDLER |
9 | 10 | ||
11 | config ARM_GIC_V2M | ||
12 | bool | ||
13 | depends on ARM_GIC | ||
14 | depends on PCI && PCI_MSI | ||
15 | select PCI_MSI_IRQ_DOMAIN | ||
16 | |||
10 | config GIC_NON_BANKED | 17 | config GIC_NON_BANKED |
11 | bool | 18 | bool |
12 | 19 | ||
@@ -14,6 +21,11 @@ config ARM_GIC_V3 | |||
14 | bool | 21 | bool |
15 | select IRQ_DOMAIN | 22 | select IRQ_DOMAIN |
16 | select MULTI_IRQ_HANDLER | 23 | select MULTI_IRQ_HANDLER |
24 | select IRQ_DOMAIN_HIERARCHY | ||
25 | |||
26 | config ARM_GIC_V3_ITS | ||
27 | bool | ||
28 | select PCI_MSI_IRQ_DOMAIN | ||
17 | 29 | ||
18 | config ARM_NVIC | 30 | config ARM_NVIC |
19 | bool | 31 | bool |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 4954a314c31e..9516a324be6d 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -19,7 +19,9 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o | |||
19 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o | 19 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o |
20 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o | 20 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o |
21 | obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o | 21 | obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o |
22 | obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o | ||
22 | obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o | 23 | obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o |
24 | obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o | ||
23 | obj-$(CONFIG_ARM_NVIC) += irq-nvic.o | 25 | obj-$(CONFIG_ARM_NVIC) += irq-nvic.o |
24 | obj-$(CONFIG_ARM_VIC) += irq-vic.o | 26 | obj-$(CONFIG_ARM_VIC) += irq-vic.o |
25 | obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o | 27 | obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o |
@@ -39,3 +41,4 @@ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o | |||
39 | obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o | 41 | obj-$(CONFIG_BRCMSTB_L2_IRQ) += irq-brcmstb-l2.o |
40 | obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o | 42 | obj-$(CONFIG_KEYSTONE_IRQ) += irq-keystone.o |
41 | obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o | 43 | obj-$(CONFIG_MIPS_GIC) += irq-mips-gic.o |
44 | obj-$(CONFIG_ARCH_MEDIATEK) += irq-mtk-sysirq.o | ||
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c new file mode 100644 index 000000000000..fdf706555d72 --- /dev/null +++ b/drivers/irqchip/irq-gic-v2m.c | |||
@@ -0,0 +1,333 @@ | |||
1 | /* | ||
2 | * ARM GIC v2m MSI(-X) support | ||
3 | * Support for Message Signaled Interrupts for systems that | ||
4 | * implement ARM Generic Interrupt Controller: GICv2m. | ||
5 | * | ||
6 | * Copyright (C) 2014 Advanced Micro Devices, Inc. | ||
7 | * Authors: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> | ||
8 | * Harish Kasiviswanathan <harish.kasiviswanathan@amd.com> | ||
9 | * Brandon Anderson <brandon.anderson@amd.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License version 2 as published | ||
13 | * by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #define pr_fmt(fmt) "GICv2m: " fmt | ||
17 | |||
18 | #include <linux/irq.h> | ||
19 | #include <linux/irqdomain.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/of_address.h> | ||
22 | #include <linux/of_pci.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/spinlock.h> | ||
25 | |||
26 | /* | ||
27 | * MSI_TYPER: | ||
28 | * [31:26] Reserved | ||
29 | * [25:16] lowest SPI assigned to MSI | ||
30 | * [15:10] Reserved | ||
31 | * [9:0] Numer of SPIs assigned to MSI | ||
32 | */ | ||
33 | #define V2M_MSI_TYPER 0x008 | ||
34 | #define V2M_MSI_TYPER_BASE_SHIFT 16 | ||
35 | #define V2M_MSI_TYPER_BASE_MASK 0x3FF | ||
36 | #define V2M_MSI_TYPER_NUM_MASK 0x3FF | ||
37 | #define V2M_MSI_SETSPI_NS 0x040 | ||
38 | #define V2M_MIN_SPI 32 | ||
39 | #define V2M_MAX_SPI 1019 | ||
40 | |||
41 | #define V2M_MSI_TYPER_BASE_SPI(x) \ | ||
42 | (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK) | ||
43 | |||
44 | #define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK) | ||
45 | |||
46 | struct v2m_data { | ||
47 | spinlock_t msi_cnt_lock; | ||
48 | struct msi_controller mchip; | ||
49 | struct resource res; /* GICv2m resource */ | ||
50 | void __iomem *base; /* GICv2m virt address */ | ||
51 | u32 spi_start; /* The SPI number that MSIs start */ | ||
52 | u32 nr_spis; /* The number of SPIs for MSIs */ | ||
53 | unsigned long *bm; /* MSI vector bitmap */ | ||
54 | struct irq_domain *domain; | ||
55 | }; | ||
56 | |||
57 | static void gicv2m_mask_msi_irq(struct irq_data *d) | ||
58 | { | ||
59 | pci_msi_mask_irq(d); | ||
60 | irq_chip_mask_parent(d); | ||
61 | } | ||
62 | |||
63 | static void gicv2m_unmask_msi_irq(struct irq_data *d) | ||
64 | { | ||
65 | pci_msi_unmask_irq(d); | ||
66 | irq_chip_unmask_parent(d); | ||
67 | } | ||
68 | |||
69 | static struct irq_chip gicv2m_msi_irq_chip = { | ||
70 | .name = "MSI", | ||
71 | .irq_mask = gicv2m_mask_msi_irq, | ||
72 | .irq_unmask = gicv2m_unmask_msi_irq, | ||
73 | .irq_eoi = irq_chip_eoi_parent, | ||
74 | .irq_write_msi_msg = pci_msi_domain_write_msg, | ||
75 | }; | ||
76 | |||
77 | static struct msi_domain_info gicv2m_msi_domain_info = { | ||
78 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | | ||
79 | MSI_FLAG_PCI_MSIX), | ||
80 | .chip = &gicv2m_msi_irq_chip, | ||
81 | }; | ||
82 | |||
83 | static int gicv2m_set_affinity(struct irq_data *irq_data, | ||
84 | const struct cpumask *mask, bool force) | ||
85 | { | ||
86 | int ret; | ||
87 | |||
88 | ret = irq_chip_set_affinity_parent(irq_data, mask, force); | ||
89 | if (ret == IRQ_SET_MASK_OK) | ||
90 | ret = IRQ_SET_MASK_OK_DONE; | ||
91 | |||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) | ||
96 | { | ||
97 | struct v2m_data *v2m = irq_data_get_irq_chip_data(data); | ||
98 | phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS; | ||
99 | |||
100 | msg->address_hi = (u32) (addr >> 32); | ||
101 | msg->address_lo = (u32) (addr); | ||
102 | msg->data = data->hwirq; | ||
103 | } | ||
104 | |||
105 | static struct irq_chip gicv2m_irq_chip = { | ||
106 | .name = "GICv2m", | ||
107 | .irq_mask = irq_chip_mask_parent, | ||
108 | .irq_unmask = irq_chip_unmask_parent, | ||
109 | .irq_eoi = irq_chip_eoi_parent, | ||
110 | .irq_set_affinity = gicv2m_set_affinity, | ||
111 | .irq_compose_msi_msg = gicv2m_compose_msi_msg, | ||
112 | }; | ||
113 | |||
114 | static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, | ||
115 | unsigned int virq, | ||
116 | irq_hw_number_t hwirq) | ||
117 | { | ||
118 | struct of_phandle_args args; | ||
119 | struct irq_data *d; | ||
120 | int err; | ||
121 | |||
122 | args.np = domain->parent->of_node; | ||
123 | args.args_count = 3; | ||
124 | args.args[0] = 0; | ||
125 | args.args[1] = hwirq - 32; | ||
126 | args.args[2] = IRQ_TYPE_EDGE_RISING; | ||
127 | |||
128 | err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args); | ||
129 | if (err) | ||
130 | return err; | ||
131 | |||
132 | /* Configure the interrupt line to be edge */ | ||
133 | d = irq_domain_get_irq_data(domain->parent, virq); | ||
134 | d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING); | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq) | ||
139 | { | ||
140 | int pos; | ||
141 | |||
142 | pos = hwirq - v2m->spi_start; | ||
143 | if (pos < 0 || pos >= v2m->nr_spis) { | ||
144 | pr_err("Failed to teardown msi. Invalid hwirq %d\n", hwirq); | ||
145 | return; | ||
146 | } | ||
147 | |||
148 | spin_lock(&v2m->msi_cnt_lock); | ||
149 | __clear_bit(pos, v2m->bm); | ||
150 | spin_unlock(&v2m->msi_cnt_lock); | ||
151 | } | ||
152 | |||
153 | static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | ||
154 | unsigned int nr_irqs, void *args) | ||
155 | { | ||
156 | struct v2m_data *v2m = domain->host_data; | ||
157 | int hwirq, offset, err = 0; | ||
158 | |||
159 | spin_lock(&v2m->msi_cnt_lock); | ||
160 | offset = find_first_zero_bit(v2m->bm, v2m->nr_spis); | ||
161 | if (offset < v2m->nr_spis) | ||
162 | __set_bit(offset, v2m->bm); | ||
163 | else | ||
164 | err = -ENOSPC; | ||
165 | spin_unlock(&v2m->msi_cnt_lock); | ||
166 | |||
167 | if (err) | ||
168 | return err; | ||
169 | |||
170 | hwirq = v2m->spi_start + offset; | ||
171 | |||
172 | err = gicv2m_irq_gic_domain_alloc(domain, virq, hwirq); | ||
173 | if (err) { | ||
174 | gicv2m_unalloc_msi(v2m, hwirq); | ||
175 | return err; | ||
176 | } | ||
177 | |||
178 | irq_domain_set_hwirq_and_chip(domain, virq, hwirq, | ||
179 | &gicv2m_irq_chip, v2m); | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static void gicv2m_irq_domain_free(struct irq_domain *domain, | ||
185 | unsigned int virq, unsigned int nr_irqs) | ||
186 | { | ||
187 | struct irq_data *d = irq_domain_get_irq_data(domain, virq); | ||
188 | struct v2m_data *v2m = irq_data_get_irq_chip_data(d); | ||
189 | |||
190 | BUG_ON(nr_irqs != 1); | ||
191 | gicv2m_unalloc_msi(v2m, d->hwirq); | ||
192 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | ||
193 | } | ||
194 | |||
195 | static const struct irq_domain_ops gicv2m_domain_ops = { | ||
196 | .alloc = gicv2m_irq_domain_alloc, | ||
197 | .free = gicv2m_irq_domain_free, | ||
198 | }; | ||
199 | |||
200 | static bool is_msi_spi_valid(u32 base, u32 num) | ||
201 | { | ||
202 | if (base < V2M_MIN_SPI) { | ||
203 | pr_err("Invalid MSI base SPI (base:%u)\n", base); | ||
204 | return false; | ||
205 | } | ||
206 | |||
207 | if ((num == 0) || (base + num > V2M_MAX_SPI)) { | ||
208 | pr_err("Number of SPIs (%u) exceed maximum (%u)\n", | ||
209 | num, V2M_MAX_SPI - V2M_MIN_SPI + 1); | ||
210 | return false; | ||
211 | } | ||
212 | |||
213 | return true; | ||
214 | } | ||
215 | |||
216 | static int __init gicv2m_init_one(struct device_node *node, | ||
217 | struct irq_domain *parent) | ||
218 | { | ||
219 | int ret; | ||
220 | struct v2m_data *v2m; | ||
221 | |||
222 | v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL); | ||
223 | if (!v2m) { | ||
224 | pr_err("Failed to allocate struct v2m_data.\n"); | ||
225 | return -ENOMEM; | ||
226 | } | ||
227 | |||
228 | ret = of_address_to_resource(node, 0, &v2m->res); | ||
229 | if (ret) { | ||
230 | pr_err("Failed to allocate v2m resource.\n"); | ||
231 | goto err_free_v2m; | ||
232 | } | ||
233 | |||
234 | v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); | ||
235 | if (!v2m->base) { | ||
236 | pr_err("Failed to map GICv2m resource\n"); | ||
237 | ret = -ENOMEM; | ||
238 | goto err_free_v2m; | ||
239 | } | ||
240 | |||
241 | if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) && | ||
242 | !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) { | ||
243 | pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n", | ||
244 | v2m->spi_start, v2m->nr_spis); | ||
245 | } else { | ||
246 | u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); | ||
247 | |||
248 | v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); | ||
249 | v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer); | ||
250 | } | ||
251 | |||
252 | if (!is_msi_spi_valid(v2m->spi_start, v2m->nr_spis)) { | ||
253 | ret = -EINVAL; | ||
254 | goto err_iounmap; | ||
255 | } | ||
256 | |||
257 | v2m->bm = kzalloc(sizeof(long) * BITS_TO_LONGS(v2m->nr_spis), | ||
258 | GFP_KERNEL); | ||
259 | if (!v2m->bm) { | ||
260 | ret = -ENOMEM; | ||
261 | goto err_iounmap; | ||
262 | } | ||
263 | |||
264 | v2m->domain = irq_domain_add_tree(NULL, &gicv2m_domain_ops, v2m); | ||
265 | if (!v2m->domain) { | ||
266 | pr_err("Failed to create GICv2m domain\n"); | ||
267 | ret = -ENOMEM; | ||
268 | goto err_free_bm; | ||
269 | } | ||
270 | |||
271 | v2m->domain->parent = parent; | ||
272 | v2m->mchip.of_node = node; | ||
273 | v2m->mchip.domain = pci_msi_create_irq_domain(node, | ||
274 | &gicv2m_msi_domain_info, | ||
275 | v2m->domain); | ||
276 | if (!v2m->mchip.domain) { | ||
277 | pr_err("Failed to create MSI domain\n"); | ||
278 | ret = -ENOMEM; | ||
279 | goto err_free_domains; | ||
280 | } | ||
281 | |||
282 | spin_lock_init(&v2m->msi_cnt_lock); | ||
283 | |||
284 | ret = of_pci_msi_chip_add(&v2m->mchip); | ||
285 | if (ret) { | ||
286 | pr_err("Failed to add msi_chip.\n"); | ||
287 | goto err_free_domains; | ||
288 | } | ||
289 | |||
290 | pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name, | ||
291 | (unsigned long)v2m->res.start, (unsigned long)v2m->res.end, | ||
292 | v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); | ||
293 | |||
294 | return 0; | ||
295 | |||
296 | err_free_domains: | ||
297 | if (v2m->mchip.domain) | ||
298 | irq_domain_remove(v2m->mchip.domain); | ||
299 | if (v2m->domain) | ||
300 | irq_domain_remove(v2m->domain); | ||
301 | err_free_bm: | ||
302 | kfree(v2m->bm); | ||
303 | err_iounmap: | ||
304 | iounmap(v2m->base); | ||
305 | err_free_v2m: | ||
306 | kfree(v2m); | ||
307 | return ret; | ||
308 | } | ||
309 | |||
310 | static struct of_device_id gicv2m_device_id[] = { | ||
311 | { .compatible = "arm,gic-v2m-frame", }, | ||
312 | {}, | ||
313 | }; | ||
314 | |||
315 | int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) | ||
316 | { | ||
317 | int ret = 0; | ||
318 | struct device_node *child; | ||
319 | |||
320 | for (child = of_find_matching_node(node, gicv2m_device_id); child; | ||
321 | child = of_find_matching_node(child, gicv2m_device_id)) { | ||
322 | if (!of_find_property(child, "msi-controller", NULL)) | ||
323 | continue; | ||
324 | |||
325 | ret = gicv2m_init_one(child, parent); | ||
326 | if (ret) { | ||
327 | of_node_put(node); | ||
328 | break; | ||
329 | } | ||
330 | } | ||
331 | |||
332 | return ret; | ||
333 | } | ||
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c new file mode 100644 index 000000000000..86e4684adeb1 --- /dev/null +++ b/drivers/irqchip/irq-gic-v3-its.c | |||
@@ -0,0 +1,1425 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013, 2014 ARM Limited, All Rights Reserved. | ||
3 | * Author: Marc Zyngier <marc.zyngier@arm.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <linux/bitmap.h> | ||
19 | #include <linux/cpu.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/log2.h> | ||
23 | #include <linux/mm.h> | ||
24 | #include <linux/msi.h> | ||
25 | #include <linux/of.h> | ||
26 | #include <linux/of_address.h> | ||
27 | #include <linux/of_irq.h> | ||
28 | #include <linux/of_pci.h> | ||
29 | #include <linux/of_platform.h> | ||
30 | #include <linux/percpu.h> | ||
31 | #include <linux/slab.h> | ||
32 | |||
33 | #include <linux/irqchip/arm-gic-v3.h> | ||
34 | |||
35 | #include <asm/cacheflush.h> | ||
36 | #include <asm/cputype.h> | ||
37 | #include <asm/exception.h> | ||
38 | |||
39 | #include "irqchip.h" | ||
40 | |||
41 | #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1 << 0) | ||
42 | |||
43 | #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) | ||
44 | |||
45 | /* | ||
46 | * Collection structure - just an ID, and a redistributor address to | ||
47 | * ping. We use one per CPU as a bag of interrupts assigned to this | ||
48 | * CPU. | ||
49 | */ | ||
50 | struct its_collection { | ||
51 | u64 target_address; | ||
52 | u16 col_id; | ||
53 | }; | ||
54 | |||
55 | /* | ||
56 | * The ITS structure - contains most of the infrastructure, with the | ||
57 | * msi_controller, the command queue, the collections, and the list of | ||
58 | * devices writing to it. | ||
59 | */ | ||
60 | struct its_node { | ||
61 | raw_spinlock_t lock; | ||
62 | struct list_head entry; | ||
63 | struct msi_controller msi_chip; | ||
64 | struct irq_domain *domain; | ||
65 | void __iomem *base; | ||
66 | unsigned long phys_base; | ||
67 | struct its_cmd_block *cmd_base; | ||
68 | struct its_cmd_block *cmd_write; | ||
69 | void *tables[GITS_BASER_NR_REGS]; | ||
70 | struct its_collection *collections; | ||
71 | struct list_head its_device_list; | ||
72 | u64 flags; | ||
73 | u32 ite_size; | ||
74 | }; | ||
75 | |||
76 | #define ITS_ITT_ALIGN SZ_256 | ||
77 | |||
78 | /* | ||
79 | * The ITS view of a device - belongs to an ITS, a collection, owns an | ||
80 | * interrupt translation table, and a list of interrupts. | ||
81 | */ | ||
82 | struct its_device { | ||
83 | struct list_head entry; | ||
84 | struct its_node *its; | ||
85 | struct its_collection *collection; | ||
86 | void *itt; | ||
87 | unsigned long *lpi_map; | ||
88 | irq_hw_number_t lpi_base; | ||
89 | int nr_lpis; | ||
90 | u32 nr_ites; | ||
91 | u32 device_id; | ||
92 | }; | ||
93 | |||
94 | static LIST_HEAD(its_nodes); | ||
95 | static DEFINE_SPINLOCK(its_lock); | ||
96 | static struct device_node *gic_root_node; | ||
97 | static struct rdists *gic_rdists; | ||
98 | |||
99 | #define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) | ||
100 | #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) | ||
101 | |||
102 | /* | ||
103 | * ITS command descriptors - parameters to be encoded in a command | ||
104 | * block. | ||
105 | */ | ||
106 | struct its_cmd_desc { | ||
107 | union { | ||
108 | struct { | ||
109 | struct its_device *dev; | ||
110 | u32 event_id; | ||
111 | } its_inv_cmd; | ||
112 | |||
113 | struct { | ||
114 | struct its_device *dev; | ||
115 | u32 event_id; | ||
116 | } its_int_cmd; | ||
117 | |||
118 | struct { | ||
119 | struct its_device *dev; | ||
120 | int valid; | ||
121 | } its_mapd_cmd; | ||
122 | |||
123 | struct { | ||
124 | struct its_collection *col; | ||
125 | int valid; | ||
126 | } its_mapc_cmd; | ||
127 | |||
128 | struct { | ||
129 | struct its_device *dev; | ||
130 | u32 phys_id; | ||
131 | u32 event_id; | ||
132 | } its_mapvi_cmd; | ||
133 | |||
134 | struct { | ||
135 | struct its_device *dev; | ||
136 | struct its_collection *col; | ||
137 | u32 id; | ||
138 | } its_movi_cmd; | ||
139 | |||
140 | struct { | ||
141 | struct its_device *dev; | ||
142 | u32 event_id; | ||
143 | } its_discard_cmd; | ||
144 | |||
145 | struct { | ||
146 | struct its_collection *col; | ||
147 | } its_invall_cmd; | ||
148 | }; | ||
149 | }; | ||
150 | |||
151 | /* | ||
152 | * The ITS command block, which is what the ITS actually parses. | ||
153 | */ | ||
154 | struct its_cmd_block { | ||
155 | u64 raw_cmd[4]; | ||
156 | }; | ||
157 | |||
158 | #define ITS_CMD_QUEUE_SZ SZ_64K | ||
159 | #define ITS_CMD_QUEUE_NR_ENTRIES (ITS_CMD_QUEUE_SZ / sizeof(struct its_cmd_block)) | ||
160 | |||
161 | typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *, | ||
162 | struct its_cmd_desc *); | ||
163 | |||
164 | static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) | ||
165 | { | ||
166 | cmd->raw_cmd[0] &= ~0xffUL; | ||
167 | cmd->raw_cmd[0] |= cmd_nr; | ||
168 | } | ||
169 | |||
170 | static void its_encode_devid(struct its_cmd_block *cmd, u32 devid) | ||
171 | { | ||
172 | cmd->raw_cmd[0] &= ~(0xffffUL << 32); | ||
173 | cmd->raw_cmd[0] |= ((u64)devid) << 32; | ||
174 | } | ||
175 | |||
176 | static void its_encode_event_id(struct its_cmd_block *cmd, u32 id) | ||
177 | { | ||
178 | cmd->raw_cmd[1] &= ~0xffffffffUL; | ||
179 | cmd->raw_cmd[1] |= id; | ||
180 | } | ||
181 | |||
182 | static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id) | ||
183 | { | ||
184 | cmd->raw_cmd[1] &= 0xffffffffUL; | ||
185 | cmd->raw_cmd[1] |= ((u64)phys_id) << 32; | ||
186 | } | ||
187 | |||
188 | static void its_encode_size(struct its_cmd_block *cmd, u8 size) | ||
189 | { | ||
190 | cmd->raw_cmd[1] &= ~0x1fUL; | ||
191 | cmd->raw_cmd[1] |= size & 0x1f; | ||
192 | } | ||
193 | |||
194 | static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr) | ||
195 | { | ||
196 | cmd->raw_cmd[2] &= ~0xffffffffffffUL; | ||
197 | cmd->raw_cmd[2] |= itt_addr & 0xffffffffff00UL; | ||
198 | } | ||
199 | |||
200 | static void its_encode_valid(struct its_cmd_block *cmd, int valid) | ||
201 | { | ||
202 | cmd->raw_cmd[2] &= ~(1UL << 63); | ||
203 | cmd->raw_cmd[2] |= ((u64)!!valid) << 63; | ||
204 | } | ||
205 | |||
206 | static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr) | ||
207 | { | ||
208 | cmd->raw_cmd[2] &= ~(0xffffffffUL << 16); | ||
209 | cmd->raw_cmd[2] |= (target_addr & (0xffffffffUL << 16)); | ||
210 | } | ||
211 | |||
212 | static void its_encode_collection(struct its_cmd_block *cmd, u16 col) | ||
213 | { | ||
214 | cmd->raw_cmd[2] &= ~0xffffUL; | ||
215 | cmd->raw_cmd[2] |= col; | ||
216 | } | ||
217 | |||
218 | static inline void its_fixup_cmd(struct its_cmd_block *cmd) | ||
219 | { | ||
220 | /* Let's fixup BE commands */ | ||
221 | cmd->raw_cmd[0] = cpu_to_le64(cmd->raw_cmd[0]); | ||
222 | cmd->raw_cmd[1] = cpu_to_le64(cmd->raw_cmd[1]); | ||
223 | cmd->raw_cmd[2] = cpu_to_le64(cmd->raw_cmd[2]); | ||
224 | cmd->raw_cmd[3] = cpu_to_le64(cmd->raw_cmd[3]); | ||
225 | } | ||
226 | |||
227 | static struct its_collection *its_build_mapd_cmd(struct its_cmd_block *cmd, | ||
228 | struct its_cmd_desc *desc) | ||
229 | { | ||
230 | unsigned long itt_addr; | ||
231 | u8 size = ilog2(desc->its_mapd_cmd.dev->nr_ites); | ||
232 | |||
233 | itt_addr = virt_to_phys(desc->its_mapd_cmd.dev->itt); | ||
234 | itt_addr = ALIGN(itt_addr, ITS_ITT_ALIGN); | ||
235 | |||
236 | its_encode_cmd(cmd, GITS_CMD_MAPD); | ||
237 | its_encode_devid(cmd, desc->its_mapd_cmd.dev->device_id); | ||
238 | its_encode_size(cmd, size - 1); | ||
239 | its_encode_itt(cmd, itt_addr); | ||
240 | its_encode_valid(cmd, desc->its_mapd_cmd.valid); | ||
241 | |||
242 | its_fixup_cmd(cmd); | ||
243 | |||
244 | return desc->its_mapd_cmd.dev->collection; | ||
245 | } | ||
246 | |||
247 | static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd, | ||
248 | struct its_cmd_desc *desc) | ||
249 | { | ||
250 | its_encode_cmd(cmd, GITS_CMD_MAPC); | ||
251 | its_encode_collection(cmd, desc->its_mapc_cmd.col->col_id); | ||
252 | its_encode_target(cmd, desc->its_mapc_cmd.col->target_address); | ||
253 | its_encode_valid(cmd, desc->its_mapc_cmd.valid); | ||
254 | |||
255 | its_fixup_cmd(cmd); | ||
256 | |||
257 | return desc->its_mapc_cmd.col; | ||
258 | } | ||
259 | |||
260 | static struct its_collection *its_build_mapvi_cmd(struct its_cmd_block *cmd, | ||
261 | struct its_cmd_desc *desc) | ||
262 | { | ||
263 | its_encode_cmd(cmd, GITS_CMD_MAPVI); | ||
264 | its_encode_devid(cmd, desc->its_mapvi_cmd.dev->device_id); | ||
265 | its_encode_event_id(cmd, desc->its_mapvi_cmd.event_id); | ||
266 | its_encode_phys_id(cmd, desc->its_mapvi_cmd.phys_id); | ||
267 | its_encode_collection(cmd, desc->its_mapvi_cmd.dev->collection->col_id); | ||
268 | |||
269 | its_fixup_cmd(cmd); | ||
270 | |||
271 | return desc->its_mapvi_cmd.dev->collection; | ||
272 | } | ||
273 | |||
274 | static struct its_collection *its_build_movi_cmd(struct its_cmd_block *cmd, | ||
275 | struct its_cmd_desc *desc) | ||
276 | { | ||
277 | its_encode_cmd(cmd, GITS_CMD_MOVI); | ||
278 | its_encode_devid(cmd, desc->its_movi_cmd.dev->device_id); | ||
279 | its_encode_event_id(cmd, desc->its_movi_cmd.id); | ||
280 | its_encode_collection(cmd, desc->its_movi_cmd.col->col_id); | ||
281 | |||
282 | its_fixup_cmd(cmd); | ||
283 | |||
284 | return desc->its_movi_cmd.dev->collection; | ||
285 | } | ||
286 | |||
287 | static struct its_collection *its_build_discard_cmd(struct its_cmd_block *cmd, | ||
288 | struct its_cmd_desc *desc) | ||
289 | { | ||
290 | its_encode_cmd(cmd, GITS_CMD_DISCARD); | ||
291 | its_encode_devid(cmd, desc->its_discard_cmd.dev->device_id); | ||
292 | its_encode_event_id(cmd, desc->its_discard_cmd.event_id); | ||
293 | |||
294 | its_fixup_cmd(cmd); | ||
295 | |||
296 | return desc->its_discard_cmd.dev->collection; | ||
297 | } | ||
298 | |||
299 | static struct its_collection *its_build_inv_cmd(struct its_cmd_block *cmd, | ||
300 | struct its_cmd_desc *desc) | ||
301 | { | ||
302 | its_encode_cmd(cmd, GITS_CMD_INV); | ||
303 | its_encode_devid(cmd, desc->its_inv_cmd.dev->device_id); | ||
304 | its_encode_event_id(cmd, desc->its_inv_cmd.event_id); | ||
305 | |||
306 | its_fixup_cmd(cmd); | ||
307 | |||
308 | return desc->its_inv_cmd.dev->collection; | ||
309 | } | ||
310 | |||
311 | static struct its_collection *its_build_invall_cmd(struct its_cmd_block *cmd, | ||
312 | struct its_cmd_desc *desc) | ||
313 | { | ||
314 | its_encode_cmd(cmd, GITS_CMD_INVALL); | ||
315 | its_encode_collection(cmd, desc->its_mapc_cmd.col->col_id); | ||
316 | |||
317 | its_fixup_cmd(cmd); | ||
318 | |||
319 | return NULL; | ||
320 | } | ||
321 | |||
322 | static u64 its_cmd_ptr_to_offset(struct its_node *its, | ||
323 | struct its_cmd_block *ptr) | ||
324 | { | ||
325 | return (ptr - its->cmd_base) * sizeof(*ptr); | ||
326 | } | ||
327 | |||
328 | static int its_queue_full(struct its_node *its) | ||
329 | { | ||
330 | int widx; | ||
331 | int ridx; | ||
332 | |||
333 | widx = its->cmd_write - its->cmd_base; | ||
334 | ridx = readl_relaxed(its->base + GITS_CREADR) / sizeof(struct its_cmd_block); | ||
335 | |||
336 | /* This is incredibly unlikely to happen, unless the ITS locks up. */ | ||
337 | if (((widx + 1) % ITS_CMD_QUEUE_NR_ENTRIES) == ridx) | ||
338 | return 1; | ||
339 | |||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | static struct its_cmd_block *its_allocate_entry(struct its_node *its) | ||
344 | { | ||
345 | struct its_cmd_block *cmd; | ||
346 | u32 count = 1000000; /* 1s! */ | ||
347 | |||
348 | while (its_queue_full(its)) { | ||
349 | count--; | ||
350 | if (!count) { | ||
351 | pr_err_ratelimited("ITS queue not draining\n"); | ||
352 | return NULL; | ||
353 | } | ||
354 | cpu_relax(); | ||
355 | udelay(1); | ||
356 | } | ||
357 | |||
358 | cmd = its->cmd_write++; | ||
359 | |||
360 | /* Handle queue wrapping */ | ||
361 | if (its->cmd_write == (its->cmd_base + ITS_CMD_QUEUE_NR_ENTRIES)) | ||
362 | its->cmd_write = its->cmd_base; | ||
363 | |||
364 | return cmd; | ||
365 | } | ||
366 | |||
367 | static struct its_cmd_block *its_post_commands(struct its_node *its) | ||
368 | { | ||
369 | u64 wr = its_cmd_ptr_to_offset(its, its->cmd_write); | ||
370 | |||
371 | writel_relaxed(wr, its->base + GITS_CWRITER); | ||
372 | |||
373 | return its->cmd_write; | ||
374 | } | ||
375 | |||
376 | static void its_flush_cmd(struct its_node *its, struct its_cmd_block *cmd) | ||
377 | { | ||
378 | /* | ||
379 | * Make sure the commands written to memory are observable by | ||
380 | * the ITS. | ||
381 | */ | ||
382 | if (its->flags & ITS_FLAGS_CMDQ_NEEDS_FLUSHING) | ||
383 | __flush_dcache_area(cmd, sizeof(*cmd)); | ||
384 | else | ||
385 | dsb(ishst); | ||
386 | } | ||
387 | |||
388 | static void its_wait_for_range_completion(struct its_node *its, | ||
389 | struct its_cmd_block *from, | ||
390 | struct its_cmd_block *to) | ||
391 | { | ||
392 | u64 rd_idx, from_idx, to_idx; | ||
393 | u32 count = 1000000; /* 1s! */ | ||
394 | |||
395 | from_idx = its_cmd_ptr_to_offset(its, from); | ||
396 | to_idx = its_cmd_ptr_to_offset(its, to); | ||
397 | |||
398 | while (1) { | ||
399 | rd_idx = readl_relaxed(its->base + GITS_CREADR); | ||
400 | if (rd_idx >= to_idx || rd_idx < from_idx) | ||
401 | break; | ||
402 | |||
403 | count--; | ||
404 | if (!count) { | ||
405 | pr_err_ratelimited("ITS queue timeout\n"); | ||
406 | return; | ||
407 | } | ||
408 | cpu_relax(); | ||
409 | udelay(1); | ||
410 | } | ||
411 | } | ||
412 | |||
413 | static void its_send_single_command(struct its_node *its, | ||
414 | its_cmd_builder_t builder, | ||
415 | struct its_cmd_desc *desc) | ||
416 | { | ||
417 | struct its_cmd_block *cmd, *sync_cmd, *next_cmd; | ||
418 | struct its_collection *sync_col; | ||
419 | |||
420 | raw_spin_lock(&its->lock); | ||
421 | |||
422 | cmd = its_allocate_entry(its); | ||
423 | if (!cmd) { /* We're soooooo screewed... */ | ||
424 | pr_err_ratelimited("ITS can't allocate, dropping command\n"); | ||
425 | raw_spin_unlock(&its->lock); | ||
426 | return; | ||
427 | } | ||
428 | sync_col = builder(cmd, desc); | ||
429 | its_flush_cmd(its, cmd); | ||
430 | |||
431 | if (sync_col) { | ||
432 | sync_cmd = its_allocate_entry(its); | ||
433 | if (!sync_cmd) { | ||
434 | pr_err_ratelimited("ITS can't SYNC, skipping\n"); | ||
435 | goto post; | ||
436 | } | ||
437 | its_encode_cmd(sync_cmd, GITS_CMD_SYNC); | ||
438 | its_encode_target(sync_cmd, sync_col->target_address); | ||
439 | its_fixup_cmd(sync_cmd); | ||
440 | its_flush_cmd(its, sync_cmd); | ||
441 | } | ||
442 | |||
443 | post: | ||
444 | next_cmd = its_post_commands(its); | ||
445 | raw_spin_unlock(&its->lock); | ||
446 | |||
447 | its_wait_for_range_completion(its, cmd, next_cmd); | ||
448 | } | ||
449 | |||
450 | static void its_send_inv(struct its_device *dev, u32 event_id) | ||
451 | { | ||
452 | struct its_cmd_desc desc; | ||
453 | |||
454 | desc.its_inv_cmd.dev = dev; | ||
455 | desc.its_inv_cmd.event_id = event_id; | ||
456 | |||
457 | its_send_single_command(dev->its, its_build_inv_cmd, &desc); | ||
458 | } | ||
459 | |||
460 | static void its_send_mapd(struct its_device *dev, int valid) | ||
461 | { | ||
462 | struct its_cmd_desc desc; | ||
463 | |||
464 | desc.its_mapd_cmd.dev = dev; | ||
465 | desc.its_mapd_cmd.valid = !!valid; | ||
466 | |||
467 | its_send_single_command(dev->its, its_build_mapd_cmd, &desc); | ||
468 | } | ||
469 | |||
470 | static void its_send_mapc(struct its_node *its, struct its_collection *col, | ||
471 | int valid) | ||
472 | { | ||
473 | struct its_cmd_desc desc; | ||
474 | |||
475 | desc.its_mapc_cmd.col = col; | ||
476 | desc.its_mapc_cmd.valid = !!valid; | ||
477 | |||
478 | its_send_single_command(its, its_build_mapc_cmd, &desc); | ||
479 | } | ||
480 | |||
481 | static void its_send_mapvi(struct its_device *dev, u32 irq_id, u32 id) | ||
482 | { | ||
483 | struct its_cmd_desc desc; | ||
484 | |||
485 | desc.its_mapvi_cmd.dev = dev; | ||
486 | desc.its_mapvi_cmd.phys_id = irq_id; | ||
487 | desc.its_mapvi_cmd.event_id = id; | ||
488 | |||
489 | its_send_single_command(dev->its, its_build_mapvi_cmd, &desc); | ||
490 | } | ||
491 | |||
492 | static void its_send_movi(struct its_device *dev, | ||
493 | struct its_collection *col, u32 id) | ||
494 | { | ||
495 | struct its_cmd_desc desc; | ||
496 | |||
497 | desc.its_movi_cmd.dev = dev; | ||
498 | desc.its_movi_cmd.col = col; | ||
499 | desc.its_movi_cmd.id = id; | ||
500 | |||
501 | its_send_single_command(dev->its, its_build_movi_cmd, &desc); | ||
502 | } | ||
503 | |||
504 | static void its_send_discard(struct its_device *dev, u32 id) | ||
505 | { | ||
506 | struct its_cmd_desc desc; | ||
507 | |||
508 | desc.its_discard_cmd.dev = dev; | ||
509 | desc.its_discard_cmd.event_id = id; | ||
510 | |||
511 | its_send_single_command(dev->its, its_build_discard_cmd, &desc); | ||
512 | } | ||
513 | |||
514 | static void its_send_invall(struct its_node *its, struct its_collection *col) | ||
515 | { | ||
516 | struct its_cmd_desc desc; | ||
517 | |||
518 | desc.its_invall_cmd.col = col; | ||
519 | |||
520 | its_send_single_command(its, its_build_invall_cmd, &desc); | ||
521 | } | ||
522 | |||
523 | /* | ||
524 | * irqchip functions - assumes MSI, mostly. | ||
525 | */ | ||
526 | |||
527 | static inline u32 its_get_event_id(struct irq_data *d) | ||
528 | { | ||
529 | struct its_device *its_dev = irq_data_get_irq_chip_data(d); | ||
530 | return d->hwirq - its_dev->lpi_base; | ||
531 | } | ||
532 | |||
533 | static void lpi_set_config(struct irq_data *d, bool enable) | ||
534 | { | ||
535 | struct its_device *its_dev = irq_data_get_irq_chip_data(d); | ||
536 | irq_hw_number_t hwirq = d->hwirq; | ||
537 | u32 id = its_get_event_id(d); | ||
538 | u8 *cfg = page_address(gic_rdists->prop_page) + hwirq - 8192; | ||
539 | |||
540 | if (enable) | ||
541 | *cfg |= LPI_PROP_ENABLED; | ||
542 | else | ||
543 | *cfg &= ~LPI_PROP_ENABLED; | ||
544 | |||
545 | /* | ||
546 | * Make the above write visible to the redistributors. | ||
547 | * And yes, we're flushing exactly: One. Single. Byte. | ||
548 | * Humpf... | ||
549 | */ | ||
550 | if (gic_rdists->flags & RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING) | ||
551 | __flush_dcache_area(cfg, sizeof(*cfg)); | ||
552 | else | ||
553 | dsb(ishst); | ||
554 | its_send_inv(its_dev, id); | ||
555 | } | ||
556 | |||
557 | static void its_mask_irq(struct irq_data *d) | ||
558 | { | ||
559 | lpi_set_config(d, false); | ||
560 | } | ||
561 | |||
562 | static void its_unmask_irq(struct irq_data *d) | ||
563 | { | ||
564 | lpi_set_config(d, true); | ||
565 | } | ||
566 | |||
567 | static void its_eoi_irq(struct irq_data *d) | ||
568 | { | ||
569 | gic_write_eoir(d->hwirq); | ||
570 | } | ||
571 | |||
572 | static int its_set_affinity(struct irq_data *d, const struct cpumask *mask_val, | ||
573 | bool force) | ||
574 | { | ||
575 | unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); | ||
576 | struct its_device *its_dev = irq_data_get_irq_chip_data(d); | ||
577 | struct its_collection *target_col; | ||
578 | u32 id = its_get_event_id(d); | ||
579 | |||
580 | if (cpu >= nr_cpu_ids) | ||
581 | return -EINVAL; | ||
582 | |||
583 | target_col = &its_dev->its->collections[cpu]; | ||
584 | its_send_movi(its_dev, target_col, id); | ||
585 | its_dev->collection = target_col; | ||
586 | |||
587 | return IRQ_SET_MASK_OK_DONE; | ||
588 | } | ||
589 | |||
590 | static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) | ||
591 | { | ||
592 | struct its_device *its_dev = irq_data_get_irq_chip_data(d); | ||
593 | struct its_node *its; | ||
594 | u64 addr; | ||
595 | |||
596 | its = its_dev->its; | ||
597 | addr = its->phys_base + GITS_TRANSLATER; | ||
598 | |||
599 | msg->address_lo = addr & ((1UL << 32) - 1); | ||
600 | msg->address_hi = addr >> 32; | ||
601 | msg->data = its_get_event_id(d); | ||
602 | } | ||
603 | |||
604 | static struct irq_chip its_irq_chip = { | ||
605 | .name = "ITS", | ||
606 | .irq_mask = its_mask_irq, | ||
607 | .irq_unmask = its_unmask_irq, | ||
608 | .irq_eoi = its_eoi_irq, | ||
609 | .irq_set_affinity = its_set_affinity, | ||
610 | .irq_compose_msi_msg = its_irq_compose_msi_msg, | ||
611 | }; | ||
612 | |||
613 | static void its_mask_msi_irq(struct irq_data *d) | ||
614 | { | ||
615 | pci_msi_mask_irq(d); | ||
616 | irq_chip_mask_parent(d); | ||
617 | } | ||
618 | |||
619 | static void its_unmask_msi_irq(struct irq_data *d) | ||
620 | { | ||
621 | pci_msi_unmask_irq(d); | ||
622 | irq_chip_unmask_parent(d); | ||
623 | } | ||
624 | |||
625 | static struct irq_chip its_msi_irq_chip = { | ||
626 | .name = "ITS-MSI", | ||
627 | .irq_unmask = its_unmask_msi_irq, | ||
628 | .irq_mask = its_mask_msi_irq, | ||
629 | .irq_eoi = irq_chip_eoi_parent, | ||
630 | .irq_write_msi_msg = pci_msi_domain_write_msg, | ||
631 | }; | ||
632 | |||
633 | /* | ||
634 | * How we allocate LPIs: | ||
635 | * | ||
636 | * The GIC has id_bits bits for interrupt identifiers. From there, we | ||
637 | * must subtract 8192 which are reserved for SGIs/PPIs/SPIs. Then, as | ||
638 | * we allocate LPIs by chunks of 32, we can shift the whole thing by 5 | ||
639 | * bits to the right. | ||
640 | * | ||
641 | * This gives us (((1UL << id_bits) - 8192) >> 5) possible allocations. | ||
642 | */ | ||
643 | #define IRQS_PER_CHUNK_SHIFT 5 | ||
644 | #define IRQS_PER_CHUNK (1 << IRQS_PER_CHUNK_SHIFT) | ||
645 | |||
646 | static unsigned long *lpi_bitmap; | ||
647 | static u32 lpi_chunks; | ||
648 | static DEFINE_SPINLOCK(lpi_lock); | ||
649 | |||
650 | static int its_lpi_to_chunk(int lpi) | ||
651 | { | ||
652 | return (lpi - 8192) >> IRQS_PER_CHUNK_SHIFT; | ||
653 | } | ||
654 | |||
655 | static int its_chunk_to_lpi(int chunk) | ||
656 | { | ||
657 | return (chunk << IRQS_PER_CHUNK_SHIFT) + 8192; | ||
658 | } | ||
659 | |||
660 | static int its_lpi_init(u32 id_bits) | ||
661 | { | ||
662 | lpi_chunks = its_lpi_to_chunk(1UL << id_bits); | ||
663 | |||
664 | lpi_bitmap = kzalloc(BITS_TO_LONGS(lpi_chunks) * sizeof(long), | ||
665 | GFP_KERNEL); | ||
666 | if (!lpi_bitmap) { | ||
667 | lpi_chunks = 0; | ||
668 | return -ENOMEM; | ||
669 | } | ||
670 | |||
671 | pr_info("ITS: Allocated %d chunks for LPIs\n", (int)lpi_chunks); | ||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | static unsigned long *its_lpi_alloc_chunks(int nr_irqs, int *base, int *nr_ids) | ||
676 | { | ||
677 | unsigned long *bitmap = NULL; | ||
678 | int chunk_id; | ||
679 | int nr_chunks; | ||
680 | int i; | ||
681 | |||
682 | nr_chunks = DIV_ROUND_UP(nr_irqs, IRQS_PER_CHUNK); | ||
683 | |||
684 | spin_lock(&lpi_lock); | ||
685 | |||
686 | do { | ||
687 | chunk_id = bitmap_find_next_zero_area(lpi_bitmap, lpi_chunks, | ||
688 | 0, nr_chunks, 0); | ||
689 | if (chunk_id < lpi_chunks) | ||
690 | break; | ||
691 | |||
692 | nr_chunks--; | ||
693 | } while (nr_chunks > 0); | ||
694 | |||
695 | if (!nr_chunks) | ||
696 | goto out; | ||
697 | |||
698 | bitmap = kzalloc(BITS_TO_LONGS(nr_chunks * IRQS_PER_CHUNK) * sizeof (long), | ||
699 | GFP_ATOMIC); | ||
700 | if (!bitmap) | ||
701 | goto out; | ||
702 | |||
703 | for (i = 0; i < nr_chunks; i++) | ||
704 | set_bit(chunk_id + i, lpi_bitmap); | ||
705 | |||
706 | *base = its_chunk_to_lpi(chunk_id); | ||
707 | *nr_ids = nr_chunks * IRQS_PER_CHUNK; | ||
708 | |||
709 | out: | ||
710 | spin_unlock(&lpi_lock); | ||
711 | |||
712 | return bitmap; | ||
713 | } | ||
714 | |||
715 | static void its_lpi_free(unsigned long *bitmap, int base, int nr_ids) | ||
716 | { | ||
717 | int lpi; | ||
718 | |||
719 | spin_lock(&lpi_lock); | ||
720 | |||
721 | for (lpi = base; lpi < (base + nr_ids); lpi += IRQS_PER_CHUNK) { | ||
722 | int chunk = its_lpi_to_chunk(lpi); | ||
723 | BUG_ON(chunk > lpi_chunks); | ||
724 | if (test_bit(chunk, lpi_bitmap)) { | ||
725 | clear_bit(chunk, lpi_bitmap); | ||
726 | } else { | ||
727 | pr_err("Bad LPI chunk %d\n", chunk); | ||
728 | } | ||
729 | } | ||
730 | |||
731 | spin_unlock(&lpi_lock); | ||
732 | |||
733 | kfree(bitmap); | ||
734 | } | ||
735 | |||
736 | /* | ||
737 | * We allocate 64kB for PROPBASE. That gives us at most 64K LPIs to | ||
738 | * deal with (one configuration byte per interrupt). PENDBASE has to | ||
739 | * be 64kB aligned (one bit per LPI, plus 8192 bits for SPI/PPI/SGI). | ||
740 | */ | ||
741 | #define LPI_PROPBASE_SZ SZ_64K | ||
742 | #define LPI_PENDBASE_SZ (LPI_PROPBASE_SZ / 8 + SZ_1K) | ||
743 | |||
744 | /* | ||
745 | * This is how many bits of ID we need, including the useless ones. | ||
746 | */ | ||
747 | #define LPI_NRBITS ilog2(LPI_PROPBASE_SZ + SZ_8K) | ||
748 | |||
749 | #define LPI_PROP_DEFAULT_PRIO 0xa0 | ||
750 | |||
751 | static int __init its_alloc_lpi_tables(void) | ||
752 | { | ||
753 | phys_addr_t paddr; | ||
754 | |||
755 | gic_rdists->prop_page = alloc_pages(GFP_NOWAIT, | ||
756 | get_order(LPI_PROPBASE_SZ)); | ||
757 | if (!gic_rdists->prop_page) { | ||
758 | pr_err("Failed to allocate PROPBASE\n"); | ||
759 | return -ENOMEM; | ||
760 | } | ||
761 | |||
762 | paddr = page_to_phys(gic_rdists->prop_page); | ||
763 | pr_info("GIC: using LPI property table @%pa\n", &paddr); | ||
764 | |||
765 | /* Priority 0xa0, Group-1, disabled */ | ||
766 | memset(page_address(gic_rdists->prop_page), | ||
767 | LPI_PROP_DEFAULT_PRIO | LPI_PROP_GROUP1, | ||
768 | LPI_PROPBASE_SZ); | ||
769 | |||
770 | /* Make sure the GIC will observe the written configuration */ | ||
771 | __flush_dcache_area(page_address(gic_rdists->prop_page), LPI_PROPBASE_SZ); | ||
772 | |||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | static const char *its_base_type_string[] = { | ||
777 | [GITS_BASER_TYPE_DEVICE] = "Devices", | ||
778 | [GITS_BASER_TYPE_VCPU] = "Virtual CPUs", | ||
779 | [GITS_BASER_TYPE_CPU] = "Physical CPUs", | ||
780 | [GITS_BASER_TYPE_COLLECTION] = "Interrupt Collections", | ||
781 | [GITS_BASER_TYPE_RESERVED5] = "Reserved (5)", | ||
782 | [GITS_BASER_TYPE_RESERVED6] = "Reserved (6)", | ||
783 | [GITS_BASER_TYPE_RESERVED7] = "Reserved (7)", | ||
784 | }; | ||
785 | |||
786 | static void its_free_tables(struct its_node *its) | ||
787 | { | ||
788 | int i; | ||
789 | |||
790 | for (i = 0; i < GITS_BASER_NR_REGS; i++) { | ||
791 | if (its->tables[i]) { | ||
792 | free_page((unsigned long)its->tables[i]); | ||
793 | its->tables[i] = NULL; | ||
794 | } | ||
795 | } | ||
796 | } | ||
797 | |||
798 | static int its_alloc_tables(struct its_node *its) | ||
799 | { | ||
800 | int err; | ||
801 | int i; | ||
802 | int psz = PAGE_SIZE; | ||
803 | u64 shr = GITS_BASER_InnerShareable; | ||
804 | |||
805 | for (i = 0; i < GITS_BASER_NR_REGS; i++) { | ||
806 | u64 val = readq_relaxed(its->base + GITS_BASER + i * 8); | ||
807 | u64 type = GITS_BASER_TYPE(val); | ||
808 | u64 entry_size = GITS_BASER_ENTRY_SIZE(val); | ||
809 | u64 tmp; | ||
810 | void *base; | ||
811 | |||
812 | if (type == GITS_BASER_TYPE_NONE) | ||
813 | continue; | ||
814 | |||
815 | /* We're lazy and only allocate a single page for now */ | ||
816 | base = (void *)get_zeroed_page(GFP_KERNEL); | ||
817 | if (!base) { | ||
818 | err = -ENOMEM; | ||
819 | goto out_free; | ||
820 | } | ||
821 | |||
822 | its->tables[i] = base; | ||
823 | |||
824 | retry_baser: | ||
825 | val = (virt_to_phys(base) | | ||
826 | (type << GITS_BASER_TYPE_SHIFT) | | ||
827 | ((entry_size - 1) << GITS_BASER_ENTRY_SIZE_SHIFT) | | ||
828 | GITS_BASER_WaWb | | ||
829 | shr | | ||
830 | GITS_BASER_VALID); | ||
831 | |||
832 | switch (psz) { | ||
833 | case SZ_4K: | ||
834 | val |= GITS_BASER_PAGE_SIZE_4K; | ||
835 | break; | ||
836 | case SZ_16K: | ||
837 | val |= GITS_BASER_PAGE_SIZE_16K; | ||
838 | break; | ||
839 | case SZ_64K: | ||
840 | val |= GITS_BASER_PAGE_SIZE_64K; | ||
841 | break; | ||
842 | } | ||
843 | |||
844 | val |= (PAGE_SIZE / psz) - 1; | ||
845 | |||
846 | writeq_relaxed(val, its->base + GITS_BASER + i * 8); | ||
847 | tmp = readq_relaxed(its->base + GITS_BASER + i * 8); | ||
848 | |||
849 | if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) { | ||
850 | /* | ||
851 | * Shareability didn't stick. Just use | ||
852 | * whatever the read reported, which is likely | ||
853 | * to be the only thing this redistributor | ||
854 | * supports. | ||
855 | */ | ||
856 | shr = tmp & GITS_BASER_SHAREABILITY_MASK; | ||
857 | goto retry_baser; | ||
858 | } | ||
859 | |||
860 | if ((val ^ tmp) & GITS_BASER_PAGE_SIZE_MASK) { | ||
861 | /* | ||
862 | * Page size didn't stick. Let's try a smaller | ||
863 | * size and retry. If we reach 4K, then | ||
864 | * something is horribly wrong... | ||
865 | */ | ||
866 | switch (psz) { | ||
867 | case SZ_16K: | ||
868 | psz = SZ_4K; | ||
869 | goto retry_baser; | ||
870 | case SZ_64K: | ||
871 | psz = SZ_16K; | ||
872 | goto retry_baser; | ||
873 | } | ||
874 | } | ||
875 | |||
876 | if (val != tmp) { | ||
877 | pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n", | ||
878 | its->msi_chip.of_node->full_name, i, | ||
879 | (unsigned long) val, (unsigned long) tmp); | ||
880 | err = -ENXIO; | ||
881 | goto out_free; | ||
882 | } | ||
883 | |||
884 | pr_info("ITS: allocated %d %s @%lx (psz %dK, shr %d)\n", | ||
885 | (int)(PAGE_SIZE / entry_size), | ||
886 | its_base_type_string[type], | ||
887 | (unsigned long)virt_to_phys(base), | ||
888 | psz / SZ_1K, (int)shr >> GITS_BASER_SHAREABILITY_SHIFT); | ||
889 | } | ||
890 | |||
891 | return 0; | ||
892 | |||
893 | out_free: | ||
894 | its_free_tables(its); | ||
895 | |||
896 | return err; | ||
897 | } | ||
898 | |||
899 | static int its_alloc_collections(struct its_node *its) | ||
900 | { | ||
901 | its->collections = kzalloc(nr_cpu_ids * sizeof(*its->collections), | ||
902 | GFP_KERNEL); | ||
903 | if (!its->collections) | ||
904 | return -ENOMEM; | ||
905 | |||
906 | return 0; | ||
907 | } | ||
908 | |||
909 | static void its_cpu_init_lpis(void) | ||
910 | { | ||
911 | void __iomem *rbase = gic_data_rdist_rd_base(); | ||
912 | struct page *pend_page; | ||
913 | u64 val, tmp; | ||
914 | |||
915 | /* If we didn't allocate the pending table yet, do it now */ | ||
916 | pend_page = gic_data_rdist()->pend_page; | ||
917 | if (!pend_page) { | ||
918 | phys_addr_t paddr; | ||
919 | /* | ||
920 | * The pending pages have to be at least 64kB aligned, | ||
921 | * hence the 'max(LPI_PENDBASE_SZ, SZ_64K)' below. | ||
922 | */ | ||
923 | pend_page = alloc_pages(GFP_NOWAIT | __GFP_ZERO, | ||
924 | get_order(max(LPI_PENDBASE_SZ, SZ_64K))); | ||
925 | if (!pend_page) { | ||
926 | pr_err("Failed to allocate PENDBASE for CPU%d\n", | ||
927 | smp_processor_id()); | ||
928 | return; | ||
929 | } | ||
930 | |||
931 | /* Make sure the GIC will observe the zero-ed page */ | ||
932 | __flush_dcache_area(page_address(pend_page), LPI_PENDBASE_SZ); | ||
933 | |||
934 | paddr = page_to_phys(pend_page); | ||
935 | pr_info("CPU%d: using LPI pending table @%pa\n", | ||
936 | smp_processor_id(), &paddr); | ||
937 | gic_data_rdist()->pend_page = pend_page; | ||
938 | } | ||
939 | |||
940 | /* Disable LPIs */ | ||
941 | val = readl_relaxed(rbase + GICR_CTLR); | ||
942 | val &= ~GICR_CTLR_ENABLE_LPIS; | ||
943 | writel_relaxed(val, rbase + GICR_CTLR); | ||
944 | |||
945 | /* | ||
946 | * Make sure any change to the table is observable by the GIC. | ||
947 | */ | ||
948 | dsb(sy); | ||
949 | |||
950 | /* set PROPBASE */ | ||
951 | val = (page_to_phys(gic_rdists->prop_page) | | ||
952 | GICR_PROPBASER_InnerShareable | | ||
953 | GICR_PROPBASER_WaWb | | ||
954 | ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); | ||
955 | |||
956 | writeq_relaxed(val, rbase + GICR_PROPBASER); | ||
957 | tmp = readq_relaxed(rbase + GICR_PROPBASER); | ||
958 | |||
959 | if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) { | ||
960 | pr_info_once("GIC: using cache flushing for LPI property table\n"); | ||
961 | gic_rdists->flags |= RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING; | ||
962 | } | ||
963 | |||
964 | /* set PENDBASE */ | ||
965 | val = (page_to_phys(pend_page) | | ||
966 | GICR_PROPBASER_InnerShareable | | ||
967 | GICR_PROPBASER_WaWb); | ||
968 | |||
969 | writeq_relaxed(val, rbase + GICR_PENDBASER); | ||
970 | |||
971 | /* Enable LPIs */ | ||
972 | val = readl_relaxed(rbase + GICR_CTLR); | ||
973 | val |= GICR_CTLR_ENABLE_LPIS; | ||
974 | writel_relaxed(val, rbase + GICR_CTLR); | ||
975 | |||
976 | /* Make sure the GIC has seen the above */ | ||
977 | dsb(sy); | ||
978 | } | ||
979 | |||
980 | static void its_cpu_init_collection(void) | ||
981 | { | ||
982 | struct its_node *its; | ||
983 | int cpu; | ||
984 | |||
985 | spin_lock(&its_lock); | ||
986 | cpu = smp_processor_id(); | ||
987 | |||
988 | list_for_each_entry(its, &its_nodes, entry) { | ||
989 | u64 target; | ||
990 | |||
991 | /* | ||
992 | * We now have to bind each collection to its target | ||
993 | * redistributor. | ||
994 | */ | ||
995 | if (readq_relaxed(its->base + GITS_TYPER) & GITS_TYPER_PTA) { | ||
996 | /* | ||
997 | * This ITS wants the physical address of the | ||
998 | * redistributor. | ||
999 | */ | ||
1000 | target = gic_data_rdist()->phys_base; | ||
1001 | } else { | ||
1002 | /* | ||
1003 | * This ITS wants a linear CPU number. | ||
1004 | */ | ||
1005 | target = readq_relaxed(gic_data_rdist_rd_base() + GICR_TYPER); | ||
1006 | target = GICR_TYPER_CPU_NUMBER(target); | ||
1007 | } | ||
1008 | |||
1009 | /* Perform collection mapping */ | ||
1010 | its->collections[cpu].target_address = target; | ||
1011 | its->collections[cpu].col_id = cpu; | ||
1012 | |||
1013 | its_send_mapc(its, &its->collections[cpu], 1); | ||
1014 | its_send_invall(its, &its->collections[cpu]); | ||
1015 | } | ||
1016 | |||
1017 | spin_unlock(&its_lock); | ||
1018 | } | ||
1019 | |||
1020 | static struct its_device *its_find_device(struct its_node *its, u32 dev_id) | ||
1021 | { | ||
1022 | struct its_device *its_dev = NULL, *tmp; | ||
1023 | |||
1024 | raw_spin_lock(&its->lock); | ||
1025 | |||
1026 | list_for_each_entry(tmp, &its->its_device_list, entry) { | ||
1027 | if (tmp->device_id == dev_id) { | ||
1028 | its_dev = tmp; | ||
1029 | break; | ||
1030 | } | ||
1031 | } | ||
1032 | |||
1033 | raw_spin_unlock(&its->lock); | ||
1034 | |||
1035 | return its_dev; | ||
1036 | } | ||
1037 | |||
1038 | static struct its_device *its_create_device(struct its_node *its, u32 dev_id, | ||
1039 | int nvecs) | ||
1040 | { | ||
1041 | struct its_device *dev; | ||
1042 | unsigned long *lpi_map; | ||
1043 | void *itt; | ||
1044 | int lpi_base; | ||
1045 | int nr_lpis; | ||
1046 | int nr_ites; | ||
1047 | int cpu; | ||
1048 | int sz; | ||
1049 | |||
1050 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
1051 | /* | ||
1052 | * At least one bit of EventID is being used, hence a minimum | ||
1053 | * of two entries. No, the architecture doesn't let you | ||
1054 | * express an ITT with a single entry. | ||
1055 | */ | ||
1056 | nr_ites = max(2, roundup_pow_of_two(nvecs)); | ||
1057 | sz = nr_ites * its->ite_size; | ||
1058 | sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; | ||
1059 | itt = kmalloc(sz, GFP_KERNEL); | ||
1060 | lpi_map = its_lpi_alloc_chunks(nvecs, &lpi_base, &nr_lpis); | ||
1061 | |||
1062 | if (!dev || !itt || !lpi_map) { | ||
1063 | kfree(dev); | ||
1064 | kfree(itt); | ||
1065 | kfree(lpi_map); | ||
1066 | return NULL; | ||
1067 | } | ||
1068 | |||
1069 | dev->its = its; | ||
1070 | dev->itt = itt; | ||
1071 | dev->nr_ites = nr_ites; | ||
1072 | dev->lpi_map = lpi_map; | ||
1073 | dev->lpi_base = lpi_base; | ||
1074 | dev->nr_lpis = nr_lpis; | ||
1075 | dev->device_id = dev_id; | ||
1076 | INIT_LIST_HEAD(&dev->entry); | ||
1077 | |||
1078 | raw_spin_lock(&its->lock); | ||
1079 | list_add(&dev->entry, &its->its_device_list); | ||
1080 | raw_spin_unlock(&its->lock); | ||
1081 | |||
1082 | /* Bind the device to the first possible CPU */ | ||
1083 | cpu = cpumask_first(cpu_online_mask); | ||
1084 | dev->collection = &its->collections[cpu]; | ||
1085 | |||
1086 | /* Map device to its ITT */ | ||
1087 | its_send_mapd(dev, 1); | ||
1088 | |||
1089 | return dev; | ||
1090 | } | ||
1091 | |||
1092 | static void its_free_device(struct its_device *its_dev) | ||
1093 | { | ||
1094 | raw_spin_lock(&its_dev->its->lock); | ||
1095 | list_del(&its_dev->entry); | ||
1096 | raw_spin_unlock(&its_dev->its->lock); | ||
1097 | kfree(its_dev->itt); | ||
1098 | kfree(its_dev); | ||
1099 | } | ||
1100 | |||
1101 | static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq) | ||
1102 | { | ||
1103 | int idx; | ||
1104 | |||
1105 | idx = find_first_zero_bit(dev->lpi_map, dev->nr_lpis); | ||
1106 | if (idx == dev->nr_lpis) | ||
1107 | return -ENOSPC; | ||
1108 | |||
1109 | *hwirq = dev->lpi_base + idx; | ||
1110 | set_bit(idx, dev->lpi_map); | ||
1111 | |||
1112 | return 0; | ||
1113 | } | ||
1114 | |||
1115 | static int its_msi_prepare(struct irq_domain *domain, struct device *dev, | ||
1116 | int nvec, msi_alloc_info_t *info) | ||
1117 | { | ||
1118 | struct pci_dev *pdev; | ||
1119 | struct its_node *its; | ||
1120 | u32 dev_id; | ||
1121 | struct its_device *its_dev; | ||
1122 | |||
1123 | if (!dev_is_pci(dev)) | ||
1124 | return -EINVAL; | ||
1125 | |||
1126 | pdev = to_pci_dev(dev); | ||
1127 | dev_id = PCI_DEVID(pdev->bus->number, pdev->devfn); | ||
1128 | its = domain->parent->host_data; | ||
1129 | |||
1130 | its_dev = its_find_device(its, dev_id); | ||
1131 | if (WARN_ON(its_dev)) | ||
1132 | return -EINVAL; | ||
1133 | |||
1134 | its_dev = its_create_device(its, dev_id, nvec); | ||
1135 | if (!its_dev) | ||
1136 | return -ENOMEM; | ||
1137 | |||
1138 | dev_dbg(&pdev->dev, "ITT %d entries, %d bits\n", nvec, ilog2(nvec)); | ||
1139 | |||
1140 | info->scratchpad[0].ptr = its_dev; | ||
1141 | info->scratchpad[1].ptr = dev; | ||
1142 | return 0; | ||
1143 | } | ||
1144 | |||
1145 | static struct msi_domain_ops its_pci_msi_ops = { | ||
1146 | .msi_prepare = its_msi_prepare, | ||
1147 | }; | ||
1148 | |||
1149 | static struct msi_domain_info its_pci_msi_domain_info = { | ||
1150 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | | ||
1151 | MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), | ||
1152 | .ops = &its_pci_msi_ops, | ||
1153 | .chip = &its_msi_irq_chip, | ||
1154 | }; | ||
1155 | |||
1156 | static int its_irq_gic_domain_alloc(struct irq_domain *domain, | ||
1157 | unsigned int virq, | ||
1158 | irq_hw_number_t hwirq) | ||
1159 | { | ||
1160 | struct of_phandle_args args; | ||
1161 | |||
1162 | args.np = domain->parent->of_node; | ||
1163 | args.args_count = 3; | ||
1164 | args.args[0] = GIC_IRQ_TYPE_LPI; | ||
1165 | args.args[1] = hwirq; | ||
1166 | args.args[2] = IRQ_TYPE_EDGE_RISING; | ||
1167 | |||
1168 | return irq_domain_alloc_irqs_parent(domain, virq, 1, &args); | ||
1169 | } | ||
1170 | |||
1171 | static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | ||
1172 | unsigned int nr_irqs, void *args) | ||
1173 | { | ||
1174 | msi_alloc_info_t *info = args; | ||
1175 | struct its_device *its_dev = info->scratchpad[0].ptr; | ||
1176 | irq_hw_number_t hwirq; | ||
1177 | int err; | ||
1178 | int i; | ||
1179 | |||
1180 | for (i = 0; i < nr_irqs; i++) { | ||
1181 | err = its_alloc_device_irq(its_dev, &hwirq); | ||
1182 | if (err) | ||
1183 | return err; | ||
1184 | |||
1185 | err = its_irq_gic_domain_alloc(domain, virq + i, hwirq); | ||
1186 | if (err) | ||
1187 | return err; | ||
1188 | |||
1189 | irq_domain_set_hwirq_and_chip(domain, virq + i, | ||
1190 | hwirq, &its_irq_chip, its_dev); | ||
1191 | dev_dbg(info->scratchpad[1].ptr, "ID:%d pID:%d vID:%d\n", | ||
1192 | (int)(hwirq - its_dev->lpi_base), (int)hwirq, virq + i); | ||
1193 | } | ||
1194 | |||
1195 | return 0; | ||
1196 | } | ||
1197 | |||
1198 | static void its_irq_domain_activate(struct irq_domain *domain, | ||
1199 | struct irq_data *d) | ||
1200 | { | ||
1201 | struct its_device *its_dev = irq_data_get_irq_chip_data(d); | ||
1202 | u32 event = its_get_event_id(d); | ||
1203 | |||
1204 | /* Map the GIC IRQ and event to the device */ | ||
1205 | its_send_mapvi(its_dev, d->hwirq, event); | ||
1206 | } | ||
1207 | |||
1208 | static void its_irq_domain_deactivate(struct irq_domain *domain, | ||
1209 | struct irq_data *d) | ||
1210 | { | ||
1211 | struct its_device *its_dev = irq_data_get_irq_chip_data(d); | ||
1212 | u32 event = its_get_event_id(d); | ||
1213 | |||
1214 | /* Stop the delivery of interrupts */ | ||
1215 | its_send_discard(its_dev, event); | ||
1216 | } | ||
1217 | |||
1218 | static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq, | ||
1219 | unsigned int nr_irqs) | ||
1220 | { | ||
1221 | struct irq_data *d = irq_domain_get_irq_data(domain, virq); | ||
1222 | struct its_device *its_dev = irq_data_get_irq_chip_data(d); | ||
1223 | int i; | ||
1224 | |||
1225 | for (i = 0; i < nr_irqs; i++) { | ||
1226 | struct irq_data *data = irq_domain_get_irq_data(domain, | ||
1227 | virq + i); | ||
1228 | u32 event = its_get_event_id(data); | ||
1229 | |||
1230 | /* Mark interrupt index as unused */ | ||
1231 | clear_bit(event, its_dev->lpi_map); | ||
1232 | |||
1233 | /* Nuke the entry in the domain */ | ||
1234 | irq_domain_reset_irq_data(data); | ||
1235 | } | ||
1236 | |||
1237 | /* If all interrupts have been freed, start mopping the floor */ | ||
1238 | if (bitmap_empty(its_dev->lpi_map, its_dev->nr_lpis)) { | ||
1239 | its_lpi_free(its_dev->lpi_map, | ||
1240 | its_dev->lpi_base, | ||
1241 | its_dev->nr_lpis); | ||
1242 | |||
1243 | /* Unmap device/itt */ | ||
1244 | its_send_mapd(its_dev, 0); | ||
1245 | its_free_device(its_dev); | ||
1246 | } | ||
1247 | |||
1248 | irq_domain_free_irqs_parent(domain, virq, nr_irqs); | ||
1249 | } | ||
1250 | |||
1251 | static const struct irq_domain_ops its_domain_ops = { | ||
1252 | .alloc = its_irq_domain_alloc, | ||
1253 | .free = its_irq_domain_free, | ||
1254 | .activate = its_irq_domain_activate, | ||
1255 | .deactivate = its_irq_domain_deactivate, | ||
1256 | }; | ||
1257 | |||
1258 | static int its_probe(struct device_node *node, struct irq_domain *parent) | ||
1259 | { | ||
1260 | struct resource res; | ||
1261 | struct its_node *its; | ||
1262 | void __iomem *its_base; | ||
1263 | u32 val; | ||
1264 | u64 baser, tmp; | ||
1265 | int err; | ||
1266 | |||
1267 | err = of_address_to_resource(node, 0, &res); | ||
1268 | if (err) { | ||
1269 | pr_warn("%s: no regs?\n", node->full_name); | ||
1270 | return -ENXIO; | ||
1271 | } | ||
1272 | |||
1273 | its_base = ioremap(res.start, resource_size(&res)); | ||
1274 | if (!its_base) { | ||
1275 | pr_warn("%s: unable to map registers\n", node->full_name); | ||
1276 | return -ENOMEM; | ||
1277 | } | ||
1278 | |||
1279 | val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK; | ||
1280 | if (val != 0x30 && val != 0x40) { | ||
1281 | pr_warn("%s: no ITS detected, giving up\n", node->full_name); | ||
1282 | err = -ENODEV; | ||
1283 | goto out_unmap; | ||
1284 | } | ||
1285 | |||
1286 | pr_info("ITS: %s\n", node->full_name); | ||
1287 | |||
1288 | its = kzalloc(sizeof(*its), GFP_KERNEL); | ||
1289 | if (!its) { | ||
1290 | err = -ENOMEM; | ||
1291 | goto out_unmap; | ||
1292 | } | ||
1293 | |||
1294 | raw_spin_lock_init(&its->lock); | ||
1295 | INIT_LIST_HEAD(&its->entry); | ||
1296 | INIT_LIST_HEAD(&its->its_device_list); | ||
1297 | its->base = its_base; | ||
1298 | its->phys_base = res.start; | ||
1299 | its->msi_chip.of_node = node; | ||
1300 | its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1; | ||
1301 | |||
1302 | its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); | ||
1303 | if (!its->cmd_base) { | ||
1304 | err = -ENOMEM; | ||
1305 | goto out_free_its; | ||
1306 | } | ||
1307 | its->cmd_write = its->cmd_base; | ||
1308 | |||
1309 | err = its_alloc_tables(its); | ||
1310 | if (err) | ||
1311 | goto out_free_cmd; | ||
1312 | |||
1313 | err = its_alloc_collections(its); | ||
1314 | if (err) | ||
1315 | goto out_free_tables; | ||
1316 | |||
1317 | baser = (virt_to_phys(its->cmd_base) | | ||
1318 | GITS_CBASER_WaWb | | ||
1319 | GITS_CBASER_InnerShareable | | ||
1320 | (ITS_CMD_QUEUE_SZ / SZ_4K - 1) | | ||
1321 | GITS_CBASER_VALID); | ||
1322 | |||
1323 | writeq_relaxed(baser, its->base + GITS_CBASER); | ||
1324 | tmp = readq_relaxed(its->base + GITS_CBASER); | ||
1325 | writeq_relaxed(0, its->base + GITS_CWRITER); | ||
1326 | writel_relaxed(1, its->base + GITS_CTLR); | ||
1327 | |||
1328 | if ((tmp ^ baser) & GITS_BASER_SHAREABILITY_MASK) { | ||
1329 | pr_info("ITS: using cache flushing for cmd queue\n"); | ||
1330 | its->flags |= ITS_FLAGS_CMDQ_NEEDS_FLUSHING; | ||
1331 | } | ||
1332 | |||
1333 | if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { | ||
1334 | its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); | ||
1335 | if (!its->domain) { | ||
1336 | err = -ENOMEM; | ||
1337 | goto out_free_tables; | ||
1338 | } | ||
1339 | |||
1340 | its->domain->parent = parent; | ||
1341 | |||
1342 | its->msi_chip.domain = pci_msi_create_irq_domain(node, | ||
1343 | &its_pci_msi_domain_info, | ||
1344 | its->domain); | ||
1345 | if (!its->msi_chip.domain) { | ||
1346 | err = -ENOMEM; | ||
1347 | goto out_free_domains; | ||
1348 | } | ||
1349 | |||
1350 | err = of_pci_msi_chip_add(&its->msi_chip); | ||
1351 | if (err) | ||
1352 | goto out_free_domains; | ||
1353 | } | ||
1354 | |||
1355 | spin_lock(&its_lock); | ||
1356 | list_add(&its->entry, &its_nodes); | ||
1357 | spin_unlock(&its_lock); | ||
1358 | |||
1359 | return 0; | ||
1360 | |||
1361 | out_free_domains: | ||
1362 | if (its->msi_chip.domain) | ||
1363 | irq_domain_remove(its->msi_chip.domain); | ||
1364 | if (its->domain) | ||
1365 | irq_domain_remove(its->domain); | ||
1366 | out_free_tables: | ||
1367 | its_free_tables(its); | ||
1368 | out_free_cmd: | ||
1369 | kfree(its->cmd_base); | ||
1370 | out_free_its: | ||
1371 | kfree(its); | ||
1372 | out_unmap: | ||
1373 | iounmap(its_base); | ||
1374 | pr_err("ITS: failed probing %s (%d)\n", node->full_name, err); | ||
1375 | return err; | ||
1376 | } | ||
1377 | |||
1378 | static bool gic_rdists_supports_plpis(void) | ||
1379 | { | ||
1380 | return !!(readl_relaxed(gic_data_rdist_rd_base() + GICR_TYPER) & GICR_TYPER_PLPIS); | ||
1381 | } | ||
1382 | |||
1383 | int its_cpu_init(void) | ||
1384 | { | ||
1385 | if (!gic_rdists_supports_plpis()) { | ||
1386 | pr_info("CPU%d: LPIs not supported\n", smp_processor_id()); | ||
1387 | return -ENXIO; | ||
1388 | } | ||
1389 | |||
1390 | if (!list_empty(&its_nodes)) { | ||
1391 | its_cpu_init_lpis(); | ||
1392 | its_cpu_init_collection(); | ||
1393 | } | ||
1394 | |||
1395 | return 0; | ||
1396 | } | ||
1397 | |||
1398 | static struct of_device_id its_device_id[] = { | ||
1399 | { .compatible = "arm,gic-v3-its", }, | ||
1400 | {}, | ||
1401 | }; | ||
1402 | |||
1403 | int its_init(struct device_node *node, struct rdists *rdists, | ||
1404 | struct irq_domain *parent_domain) | ||
1405 | { | ||
1406 | struct device_node *np; | ||
1407 | |||
1408 | for (np = of_find_matching_node(node, its_device_id); np; | ||
1409 | np = of_find_matching_node(np, its_device_id)) { | ||
1410 | its_probe(np, parent_domain); | ||
1411 | } | ||
1412 | |||
1413 | if (list_empty(&its_nodes)) { | ||
1414 | pr_warn("ITS: No ITS available, not enabling LPIs\n"); | ||
1415 | return -ENXIO; | ||
1416 | } | ||
1417 | |||
1418 | gic_rdists = rdists; | ||
1419 | gic_root_node = node; | ||
1420 | |||
1421 | its_alloc_lpi_tables(); | ||
1422 | its_lpi_init(rdists->id_bits); | ||
1423 | |||
1424 | return 0; | ||
1425 | } | ||
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index aa17ae805a70..1a146ccee701 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c | |||
@@ -34,20 +34,25 @@ | |||
34 | #include "irq-gic-common.h" | 34 | #include "irq-gic-common.h" |
35 | #include "irqchip.h" | 35 | #include "irqchip.h" |
36 | 36 | ||
37 | struct redist_region { | ||
38 | void __iomem *redist_base; | ||
39 | phys_addr_t phys_base; | ||
40 | }; | ||
41 | |||
37 | struct gic_chip_data { | 42 | struct gic_chip_data { |
38 | void __iomem *dist_base; | 43 | void __iomem *dist_base; |
39 | void __iomem **redist_base; | 44 | struct redist_region *redist_regions; |
40 | void __iomem * __percpu *rdist; | 45 | struct rdists rdists; |
41 | struct irq_domain *domain; | 46 | struct irq_domain *domain; |
42 | u64 redist_stride; | 47 | u64 redist_stride; |
43 | u32 redist_regions; | 48 | u32 nr_redist_regions; |
44 | unsigned int irq_nr; | 49 | unsigned int irq_nr; |
45 | }; | 50 | }; |
46 | 51 | ||
47 | static struct gic_chip_data gic_data __read_mostly; | 52 | static struct gic_chip_data gic_data __read_mostly; |
48 | 53 | ||
49 | #define gic_data_rdist() (this_cpu_ptr(gic_data.rdist)) | 54 | #define gic_data_rdist() (this_cpu_ptr(gic_data.rdists.rdist)) |
50 | #define gic_data_rdist_rd_base() (*gic_data_rdist()) | 55 | #define gic_data_rdist_rd_base() (gic_data_rdist()->rd_base) |
51 | #define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K) | 56 | #define gic_data_rdist_sgi_base() (gic_data_rdist_rd_base() + SZ_64K) |
52 | 57 | ||
53 | /* Our default, arbitrary priority value. Linux only uses one anyway. */ | 58 | /* Our default, arbitrary priority value. Linux only uses one anyway. */ |
@@ -71,9 +76,6 @@ static inline void __iomem *gic_dist_base(struct irq_data *d) | |||
71 | if (d->hwirq <= 1023) /* SPI -> dist_base */ | 76 | if (d->hwirq <= 1023) /* SPI -> dist_base */ |
72 | return gic_data.dist_base; | 77 | return gic_data.dist_base; |
73 | 78 | ||
74 | if (d->hwirq >= 8192) | ||
75 | BUG(); /* LPI Detected!!! */ | ||
76 | |||
77 | return NULL; | 79 | return NULL; |
78 | } | 80 | } |
79 | 81 | ||
@@ -271,11 +273,11 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs | |||
271 | do { | 273 | do { |
272 | irqnr = gic_read_iar(); | 274 | irqnr = gic_read_iar(); |
273 | 275 | ||
274 | if (likely(irqnr > 15 && irqnr < 1020)) { | 276 | if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) { |
275 | int err; | 277 | int err; |
276 | err = handle_domain_irq(gic_data.domain, irqnr, regs); | 278 | err = handle_domain_irq(gic_data.domain, irqnr, regs); |
277 | if (err) { | 279 | if (err) { |
278 | WARN_ONCE(true, "Unexpected SPI received!\n"); | 280 | WARN_ONCE(true, "Unexpected interrupt received!\n"); |
279 | gic_write_eoir(irqnr); | 281 | gic_write_eoir(irqnr); |
280 | } | 282 | } |
281 | continue; | 283 | continue; |
@@ -333,8 +335,8 @@ static int gic_populate_rdist(void) | |||
333 | MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 | | 335 | MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 | |
334 | MPIDR_AFFINITY_LEVEL(mpidr, 0)); | 336 | MPIDR_AFFINITY_LEVEL(mpidr, 0)); |
335 | 337 | ||
336 | for (i = 0; i < gic_data.redist_regions; i++) { | 338 | for (i = 0; i < gic_data.nr_redist_regions; i++) { |
337 | void __iomem *ptr = gic_data.redist_base[i]; | 339 | void __iomem *ptr = gic_data.redist_regions[i].redist_base; |
338 | u32 reg; | 340 | u32 reg; |
339 | 341 | ||
340 | reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK; | 342 | reg = readl_relaxed(ptr + GICR_PIDR2) & GIC_PIDR2_ARCH_MASK; |
@@ -347,10 +349,13 @@ static int gic_populate_rdist(void) | |||
347 | do { | 349 | do { |
348 | typer = readq_relaxed(ptr + GICR_TYPER); | 350 | typer = readq_relaxed(ptr + GICR_TYPER); |
349 | if ((typer >> 32) == aff) { | 351 | if ((typer >> 32) == aff) { |
352 | u64 offset = ptr - gic_data.redist_regions[i].redist_base; | ||
350 | gic_data_rdist_rd_base() = ptr; | 353 | gic_data_rdist_rd_base() = ptr; |
351 | pr_info("CPU%d: found redistributor %llx @%p\n", | 354 | gic_data_rdist()->phys_base = gic_data.redist_regions[i].phys_base + offset; |
355 | pr_info("CPU%d: found redistributor %llx region %d:%pa\n", | ||
352 | smp_processor_id(), | 356 | smp_processor_id(), |
353 | (unsigned long long)mpidr, ptr); | 357 | (unsigned long long)mpidr, |
358 | i, &gic_data_rdist()->phys_base); | ||
354 | return 0; | 359 | return 0; |
355 | } | 360 | } |
356 | 361 | ||
@@ -385,6 +390,11 @@ static void gic_cpu_sys_reg_init(void) | |||
385 | gic_write_grpen1(1); | 390 | gic_write_grpen1(1); |
386 | } | 391 | } |
387 | 392 | ||
393 | static int gic_dist_supports_lpis(void) | ||
394 | { | ||
395 | return !!(readl_relaxed(gic_data.dist_base + GICD_TYPER) & GICD_TYPER_LPIS); | ||
396 | } | ||
397 | |||
388 | static void gic_cpu_init(void) | 398 | static void gic_cpu_init(void) |
389 | { | 399 | { |
390 | void __iomem *rbase; | 400 | void __iomem *rbase; |
@@ -399,6 +409,10 @@ static void gic_cpu_init(void) | |||
399 | 409 | ||
400 | gic_cpu_config(rbase, gic_redist_wait_for_rwp); | 410 | gic_cpu_config(rbase, gic_redist_wait_for_rwp); |
401 | 411 | ||
412 | /* Give LPIs a spin */ | ||
413 | if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) | ||
414 | its_cpu_init(); | ||
415 | |||
402 | /* initialise system registers */ | 416 | /* initialise system registers */ |
403 | gic_cpu_sys_reg_init(); | 417 | gic_cpu_sys_reg_init(); |
404 | } | 418 | } |
@@ -585,26 +599,43 @@ static struct irq_chip gic_chip = { | |||
585 | .irq_set_affinity = gic_set_affinity, | 599 | .irq_set_affinity = gic_set_affinity, |
586 | }; | 600 | }; |
587 | 601 | ||
602 | #define GIC_ID_NR (1U << gic_data.rdists.id_bits) | ||
603 | |||
588 | static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, | 604 | static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, |
589 | irq_hw_number_t hw) | 605 | irq_hw_number_t hw) |
590 | { | 606 | { |
591 | /* SGIs are private to the core kernel */ | 607 | /* SGIs are private to the core kernel */ |
592 | if (hw < 16) | 608 | if (hw < 16) |
593 | return -EPERM; | 609 | return -EPERM; |
610 | /* Nothing here */ | ||
611 | if (hw >= gic_data.irq_nr && hw < 8192) | ||
612 | return -EPERM; | ||
613 | /* Off limits */ | ||
614 | if (hw >= GIC_ID_NR) | ||
615 | return -EPERM; | ||
616 | |||
594 | /* PPIs */ | 617 | /* PPIs */ |
595 | if (hw < 32) { | 618 | if (hw < 32) { |
596 | irq_set_percpu_devid(irq); | 619 | irq_set_percpu_devid(irq); |
597 | irq_set_chip_and_handler(irq, &gic_chip, | 620 | irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, |
598 | handle_percpu_devid_irq); | 621 | handle_percpu_devid_irq, NULL, NULL); |
599 | set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); | 622 | set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); |
600 | } | 623 | } |
601 | /* SPIs */ | 624 | /* SPIs */ |
602 | if (hw >= 32 && hw < gic_data.irq_nr) { | 625 | if (hw >= 32 && hw < gic_data.irq_nr) { |
603 | irq_set_chip_and_handler(irq, &gic_chip, | 626 | irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, |
604 | handle_fasteoi_irq); | 627 | handle_fasteoi_irq, NULL, NULL); |
605 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | 628 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
606 | } | 629 | } |
607 | irq_set_chip_data(irq, d->host_data); | 630 | /* LPIs */ |
631 | if (hw >= 8192 && hw < GIC_ID_NR) { | ||
632 | if (!gic_dist_supports_lpis()) | ||
633 | return -EPERM; | ||
634 | irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, | ||
635 | handle_fasteoi_irq, NULL, NULL); | ||
636 | set_irq_flags(irq, IRQF_VALID); | ||
637 | } | ||
638 | |||
608 | return 0; | 639 | return 0; |
609 | } | 640 | } |
610 | 641 | ||
@@ -625,6 +656,9 @@ static int gic_irq_domain_xlate(struct irq_domain *d, | |||
625 | case 1: /* PPI */ | 656 | case 1: /* PPI */ |
626 | *out_hwirq = intspec[1] + 16; | 657 | *out_hwirq = intspec[1] + 16; |
627 | break; | 658 | break; |
659 | case GIC_IRQ_TYPE_LPI: /* LPI */ | ||
660 | *out_hwirq = intspec[1]; | ||
661 | break; | ||
628 | default: | 662 | default: |
629 | return -EINVAL; | 663 | return -EINVAL; |
630 | } | 664 | } |
@@ -633,17 +667,50 @@ static int gic_irq_domain_xlate(struct irq_domain *d, | |||
633 | return 0; | 667 | return 0; |
634 | } | 668 | } |
635 | 669 | ||
670 | static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | ||
671 | unsigned int nr_irqs, void *arg) | ||
672 | { | ||
673 | int i, ret; | ||
674 | irq_hw_number_t hwirq; | ||
675 | unsigned int type = IRQ_TYPE_NONE; | ||
676 | struct of_phandle_args *irq_data = arg; | ||
677 | |||
678 | ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args, | ||
679 | irq_data->args_count, &hwirq, &type); | ||
680 | if (ret) | ||
681 | return ret; | ||
682 | |||
683 | for (i = 0; i < nr_irqs; i++) | ||
684 | gic_irq_domain_map(domain, virq + i, hwirq + i); | ||
685 | |||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq, | ||
690 | unsigned int nr_irqs) | ||
691 | { | ||
692 | int i; | ||
693 | |||
694 | for (i = 0; i < nr_irqs; i++) { | ||
695 | struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); | ||
696 | irq_set_handler(virq + i, NULL); | ||
697 | irq_domain_reset_irq_data(d); | ||
698 | } | ||
699 | } | ||
700 | |||
636 | static const struct irq_domain_ops gic_irq_domain_ops = { | 701 | static const struct irq_domain_ops gic_irq_domain_ops = { |
637 | .map = gic_irq_domain_map, | ||
638 | .xlate = gic_irq_domain_xlate, | 702 | .xlate = gic_irq_domain_xlate, |
703 | .alloc = gic_irq_domain_alloc, | ||
704 | .free = gic_irq_domain_free, | ||
639 | }; | 705 | }; |
640 | 706 | ||
641 | static int __init gic_of_init(struct device_node *node, struct device_node *parent) | 707 | static int __init gic_of_init(struct device_node *node, struct device_node *parent) |
642 | { | 708 | { |
643 | void __iomem *dist_base; | 709 | void __iomem *dist_base; |
644 | void __iomem **redist_base; | 710 | struct redist_region *rdist_regs; |
645 | u64 redist_stride; | 711 | u64 redist_stride; |
646 | u32 redist_regions; | 712 | u32 nr_redist_regions; |
713 | u32 typer; | ||
647 | u32 reg; | 714 | u32 reg; |
648 | int gic_irqs; | 715 | int gic_irqs; |
649 | int err; | 716 | int err; |
@@ -664,54 +731,63 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare | |||
664 | goto out_unmap_dist; | 731 | goto out_unmap_dist; |
665 | } | 732 | } |
666 | 733 | ||
667 | if (of_property_read_u32(node, "#redistributor-regions", &redist_regions)) | 734 | if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions)) |
668 | redist_regions = 1; | 735 | nr_redist_regions = 1; |
669 | 736 | ||
670 | redist_base = kzalloc(sizeof(*redist_base) * redist_regions, GFP_KERNEL); | 737 | rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL); |
671 | if (!redist_base) { | 738 | if (!rdist_regs) { |
672 | err = -ENOMEM; | 739 | err = -ENOMEM; |
673 | goto out_unmap_dist; | 740 | goto out_unmap_dist; |
674 | } | 741 | } |
675 | 742 | ||
676 | for (i = 0; i < redist_regions; i++) { | 743 | for (i = 0; i < nr_redist_regions; i++) { |
677 | redist_base[i] = of_iomap(node, 1 + i); | 744 | struct resource res; |
678 | if (!redist_base[i]) { | 745 | int ret; |
746 | |||
747 | ret = of_address_to_resource(node, 1 + i, &res); | ||
748 | rdist_regs[i].redist_base = of_iomap(node, 1 + i); | ||
749 | if (ret || !rdist_regs[i].redist_base) { | ||
679 | pr_err("%s: couldn't map region %d\n", | 750 | pr_err("%s: couldn't map region %d\n", |
680 | node->full_name, i); | 751 | node->full_name, i); |
681 | err = -ENODEV; | 752 | err = -ENODEV; |
682 | goto out_unmap_rdist; | 753 | goto out_unmap_rdist; |
683 | } | 754 | } |
755 | rdist_regs[i].phys_base = res.start; | ||
684 | } | 756 | } |
685 | 757 | ||
686 | if (of_property_read_u64(node, "redistributor-stride", &redist_stride)) | 758 | if (of_property_read_u64(node, "redistributor-stride", &redist_stride)) |
687 | redist_stride = 0; | 759 | redist_stride = 0; |
688 | 760 | ||
689 | gic_data.dist_base = dist_base; | 761 | gic_data.dist_base = dist_base; |
690 | gic_data.redist_base = redist_base; | 762 | gic_data.redist_regions = rdist_regs; |
691 | gic_data.redist_regions = redist_regions; | 763 | gic_data.nr_redist_regions = nr_redist_regions; |
692 | gic_data.redist_stride = redist_stride; | 764 | gic_data.redist_stride = redist_stride; |
693 | 765 | ||
694 | /* | 766 | /* |
695 | * Find out how many interrupts are supported. | 767 | * Find out how many interrupts are supported. |
696 | * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI) | 768 | * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI) |
697 | */ | 769 | */ |
698 | gic_irqs = readl_relaxed(gic_data.dist_base + GICD_TYPER) & 0x1f; | 770 | typer = readl_relaxed(gic_data.dist_base + GICD_TYPER); |
699 | gic_irqs = (gic_irqs + 1) * 32; | 771 | gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer); |
772 | gic_irqs = GICD_TYPER_IRQS(typer); | ||
700 | if (gic_irqs > 1020) | 773 | if (gic_irqs > 1020) |
701 | gic_irqs = 1020; | 774 | gic_irqs = 1020; |
702 | gic_data.irq_nr = gic_irqs; | 775 | gic_data.irq_nr = gic_irqs; |
703 | 776 | ||
704 | gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops, | 777 | gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops, |
705 | &gic_data); | 778 | &gic_data); |
706 | gic_data.rdist = alloc_percpu(typeof(*gic_data.rdist)); | 779 | gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist)); |
707 | 780 | ||
708 | if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdist)) { | 781 | if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) { |
709 | err = -ENOMEM; | 782 | err = -ENOMEM; |
710 | goto out_free; | 783 | goto out_free; |
711 | } | 784 | } |
712 | 785 | ||
713 | set_handle_irq(gic_handle_irq); | 786 | set_handle_irq(gic_handle_irq); |
714 | 787 | ||
788 | if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) | ||
789 | its_init(node, &gic_data.rdists, gic_data.domain); | ||
790 | |||
715 | gic_smp_init(); | 791 | gic_smp_init(); |
716 | gic_dist_init(); | 792 | gic_dist_init(); |
717 | gic_cpu_init(); | 793 | gic_cpu_init(); |
@@ -722,12 +798,12 @@ static int __init gic_of_init(struct device_node *node, struct device_node *pare | |||
722 | out_free: | 798 | out_free: |
723 | if (gic_data.domain) | 799 | if (gic_data.domain) |
724 | irq_domain_remove(gic_data.domain); | 800 | irq_domain_remove(gic_data.domain); |
725 | free_percpu(gic_data.rdist); | 801 | free_percpu(gic_data.rdists.rdist); |
726 | out_unmap_rdist: | 802 | out_unmap_rdist: |
727 | for (i = 0; i < redist_regions; i++) | 803 | for (i = 0; i < nr_redist_regions; i++) |
728 | if (redist_base[i]) | 804 | if (rdist_regs[i].redist_base) |
729 | iounmap(redist_base[i]); | 805 | iounmap(rdist_regs[i].redist_base); |
730 | kfree(redist_base); | 806 | kfree(rdist_regs); |
731 | out_unmap_dist: | 807 | out_unmap_dist: |
732 | iounmap(dist_base); | 808 | iounmap(dist_base); |
733 | return err; | 809 | return err; |
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 7f9be0785c6a..d617ee5a3d8a 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c | |||
@@ -788,17 +788,16 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, | |||
788 | { | 788 | { |
789 | if (hw < 32) { | 789 | if (hw < 32) { |
790 | irq_set_percpu_devid(irq); | 790 | irq_set_percpu_devid(irq); |
791 | irq_set_chip_and_handler(irq, &gic_chip, | 791 | irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, |
792 | handle_percpu_devid_irq); | 792 | handle_percpu_devid_irq, NULL, NULL); |
793 | set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); | 793 | set_irq_flags(irq, IRQF_VALID | IRQF_NOAUTOEN); |
794 | } else { | 794 | } else { |
795 | irq_set_chip_and_handler(irq, &gic_chip, | 795 | irq_domain_set_info(d, irq, hw, &gic_chip, d->host_data, |
796 | handle_fasteoi_irq); | 796 | handle_fasteoi_irq, NULL, NULL); |
797 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | 797 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); |
798 | 798 | ||
799 | gic_routable_irq_domain_ops->map(d, irq, hw); | 799 | gic_routable_irq_domain_ops->map(d, irq, hw); |
800 | } | 800 | } |
801 | irq_set_chip_data(irq, d->host_data); | ||
802 | return 0; | 801 | return 0; |
803 | } | 802 | } |
804 | 803 | ||
@@ -858,6 +857,31 @@ static struct notifier_block gic_cpu_notifier = { | |||
858 | }; | 857 | }; |
859 | #endif | 858 | #endif |
860 | 859 | ||
860 | static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | ||
861 | unsigned int nr_irqs, void *arg) | ||
862 | { | ||
863 | int i, ret; | ||
864 | irq_hw_number_t hwirq; | ||
865 | unsigned int type = IRQ_TYPE_NONE; | ||
866 | struct of_phandle_args *irq_data = arg; | ||
867 | |||
868 | ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args, | ||
869 | irq_data->args_count, &hwirq, &type); | ||
870 | if (ret) | ||
871 | return ret; | ||
872 | |||
873 | for (i = 0; i < nr_irqs; i++) | ||
874 | gic_irq_domain_map(domain, virq + i, hwirq + i); | ||
875 | |||
876 | return 0; | ||
877 | } | ||
878 | |||
879 | static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = { | ||
880 | .xlate = gic_irq_domain_xlate, | ||
881 | .alloc = gic_irq_domain_alloc, | ||
882 | .free = irq_domain_free_irqs_top, | ||
883 | }; | ||
884 | |||
861 | static const struct irq_domain_ops gic_irq_domain_ops = { | 885 | static const struct irq_domain_ops gic_irq_domain_ops = { |
862 | .map = gic_irq_domain_map, | 886 | .map = gic_irq_domain_map, |
863 | .unmap = gic_irq_domain_unmap, | 887 | .unmap = gic_irq_domain_unmap, |
@@ -948,18 +972,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, | |||
948 | gic_cpu_map[i] = 0xff; | 972 | gic_cpu_map[i] = 0xff; |
949 | 973 | ||
950 | /* | 974 | /* |
951 | * For primary GICs, skip over SGIs. | ||
952 | * For secondary GICs, skip over PPIs, too. | ||
953 | */ | ||
954 | if (gic_nr == 0 && (irq_start & 31) > 0) { | ||
955 | hwirq_base = 16; | ||
956 | if (irq_start != -1) | ||
957 | irq_start = (irq_start & ~31) + 16; | ||
958 | } else { | ||
959 | hwirq_base = 32; | ||
960 | } | ||
961 | |||
962 | /* | ||
963 | * Find out how many interrupts are supported. | 975 | * Find out how many interrupts are supported. |
964 | * The GIC only supports up to 1020 interrupt sources. | 976 | * The GIC only supports up to 1020 interrupt sources. |
965 | */ | 977 | */ |
@@ -969,10 +981,31 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, | |||
969 | gic_irqs = 1020; | 981 | gic_irqs = 1020; |
970 | gic->gic_irqs = gic_irqs; | 982 | gic->gic_irqs = gic_irqs; |
971 | 983 | ||
972 | gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ | 984 | if (node) { /* DT case */ |
985 | const struct irq_domain_ops *ops = &gic_irq_domain_hierarchy_ops; | ||
986 | |||
987 | if (!of_property_read_u32(node, "arm,routable-irqs", | ||
988 | &nr_routable_irqs)) { | ||
989 | ops = &gic_irq_domain_ops; | ||
990 | gic_irqs = nr_routable_irqs; | ||
991 | } | ||
992 | |||
993 | gic->domain = irq_domain_add_linear(node, gic_irqs, ops, gic); | ||
994 | } else { /* Non-DT case */ | ||
995 | /* | ||
996 | * For primary GICs, skip over SGIs. | ||
997 | * For secondary GICs, skip over PPIs, too. | ||
998 | */ | ||
999 | if (gic_nr == 0 && (irq_start & 31) > 0) { | ||
1000 | hwirq_base = 16; | ||
1001 | if (irq_start != -1) | ||
1002 | irq_start = (irq_start & ~31) + 16; | ||
1003 | } else { | ||
1004 | hwirq_base = 32; | ||
1005 | } | ||
1006 | |||
1007 | gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */ | ||
973 | 1008 | ||
974 | if (of_property_read_u32(node, "arm,routable-irqs", | ||
975 | &nr_routable_irqs)) { | ||
976 | irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, | 1009 | irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, |
977 | numa_node_id()); | 1010 | numa_node_id()); |
978 | if (IS_ERR_VALUE(irq_base)) { | 1011 | if (IS_ERR_VALUE(irq_base)) { |
@@ -983,10 +1016,6 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, | |||
983 | 1016 | ||
984 | gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, | 1017 | gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base, |
985 | hwirq_base, &gic_irq_domain_ops, gic); | 1018 | hwirq_base, &gic_irq_domain_ops, gic); |
986 | } else { | ||
987 | gic->domain = irq_domain_add_linear(node, nr_routable_irqs, | ||
988 | &gic_irq_domain_ops, | ||
989 | gic); | ||
990 | } | 1019 | } |
991 | 1020 | ||
992 | if (WARN_ON(!gic->domain)) | 1021 | if (WARN_ON(!gic->domain)) |
@@ -1037,6 +1066,10 @@ gic_of_init(struct device_node *node, struct device_node *parent) | |||
1037 | irq = irq_of_parse_and_map(node, 0); | 1066 | irq = irq_of_parse_and_map(node, 0); |
1038 | gic_cascade_irq(gic_cnt, irq); | 1067 | gic_cascade_irq(gic_cnt, irq); |
1039 | } | 1068 | } |
1069 | |||
1070 | if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) | ||
1071 | gicv2m_of_init(node, gic_data[gic_cnt].domain); | ||
1072 | |||
1040 | gic_cnt++; | 1073 | gic_cnt++; |
1041 | return 0; | 1074 | return 0; |
1042 | } | 1075 | } |
diff --git a/drivers/irqchip/irq-mtk-sysirq.c b/drivers/irqchip/irq-mtk-sysirq.c new file mode 100644 index 000000000000..7e342df6a62f --- /dev/null +++ b/drivers/irqchip/irq-mtk-sysirq.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014 MediaTek Inc. | ||
3 | * Author: Joe.C <yingjoe.chen@mediatek.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/irq.h> | ||
16 | #include <linux/irqdomain.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_irq.h> | ||
19 | #include <linux/of_address.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | |||
24 | #include "irqchip.h" | ||
25 | |||
26 | #define MT6577_SYS_INTPOL_NUM (224) | ||
27 | |||
28 | struct mtk_sysirq_chip_data { | ||
29 | spinlock_t lock; | ||
30 | void __iomem *intpol_base; | ||
31 | }; | ||
32 | |||
33 | static int mtk_sysirq_set_type(struct irq_data *data, unsigned int type) | ||
34 | { | ||
35 | irq_hw_number_t hwirq = data->hwirq; | ||
36 | struct mtk_sysirq_chip_data *chip_data = data->chip_data; | ||
37 | u32 offset, reg_index, value; | ||
38 | unsigned long flags; | ||
39 | int ret; | ||
40 | |||
41 | offset = hwirq & 0x1f; | ||
42 | reg_index = hwirq >> 5; | ||
43 | |||
44 | spin_lock_irqsave(&chip_data->lock, flags); | ||
45 | value = readl_relaxed(chip_data->intpol_base + reg_index * 4); | ||
46 | if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_EDGE_FALLING) { | ||
47 | if (type == IRQ_TYPE_LEVEL_LOW) | ||
48 | type = IRQ_TYPE_LEVEL_HIGH; | ||
49 | else | ||
50 | type = IRQ_TYPE_EDGE_RISING; | ||
51 | value |= (1 << offset); | ||
52 | } else { | ||
53 | value &= ~(1 << offset); | ||
54 | } | ||
55 | writel(value, chip_data->intpol_base + reg_index * 4); | ||
56 | |||
57 | data = data->parent_data; | ||
58 | ret = data->chip->irq_set_type(data, type); | ||
59 | spin_unlock_irqrestore(&chip_data->lock, flags); | ||
60 | return ret; | ||
61 | } | ||
62 | |||
63 | static struct irq_chip mtk_sysirq_chip = { | ||
64 | .name = "MT_SYSIRQ", | ||
65 | .irq_mask = irq_chip_mask_parent, | ||
66 | .irq_unmask = irq_chip_unmask_parent, | ||
67 | .irq_eoi = irq_chip_eoi_parent, | ||
68 | .irq_set_type = mtk_sysirq_set_type, | ||
69 | .irq_retrigger = irq_chip_retrigger_hierarchy, | ||
70 | .irq_set_affinity = irq_chip_set_affinity_parent, | ||
71 | }; | ||
72 | |||
73 | static int mtk_sysirq_domain_xlate(struct irq_domain *d, | ||
74 | struct device_node *controller, | ||
75 | const u32 *intspec, unsigned int intsize, | ||
76 | unsigned long *out_hwirq, | ||
77 | unsigned int *out_type) | ||
78 | { | ||
79 | if (intsize != 3) | ||
80 | return -EINVAL; | ||
81 | |||
82 | /* sysirq doesn't support PPI */ | ||
83 | if (intspec[0]) | ||
84 | return -EINVAL; | ||
85 | |||
86 | *out_hwirq = intspec[1]; | ||
87 | *out_type = intspec[2] & IRQ_TYPE_SENSE_MASK; | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static int mtk_sysirq_domain_alloc(struct irq_domain *domain, unsigned int virq, | ||
92 | unsigned int nr_irqs, void *arg) | ||
93 | { | ||
94 | int i; | ||
95 | irq_hw_number_t hwirq; | ||
96 | struct of_phandle_args *irq_data = arg; | ||
97 | struct of_phandle_args gic_data = *irq_data; | ||
98 | |||
99 | if (irq_data->args_count != 3) | ||
100 | return -EINVAL; | ||
101 | |||
102 | /* sysirq doesn't support PPI */ | ||
103 | if (irq_data->args[0]) | ||
104 | return -EINVAL; | ||
105 | |||
106 | hwirq = irq_data->args[1]; | ||
107 | for (i = 0; i < nr_irqs; i++) | ||
108 | irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, | ||
109 | &mtk_sysirq_chip, | ||
110 | domain->host_data); | ||
111 | |||
112 | gic_data.np = domain->parent->of_node; | ||
113 | return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &gic_data); | ||
114 | } | ||
115 | |||
116 | static struct irq_domain_ops sysirq_domain_ops = { | ||
117 | .xlate = mtk_sysirq_domain_xlate, | ||
118 | .alloc = mtk_sysirq_domain_alloc, | ||
119 | .free = irq_domain_free_irqs_common, | ||
120 | }; | ||
121 | |||
122 | static int __init mtk_sysirq_of_init(struct device_node *node, | ||
123 | struct device_node *parent) | ||
124 | { | ||
125 | struct irq_domain *domain, *domain_parent; | ||
126 | struct mtk_sysirq_chip_data *chip_data; | ||
127 | int ret = 0; | ||
128 | |||
129 | domain_parent = irq_find_host(parent); | ||
130 | if (!domain_parent) { | ||
131 | pr_err("mtk_sysirq: interrupt-parent not found\n"); | ||
132 | return -EINVAL; | ||
133 | } | ||
134 | |||
135 | chip_data = kzalloc(sizeof(*chip_data), GFP_KERNEL); | ||
136 | if (!chip_data) | ||
137 | return -ENOMEM; | ||
138 | |||
139 | chip_data->intpol_base = of_io_request_and_map(node, 0, "intpol"); | ||
140 | if (!chip_data->intpol_base) { | ||
141 | pr_err("mtk_sysirq: unable to map sysirq register\n"); | ||
142 | ret = -ENOMEM; | ||
143 | goto out_free; | ||
144 | } | ||
145 | |||
146 | domain = irq_domain_add_hierarchy(domain_parent, 0, | ||
147 | MT6577_SYS_INTPOL_NUM, node, | ||
148 | &sysirq_domain_ops, chip_data); | ||
149 | if (!domain) { | ||
150 | ret = -ENOMEM; | ||
151 | goto out_unmap; | ||
152 | } | ||
153 | spin_lock_init(&chip_data->lock); | ||
154 | |||
155 | return 0; | ||
156 | |||
157 | out_unmap: | ||
158 | iounmap(chip_data->intpol_base); | ||
159 | out_free: | ||
160 | kfree(chip_data); | ||
161 | return ret; | ||
162 | } | ||
163 | IRQCHIP_DECLARE(mtk_sysirq, "mediatek,mt6577-sysirq", mtk_sysirq_of_init); | ||
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h index 03a4ea37ba86..1e8b0cf30792 100644 --- a/include/linux/irqchip/arm-gic-v3.h +++ b/include/linux/irqchip/arm-gic-v3.h | |||
@@ -49,6 +49,10 @@ | |||
49 | #define GICD_CTLR_ENABLE_G1A (1U << 1) | 49 | #define GICD_CTLR_ENABLE_G1A (1U << 1) |
50 | #define GICD_CTLR_ENABLE_G1 (1U << 0) | 50 | #define GICD_CTLR_ENABLE_G1 (1U << 0) |
51 | 51 | ||
52 | #define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1) | ||
53 | #define GICD_TYPER_IRQS(typer) ((((typer) & 0x1f) + 1) * 32) | ||
54 | #define GICD_TYPER_LPIS (1U << 17) | ||
55 | |||
52 | #define GICD_IROUTER_SPI_MODE_ONE (0U << 31) | 56 | #define GICD_IROUTER_SPI_MODE_ONE (0U << 31) |
53 | #define GICD_IROUTER_SPI_MODE_ANY (1U << 31) | 57 | #define GICD_IROUTER_SPI_MODE_ANY (1U << 31) |
54 | 58 | ||
@@ -76,9 +80,27 @@ | |||
76 | #define GICR_MOVALLR 0x0110 | 80 | #define GICR_MOVALLR 0x0110 |
77 | #define GICR_PIDR2 GICD_PIDR2 | 81 | #define GICR_PIDR2 GICD_PIDR2 |
78 | 82 | ||
83 | #define GICR_CTLR_ENABLE_LPIS (1UL << 0) | ||
84 | |||
85 | #define GICR_TYPER_CPU_NUMBER(r) (((r) >> 8) & 0xffff) | ||
86 | |||
79 | #define GICR_WAKER_ProcessorSleep (1U << 1) | 87 | #define GICR_WAKER_ProcessorSleep (1U << 1) |
80 | #define GICR_WAKER_ChildrenAsleep (1U << 2) | 88 | #define GICR_WAKER_ChildrenAsleep (1U << 2) |
81 | 89 | ||
90 | #define GICR_PROPBASER_NonShareable (0U << 10) | ||
91 | #define GICR_PROPBASER_InnerShareable (1U << 10) | ||
92 | #define GICR_PROPBASER_OuterShareable (2U << 10) | ||
93 | #define GICR_PROPBASER_SHAREABILITY_MASK (3UL << 10) | ||
94 | #define GICR_PROPBASER_nCnB (0U << 7) | ||
95 | #define GICR_PROPBASER_nC (1U << 7) | ||
96 | #define GICR_PROPBASER_RaWt (2U << 7) | ||
97 | #define GICR_PROPBASER_RaWb (3U << 7) | ||
98 | #define GICR_PROPBASER_WaWt (4U << 7) | ||
99 | #define GICR_PROPBASER_WaWb (5U << 7) | ||
100 | #define GICR_PROPBASER_RaWaWt (6U << 7) | ||
101 | #define GICR_PROPBASER_RaWaWb (7U << 7) | ||
102 | #define GICR_PROPBASER_IDBITS_MASK (0x1f) | ||
103 | |||
82 | /* | 104 | /* |
83 | * Re-Distributor registers, offsets from SGI_base | 105 | * Re-Distributor registers, offsets from SGI_base |
84 | */ | 106 | */ |
@@ -91,9 +113,93 @@ | |||
91 | #define GICR_IPRIORITYR0 GICD_IPRIORITYR | 113 | #define GICR_IPRIORITYR0 GICD_IPRIORITYR |
92 | #define GICR_ICFGR0 GICD_ICFGR | 114 | #define GICR_ICFGR0 GICD_ICFGR |
93 | 115 | ||
116 | #define GICR_TYPER_PLPIS (1U << 0) | ||
94 | #define GICR_TYPER_VLPIS (1U << 1) | 117 | #define GICR_TYPER_VLPIS (1U << 1) |
95 | #define GICR_TYPER_LAST (1U << 4) | 118 | #define GICR_TYPER_LAST (1U << 4) |
96 | 119 | ||
120 | #define LPI_PROP_GROUP1 (1 << 1) | ||
121 | #define LPI_PROP_ENABLED (1 << 0) | ||
122 | |||
123 | /* | ||
124 | * ITS registers, offsets from ITS_base | ||
125 | */ | ||
126 | #define GITS_CTLR 0x0000 | ||
127 | #define GITS_IIDR 0x0004 | ||
128 | #define GITS_TYPER 0x0008 | ||
129 | #define GITS_CBASER 0x0080 | ||
130 | #define GITS_CWRITER 0x0088 | ||
131 | #define GITS_CREADR 0x0090 | ||
132 | #define GITS_BASER 0x0100 | ||
133 | #define GITS_PIDR2 GICR_PIDR2 | ||
134 | |||
135 | #define GITS_TRANSLATER 0x10040 | ||
136 | |||
137 | #define GITS_TYPER_PTA (1UL << 19) | ||
138 | |||
139 | #define GITS_CBASER_VALID (1UL << 63) | ||
140 | #define GITS_CBASER_nCnB (0UL << 59) | ||
141 | #define GITS_CBASER_nC (1UL << 59) | ||
142 | #define GITS_CBASER_RaWt (2UL << 59) | ||
143 | #define GITS_CBASER_RaWb (3UL << 59) | ||
144 | #define GITS_CBASER_WaWt (4UL << 59) | ||
145 | #define GITS_CBASER_WaWb (5UL << 59) | ||
146 | #define GITS_CBASER_RaWaWt (6UL << 59) | ||
147 | #define GITS_CBASER_RaWaWb (7UL << 59) | ||
148 | #define GITS_CBASER_NonShareable (0UL << 10) | ||
149 | #define GITS_CBASER_InnerShareable (1UL << 10) | ||
150 | #define GITS_CBASER_OuterShareable (2UL << 10) | ||
151 | #define GITS_CBASER_SHAREABILITY_MASK (3UL << 10) | ||
152 | |||
153 | #define GITS_BASER_NR_REGS 8 | ||
154 | |||
155 | #define GITS_BASER_VALID (1UL << 63) | ||
156 | #define GITS_BASER_nCnB (0UL << 59) | ||
157 | #define GITS_BASER_nC (1UL << 59) | ||
158 | #define GITS_BASER_RaWt (2UL << 59) | ||
159 | #define GITS_BASER_RaWb (3UL << 59) | ||
160 | #define GITS_BASER_WaWt (4UL << 59) | ||
161 | #define GITS_BASER_WaWb (5UL << 59) | ||
162 | #define GITS_BASER_RaWaWt (6UL << 59) | ||
163 | #define GITS_BASER_RaWaWb (7UL << 59) | ||
164 | #define GITS_BASER_TYPE_SHIFT (56) | ||
165 | #define GITS_BASER_TYPE(r) (((r) >> GITS_BASER_TYPE_SHIFT) & 7) | ||
166 | #define GITS_BASER_ENTRY_SIZE_SHIFT (48) | ||
167 | #define GITS_BASER_ENTRY_SIZE(r) ((((r) >> GITS_BASER_ENTRY_SIZE_SHIFT) & 0xff) + 1) | ||
168 | #define GITS_BASER_NonShareable (0UL << 10) | ||
169 | #define GITS_BASER_InnerShareable (1UL << 10) | ||
170 | #define GITS_BASER_OuterShareable (2UL << 10) | ||
171 | #define GITS_BASER_SHAREABILITY_SHIFT (10) | ||
172 | #define GITS_BASER_SHAREABILITY_MASK (3UL << GITS_BASER_SHAREABILITY_SHIFT) | ||
173 | #define GITS_BASER_PAGE_SIZE_SHIFT (8) | ||
174 | #define GITS_BASER_PAGE_SIZE_4K (0UL << GITS_BASER_PAGE_SIZE_SHIFT) | ||
175 | #define GITS_BASER_PAGE_SIZE_16K (1UL << GITS_BASER_PAGE_SIZE_SHIFT) | ||
176 | #define GITS_BASER_PAGE_SIZE_64K (2UL << GITS_BASER_PAGE_SIZE_SHIFT) | ||
177 | #define GITS_BASER_PAGE_SIZE_MASK (3UL << GITS_BASER_PAGE_SIZE_SHIFT) | ||
178 | |||
179 | #define GITS_BASER_TYPE_NONE 0 | ||
180 | #define GITS_BASER_TYPE_DEVICE 1 | ||
181 | #define GITS_BASER_TYPE_VCPU 2 | ||
182 | #define GITS_BASER_TYPE_CPU 3 | ||
183 | #define GITS_BASER_TYPE_COLLECTION 4 | ||
184 | #define GITS_BASER_TYPE_RESERVED5 5 | ||
185 | #define GITS_BASER_TYPE_RESERVED6 6 | ||
186 | #define GITS_BASER_TYPE_RESERVED7 7 | ||
187 | |||
188 | /* | ||
189 | * ITS commands | ||
190 | */ | ||
191 | #define GITS_CMD_MAPD 0x08 | ||
192 | #define GITS_CMD_MAPC 0x09 | ||
193 | #define GITS_CMD_MAPVI 0x0a | ||
194 | #define GITS_CMD_MOVI 0x01 | ||
195 | #define GITS_CMD_DISCARD 0x0f | ||
196 | #define GITS_CMD_INV 0x0c | ||
197 | #define GITS_CMD_MOVALL 0x0e | ||
198 | #define GITS_CMD_INVALL 0x0d | ||
199 | #define GITS_CMD_INT 0x03 | ||
200 | #define GITS_CMD_CLEAR 0x04 | ||
201 | #define GITS_CMD_SYNC 0x05 | ||
202 | |||
97 | /* | 203 | /* |
98 | * CPU interface registers | 204 | * CPU interface registers |
99 | */ | 205 | */ |
@@ -189,12 +295,34 @@ | |||
189 | 295 | ||
190 | #include <linux/stringify.h> | 296 | #include <linux/stringify.h> |
191 | 297 | ||
298 | /* | ||
299 | * We need a value to serve as a irq-type for LPIs. Choose one that will | ||
300 | * hopefully pique the interest of the reviewer. | ||
301 | */ | ||
302 | #define GIC_IRQ_TYPE_LPI 0xa110c8ed | ||
303 | |||
304 | struct rdists { | ||
305 | struct { | ||
306 | void __iomem *rd_base; | ||
307 | struct page *pend_page; | ||
308 | phys_addr_t phys_base; | ||
309 | } __percpu *rdist; | ||
310 | struct page *prop_page; | ||
311 | int id_bits; | ||
312 | u64 flags; | ||
313 | }; | ||
314 | |||
192 | static inline void gic_write_eoir(u64 irq) | 315 | static inline void gic_write_eoir(u64 irq) |
193 | { | 316 | { |
194 | asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" (irq)); | 317 | asm volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" (irq)); |
195 | isb(); | 318 | isb(); |
196 | } | 319 | } |
197 | 320 | ||
321 | struct irq_domain; | ||
322 | int its_cpu_init(void); | ||
323 | int its_init(struct device_node *node, struct rdists *rdists, | ||
324 | struct irq_domain *domain); | ||
325 | |||
198 | #endif | 326 | #endif |
199 | 327 | ||
200 | #endif | 328 | #endif |
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 13eed92c7d24..71d706d5f169 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h | |||
@@ -91,6 +91,8 @@ | |||
91 | 91 | ||
92 | #ifndef __ASSEMBLY__ | 92 | #ifndef __ASSEMBLY__ |
93 | 93 | ||
94 | #include <linux/irqdomain.h> | ||
95 | |||
94 | struct device_node; | 96 | struct device_node; |
95 | 97 | ||
96 | extern struct irq_chip gic_arch_extn; | 98 | extern struct irq_chip gic_arch_extn; |
@@ -106,6 +108,8 @@ static inline void gic_init(unsigned int nr, int start, | |||
106 | gic_init_bases(nr, start, dist, cpu, 0, NULL); | 108 | gic_init_bases(nr, start, dist, cpu, 0, NULL); |
107 | } | 109 | } |
108 | 110 | ||
111 | int gicv2m_of_init(struct device_node *node, struct irq_domain *parent); | ||
112 | |||
109 | void gic_send_sgi(unsigned int cpu_id, unsigned int irq); | 113 | void gic_send_sgi(unsigned int cpu_id, unsigned int irq); |
110 | int gic_get_cpu_id(unsigned int cpu); | 114 | int gic_get_cpu_id(unsigned int cpu); |
111 | void gic_migrate_target(unsigned int new_cpu_id); | 115 | void gic_migrate_target(unsigned int new_cpu_id); |