diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-11 21:28:06 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-01-11 21:28:06 -0500 |
commit | 3d116a66ed9df0271b8d267093b3bfde2be19b3a (patch) | |
tree | 8415b051c87b44ea9f7ecd50bf850155b028737e | |
parent | b4cee21ee057ff3e5c9014fb6a175bd932c5ce62 (diff) | |
parent | d3b421cd07e4c0d4d6c0bbd55ca169c054fc081d (diff) |
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner:
"The irq department provides:
- Support for MSI to wire bridges and a first user of it
- More ACPI support for ARM/GIC
- A new TS-4800 interrupt controller driver
- RCU based free of interrupt descriptors to support the upcoming
Intel VMD technology without introducing a locking nightmare
- The usual pile of fixes and updates to drivers and core code"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (41 commits)
irqchip/omap-intc: Add support for spurious irq handling
irqchip/zevio: Use irq_data_get_chip_type() helper
irqchip/omap-intc: Remove duplicate setup for IRQ chip type handler
irqchip/ts4800: Add TS-4800 interrupt controller
irqchip/ts4800: Add documentation for TS-4800 interrupt controller
irq/platform-MSI: Increase the maximum MSIs the MSI framework can support
irqchip/gicv2m: Miscellaneous fixes for v2m resources and SPI ranges
irqchip/bcm2836: Make code more readable
irqchip/bcm2836: Tolerate IRQs while no flag is set in ISR
irqchip/bcm2836: Add SMP support for the 2836
irqchip/bcm2836: Fix initialization of the LOCAL_IRQ_CNT timers
irqchip/gic-v2m: acpi: Introducing GICv2m ACPI support
irqchip/gic-v2m: Refactor to prepare for ACPI support
irqdomain: Introduce is_fwnode_irqchip helper
acpi: pci: Setup MSI domain for ACPI based pci devices
genirq/msi: Export functions to allow MSI domains in modules
irqchip/mbigen: Implement the mbigen irq chip operation functions
irqchip/mbigen: Create irq domain for each mbigen device
irqchip/mgigen: Add platform device driver for mbigen device
dt-bindings: Documents the mbigen bindings
...
32 files changed, 1350 insertions, 209 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-nmi.txt index d1c5cdabc3e0..81cd3692405e 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/allwinner,sun67i-sc-nmi.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/allwinner,sunxi-nmi.txt | |||
@@ -4,7 +4,7 @@ Allwinner Sunxi NMI Controller | |||
4 | Required properties: | 4 | Required properties: |
5 | 5 | ||
6 | - compatible : should be "allwinner,sun7i-a20-sc-nmi" or | 6 | - compatible : should be "allwinner,sun7i-a20-sc-nmi" or |
7 | "allwinner,sun6i-a31-sc-nmi" | 7 | "allwinner,sun6i-a31-sc-nmi" or "allwinner,sun9i-a80-nmi" |
8 | - reg : Specifies base physical address and size of the registers. | 8 | - reg : Specifies base physical address and size of the registers. |
9 | - interrupt-controller : Identifies the node as an interrupt controller | 9 | - interrupt-controller : Identifies the node as an interrupt controller |
10 | - #interrupt-cells : Specifies the number of cells needed to encode an | 10 | - #interrupt-cells : Specifies the number of cells needed to encode an |
diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt index cc56021eb60b..5a1cb4bc3dfe 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt | |||
@@ -18,6 +18,7 @@ Main node required properties: | |||
18 | "arm,cortex-a9-gic" | 18 | "arm,cortex-a9-gic" |
19 | "arm,gic-400" | 19 | "arm,gic-400" |
20 | "arm,pl390" | 20 | "arm,pl390" |
21 | "arm,tc11mp-gic" | ||
21 | "brcm,brahma-b15-gic" | 22 | "brcm,brahma-b15-gic" |
22 | "qcom,msm-8660-qgic" | 23 | "qcom,msm-8660-qgic" |
23 | "qcom,msm-qgic2" | 24 | "qcom,msm-qgic2" |
diff --git a/Documentation/devicetree/bindings/interrupt-controller/hisilicon,mbigen-v2.txt b/Documentation/devicetree/bindings/interrupt-controller/hisilicon,mbigen-v2.txt new file mode 100644 index 000000000000..720f7c92e9a1 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/hisilicon,mbigen-v2.txt | |||
@@ -0,0 +1,74 @@ | |||
1 | Hisilicon mbigen device tree bindings. | ||
2 | ======================================= | ||
3 | |||
4 | Mbigen means: message based interrupt generator. | ||
5 | |||
6 | MBI is kind of msi interrupt only used on Non-PCI devices. | ||
7 | |||
8 | To reduce the wired interrupt number connected to GIC, | ||
9 | Hisilicon designed mbigen to collect and generate interrupt. | ||
10 | |||
11 | |||
12 | Non-pci devices can connect to mbigen and generate the | ||
13 | interrupt by writing ITS register. | ||
14 | |||
15 | The mbigen chip and devices connect to mbigen have the following properties: | ||
16 | |||
17 | Mbigen main node required properties: | ||
18 | ------------------------------------------- | ||
19 | - compatible: Should be "hisilicon,mbigen-v2" | ||
20 | |||
21 | - reg: Specifies the base physical address and size of the Mbigen | ||
22 | registers. | ||
23 | |||
24 | - interrupt controller: Identifies the node as an interrupt controller | ||
25 | |||
26 | - msi-parent: Specifies the MSI controller this mbigen use. | ||
27 | For more detail information,please refer to the generic msi-parent binding in | ||
28 | Documentation/devicetree/bindings/interrupt-controller/msi.txt. | ||
29 | |||
30 | - num-pins: the total number of pins implemented in this Mbigen | ||
31 | instance. | ||
32 | |||
33 | - #interrupt-cells : Specifies the number of cells needed to encode an | ||
34 | interrupt source. The value must be 2. | ||
35 | |||
36 | The 1st cell is hardware pin number of the interrupt.This number is local to | ||
37 | each mbigen chip and in the range from 0 to the maximum interrupts number | ||
38 | of the mbigen. | ||
39 | |||
40 | The 2nd cell is the interrupt trigger type. | ||
41 | The value of this cell should be: | ||
42 | 1: rising edge triggered | ||
43 | or | ||
44 | 4: high level triggered | ||
45 | |||
46 | Examples: | ||
47 | |||
48 | mbigen_device_gmac:intc { | ||
49 | compatible = "hisilicon,mbigen-v2"; | ||
50 | reg = <0x0 0xc0080000 0x0 0x10000>; | ||
51 | interrupt-controller; | ||
52 | msi-parent = <&its_dsa 0x40b1c>; | ||
53 | num-pins = <9>; | ||
54 | #interrupt-cells = <2>; | ||
55 | }; | ||
56 | |||
57 | Devices connect to mbigen required properties: | ||
58 | ---------------------------------------------------- | ||
59 | -interrupt-parent: Specifies the mbigen device node which device connected. | ||
60 | |||
61 | -interrupts:Specifies the interrupt source. | ||
62 | For the specific information of each cell in this property,please refer to | ||
63 | the "interrupt-cells" description mentioned above. | ||
64 | |||
65 | Examples: | ||
66 | gmac0: ethernet@c2080000 { | ||
67 | #address-cells = <1>; | ||
68 | #size-cells = <0>; | ||
69 | reg = <0 0xc2080000 0 0x20000>, | ||
70 | <0 0xc0000000 0 0x1000>; | ||
71 | interrupt-parent = <&mbigen_device_gmac>; | ||
72 | interrupts = <656 1>, | ||
73 | <657 1>; | ||
74 | }; | ||
diff --git a/Documentation/devicetree/bindings/interrupt-controller/technologic,ts4800.txt b/Documentation/devicetree/bindings/interrupt-controller/technologic,ts4800.txt new file mode 100644 index 000000000000..7f15f1b0325b --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/technologic,ts4800.txt | |||
@@ -0,0 +1,16 @@ | |||
1 | TS-4800 FPGA interrupt controller | ||
2 | |||
3 | TS-4800 FPGA has an internal interrupt controller. When one of the | ||
4 | interrupts is triggered, the SoC is notified, usually using a GPIO as | ||
5 | parent interrupt source. | ||
6 | |||
7 | Required properties: | ||
8 | - compatible: should be "technologic,ts4800-irqc" | ||
9 | - interrupt-controller: identifies the node as an interrupt controller | ||
10 | - reg: physical base address of the controller and length of memory mapped | ||
11 | region | ||
12 | - #interrupt-cells: specifies the number of cells needed to encode an interrupt | ||
13 | source, should be 1. | ||
14 | - interrupt-parent: phandle to the parent interrupt controller this one is | ||
15 | cascaded from | ||
16 | - interrupts: specifies the interrupt line in the interrupt-parent controller | ||
diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 5df4575b5ba7..47c43386786b 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c | |||
@@ -24,13 +24,17 @@ | |||
24 | #include <linux/msi.h> | 24 | #include <linux/msi.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | 26 | ||
27 | #define DEV_ID_SHIFT 24 | 27 | #define DEV_ID_SHIFT 21 |
28 | #define MAX_DEV_MSIS (1 << (32 - DEV_ID_SHIFT)) | ||
28 | 29 | ||
29 | /* | 30 | /* |
30 | * Internal data structure containing a (made up, but unique) devid | 31 | * Internal data structure containing a (made up, but unique) devid |
31 | * and the callback to write the MSI message. | 32 | * and the callback to write the MSI message. |
32 | */ | 33 | */ |
33 | struct platform_msi_priv_data { | 34 | struct platform_msi_priv_data { |
35 | struct device *dev; | ||
36 | void *host_data; | ||
37 | msi_alloc_info_t arg; | ||
34 | irq_write_msi_msg_t write_msg; | 38 | irq_write_msi_msg_t write_msg; |
35 | int devid; | 39 | int devid; |
36 | }; | 40 | }; |
@@ -110,39 +114,49 @@ static void platform_msi_update_chip_ops(struct msi_domain_info *info) | |||
110 | chip->irq_write_msi_msg = platform_msi_write_msg; | 114 | chip->irq_write_msi_msg = platform_msi_write_msg; |
111 | } | 115 | } |
112 | 116 | ||
113 | static void platform_msi_free_descs(struct device *dev) | 117 | static void platform_msi_free_descs(struct device *dev, int base, int nvec) |
114 | { | 118 | { |
115 | struct msi_desc *desc, *tmp; | 119 | struct msi_desc *desc, *tmp; |
116 | 120 | ||
117 | list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) { | 121 | list_for_each_entry_safe(desc, tmp, dev_to_msi_list(dev), list) { |
118 | list_del(&desc->list); | 122 | if (desc->platform.msi_index >= base && |
119 | free_msi_entry(desc); | 123 | desc->platform.msi_index < (base + nvec)) { |
124 | list_del(&desc->list); | ||
125 | free_msi_entry(desc); | ||
126 | } | ||
120 | } | 127 | } |
121 | } | 128 | } |
122 | 129 | ||
123 | static int platform_msi_alloc_descs(struct device *dev, int nvec, | 130 | static int platform_msi_alloc_descs_with_irq(struct device *dev, int virq, |
124 | struct platform_msi_priv_data *data) | 131 | int nvec, |
132 | struct platform_msi_priv_data *data) | ||
125 | 133 | ||
126 | { | 134 | { |
127 | int i; | 135 | struct msi_desc *desc; |
136 | int i, base = 0; | ||
128 | 137 | ||
129 | for (i = 0; i < nvec; i++) { | 138 | if (!list_empty(dev_to_msi_list(dev))) { |
130 | struct msi_desc *desc; | 139 | desc = list_last_entry(dev_to_msi_list(dev), |
140 | struct msi_desc, list); | ||
141 | base = desc->platform.msi_index + 1; | ||
142 | } | ||
131 | 143 | ||
144 | for (i = 0; i < nvec; i++) { | ||
132 | desc = alloc_msi_entry(dev); | 145 | desc = alloc_msi_entry(dev); |
133 | if (!desc) | 146 | if (!desc) |
134 | break; | 147 | break; |
135 | 148 | ||
136 | desc->platform.msi_priv_data = data; | 149 | desc->platform.msi_priv_data = data; |
137 | desc->platform.msi_index = i; | 150 | desc->platform.msi_index = base + i; |
138 | desc->nvec_used = 1; | 151 | desc->nvec_used = 1; |
152 | desc->irq = virq ? virq + i : 0; | ||
139 | 153 | ||
140 | list_add_tail(&desc->list, dev_to_msi_list(dev)); | 154 | list_add_tail(&desc->list, dev_to_msi_list(dev)); |
141 | } | 155 | } |
142 | 156 | ||
143 | if (i != nvec) { | 157 | if (i != nvec) { |
144 | /* Clean up the mess */ | 158 | /* Clean up the mess */ |
145 | platform_msi_free_descs(dev); | 159 | platform_msi_free_descs(dev, base, nvec); |
146 | 160 | ||
147 | return -ENOMEM; | 161 | return -ENOMEM; |
148 | } | 162 | } |
@@ -150,6 +164,13 @@ static int platform_msi_alloc_descs(struct device *dev, int nvec, | |||
150 | return 0; | 164 | return 0; |
151 | } | 165 | } |
152 | 166 | ||
167 | static int platform_msi_alloc_descs(struct device *dev, int nvec, | ||
168 | struct platform_msi_priv_data *data) | ||
169 | |||
170 | { | ||
171 | return platform_msi_alloc_descs_with_irq(dev, 0, nvec, data); | ||
172 | } | ||
173 | |||
153 | /** | 174 | /** |
154 | * platform_msi_create_irq_domain - Create a platform MSI interrupt domain | 175 | * platform_msi_create_irq_domain - Create a platform MSI interrupt domain |
155 | * @fwnode: Optional fwnode of the interrupt controller | 176 | * @fwnode: Optional fwnode of the interrupt controller |
@@ -180,56 +201,75 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode, | |||
180 | return domain; | 201 | return domain; |
181 | } | 202 | } |
182 | 203 | ||
183 | /** | 204 | static struct platform_msi_priv_data * |
184 | * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev | 205 | platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec, |
185 | * @dev: The device for which to allocate interrupts | 206 | irq_write_msi_msg_t write_msi_msg) |
186 | * @nvec: The number of interrupts to allocate | ||
187 | * @write_msi_msg: Callback to write an interrupt message for @dev | ||
188 | * | ||
189 | * Returns: | ||
190 | * Zero for success, or an error code in case of failure | ||
191 | */ | ||
192 | int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, | ||
193 | irq_write_msi_msg_t write_msi_msg) | ||
194 | { | 207 | { |
195 | struct platform_msi_priv_data *priv_data; | 208 | struct platform_msi_priv_data *datap; |
196 | int err; | ||
197 | |||
198 | /* | 209 | /* |
199 | * Limit the number of interrupts to 256 per device. Should we | 210 | * Limit the number of interrupts to 256 per device. Should we |
200 | * need to bump this up, DEV_ID_SHIFT should be adjusted | 211 | * need to bump this up, DEV_ID_SHIFT should be adjusted |
201 | * accordingly (which would impact the max number of MSI | 212 | * accordingly (which would impact the max number of MSI |
202 | * capable devices). | 213 | * capable devices). |
203 | */ | 214 | */ |
204 | if (!dev->msi_domain || !write_msi_msg || !nvec || | 215 | if (!dev->msi_domain || !write_msi_msg || !nvec || nvec > MAX_DEV_MSIS) |
205 | nvec > (1 << (32 - DEV_ID_SHIFT))) | 216 | return ERR_PTR(-EINVAL); |
206 | return -EINVAL; | ||
207 | 217 | ||
208 | if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) { | 218 | if (dev->msi_domain->bus_token != DOMAIN_BUS_PLATFORM_MSI) { |
209 | dev_err(dev, "Incompatible msi_domain, giving up\n"); | 219 | dev_err(dev, "Incompatible msi_domain, giving up\n"); |
210 | return -EINVAL; | 220 | return ERR_PTR(-EINVAL); |
211 | } | 221 | } |
212 | 222 | ||
213 | /* Already had a helping of MSI? Greed... */ | 223 | /* Already had a helping of MSI? Greed... */ |
214 | if (!list_empty(dev_to_msi_list(dev))) | 224 | if (!list_empty(dev_to_msi_list(dev))) |
215 | return -EBUSY; | 225 | return ERR_PTR(-EBUSY); |
226 | |||
227 | datap = kzalloc(sizeof(*datap), GFP_KERNEL); | ||
228 | if (!datap) | ||
229 | return ERR_PTR(-ENOMEM); | ||
230 | |||
231 | datap->devid = ida_simple_get(&platform_msi_devid_ida, | ||
232 | 0, 1 << DEV_ID_SHIFT, GFP_KERNEL); | ||
233 | if (datap->devid < 0) { | ||
234 | int err = datap->devid; | ||
235 | kfree(datap); | ||
236 | return ERR_PTR(err); | ||
237 | } | ||
216 | 238 | ||
217 | priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL); | 239 | datap->write_msg = write_msi_msg; |
218 | if (!priv_data) | 240 | datap->dev = dev; |
219 | return -ENOMEM; | ||
220 | 241 | ||
221 | priv_data->devid = ida_simple_get(&platform_msi_devid_ida, | 242 | return datap; |
222 | 0, 1 << DEV_ID_SHIFT, GFP_KERNEL); | 243 | } |
223 | if (priv_data->devid < 0) { | 244 | |
224 | err = priv_data->devid; | 245 | static void platform_msi_free_priv_data(struct platform_msi_priv_data *data) |
225 | goto out_free_data; | 246 | { |
226 | } | 247 | ida_simple_remove(&platform_msi_devid_ida, data->devid); |
248 | kfree(data); | ||
249 | } | ||
250 | |||
251 | /** | ||
252 | * platform_msi_domain_alloc_irqs - Allocate MSI interrupts for @dev | ||
253 | * @dev: The device for which to allocate interrupts | ||
254 | * @nvec: The number of interrupts to allocate | ||
255 | * @write_msi_msg: Callback to write an interrupt message for @dev | ||
256 | * | ||
257 | * Returns: | ||
258 | * Zero for success, or an error code in case of failure | ||
259 | */ | ||
260 | int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, | ||
261 | irq_write_msi_msg_t write_msi_msg) | ||
262 | { | ||
263 | struct platform_msi_priv_data *priv_data; | ||
264 | int err; | ||
227 | 265 | ||
228 | priv_data->write_msg = write_msi_msg; | 266 | priv_data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); |
267 | if (IS_ERR(priv_data)) | ||
268 | return PTR_ERR(priv_data); | ||
229 | 269 | ||
230 | err = platform_msi_alloc_descs(dev, nvec, priv_data); | 270 | err = platform_msi_alloc_descs(dev, nvec, priv_data); |
231 | if (err) | 271 | if (err) |
232 | goto out_free_id; | 272 | goto out_free_priv_data; |
233 | 273 | ||
234 | err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec); | 274 | err = msi_domain_alloc_irqs(dev->msi_domain, dev, nvec); |
235 | if (err) | 275 | if (err) |
@@ -238,11 +278,9 @@ int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, | |||
238 | return 0; | 278 | return 0; |
239 | 279 | ||
240 | out_free_desc: | 280 | out_free_desc: |
241 | platform_msi_free_descs(dev); | 281 | platform_msi_free_descs(dev, 0, nvec); |
242 | out_free_id: | 282 | out_free_priv_data: |
243 | ida_simple_remove(&platform_msi_devid_ida, priv_data->devid); | 283 | platform_msi_free_priv_data(priv_data); |
244 | out_free_data: | ||
245 | kfree(priv_data); | ||
246 | 284 | ||
247 | return err; | 285 | return err; |
248 | } | 286 | } |
@@ -253,18 +291,126 @@ out_free_data: | |||
253 | */ | 291 | */ |
254 | void platform_msi_domain_free_irqs(struct device *dev) | 292 | void platform_msi_domain_free_irqs(struct device *dev) |
255 | { | 293 | { |
256 | struct msi_desc *desc; | 294 | if (!list_empty(dev_to_msi_list(dev))) { |
295 | struct msi_desc *desc; | ||
296 | |||
297 | desc = first_msi_entry(dev); | ||
298 | platform_msi_free_priv_data(desc->platform.msi_priv_data); | ||
299 | } | ||
300 | |||
301 | msi_domain_free_irqs(dev->msi_domain, dev); | ||
302 | platform_msi_free_descs(dev, 0, MAX_DEV_MSIS); | ||
303 | } | ||
304 | |||
305 | /** | ||
306 | * platform_msi_get_host_data - Query the private data associated with | ||
307 | * a platform-msi domain | ||
308 | * @domain: The platform-msi domain | ||
309 | * | ||
310 | * Returns the private data provided when calling | ||
311 | * platform_msi_create_device_domain. | ||
312 | */ | ||
313 | void *platform_msi_get_host_data(struct irq_domain *domain) | ||
314 | { | ||
315 | struct platform_msi_priv_data *data = domain->host_data; | ||
316 | return data->host_data; | ||
317 | } | ||
318 | |||
319 | /** | ||
320 | * platform_msi_create_device_domain - Create a platform-msi domain | ||
321 | * | ||
322 | * @dev: The device generating the MSIs | ||
323 | * @nvec: The number of MSIs that need to be allocated | ||
324 | * @write_msi_msg: Callback to write an interrupt message for @dev | ||
325 | * @ops: The hierarchy domain operations to use | ||
326 | * @host_data: Private data associated to this domain | ||
327 | * | ||
328 | * Returns an irqdomain for @nvec interrupts | ||
329 | */ | ||
330 | struct irq_domain * | ||
331 | platform_msi_create_device_domain(struct device *dev, | ||
332 | unsigned int nvec, | ||
333 | irq_write_msi_msg_t write_msi_msg, | ||
334 | const struct irq_domain_ops *ops, | ||
335 | void *host_data) | ||
336 | { | ||
337 | struct platform_msi_priv_data *data; | ||
338 | struct irq_domain *domain; | ||
339 | int err; | ||
340 | |||
341 | data = platform_msi_alloc_priv_data(dev, nvec, write_msi_msg); | ||
342 | if (IS_ERR(data)) | ||
343 | return NULL; | ||
344 | |||
345 | data->host_data = host_data; | ||
346 | domain = irq_domain_create_hierarchy(dev->msi_domain, 0, nvec, | ||
347 | of_node_to_fwnode(dev->of_node), | ||
348 | ops, data); | ||
349 | if (!domain) | ||
350 | goto free_priv; | ||
257 | 351 | ||
258 | desc = first_msi_entry(dev); | 352 | err = msi_domain_prepare_irqs(domain->parent, dev, nvec, &data->arg); |
259 | if (desc) { | 353 | if (err) |
260 | struct platform_msi_priv_data *data; | 354 | goto free_domain; |
355 | |||
356 | return domain; | ||
261 | 357 | ||
262 | data = desc->platform.msi_priv_data; | 358 | free_domain: |
359 | irq_domain_remove(domain); | ||
360 | free_priv: | ||
361 | platform_msi_free_priv_data(data); | ||
362 | return NULL; | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * platform_msi_domain_free - Free interrupts associated with a platform-msi | ||
367 | * domain | ||
368 | * | ||
369 | * @domain: The platform-msi domain | ||
370 | * @virq: The base irq from which to perform the free operation | ||
371 | * @nvec: How many interrupts to free from @virq | ||
372 | */ | ||
373 | void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq, | ||
374 | unsigned int nvec) | ||
375 | { | ||
376 | struct platform_msi_priv_data *data = domain->host_data; | ||
377 | struct msi_desc *desc; | ||
378 | for_each_msi_entry(desc, data->dev) { | ||
379 | if (WARN_ON(!desc->irq || desc->nvec_used != 1)) | ||
380 | return; | ||
381 | if (!(desc->irq >= virq && desc->irq < (virq + nvec))) | ||
382 | continue; | ||
263 | 383 | ||
264 | ida_simple_remove(&platform_msi_devid_ida, data->devid); | 384 | irq_domain_free_irqs_common(domain, desc->irq, 1); |
265 | kfree(data); | ||
266 | } | 385 | } |
386 | } | ||
267 | 387 | ||
268 | msi_domain_free_irqs(dev->msi_domain, dev); | 388 | /** |
269 | platform_msi_free_descs(dev); | 389 | * platform_msi_domain_alloc - Allocate interrupts associated with |
390 | * a platform-msi domain | ||
391 | * | ||
392 | * @domain: The platform-msi domain | ||
393 | * @virq: The base irq from which to perform the allocate operation | ||
394 | * @nvec: How many interrupts to free from @virq | ||
395 | * | ||
396 | * Return 0 on success, or an error code on failure. Must be called | ||
397 | * with irq_domain_mutex held (which can only be done as part of a | ||
398 | * top-level interrupt allocation). | ||
399 | */ | ||
400 | int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, | ||
401 | unsigned int nr_irqs) | ||
402 | { | ||
403 | struct platform_msi_priv_data *data = domain->host_data; | ||
404 | int err; | ||
405 | |||
406 | err = platform_msi_alloc_descs_with_irq(data->dev, virq, nr_irqs, data); | ||
407 | if (err) | ||
408 | return err; | ||
409 | |||
410 | err = msi_domain_populate_irqs(domain->parent, data->dev, | ||
411 | virq, nr_irqs, &data->arg); | ||
412 | if (err) | ||
413 | platform_msi_domain_free(domain, virq, nr_irqs); | ||
414 | |||
415 | return err; | ||
270 | } | 416 | } |
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 4d7294e5d982..11fc2a27fa2e 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -8,6 +8,11 @@ config ARM_GIC | |||
8 | select IRQ_DOMAIN_HIERARCHY | 8 | select IRQ_DOMAIN_HIERARCHY |
9 | select MULTI_IRQ_HANDLER | 9 | select MULTI_IRQ_HANDLER |
10 | 10 | ||
11 | config ARM_GIC_MAX_NR | ||
12 | int | ||
13 | default 2 if ARCH_REALVIEW | ||
14 | default 1 | ||
15 | |||
11 | config ARM_GIC_V2M | 16 | config ARM_GIC_V2M |
12 | bool | 17 | bool |
13 | depends on ARM_GIC | 18 | depends on ARM_GIC |
@@ -27,6 +32,14 @@ config ARM_GIC_V3_ITS | |||
27 | bool | 32 | bool |
28 | select PCI_MSI_IRQ_DOMAIN | 33 | select PCI_MSI_IRQ_DOMAIN |
29 | 34 | ||
35 | config HISILICON_IRQ_MBIGEN | ||
36 | bool "Support mbigen interrupt controller" | ||
37 | default n | ||
38 | depends on ARM_GIC_V3 && ARM_GIC_V3_ITS && GENERIC_MSI_IRQ_DOMAIN | ||
39 | help | ||
40 | Enable the mbigen interrupt controller used on | ||
41 | Hisilicon platform. | ||
42 | |||
30 | config ARM_NVIC | 43 | config ARM_NVIC |
31 | bool | 44 | bool |
32 | select IRQ_DOMAIN | 45 | select IRQ_DOMAIN |
@@ -138,6 +151,12 @@ config TB10X_IRQC | |||
138 | select IRQ_DOMAIN | 151 | select IRQ_DOMAIN |
139 | select GENERIC_IRQ_CHIP | 152 | select GENERIC_IRQ_CHIP |
140 | 153 | ||
154 | config TS4800_IRQ | ||
155 | tristate "TS-4800 IRQ controller" | ||
156 | select IRQ_DOMAIN | ||
157 | help | ||
158 | Support for the TS-4800 FPGA IRQ controller | ||
159 | |||
141 | config VERSATILE_FPGA_IRQ | 160 | config VERSATILE_FPGA_IRQ |
142 | bool | 161 | bool |
143 | select IRQ_DOMAIN | 162 | select IRQ_DOMAIN |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 177f78f6e6d6..d4c2e4ebc308 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -21,9 +21,11 @@ obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o | |||
21 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o | 21 | obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o |
22 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o | 22 | obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o |
23 | obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o | 23 | obj-$(CONFIG_ARM_GIC) += irq-gic.o irq-gic-common.o |
24 | obj-$(CONFIG_REALVIEW_DT) += irq-gic-realview.o | ||
24 | obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o | 25 | obj-$(CONFIG_ARM_GIC_V2M) += irq-gic-v2m.o |
25 | obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o | 26 | obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-common.o |
26 | obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o | 27 | obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v3-its-pci-msi.o irq-gic-v3-its-platform-msi.o |
28 | obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o | ||
27 | obj-$(CONFIG_ARM_NVIC) += irq-nvic.o | 29 | obj-$(CONFIG_ARM_NVIC) += irq-nvic.o |
28 | obj-$(CONFIG_ARM_VIC) += irq-vic.o | 30 | obj-$(CONFIG_ARM_VIC) += irq-vic.o |
29 | obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o | 31 | obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o |
@@ -39,6 +41,7 @@ obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o | |||
39 | obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o | 41 | obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o |
40 | obj-$(CONFIG_ST_IRQCHIP) += irq-st.o | 42 | obj-$(CONFIG_ST_IRQCHIP) += irq-st.o |
41 | obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o | 43 | obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o |
44 | obj-$(CONFIG_TS4800_IRQ) += irq-ts4800.o | ||
42 | obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o | 45 | obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o |
43 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o | 46 | obj-$(CONFIG_XTENSA_MX) += irq-xtensa-mx.o |
44 | obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o | 47 | obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o |
diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index f68708281fcf..963065a0d774 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c | |||
@@ -21,6 +21,9 @@ | |||
21 | #include <linux/irqdomain.h> | 21 | #include <linux/irqdomain.h> |
22 | #include <asm/exception.h> | 22 | #include <asm/exception.h> |
23 | 23 | ||
24 | #define LOCAL_CONTROL 0x000 | ||
25 | #define LOCAL_PRESCALER 0x008 | ||
26 | |||
24 | /* | 27 | /* |
25 | * The low 2 bits identify the CPU that the GPU IRQ goes to, and the | 28 | * The low 2 bits identify the CPU that the GPU IRQ goes to, and the |
26 | * next 2 bits identify the CPU that the GPU FIQ goes to. | 29 | * next 2 bits identify the CPU that the GPU FIQ goes to. |
@@ -50,14 +53,16 @@ | |||
50 | /* Same status bits as above, but for FIQ. */ | 53 | /* Same status bits as above, but for FIQ. */ |
51 | #define LOCAL_FIQ_PENDING0 0x070 | 54 | #define LOCAL_FIQ_PENDING0 0x070 |
52 | /* | 55 | /* |
53 | * Mailbox0 write-to-set bits. There are 16 mailboxes, 4 per CPU, and | 56 | * Mailbox write-to-set bits. There are 16 mailboxes, 4 per CPU, and |
54 | * these bits are organized by mailbox number and then CPU number. We | 57 | * these bits are organized by mailbox number and then CPU number. We |
55 | * use mailbox 0 for IPIs. The mailbox's interrupt is raised while | 58 | * use mailbox 0 for IPIs. The mailbox's interrupt is raised while |
56 | * any bit is set. | 59 | * any bit is set. |
57 | */ | 60 | */ |
58 | #define LOCAL_MAILBOX0_SET0 0x080 | 61 | #define LOCAL_MAILBOX0_SET0 0x080 |
59 | /* Mailbox0 write-to-clear bits. */ | 62 | #define LOCAL_MAILBOX3_SET0 0x08c |
63 | /* Mailbox write-to-clear bits. */ | ||
60 | #define LOCAL_MAILBOX0_CLR0 0x0c0 | 64 | #define LOCAL_MAILBOX0_CLR0 0x0c0 |
65 | #define LOCAL_MAILBOX3_CLR0 0x0cc | ||
61 | 66 | ||
62 | #define LOCAL_IRQ_CNTPSIRQ 0 | 67 | #define LOCAL_IRQ_CNTPSIRQ 0 |
63 | #define LOCAL_IRQ_CNTPNSIRQ 1 | 68 | #define LOCAL_IRQ_CNTPNSIRQ 1 |
@@ -162,7 +167,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs) | |||
162 | u32 stat; | 167 | u32 stat; |
163 | 168 | ||
164 | stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu); | 169 | stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu); |
165 | if (stat & 0x10) { | 170 | if (stat & BIT(LOCAL_IRQ_MAILBOX0)) { |
166 | #ifdef CONFIG_SMP | 171 | #ifdef CONFIG_SMP |
167 | void __iomem *mailbox0 = (intc.base + | 172 | void __iomem *mailbox0 = (intc.base + |
168 | LOCAL_MAILBOX0_CLR0 + 16 * cpu); | 173 | LOCAL_MAILBOX0_CLR0 + 16 * cpu); |
@@ -172,7 +177,7 @@ __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs) | |||
172 | writel(1 << ipi, mailbox0); | 177 | writel(1 << ipi, mailbox0); |
173 | handle_IPI(ipi, regs); | 178 | handle_IPI(ipi, regs); |
174 | #endif | 179 | #endif |
175 | } else { | 180 | } else if (stat) { |
176 | u32 hwirq = ffs(stat) - 1; | 181 | u32 hwirq = ffs(stat) - 1; |
177 | 182 | ||
178 | handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs); | 183 | handle_IRQ(irq_linear_revmap(intc.domain, hwirq), regs); |
@@ -217,6 +222,24 @@ static struct notifier_block bcm2836_arm_irqchip_cpu_notifier = { | |||
217 | .notifier_call = bcm2836_arm_irqchip_cpu_notify, | 222 | .notifier_call = bcm2836_arm_irqchip_cpu_notify, |
218 | .priority = 100, | 223 | .priority = 100, |
219 | }; | 224 | }; |
225 | |||
226 | int __init bcm2836_smp_boot_secondary(unsigned int cpu, | ||
227 | struct task_struct *idle) | ||
228 | { | ||
229 | unsigned long secondary_startup_phys = | ||
230 | (unsigned long)virt_to_phys((void *)secondary_startup); | ||
231 | |||
232 | dsb(); | ||
233 | writel(secondary_startup_phys, | ||
234 | intc.base + LOCAL_MAILBOX3_SET0 + 16 * cpu); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | static const struct smp_operations bcm2836_smp_ops __initconst = { | ||
240 | .smp_boot_secondary = bcm2836_smp_boot_secondary, | ||
241 | }; | ||
242 | |||
220 | #endif | 243 | #endif |
221 | 244 | ||
222 | static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = { | 245 | static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = { |
@@ -234,9 +257,31 @@ bcm2836_arm_irqchip_smp_init(void) | |||
234 | register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier); | 257 | register_cpu_notifier(&bcm2836_arm_irqchip_cpu_notifier); |
235 | 258 | ||
236 | set_smp_cross_call(bcm2836_arm_irqchip_send_ipi); | 259 | set_smp_cross_call(bcm2836_arm_irqchip_send_ipi); |
260 | smp_set_ops(&bcm2836_smp_ops); | ||
237 | #endif | 261 | #endif |
238 | } | 262 | } |
239 | 263 | ||
264 | /* | ||
265 | * The LOCAL_IRQ_CNT* timer firings are based off of the external | ||
266 | * oscillator with some scaling. The firmware sets up CNTFRQ to | ||
267 | * report 19.2Mhz, but doesn't set up the scaling registers. | ||
268 | */ | ||
269 | static void bcm2835_init_local_timer_frequency(void) | ||
270 | { | ||
271 | /* | ||
272 | * Set the timer to source from the 19.2Mhz crystal clock (bit | ||
273 | * 8 unset), and only increment by 1 instead of 2 (bit 9 | ||
274 | * unset). | ||
275 | */ | ||
276 | writel(0, intc.base + LOCAL_CONTROL); | ||
277 | |||
278 | /* | ||
279 | * Set the timer prescaler to 1:1 (timer freq = input freq * | ||
280 | * 2**31 / prescaler) | ||
281 | */ | ||
282 | writel(0x80000000, intc.base + LOCAL_PRESCALER); | ||
283 | } | ||
284 | |||
240 | static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node, | 285 | static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node, |
241 | struct device_node *parent) | 286 | struct device_node *parent) |
242 | { | 287 | { |
@@ -246,6 +291,8 @@ static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node, | |||
246 | node->full_name); | 291 | node->full_name); |
247 | } | 292 | } |
248 | 293 | ||
294 | bcm2835_init_local_timer_frequency(); | ||
295 | |||
249 | intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1, | 296 | intc.domain = irq_domain_add_linear(node, LAST_IRQ + 1, |
250 | &bcm2836_arm_irqchip_intc_ops, | 297 | &bcm2836_arm_irqchip_intc_ops, |
251 | NULL); | 298 | NULL); |
diff --git a/drivers/irqchip/irq-gic-realview.c b/drivers/irqchip/irq-gic-realview.c new file mode 100644 index 000000000000..aa46eb280a7f --- /dev/null +++ b/drivers/irqchip/irq-gic-realview.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * Special GIC quirks for the ARM RealView | ||
3 | * Copyright (C) 2015 Linus Walleij | ||
4 | */ | ||
5 | #include <linux/of.h> | ||
6 | #include <linux/regmap.h> | ||
7 | #include <linux/mfd/syscon.h> | ||
8 | #include <linux/bitops.h> | ||
9 | #include <linux/irqchip.h> | ||
10 | #include <linux/irqchip/arm-gic.h> | ||
11 | |||
12 | #define REALVIEW_SYS_LOCK_OFFSET 0x20 | ||
13 | #define REALVIEW_PB11MP_SYS_PLD_CTRL1 0x74 | ||
14 | #define VERSATILE_LOCK_VAL 0xA05F | ||
15 | #define PLD_INTMODE_MASK BIT(22)|BIT(23)|BIT(24) | ||
16 | #define PLD_INTMODE_LEGACY 0x0 | ||
17 | #define PLD_INTMODE_NEW_DCC BIT(22) | ||
18 | #define PLD_INTMODE_NEW_NO_DCC BIT(23) | ||
19 | #define PLD_INTMODE_FIQ_ENABLE BIT(24) | ||
20 | |||
21 | static int __init | ||
22 | realview_gic_of_init(struct device_node *node, struct device_node *parent) | ||
23 | { | ||
24 | static struct regmap *map; | ||
25 | |||
26 | /* The PB11MPCore GIC needs to be configured in the syscon */ | ||
27 | map = syscon_regmap_lookup_by_compatible("arm,realview-pb11mp-syscon"); | ||
28 | if (!IS_ERR(map)) { | ||
29 | /* new irq mode with no DCC */ | ||
30 | regmap_write(map, REALVIEW_SYS_LOCK_OFFSET, | ||
31 | VERSATILE_LOCK_VAL); | ||
32 | regmap_update_bits(map, REALVIEW_PB11MP_SYS_PLD_CTRL1, | ||
33 | PLD_INTMODE_NEW_NO_DCC, | ||
34 | PLD_INTMODE_MASK); | ||
35 | regmap_write(map, REALVIEW_SYS_LOCK_OFFSET, 0x0000); | ||
36 | pr_info("TC11MP GIC: set up interrupt controller to NEW mode, no DCC\n"); | ||
37 | } else { | ||
38 | pr_err("TC11MP GIC setup: could not find syscon\n"); | ||
39 | return -ENXIO; | ||
40 | } | ||
41 | return gic_of_init(node, parent); | ||
42 | } | ||
43 | IRQCHIP_DECLARE(armtc11mp_gic, "arm,tc11mp-gic", realview_gic_of_init); | ||
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 87f8d104acab..c779f83e511d 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c | |||
@@ -15,9 +15,11 @@ | |||
15 | 15 | ||
16 | #define pr_fmt(fmt) "GICv2m: " fmt | 16 | #define pr_fmt(fmt) "GICv2m: " fmt |
17 | 17 | ||
18 | #include <linux/acpi.h> | ||
18 | #include <linux/irq.h> | 19 | #include <linux/irq.h> |
19 | #include <linux/irqdomain.h> | 20 | #include <linux/irqdomain.h> |
20 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
22 | #include <linux/msi.h> | ||
21 | #include <linux/of_address.h> | 23 | #include <linux/of_address.h> |
22 | #include <linux/of_pci.h> | 24 | #include <linux/of_pci.h> |
23 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
@@ -55,7 +57,7 @@ static DEFINE_SPINLOCK(v2m_lock); | |||
55 | 57 | ||
56 | struct v2m_data { | 58 | struct v2m_data { |
57 | struct list_head entry; | 59 | struct list_head entry; |
58 | struct device_node *node; | 60 | struct fwnode_handle *fwnode; |
59 | struct resource res; /* GICv2m resource */ | 61 | struct resource res; /* GICv2m resource */ |
60 | void __iomem *base; /* GICv2m virt address */ | 62 | void __iomem *base; /* GICv2m virt address */ |
61 | u32 spi_start; /* The SPI number that MSIs start */ | 63 | u32 spi_start; /* The SPI number that MSIs start */ |
@@ -138,6 +140,11 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, | |||
138 | fwspec.param[0] = 0; | 140 | fwspec.param[0] = 0; |
139 | fwspec.param[1] = hwirq - 32; | 141 | fwspec.param[1] = hwirq - 32; |
140 | fwspec.param[2] = IRQ_TYPE_EDGE_RISING; | 142 | fwspec.param[2] = IRQ_TYPE_EDGE_RISING; |
143 | } else if (is_fwnode_irqchip(domain->parent->fwnode)) { | ||
144 | fwspec.fwnode = domain->parent->fwnode; | ||
145 | fwspec.param_count = 2; | ||
146 | fwspec.param[0] = hwirq; | ||
147 | fwspec.param[1] = IRQ_TYPE_EDGE_RISING; | ||
141 | } else { | 148 | } else { |
142 | return -EINVAL; | 149 | return -EINVAL; |
143 | } | 150 | } |
@@ -254,7 +261,9 @@ static void gicv2m_teardown(void) | |||
254 | list_del(&v2m->entry); | 261 | list_del(&v2m->entry); |
255 | kfree(v2m->bm); | 262 | kfree(v2m->bm); |
256 | iounmap(v2m->base); | 263 | iounmap(v2m->base); |
257 | of_node_put(v2m->node); | 264 | of_node_put(to_of_node(v2m->fwnode)); |
265 | if (is_fwnode_irqchip(v2m->fwnode)) | ||
266 | irq_domain_free_fwnode(v2m->fwnode); | ||
258 | kfree(v2m); | 267 | kfree(v2m); |
259 | } | 268 | } |
260 | } | 269 | } |
@@ -268,7 +277,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) | |||
268 | if (!v2m) | 277 | if (!v2m) |
269 | return 0; | 278 | return 0; |
270 | 279 | ||
271 | inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node), | 280 | inner_domain = irq_domain_create_tree(v2m->fwnode, |
272 | &gicv2m_domain_ops, v2m); | 281 | &gicv2m_domain_ops, v2m); |
273 | if (!inner_domain) { | 282 | if (!inner_domain) { |
274 | pr_err("Failed to create GICv2m domain\n"); | 283 | pr_err("Failed to create GICv2m domain\n"); |
@@ -277,10 +286,10 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) | |||
277 | 286 | ||
278 | inner_domain->bus_token = DOMAIN_BUS_NEXUS; | 287 | inner_domain->bus_token = DOMAIN_BUS_NEXUS; |
279 | inner_domain->parent = parent; | 288 | inner_domain->parent = parent; |
280 | pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node), | 289 | pci_domain = pci_msi_create_irq_domain(v2m->fwnode, |
281 | &gicv2m_msi_domain_info, | 290 | &gicv2m_msi_domain_info, |
282 | inner_domain); | 291 | inner_domain); |
283 | plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node), | 292 | plat_domain = platform_msi_create_irq_domain(v2m->fwnode, |
284 | &gicv2m_pmsi_domain_info, | 293 | &gicv2m_pmsi_domain_info, |
285 | inner_domain); | 294 | inner_domain); |
286 | if (!pci_domain || !plat_domain) { | 295 | if (!pci_domain || !plat_domain) { |
@@ -296,8 +305,9 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) | |||
296 | return 0; | 305 | return 0; |
297 | } | 306 | } |
298 | 307 | ||
299 | static int __init gicv2m_init_one(struct device_node *node, | 308 | static int __init gicv2m_init_one(struct fwnode_handle *fwnode, |
300 | struct irq_domain *parent) | 309 | u32 spi_start, u32 nr_spis, |
310 | struct resource *res) | ||
301 | { | 311 | { |
302 | int ret; | 312 | int ret; |
303 | struct v2m_data *v2m; | 313 | struct v2m_data *v2m; |
@@ -309,13 +319,9 @@ static int __init gicv2m_init_one(struct device_node *node, | |||
309 | } | 319 | } |
310 | 320 | ||
311 | INIT_LIST_HEAD(&v2m->entry); | 321 | INIT_LIST_HEAD(&v2m->entry); |
312 | v2m->node = node; | 322 | v2m->fwnode = fwnode; |
313 | 323 | ||
314 | ret = of_address_to_resource(node, 0, &v2m->res); | 324 | memcpy(&v2m->res, res, sizeof(struct resource)); |
315 | if (ret) { | ||
316 | pr_err("Failed to allocate v2m resource.\n"); | ||
317 | goto err_free_v2m; | ||
318 | } | ||
319 | 325 | ||
320 | v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); | 326 | v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); |
321 | if (!v2m->base) { | 327 | if (!v2m->base) { |
@@ -324,10 +330,9 @@ static int __init gicv2m_init_one(struct device_node *node, | |||
324 | goto err_free_v2m; | 330 | goto err_free_v2m; |
325 | } | 331 | } |
326 | 332 | ||
327 | if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) && | 333 | if (spi_start && nr_spis) { |
328 | !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) { | 334 | v2m->spi_start = spi_start; |
329 | pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n", | 335 | v2m->nr_spis = nr_spis; |
330 | v2m->spi_start, v2m->nr_spis); | ||
331 | } else { | 336 | } else { |
332 | u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); | 337 | u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); |
333 | 338 | ||
@@ -359,10 +364,9 @@ static int __init gicv2m_init_one(struct device_node *node, | |||
359 | } | 364 | } |
360 | 365 | ||
361 | list_add_tail(&v2m->entry, &v2m_nodes); | 366 | list_add_tail(&v2m->entry, &v2m_nodes); |
362 | pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name, | ||
363 | (unsigned long)v2m->res.start, (unsigned long)v2m->res.end, | ||
364 | v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); | ||
365 | 367 | ||
368 | pr_info("range%pR, SPI[%d:%d]\n", res, | ||
369 | v2m->spi_start, (v2m->spi_start + v2m->nr_spis - 1)); | ||
366 | return 0; | 370 | return 0; |
367 | 371 | ||
368 | err_iounmap: | 372 | err_iounmap: |
@@ -377,19 +381,36 @@ static struct of_device_id gicv2m_device_id[] = { | |||
377 | {}, | 381 | {}, |
378 | }; | 382 | }; |
379 | 383 | ||
380 | int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) | 384 | static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, |
385 | struct irq_domain *parent) | ||
381 | { | 386 | { |
382 | int ret = 0; | 387 | int ret = 0; |
388 | struct device_node *node = to_of_node(parent_handle); | ||
383 | struct device_node *child; | 389 | struct device_node *child; |
384 | 390 | ||
385 | for (child = of_find_matching_node(node, gicv2m_device_id); child; | 391 | for (child = of_find_matching_node(node, gicv2m_device_id); child; |
386 | child = of_find_matching_node(child, gicv2m_device_id)) { | 392 | child = of_find_matching_node(child, gicv2m_device_id)) { |
393 | u32 spi_start = 0, nr_spis = 0; | ||
394 | struct resource res; | ||
395 | |||
387 | if (!of_find_property(child, "msi-controller", NULL)) | 396 | if (!of_find_property(child, "msi-controller", NULL)) |
388 | continue; | 397 | continue; |
389 | 398 | ||
390 | ret = gicv2m_init_one(child, parent); | 399 | ret = of_address_to_resource(child, 0, &res); |
400 | if (ret) { | ||
401 | pr_err("Failed to allocate v2m resource.\n"); | ||
402 | break; | ||
403 | } | ||
404 | |||
405 | if (!of_property_read_u32(child, "arm,msi-base-spi", | ||
406 | &spi_start) && | ||
407 | !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis)) | ||
408 | pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n", | ||
409 | spi_start, nr_spis); | ||
410 | |||
411 | ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, &res); | ||
391 | if (ret) { | 412 | if (ret) { |
392 | of_node_put(node); | 413 | of_node_put(child); |
393 | break; | 414 | break; |
394 | } | 415 | } |
395 | } | 416 | } |
@@ -400,3 +421,101 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) | |||
400 | gicv2m_teardown(); | 421 | gicv2m_teardown(); |
401 | return ret; | 422 | return ret; |
402 | } | 423 | } |
424 | |||
425 | #ifdef CONFIG_ACPI | ||
426 | static int acpi_num_msi; | ||
427 | |||
428 | static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) | ||
429 | { | ||
430 | struct v2m_data *data; | ||
431 | |||
432 | if (WARN_ON(acpi_num_msi <= 0)) | ||
433 | return NULL; | ||
434 | |||
435 | /* We only return the fwnode of the first MSI frame. */ | ||
436 | data = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry); | ||
437 | if (!data) | ||
438 | return NULL; | ||
439 | |||
440 | return data->fwnode; | ||
441 | } | ||
442 | |||
443 | static int __init | ||
444 | acpi_parse_madt_msi(struct acpi_subtable_header *header, | ||
445 | const unsigned long end) | ||
446 | { | ||
447 | int ret; | ||
448 | struct resource res; | ||
449 | u32 spi_start = 0, nr_spis = 0; | ||
450 | struct acpi_madt_generic_msi_frame *m; | ||
451 | struct fwnode_handle *fwnode; | ||
452 | |||
453 | m = (struct acpi_madt_generic_msi_frame *)header; | ||
454 | if (BAD_MADT_ENTRY(m, end)) | ||
455 | return -EINVAL; | ||
456 | |||
457 | res.start = m->base_address; | ||
458 | res.end = m->base_address + SZ_4K - 1; | ||
459 | res.flags = IORESOURCE_MEM; | ||
460 | |||
461 | if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { | ||
462 | spi_start = m->spi_base; | ||
463 | nr_spis = m->spi_count; | ||
464 | |||
465 | pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n", | ||
466 | spi_start, nr_spis); | ||
467 | } | ||
468 | |||
469 | fwnode = irq_domain_alloc_fwnode((void *)m->base_address); | ||
470 | if (!fwnode) { | ||
471 | pr_err("Unable to allocate GICv2m domain token\n"); | ||
472 | return -EINVAL; | ||
473 | } | ||
474 | |||
475 | ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res); | ||
476 | if (ret) | ||
477 | irq_domain_free_fwnode(fwnode); | ||
478 | |||
479 | return ret; | ||
480 | } | ||
481 | |||
482 | static int __init gicv2m_acpi_init(struct irq_domain *parent) | ||
483 | { | ||
484 | int ret; | ||
485 | |||
486 | if (acpi_num_msi > 0) | ||
487 | return 0; | ||
488 | |||
489 | acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME, | ||
490 | acpi_parse_madt_msi, 0); | ||
491 | |||
492 | if (acpi_num_msi <= 0) | ||
493 | goto err_out; | ||
494 | |||
495 | ret = gicv2m_allocate_domains(parent); | ||
496 | if (ret) | ||
497 | goto err_out; | ||
498 | |||
499 | pci_msi_register_fwnode_provider(&gicv2m_get_fwnode); | ||
500 | |||
501 | return 0; | ||
502 | |||
503 | err_out: | ||
504 | gicv2m_teardown(); | ||
505 | return -EINVAL; | ||
506 | } | ||
507 | #else /* CONFIG_ACPI */ | ||
508 | static int __init gicv2m_acpi_init(struct irq_domain *parent) | ||
509 | { | ||
510 | return -EINVAL; | ||
511 | } | ||
512 | #endif /* CONFIG_ACPI */ | ||
513 | |||
514 | int __init gicv2m_init(struct fwnode_handle *parent_handle, | ||
515 | struct irq_domain *parent) | ||
516 | { | ||
517 | if (is_of_node(parent_handle)) | ||
518 | return gicv2m_of_init(parent_handle, parent); | ||
519 | |||
520 | return gicv2m_acpi_init(parent); | ||
521 | } | ||
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index abf2ffaed392..911758c056c1 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c | |||
@@ -69,6 +69,7 @@ union gic_base { | |||
69 | }; | 69 | }; |
70 | 70 | ||
71 | struct gic_chip_data { | 71 | struct gic_chip_data { |
72 | struct irq_chip chip; | ||
72 | union gic_base dist_base; | 73 | union gic_base dist_base; |
73 | union gic_base cpu_base; | 74 | union gic_base cpu_base; |
74 | #ifdef CONFIG_CPU_PM | 75 | #ifdef CONFIG_CPU_PM |
@@ -99,11 +100,7 @@ static u8 gic_cpu_map[NR_GIC_CPU_IF] __read_mostly; | |||
99 | 100 | ||
100 | static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE; | 101 | static struct static_key supports_deactivate = STATIC_KEY_INIT_TRUE; |
101 | 102 | ||
102 | #ifndef MAX_GIC_NR | 103 | static struct gic_chip_data gic_data[CONFIG_ARM_GIC_MAX_NR] __read_mostly; |
103 | #define MAX_GIC_NR 1 | ||
104 | #endif | ||
105 | |||
106 | static struct gic_chip_data gic_data[MAX_GIC_NR] __read_mostly; | ||
107 | 104 | ||
108 | #ifdef CONFIG_GIC_NON_BANKED | 105 | #ifdef CONFIG_GIC_NON_BANKED |
109 | static void __iomem *gic_get_percpu_base(union gic_base *base) | 106 | static void __iomem *gic_get_percpu_base(union gic_base *base) |
@@ -336,7 +333,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) | |||
336 | irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); | 333 | irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK); |
337 | irqnr = irqstat & GICC_IAR_INT_ID_MASK; | 334 | irqnr = irqstat & GICC_IAR_INT_ID_MASK; |
338 | 335 | ||
339 | if (likely(irqnr > 15 && irqnr < 1021)) { | 336 | if (likely(irqnr > 15 && irqnr < 1020)) { |
340 | if (static_key_true(&supports_deactivate)) | 337 | if (static_key_true(&supports_deactivate)) |
341 | writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); | 338 | writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); |
342 | handle_domain_irq(gic->domain, irqnr, regs); | 339 | handle_domain_irq(gic->domain, irqnr, regs); |
@@ -383,7 +380,6 @@ static void gic_handle_cascade_irq(struct irq_desc *desc) | |||
383 | } | 380 | } |
384 | 381 | ||
385 | static struct irq_chip gic_chip = { | 382 | static struct irq_chip gic_chip = { |
386 | .name = "GIC", | ||
387 | .irq_mask = gic_mask_irq, | 383 | .irq_mask = gic_mask_irq, |
388 | .irq_unmask = gic_unmask_irq, | 384 | .irq_unmask = gic_unmask_irq, |
389 | .irq_eoi = gic_eoi_irq, | 385 | .irq_eoi = gic_eoi_irq, |
@@ -417,8 +413,7 @@ static struct irq_chip gic_eoimode1_chip = { | |||
417 | 413 | ||
418 | void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) | 414 | void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) |
419 | { | 415 | { |
420 | if (gic_nr >= MAX_GIC_NR) | 416 | BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); |
421 | BUG(); | ||
422 | irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, | 417 | irq_set_chained_handler_and_data(irq, gic_handle_cascade_irq, |
423 | &gic_data[gic_nr]); | 418 | &gic_data[gic_nr]); |
424 | } | 419 | } |
@@ -524,7 +519,7 @@ int gic_cpu_if_down(unsigned int gic_nr) | |||
524 | void __iomem *cpu_base; | 519 | void __iomem *cpu_base; |
525 | u32 val = 0; | 520 | u32 val = 0; |
526 | 521 | ||
527 | if (gic_nr >= MAX_GIC_NR) | 522 | if (gic_nr >= CONFIG_ARM_GIC_MAX_NR) |
528 | return -EINVAL; | 523 | return -EINVAL; |
529 | 524 | ||
530 | cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); | 525 | cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); |
@@ -548,8 +543,7 @@ static void gic_dist_save(unsigned int gic_nr) | |||
548 | void __iomem *dist_base; | 543 | void __iomem *dist_base; |
549 | int i; | 544 | int i; |
550 | 545 | ||
551 | if (gic_nr >= MAX_GIC_NR) | 546 | BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); |
552 | BUG(); | ||
553 | 547 | ||
554 | gic_irqs = gic_data[gic_nr].gic_irqs; | 548 | gic_irqs = gic_data[gic_nr].gic_irqs; |
555 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); | 549 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); |
@@ -587,8 +581,7 @@ static void gic_dist_restore(unsigned int gic_nr) | |||
587 | unsigned int i; | 581 | unsigned int i; |
588 | void __iomem *dist_base; | 582 | void __iomem *dist_base; |
589 | 583 | ||
590 | if (gic_nr >= MAX_GIC_NR) | 584 | BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); |
591 | BUG(); | ||
592 | 585 | ||
593 | gic_irqs = gic_data[gic_nr].gic_irqs; | 586 | gic_irqs = gic_data[gic_nr].gic_irqs; |
594 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); | 587 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); |
@@ -634,8 +627,7 @@ static void gic_cpu_save(unsigned int gic_nr) | |||
634 | void __iomem *dist_base; | 627 | void __iomem *dist_base; |
635 | void __iomem *cpu_base; | 628 | void __iomem *cpu_base; |
636 | 629 | ||
637 | if (gic_nr >= MAX_GIC_NR) | 630 | BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); |
638 | BUG(); | ||
639 | 631 | ||
640 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); | 632 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); |
641 | cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); | 633 | cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); |
@@ -664,8 +656,7 @@ static void gic_cpu_restore(unsigned int gic_nr) | |||
664 | void __iomem *dist_base; | 656 | void __iomem *dist_base; |
665 | void __iomem *cpu_base; | 657 | void __iomem *cpu_base; |
666 | 658 | ||
667 | if (gic_nr >= MAX_GIC_NR) | 659 | BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); |
668 | BUG(); | ||
669 | 660 | ||
670 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); | 661 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); |
671 | cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); | 662 | cpu_base = gic_data_cpu_base(&gic_data[gic_nr]); |
@@ -703,7 +694,7 @@ static int gic_notifier(struct notifier_block *self, unsigned long cmd, void *v) | |||
703 | { | 694 | { |
704 | int i; | 695 | int i; |
705 | 696 | ||
706 | for (i = 0; i < MAX_GIC_NR; i++) { | 697 | for (i = 0; i < CONFIG_ARM_GIC_MAX_NR; i++) { |
707 | #ifdef CONFIG_GIC_NON_BANKED | 698 | #ifdef CONFIG_GIC_NON_BANKED |
708 | /* Skip over unused GICs */ | 699 | /* Skip over unused GICs */ |
709 | if (!gic_data[i].get_base) | 700 | if (!gic_data[i].get_base) |
@@ -835,8 +826,7 @@ void gic_migrate_target(unsigned int new_cpu_id) | |||
835 | int i, ror_val, cpu = smp_processor_id(); | 826 | int i, ror_val, cpu = smp_processor_id(); |
836 | u32 val, cur_target_mask, active_mask; | 827 | u32 val, cur_target_mask, active_mask; |
837 | 828 | ||
838 | if (gic_nr >= MAX_GIC_NR) | 829 | BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); |
839 | BUG(); | ||
840 | 830 | ||
841 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); | 831 | dist_base = gic_data_dist_base(&gic_data[gic_nr]); |
842 | if (!dist_base) | 832 | if (!dist_base) |
@@ -925,20 +915,15 @@ void __init gic_init_physaddr(struct device_node *node) | |||
925 | static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, | 915 | static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, |
926 | irq_hw_number_t hw) | 916 | irq_hw_number_t hw) |
927 | { | 917 | { |
928 | struct irq_chip *chip = &gic_chip; | 918 | struct gic_chip_data *gic = d->host_data; |
929 | |||
930 | if (static_key_true(&supports_deactivate)) { | ||
931 | if (d->host_data == (void *)&gic_data[0]) | ||
932 | chip = &gic_eoimode1_chip; | ||
933 | } | ||
934 | 919 | ||
935 | if (hw < 32) { | 920 | if (hw < 32) { |
936 | irq_set_percpu_devid(irq); | 921 | irq_set_percpu_devid(irq); |
937 | irq_domain_set_info(d, irq, hw, chip, d->host_data, | 922 | irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data, |
938 | handle_percpu_devid_irq, NULL, NULL); | 923 | handle_percpu_devid_irq, NULL, NULL); |
939 | irq_set_status_flags(irq, IRQ_NOAUTOEN); | 924 | irq_set_status_flags(irq, IRQ_NOAUTOEN); |
940 | } else { | 925 | } else { |
941 | irq_domain_set_info(d, irq, hw, chip, d->host_data, | 926 | irq_domain_set_info(d, irq, hw, &gic->chip, d->host_data, |
942 | handle_fasteoi_irq, NULL, NULL); | 927 | handle_fasteoi_irq, NULL, NULL); |
943 | irq_set_probe(irq); | 928 | irq_set_probe(irq); |
944 | } | 929 | } |
@@ -972,7 +957,7 @@ static int gic_irq_domain_translate(struct irq_domain *d, | |||
972 | return 0; | 957 | return 0; |
973 | } | 958 | } |
974 | 959 | ||
975 | if (fwspec->fwnode->type == FWNODE_IRQCHIP) { | 960 | if (is_fwnode_irqchip(fwspec->fwnode)) { |
976 | if(fwspec->param_count != 2) | 961 | if(fwspec->param_count != 2) |
977 | return -EINVAL; | 962 | return -EINVAL; |
978 | 963 | ||
@@ -1040,11 +1025,20 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, | |||
1040 | struct gic_chip_data *gic; | 1025 | struct gic_chip_data *gic; |
1041 | int gic_irqs, irq_base, i; | 1026 | int gic_irqs, irq_base, i; |
1042 | 1027 | ||
1043 | BUG_ON(gic_nr >= MAX_GIC_NR); | 1028 | BUG_ON(gic_nr >= CONFIG_ARM_GIC_MAX_NR); |
1044 | 1029 | ||
1045 | gic_check_cpu_features(); | 1030 | gic_check_cpu_features(); |
1046 | 1031 | ||
1047 | gic = &gic_data[gic_nr]; | 1032 | gic = &gic_data[gic_nr]; |
1033 | |||
1034 | /* Initialize irq_chip */ | ||
1035 | if (static_key_true(&supports_deactivate) && gic_nr == 0) { | ||
1036 | gic->chip = gic_eoimode1_chip; | ||
1037 | } else { | ||
1038 | gic->chip = gic_chip; | ||
1039 | gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); | ||
1040 | } | ||
1041 | |||
1048 | #ifdef CONFIG_GIC_NON_BANKED | 1042 | #ifdef CONFIG_GIC_NON_BANKED |
1049 | if (percpu_offset) { /* Frankein-GIC without banked registers... */ | 1043 | if (percpu_offset) { /* Frankein-GIC without banked registers... */ |
1050 | unsigned int cpu; | 1044 | unsigned int cpu; |
@@ -1196,7 +1190,7 @@ static bool gic_check_eoimode(struct device_node *node, void __iomem **base) | |||
1196 | return true; | 1190 | return true; |
1197 | } | 1191 | } |
1198 | 1192 | ||
1199 | static int __init | 1193 | int __init |
1200 | gic_of_init(struct device_node *node, struct device_node *parent) | 1194 | gic_of_init(struct device_node *node, struct device_node *parent) |
1201 | { | 1195 | { |
1202 | void __iomem *cpu_base; | 1196 | void __iomem *cpu_base; |
@@ -1234,7 +1228,7 @@ gic_of_init(struct device_node *node, struct device_node *parent) | |||
1234 | } | 1228 | } |
1235 | 1229 | ||
1236 | if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) | 1230 | if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) |
1237 | gicv2m_of_init(node, gic_data[gic_cnt].domain); | 1231 | gicv2m_init(&node->fwnode, gic_data[gic_cnt].domain); |
1238 | 1232 | ||
1239 | gic_cnt++; | 1233 | gic_cnt++; |
1240 | return 0; | 1234 | return 0; |
@@ -1359,6 +1353,10 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, | |||
1359 | __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); | 1353 | __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); |
1360 | 1354 | ||
1361 | acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle); | 1355 | acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle); |
1356 | |||
1357 | if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) | ||
1358 | gicv2m_init(NULL, gic_data[0].domain); | ||
1359 | |||
1362 | return 0; | 1360 | return 0; |
1363 | } | 1361 | } |
1364 | IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, | 1362 | IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, |
diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c new file mode 100644 index 000000000000..4dd3eb8a40b3 --- /dev/null +++ b/drivers/irqchip/irq-mbigen.c | |||
@@ -0,0 +1,297 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Hisilicon Limited, All Rights Reserved. | ||
3 | * Author: Jun Ma <majun258@huawei.com> | ||
4 | * Author: Yun Wu <wuyun.wu@huawei.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/irqchip.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/msi.h> | ||
23 | #include <linux/of_address.h> | ||
24 | #include <linux/of_irq.h> | ||
25 | #include <linux/of_platform.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/slab.h> | ||
28 | |||
29 | /* Interrupt numbers per mbigen node supported */ | ||
30 | #define IRQS_PER_MBIGEN_NODE 128 | ||
31 | |||
32 | /* 64 irqs (Pin0-pin63) are reserved for each mbigen chip */ | ||
33 | #define RESERVED_IRQ_PER_MBIGEN_CHIP 64 | ||
34 | |||
35 | /* The maximum IRQ pin number of mbigen chip(start from 0) */ | ||
36 | #define MAXIMUM_IRQ_PIN_NUM 1407 | ||
37 | |||
38 | /** | ||
39 | * In mbigen vector register | ||
40 | * bit[21:12]: event id value | ||
41 | * bit[11:0]: device id | ||
42 | */ | ||
43 | #define IRQ_EVENT_ID_SHIFT 12 | ||
44 | #define IRQ_EVENT_ID_MASK 0x3ff | ||
45 | |||
46 | /* register range of each mbigen node */ | ||
47 | #define MBIGEN_NODE_OFFSET 0x1000 | ||
48 | |||
49 | /* offset of vector register in mbigen node */ | ||
50 | #define REG_MBIGEN_VEC_OFFSET 0x200 | ||
51 | |||
52 | /** | ||
53 | * offset of clear register in mbigen node | ||
54 | * This register is used to clear the status | ||
55 | * of interrupt | ||
56 | */ | ||
57 | #define REG_MBIGEN_CLEAR_OFFSET 0xa000 | ||
58 | |||
59 | /** | ||
60 | * offset of interrupt type register | ||
61 | * This register is used to configure interrupt | ||
62 | * trigger type | ||
63 | */ | ||
64 | #define REG_MBIGEN_TYPE_OFFSET 0x0 | ||
65 | |||
66 | /** | ||
67 | * struct mbigen_device - holds the information of mbigen device. | ||
68 | * | ||
69 | * @pdev: pointer to the platform device structure of mbigen chip. | ||
70 | * @base: mapped address of this mbigen chip. | ||
71 | */ | ||
72 | struct mbigen_device { | ||
73 | struct platform_device *pdev; | ||
74 | void __iomem *base; | ||
75 | }; | ||
76 | |||
77 | static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq) | ||
78 | { | ||
79 | unsigned int nid, pin; | ||
80 | |||
81 | hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP; | ||
82 | nid = hwirq / IRQS_PER_MBIGEN_NODE + 1; | ||
83 | pin = hwirq % IRQS_PER_MBIGEN_NODE; | ||
84 | |||
85 | return pin * 4 + nid * MBIGEN_NODE_OFFSET | ||
86 | + REG_MBIGEN_VEC_OFFSET; | ||
87 | } | ||
88 | |||
89 | static inline void get_mbigen_type_reg(irq_hw_number_t hwirq, | ||
90 | u32 *mask, u32 *addr) | ||
91 | { | ||
92 | unsigned int nid, irq_ofst, ofst; | ||
93 | |||
94 | hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP; | ||
95 | nid = hwirq / IRQS_PER_MBIGEN_NODE + 1; | ||
96 | irq_ofst = hwirq % IRQS_PER_MBIGEN_NODE; | ||
97 | |||
98 | *mask = 1 << (irq_ofst % 32); | ||
99 | ofst = irq_ofst / 32 * 4; | ||
100 | |||
101 | *addr = ofst + nid * MBIGEN_NODE_OFFSET | ||
102 | + REG_MBIGEN_TYPE_OFFSET; | ||
103 | } | ||
104 | |||
105 | static inline void get_mbigen_clear_reg(irq_hw_number_t hwirq, | ||
106 | u32 *mask, u32 *addr) | ||
107 | { | ||
108 | unsigned int ofst; | ||
109 | |||
110 | hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP; | ||
111 | ofst = hwirq / 32 * 4; | ||
112 | |||
113 | *mask = 1 << (hwirq % 32); | ||
114 | *addr = ofst + REG_MBIGEN_CLEAR_OFFSET; | ||
115 | } | ||
116 | |||
117 | static void mbigen_eoi_irq(struct irq_data *data) | ||
118 | { | ||
119 | void __iomem *base = data->chip_data; | ||
120 | u32 mask, addr; | ||
121 | |||
122 | get_mbigen_clear_reg(data->hwirq, &mask, &addr); | ||
123 | |||
124 | writel_relaxed(mask, base + addr); | ||
125 | |||
126 | irq_chip_eoi_parent(data); | ||
127 | } | ||
128 | |||
129 | static int mbigen_set_type(struct irq_data *data, unsigned int type) | ||
130 | { | ||
131 | void __iomem *base = data->chip_data; | ||
132 | u32 mask, addr, val; | ||
133 | |||
134 | if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING) | ||
135 | return -EINVAL; | ||
136 | |||
137 | get_mbigen_type_reg(data->hwirq, &mask, &addr); | ||
138 | |||
139 | val = readl_relaxed(base + addr); | ||
140 | |||
141 | if (type == IRQ_TYPE_LEVEL_HIGH) | ||
142 | val |= mask; | ||
143 | else | ||
144 | val &= ~mask; | ||
145 | |||
146 | writel_relaxed(val, base + addr); | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static struct irq_chip mbigen_irq_chip = { | ||
152 | .name = "mbigen-v2", | ||
153 | .irq_mask = irq_chip_mask_parent, | ||
154 | .irq_unmask = irq_chip_unmask_parent, | ||
155 | .irq_eoi = mbigen_eoi_irq, | ||
156 | .irq_set_type = mbigen_set_type, | ||
157 | .irq_set_affinity = irq_chip_set_affinity_parent, | ||
158 | }; | ||
159 | |||
160 | static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg) | ||
161 | { | ||
162 | struct irq_data *d = irq_get_irq_data(desc->irq); | ||
163 | void __iomem *base = d->chip_data; | ||
164 | u32 val; | ||
165 | |||
166 | base += get_mbigen_vec_reg(d->hwirq); | ||
167 | val = readl_relaxed(base); | ||
168 | |||
169 | val &= ~(IRQ_EVENT_ID_MASK << IRQ_EVENT_ID_SHIFT); | ||
170 | val |= (msg->data << IRQ_EVENT_ID_SHIFT); | ||
171 | |||
172 | /* The address of doorbell is encoded in mbigen register by default | ||
173 | * So,we don't need to program the doorbell address at here | ||
174 | */ | ||
175 | writel_relaxed(val, base); | ||
176 | } | ||
177 | |||
178 | static int mbigen_domain_translate(struct irq_domain *d, | ||
179 | struct irq_fwspec *fwspec, | ||
180 | unsigned long *hwirq, | ||
181 | unsigned int *type) | ||
182 | { | ||
183 | if (is_of_node(fwspec->fwnode)) { | ||
184 | if (fwspec->param_count != 2) | ||
185 | return -EINVAL; | ||
186 | |||
187 | if ((fwspec->param[0] > MAXIMUM_IRQ_PIN_NUM) || | ||
188 | (fwspec->param[0] < RESERVED_IRQ_PER_MBIGEN_CHIP)) | ||
189 | return -EINVAL; | ||
190 | else | ||
191 | *hwirq = fwspec->param[0]; | ||
192 | |||
193 | /* If there is no valid irq type, just use the default type */ | ||
194 | if ((fwspec->param[1] == IRQ_TYPE_EDGE_RISING) || | ||
195 | (fwspec->param[1] == IRQ_TYPE_LEVEL_HIGH)) | ||
196 | *type = fwspec->param[1]; | ||
197 | else | ||
198 | return -EINVAL; | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | return -EINVAL; | ||
203 | } | ||
204 | |||
205 | static int mbigen_irq_domain_alloc(struct irq_domain *domain, | ||
206 | unsigned int virq, | ||
207 | unsigned int nr_irqs, | ||
208 | void *args) | ||
209 | { | ||
210 | struct irq_fwspec *fwspec = args; | ||
211 | irq_hw_number_t hwirq; | ||
212 | unsigned int type; | ||
213 | struct mbigen_device *mgn_chip; | ||
214 | int i, err; | ||
215 | |||
216 | err = mbigen_domain_translate(domain, fwspec, &hwirq, &type); | ||
217 | if (err) | ||
218 | return err; | ||
219 | |||
220 | err = platform_msi_domain_alloc(domain, virq, nr_irqs); | ||
221 | if (err) | ||
222 | return err; | ||
223 | |||
224 | mgn_chip = platform_msi_get_host_data(domain); | ||
225 | |||
226 | for (i = 0; i < nr_irqs; i++) | ||
227 | irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, | ||
228 | &mbigen_irq_chip, mgn_chip->base); | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static struct irq_domain_ops mbigen_domain_ops = { | ||
234 | .translate = mbigen_domain_translate, | ||
235 | .alloc = mbigen_irq_domain_alloc, | ||
236 | .free = irq_domain_free_irqs_common, | ||
237 | }; | ||
238 | |||
239 | static int mbigen_device_probe(struct platform_device *pdev) | ||
240 | { | ||
241 | struct mbigen_device *mgn_chip; | ||
242 | struct resource *res; | ||
243 | struct irq_domain *domain; | ||
244 | u32 num_pins; | ||
245 | |||
246 | mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL); | ||
247 | if (!mgn_chip) | ||
248 | return -ENOMEM; | ||
249 | |||
250 | mgn_chip->pdev = pdev; | ||
251 | |||
252 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
253 | mgn_chip->base = devm_ioremap_resource(&pdev->dev, res); | ||
254 | if (IS_ERR(mgn_chip->base)) | ||
255 | return PTR_ERR(mgn_chip->base); | ||
256 | |||
257 | if (of_property_read_u32(pdev->dev.of_node, "num-pins", &num_pins) < 0) { | ||
258 | dev_err(&pdev->dev, "No num-pins property\n"); | ||
259 | return -EINVAL; | ||
260 | } | ||
261 | |||
262 | domain = platform_msi_create_device_domain(&pdev->dev, num_pins, | ||
263 | mbigen_write_msg, | ||
264 | &mbigen_domain_ops, | ||
265 | mgn_chip); | ||
266 | |||
267 | if (!domain) | ||
268 | return -ENOMEM; | ||
269 | |||
270 | platform_set_drvdata(pdev, mgn_chip); | ||
271 | |||
272 | dev_info(&pdev->dev, "Allocated %d MSIs\n", num_pins); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static const struct of_device_id mbigen_of_match[] = { | ||
278 | { .compatible = "hisilicon,mbigen-v2" }, | ||
279 | { /* END */ } | ||
280 | }; | ||
281 | MODULE_DEVICE_TABLE(of, mbigen_of_match); | ||
282 | |||
283 | static struct platform_driver mbigen_platform_driver = { | ||
284 | .driver = { | ||
285 | .name = "Hisilicon MBIGEN-V2", | ||
286 | .owner = THIS_MODULE, | ||
287 | .of_match_table = mbigen_of_match, | ||
288 | }, | ||
289 | .probe = mbigen_device_probe, | ||
290 | }; | ||
291 | |||
292 | module_platform_driver(mbigen_platform_driver); | ||
293 | |||
294 | MODULE_AUTHOR("Jun Ma <majun258@huawei.com>"); | ||
295 | MODULE_AUTHOR("Yun Wu <wuyun.wu@huawei.com>"); | ||
296 | MODULE_LICENSE("GPL"); | ||
297 | MODULE_DESCRIPTION("Hisilicon MBI Generator driver"); | ||
diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index 8587d0f8d8c0..9d1bcfc33e4c 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c | |||
@@ -47,6 +47,7 @@ | |||
47 | #define INTC_ILR0 0x0100 | 47 | #define INTC_ILR0 0x0100 |
48 | 48 | ||
49 | #define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ | 49 | #define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ |
50 | #define SPURIOUSIRQ_MASK (0x1ffffff << 7) | ||
50 | #define INTCPS_NR_ILR_REGS 128 | 51 | #define INTCPS_NR_ILR_REGS 128 |
51 | #define INTCPS_NR_MIR_REGS 4 | 52 | #define INTCPS_NR_MIR_REGS 4 |
52 | 53 | ||
@@ -207,7 +208,6 @@ static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base) | |||
207 | ct = gc->chip_types; | 208 | ct = gc->chip_types; |
208 | 209 | ||
209 | ct->type = IRQ_TYPE_LEVEL_MASK; | 210 | ct->type = IRQ_TYPE_LEVEL_MASK; |
210 | ct->handler = handle_level_irq; | ||
211 | 211 | ||
212 | ct->chip.irq_ack = omap_mask_ack_irq; | 212 | ct->chip.irq_ack = omap_mask_ack_irq; |
213 | ct->chip.irq_mask = irq_gc_mask_disable_reg; | 213 | ct->chip.irq_mask = irq_gc_mask_disable_reg; |
@@ -330,11 +330,35 @@ static int __init omap_init_irq(u32 base, struct device_node *node) | |||
330 | static asmlinkage void __exception_irq_entry | 330 | static asmlinkage void __exception_irq_entry |
331 | omap_intc_handle_irq(struct pt_regs *regs) | 331 | omap_intc_handle_irq(struct pt_regs *regs) |
332 | { | 332 | { |
333 | extern unsigned long irq_err_count; | ||
333 | u32 irqnr; | 334 | u32 irqnr; |
334 | 335 | ||
335 | irqnr = intc_readl(INTC_SIR); | 336 | irqnr = intc_readl(INTC_SIR); |
337 | |||
338 | /* | ||
339 | * A spurious IRQ can result if interrupt that triggered the | ||
340 | * sorting is no longer active during the sorting (10 INTC | ||
341 | * functional clock cycles after interrupt assertion). Or a | ||
342 | * change in interrupt mask affected the result during sorting | ||
343 | * time. There is no special handling required except ignoring | ||
344 | * the SIR register value just read and retrying. | ||
345 | * See section 6.2.5 of AM335x TRM Literature Number: SPRUH73K | ||
346 | * | ||
347 | * Many a times, a spurious interrupt situation has been fixed | ||
348 | * by adding a flush for the posted write acking the IRQ in | ||
349 | * the device driver. Typically, this is going be the device | ||
350 | * driver whose interrupt was handled just before the spurious | ||
351 | * IRQ occurred. Pay attention to those device drivers if you | ||
352 | * run into hitting the spurious IRQ condition below. | ||
353 | */ | ||
354 | if (unlikely((irqnr & SPURIOUSIRQ_MASK) == SPURIOUSIRQ_MASK)) { | ||
355 | pr_err_once("%s: spurious irq!\n", __func__); | ||
356 | irq_err_count++; | ||
357 | omap_ack_irq(NULL); | ||
358 | return; | ||
359 | } | ||
360 | |||
336 | irqnr &= ACTIVEIRQ_MASK; | 361 | irqnr &= ACTIVEIRQ_MASK; |
337 | WARN_ONCE(!irqnr, "Spurious IRQ ?\n"); | ||
338 | handle_domain_irq(domain, irqnr, regs); | 362 | handle_domain_irq(domain, irqnr, regs); |
339 | } | 363 | } |
340 | 364 | ||
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index c325806561be..713177d97c7a 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c | |||
@@ -31,7 +31,6 @@ | |||
31 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | #include <linux/module.h> | 32 | #include <linux/module.h> |
33 | #include <linux/of_device.h> | 33 | #include <linux/of_device.h> |
34 | #include <linux/platform_data/irq-renesas-intc-irqpin.h> | ||
35 | #include <linux/pm_runtime.h> | 34 | #include <linux/pm_runtime.h> |
36 | 35 | ||
37 | #define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ | 36 | #define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ |
@@ -75,18 +74,20 @@ struct intc_irqpin_irq { | |||
75 | struct intc_irqpin_priv { | 74 | struct intc_irqpin_priv { |
76 | struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; | 75 | struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; |
77 | struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; | 76 | struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; |
78 | struct renesas_intc_irqpin_config config; | 77 | unsigned int sense_bitfield_width; |
79 | unsigned int number_of_irqs; | ||
80 | struct platform_device *pdev; | 78 | struct platform_device *pdev; |
81 | struct irq_chip irq_chip; | 79 | struct irq_chip irq_chip; |
82 | struct irq_domain *irq_domain; | 80 | struct irq_domain *irq_domain; |
83 | struct clk *clk; | 81 | struct clk *clk; |
84 | bool shared_irqs; | 82 | unsigned shared_irqs:1; |
83 | unsigned needs_clk:1; | ||
85 | u8 shared_irq_mask; | 84 | u8 shared_irq_mask; |
86 | }; | 85 | }; |
87 | 86 | ||
88 | struct intc_irqpin_irlm_config { | 87 | struct intc_irqpin_config { |
89 | unsigned int irlm_bit; | 88 | unsigned int irlm_bit; |
89 | unsigned needs_irlm:1; | ||
90 | unsigned needs_clk:1; | ||
90 | }; | 91 | }; |
91 | 92 | ||
92 | static unsigned long intc_irqpin_read32(void __iomem *iomem) | 93 | static unsigned long intc_irqpin_read32(void __iomem *iomem) |
@@ -171,7 +172,7 @@ static void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, | |||
171 | static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) | 172 | static int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) |
172 | { | 173 | { |
173 | /* The SENSE register is assumed to be 32-bit. */ | 174 | /* The SENSE register is assumed to be 32-bit. */ |
174 | int bitfield_width = p->config.sense_bitfield_width; | 175 | int bitfield_width = p->sense_bitfield_width; |
175 | int shift = 32 - (irq + 1) * bitfield_width; | 176 | int shift = 32 - (irq + 1) * bitfield_width; |
176 | 177 | ||
177 | dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); | 178 | dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); |
@@ -361,8 +362,15 @@ static const struct irq_domain_ops intc_irqpin_irq_domain_ops = { | |||
361 | .xlate = irq_domain_xlate_twocell, | 362 | .xlate = irq_domain_xlate_twocell, |
362 | }; | 363 | }; |
363 | 364 | ||
364 | static const struct intc_irqpin_irlm_config intc_irqpin_irlm_r8a777x = { | 365 | static const struct intc_irqpin_config intc_irqpin_irlm_r8a777x = { |
365 | .irlm_bit = 23, /* ICR0.IRLM0 */ | 366 | .irlm_bit = 23, /* ICR0.IRLM0 */ |
367 | .needs_irlm = 1, | ||
368 | .needs_clk = 0, | ||
369 | }; | ||
370 | |||
371 | static const struct intc_irqpin_config intc_irqpin_rmobile = { | ||
372 | .needs_irlm = 0, | ||
373 | .needs_clk = 1, | ||
366 | }; | 374 | }; |
367 | 375 | ||
368 | static const struct of_device_id intc_irqpin_dt_ids[] = { | 376 | static const struct of_device_id intc_irqpin_dt_ids[] = { |
@@ -371,14 +379,18 @@ static const struct of_device_id intc_irqpin_dt_ids[] = { | |||
371 | .data = &intc_irqpin_irlm_r8a777x }, | 379 | .data = &intc_irqpin_irlm_r8a777x }, |
372 | { .compatible = "renesas,intc-irqpin-r8a7779", | 380 | { .compatible = "renesas,intc-irqpin-r8a7779", |
373 | .data = &intc_irqpin_irlm_r8a777x }, | 381 | .data = &intc_irqpin_irlm_r8a777x }, |
382 | { .compatible = "renesas,intc-irqpin-r8a7740", | ||
383 | .data = &intc_irqpin_rmobile }, | ||
384 | { .compatible = "renesas,intc-irqpin-sh73a0", | ||
385 | .data = &intc_irqpin_rmobile }, | ||
374 | {}, | 386 | {}, |
375 | }; | 387 | }; |
376 | MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); | 388 | MODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); |
377 | 389 | ||
378 | static int intc_irqpin_probe(struct platform_device *pdev) | 390 | static int intc_irqpin_probe(struct platform_device *pdev) |
379 | { | 391 | { |
392 | const struct intc_irqpin_config *config = NULL; | ||
380 | struct device *dev = &pdev->dev; | 393 | struct device *dev = &pdev->dev; |
381 | struct renesas_intc_irqpin_config *pdata = dev->platform_data; | ||
382 | const struct of_device_id *of_id; | 394 | const struct of_device_id *of_id; |
383 | struct intc_irqpin_priv *p; | 395 | struct intc_irqpin_priv *p; |
384 | struct intc_irqpin_iomem *i; | 396 | struct intc_irqpin_iomem *i; |
@@ -388,6 +400,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
388 | void (*enable_fn)(struct irq_data *d); | 400 | void (*enable_fn)(struct irq_data *d); |
389 | void (*disable_fn)(struct irq_data *d); | 401 | void (*disable_fn)(struct irq_data *d); |
390 | const char *name = dev_name(dev); | 402 | const char *name = dev_name(dev); |
403 | bool control_parent; | ||
404 | unsigned int nirqs; | ||
391 | int ref_irq; | 405 | int ref_irq; |
392 | int ret; | 406 | int ret; |
393 | int k; | 407 | int k; |
@@ -399,23 +413,28 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
399 | } | 413 | } |
400 | 414 | ||
401 | /* deal with driver instance configuration */ | 415 | /* deal with driver instance configuration */ |
402 | if (pdata) { | 416 | of_property_read_u32(dev->of_node, "sense-bitfield-width", |
403 | memcpy(&p->config, pdata, sizeof(*pdata)); | 417 | &p->sense_bitfield_width); |
404 | } else { | 418 | control_parent = of_property_read_bool(dev->of_node, "control-parent"); |
405 | of_property_read_u32(dev->of_node, "sense-bitfield-width", | 419 | if (!p->sense_bitfield_width) |
406 | &p->config.sense_bitfield_width); | 420 | p->sense_bitfield_width = 4; /* default to 4 bits */ |
407 | p->config.control_parent = of_property_read_bool(dev->of_node, | ||
408 | "control-parent"); | ||
409 | } | ||
410 | if (!p->config.sense_bitfield_width) | ||
411 | p->config.sense_bitfield_width = 4; /* default to 4 bits */ | ||
412 | 421 | ||
413 | p->pdev = pdev; | 422 | p->pdev = pdev; |
414 | platform_set_drvdata(pdev, p); | 423 | platform_set_drvdata(pdev, p); |
415 | 424 | ||
425 | of_id = of_match_device(intc_irqpin_dt_ids, dev); | ||
426 | if (of_id && of_id->data) { | ||
427 | config = of_id->data; | ||
428 | p->needs_clk = config->needs_clk; | ||
429 | } | ||
430 | |||
416 | p->clk = devm_clk_get(dev, NULL); | 431 | p->clk = devm_clk_get(dev, NULL); |
417 | if (IS_ERR(p->clk)) { | 432 | if (IS_ERR(p->clk)) { |
418 | dev_warn(dev, "unable to get clock\n"); | 433 | if (p->needs_clk) { |
434 | dev_err(dev, "unable to get clock\n"); | ||
435 | ret = PTR_ERR(p->clk); | ||
436 | goto err0; | ||
437 | } | ||
419 | p->clk = NULL; | 438 | p->clk = NULL; |
420 | } | 439 | } |
421 | 440 | ||
@@ -443,8 +462,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
443 | p->irq[k].requested_irq = irq->start; | 462 | p->irq[k].requested_irq = irq->start; |
444 | } | 463 | } |
445 | 464 | ||
446 | p->number_of_irqs = k; | 465 | nirqs = k; |
447 | if (p->number_of_irqs < 1) { | 466 | if (nirqs < 1) { |
448 | dev_err(dev, "not enough IRQ resources\n"); | 467 | dev_err(dev, "not enough IRQ resources\n"); |
449 | ret = -EINVAL; | 468 | ret = -EINVAL; |
450 | goto err0; | 469 | goto err0; |
@@ -485,20 +504,16 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
485 | } | 504 | } |
486 | 505 | ||
487 | /* configure "individual IRQ mode" where needed */ | 506 | /* configure "individual IRQ mode" where needed */ |
488 | of_id = of_match_device(intc_irqpin_dt_ids, dev); | 507 | if (config && config->needs_irlm) { |
489 | if (of_id && of_id->data) { | ||
490 | const struct intc_irqpin_irlm_config *irlm_config = of_id->data; | ||
491 | |||
492 | if (io[INTC_IRQPIN_REG_IRLM]) | 508 | if (io[INTC_IRQPIN_REG_IRLM]) |
493 | intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_IRLM, | 509 | intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_IRLM, |
494 | irlm_config->irlm_bit, | 510 | config->irlm_bit, 1, 1); |
495 | 1, 1); | ||
496 | else | 511 | else |
497 | dev_warn(dev, "unable to select IRLM mode\n"); | 512 | dev_warn(dev, "unable to select IRLM mode\n"); |
498 | } | 513 | } |
499 | 514 | ||
500 | /* mask all interrupts using priority */ | 515 | /* mask all interrupts using priority */ |
501 | for (k = 0; k < p->number_of_irqs; k++) | 516 | for (k = 0; k < nirqs; k++) |
502 | intc_irqpin_mask_unmask_prio(p, k, 1); | 517 | intc_irqpin_mask_unmask_prio(p, k, 1); |
503 | 518 | ||
504 | /* clear all pending interrupts */ | 519 | /* clear all pending interrupts */ |
@@ -506,16 +521,16 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
506 | 521 | ||
507 | /* scan for shared interrupt lines */ | 522 | /* scan for shared interrupt lines */ |
508 | ref_irq = p->irq[0].requested_irq; | 523 | ref_irq = p->irq[0].requested_irq; |
509 | p->shared_irqs = true; | 524 | p->shared_irqs = 1; |
510 | for (k = 1; k < p->number_of_irqs; k++) { | 525 | for (k = 1; k < nirqs; k++) { |
511 | if (ref_irq != p->irq[k].requested_irq) { | 526 | if (ref_irq != p->irq[k].requested_irq) { |
512 | p->shared_irqs = false; | 527 | p->shared_irqs = 0; |
513 | break; | 528 | break; |
514 | } | 529 | } |
515 | } | 530 | } |
516 | 531 | ||
517 | /* use more severe masking method if requested */ | 532 | /* use more severe masking method if requested */ |
518 | if (p->config.control_parent) { | 533 | if (control_parent) { |
519 | enable_fn = intc_irqpin_irq_enable_force; | 534 | enable_fn = intc_irqpin_irq_enable_force; |
520 | disable_fn = intc_irqpin_irq_disable_force; | 535 | disable_fn = intc_irqpin_irq_disable_force; |
521 | } else if (!p->shared_irqs) { | 536 | } else if (!p->shared_irqs) { |
@@ -534,9 +549,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
534 | irq_chip->irq_set_wake = intc_irqpin_irq_set_wake; | 549 | irq_chip->irq_set_wake = intc_irqpin_irq_set_wake; |
535 | irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; | 550 | irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; |
536 | 551 | ||
537 | p->irq_domain = irq_domain_add_simple(dev->of_node, | 552 | p->irq_domain = irq_domain_add_simple(dev->of_node, nirqs, 0, |
538 | p->number_of_irqs, | ||
539 | p->config.irq_base, | ||
540 | &intc_irqpin_irq_domain_ops, p); | 553 | &intc_irqpin_irq_domain_ops, p); |
541 | if (!p->irq_domain) { | 554 | if (!p->irq_domain) { |
542 | ret = -ENXIO; | 555 | ret = -ENXIO; |
@@ -555,7 +568,7 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
555 | } | 568 | } |
556 | } else { | 569 | } else { |
557 | /* request interrupts one by one */ | 570 | /* request interrupts one by one */ |
558 | for (k = 0; k < p->number_of_irqs; k++) { | 571 | for (k = 0; k < nirqs; k++) { |
559 | if (devm_request_irq(dev, p->irq[k].requested_irq, | 572 | if (devm_request_irq(dev, p->irq[k].requested_irq, |
560 | intc_irqpin_irq_handler, 0, name, | 573 | intc_irqpin_irq_handler, 0, name, |
561 | &p->irq[k])) { | 574 | &p->irq[k])) { |
@@ -567,17 +580,10 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
567 | } | 580 | } |
568 | 581 | ||
569 | /* unmask all interrupts on prio level */ | 582 | /* unmask all interrupts on prio level */ |
570 | for (k = 0; k < p->number_of_irqs; k++) | 583 | for (k = 0; k < nirqs; k++) |
571 | intc_irqpin_mask_unmask_prio(p, k, 0); | 584 | intc_irqpin_mask_unmask_prio(p, k, 0); |
572 | 585 | ||
573 | dev_info(dev, "driving %d irqs\n", p->number_of_irqs); | 586 | dev_info(dev, "driving %d irqs\n", nirqs); |
574 | |||
575 | /* warn in case of mismatch if irq base is specified */ | ||
576 | if (p->config.irq_base) { | ||
577 | if (p->config.irq_base != p->irq[0].domain_irq) | ||
578 | dev_warn(dev, "irq base mismatch (%d/%d)\n", | ||
579 | p->config.irq_base, p->irq[0].domain_irq); | ||
580 | } | ||
581 | 587 | ||
582 | return 0; | 588 | return 0; |
583 | 589 | ||
diff --git a/drivers/irqchip/irq-sunxi-nmi.c b/drivers/irqchip/irq-sunxi-nmi.c index 4ef178078e5b..0820f67cc9a7 100644 --- a/drivers/irqchip/irq-sunxi-nmi.c +++ b/drivers/irqchip/irq-sunxi-nmi.c | |||
@@ -50,6 +50,12 @@ static struct sunxi_sc_nmi_reg_offs sun6i_reg_offs = { | |||
50 | .enable = 0x34, | 50 | .enable = 0x34, |
51 | }; | 51 | }; |
52 | 52 | ||
53 | static struct sunxi_sc_nmi_reg_offs sun9i_reg_offs = { | ||
54 | .ctrl = 0x00, | ||
55 | .pend = 0x08, | ||
56 | .enable = 0x04, | ||
57 | }; | ||
58 | |||
53 | static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off, | 59 | static inline void sunxi_sc_nmi_write(struct irq_chip_generic *gc, u32 off, |
54 | u32 val) | 60 | u32 val) |
55 | { | 61 | { |
@@ -207,3 +213,10 @@ static int __init sun7i_sc_nmi_irq_init(struct device_node *node, | |||
207 | return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs); | 213 | return sunxi_sc_nmi_irq_init(node, &sun7i_reg_offs); |
208 | } | 214 | } |
209 | IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init); | 215 | IRQCHIP_DECLARE(sun7i_sc_nmi, "allwinner,sun7i-a20-sc-nmi", sun7i_sc_nmi_irq_init); |
216 | |||
217 | static int __init sun9i_nmi_irq_init(struct device_node *node, | ||
218 | struct device_node *parent) | ||
219 | { | ||
220 | return sunxi_sc_nmi_irq_init(node, &sun9i_reg_offs); | ||
221 | } | ||
222 | IRQCHIP_DECLARE(sun9i_nmi, "allwinner,sun9i-a80-nmi", sun9i_nmi_irq_init); | ||
diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c new file mode 100644 index 000000000000..4192bdcd2734 --- /dev/null +++ b/drivers/irqchip/irq-ts4800.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /* | ||
2 | * Multiplexed-IRQs driver for TS-4800's FPGA | ||
3 | * | ||
4 | * Copyright (c) 2015 - Savoir-faire Linux | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public | ||
7 | * License version 2. This program is licensed "as is" without any | ||
8 | * warranty of any kind, whether express or implied. | ||
9 | */ | ||
10 | |||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/io.h> | ||
13 | #include <linux/irq.h> | ||
14 | #include <linux/irqchip.h> | ||
15 | #include <linux/irqchip/chained_irq.h> | ||
16 | #include <linux/irqdomain.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/of_address.h> | ||
20 | #include <linux/of_irq.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | |||
23 | #define IRQ_MASK 0x4 | ||
24 | #define IRQ_STATUS 0x8 | ||
25 | |||
26 | struct ts4800_irq_data { | ||
27 | void __iomem *base; | ||
28 | struct irq_domain *domain; | ||
29 | struct irq_chip irq_chip; | ||
30 | }; | ||
31 | |||
32 | static void ts4800_irq_mask(struct irq_data *d) | ||
33 | { | ||
34 | struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d); | ||
35 | u16 reg = readw(data->base + IRQ_MASK); | ||
36 | u16 mask = 1 << d->hwirq; | ||
37 | |||
38 | writew(reg | mask, data->base + IRQ_MASK); | ||
39 | } | ||
40 | |||
41 | static void ts4800_irq_unmask(struct irq_data *d) | ||
42 | { | ||
43 | struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d); | ||
44 | u16 reg = readw(data->base + IRQ_MASK); | ||
45 | u16 mask = 1 << d->hwirq; | ||
46 | |||
47 | writew(reg & ~mask, data->base + IRQ_MASK); | ||
48 | } | ||
49 | |||
50 | static int ts4800_irqdomain_map(struct irq_domain *d, unsigned int irq, | ||
51 | irq_hw_number_t hwirq) | ||
52 | { | ||
53 | struct ts4800_irq_data *data = d->host_data; | ||
54 | |||
55 | irq_set_chip_and_handler(irq, &data->irq_chip, handle_simple_irq); | ||
56 | irq_set_chip_data(irq, data); | ||
57 | irq_set_noprobe(irq); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | struct irq_domain_ops ts4800_ic_ops = { | ||
63 | .map = ts4800_irqdomain_map, | ||
64 | .xlate = irq_domain_xlate_onecell, | ||
65 | }; | ||
66 | |||
67 | static void ts4800_ic_chained_handle_irq(struct irq_desc *desc) | ||
68 | { | ||
69 | struct ts4800_irq_data *data = irq_desc_get_handler_data(desc); | ||
70 | struct irq_chip *chip = irq_desc_get_chip(desc); | ||
71 | u16 status = readw(data->base + IRQ_STATUS); | ||
72 | |||
73 | chained_irq_enter(chip, desc); | ||
74 | |||
75 | if (unlikely(status == 0)) { | ||
76 | handle_bad_irq(desc); | ||
77 | goto out; | ||
78 | } | ||
79 | |||
80 | do { | ||
81 | unsigned int bit = __ffs(status); | ||
82 | int irq = irq_find_mapping(data->domain, bit); | ||
83 | |||
84 | status &= ~(1 << bit); | ||
85 | generic_handle_irq(irq); | ||
86 | } while (status); | ||
87 | |||
88 | out: | ||
89 | chained_irq_exit(chip, desc); | ||
90 | } | ||
91 | |||
92 | static int ts4800_ic_probe(struct platform_device *pdev) | ||
93 | { | ||
94 | struct device_node *node = pdev->dev.of_node; | ||
95 | struct ts4800_irq_data *data; | ||
96 | struct irq_chip *irq_chip; | ||
97 | struct resource *res; | ||
98 | int parent_irq; | ||
99 | |||
100 | data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); | ||
101 | if (!data) | ||
102 | return -ENOMEM; | ||
103 | |||
104 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
105 | data->base = devm_ioremap_resource(&pdev->dev, res); | ||
106 | if (IS_ERR(data->base)) | ||
107 | return PTR_ERR(data->base); | ||
108 | |||
109 | writew(0xFFFF, data->base + IRQ_MASK); | ||
110 | |||
111 | parent_irq = irq_of_parse_and_map(node, 0); | ||
112 | if (!parent_irq) { | ||
113 | dev_err(&pdev->dev, "failed to get parent IRQ\n"); | ||
114 | return -EINVAL; | ||
115 | } | ||
116 | |||
117 | irq_chip = &data->irq_chip; | ||
118 | irq_chip->name = dev_name(&pdev->dev); | ||
119 | irq_chip->irq_mask = ts4800_irq_mask; | ||
120 | irq_chip->irq_unmask = ts4800_irq_unmask; | ||
121 | |||
122 | data->domain = irq_domain_add_linear(node, 8, &ts4800_ic_ops, data); | ||
123 | if (!data->domain) { | ||
124 | dev_err(&pdev->dev, "cannot add IRQ domain\n"); | ||
125 | return -ENOMEM; | ||
126 | } | ||
127 | |||
128 | irq_set_chained_handler_and_data(parent_irq, | ||
129 | ts4800_ic_chained_handle_irq, data); | ||
130 | |||
131 | platform_set_drvdata(pdev, data); | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static int ts4800_ic_remove(struct platform_device *pdev) | ||
137 | { | ||
138 | struct ts4800_irq_data *data = platform_get_drvdata(pdev); | ||
139 | |||
140 | irq_domain_remove(data->domain); | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static const struct of_device_id ts4800_ic_of_match[] = { | ||
146 | { .compatible = "technologic,ts4800-irqc", }, | ||
147 | {}, | ||
148 | }; | ||
149 | MODULE_DEVICE_TABLE(of, ts4800_ic_of_match); | ||
150 | |||
151 | static struct platform_driver ts4800_ic_driver = { | ||
152 | .probe = ts4800_ic_probe, | ||
153 | .remove = ts4800_ic_remove, | ||
154 | .driver = { | ||
155 | .name = "ts4800-irqc", | ||
156 | .of_match_table = ts4800_ic_of_match, | ||
157 | }, | ||
158 | }; | ||
159 | module_platform_driver(ts4800_ic_driver); | ||
160 | |||
161 | MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>"); | ||
162 | MODULE_LICENSE("GPL v2"); | ||
163 | MODULE_ALIAS("platform:ts4800_irqc"); | ||
diff --git a/drivers/irqchip/irq-zevio.c b/drivers/irqchip/irq-zevio.c index 4c48fa88a03d..cb9d8ec37507 100644 --- a/drivers/irqchip/irq-zevio.c +++ b/drivers/irqchip/irq-zevio.c | |||
@@ -43,8 +43,7 @@ static void __iomem *zevio_irq_io; | |||
43 | static void zevio_irq_ack(struct irq_data *irqd) | 43 | static void zevio_irq_ack(struct irq_data *irqd) |
44 | { | 44 | { |
45 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(irqd); | 45 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(irqd); |
46 | struct irq_chip_regs *regs = | 46 | struct irq_chip_regs *regs = &irq_data_get_chip_type(irqd)->regs; |
47 | &container_of(irqd->chip, struct irq_chip_type, chip)->regs; | ||
48 | 47 | ||
49 | readl(gc->reg_base + regs->ack); | 48 | readl(gc->reg_base + regs->ack); |
50 | } | 49 | } |
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 7eaa4c87fec7..7a0df3fdbfae 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -257,6 +257,7 @@ void pci_msi_mask_irq(struct irq_data *data) | |||
257 | { | 257 | { |
258 | msi_set_mask_bit(data, 1); | 258 | msi_set_mask_bit(data, 1); |
259 | } | 259 | } |
260 | EXPORT_SYMBOL_GPL(pci_msi_mask_irq); | ||
260 | 261 | ||
261 | /** | 262 | /** |
262 | * pci_msi_unmask_irq - Generic irq chip callback to unmask PCI/MSI interrupts | 263 | * pci_msi_unmask_irq - Generic irq chip callback to unmask PCI/MSI interrupts |
@@ -266,6 +267,7 @@ void pci_msi_unmask_irq(struct irq_data *data) | |||
266 | { | 267 | { |
267 | msi_set_mask_bit(data, 0); | 268 | msi_set_mask_bit(data, 0); |
268 | } | 269 | } |
270 | EXPORT_SYMBOL_GPL(pci_msi_unmask_irq); | ||
269 | 271 | ||
270 | void default_restore_msi_irqs(struct pci_dev *dev) | 272 | void default_restore_msi_irqs(struct pci_dev *dev) |
271 | { | 273 | { |
@@ -1126,6 +1128,7 @@ struct pci_dev *msi_desc_to_pci_dev(struct msi_desc *desc) | |||
1126 | { | 1128 | { |
1127 | return to_pci_dev(desc->dev); | 1129 | return to_pci_dev(desc->dev); |
1128 | } | 1130 | } |
1131 | EXPORT_SYMBOL(msi_desc_to_pci_dev); | ||
1129 | 1132 | ||
1130 | void *msi_desc_to_pci_sysdata(struct msi_desc *desc) | 1133 | void *msi_desc_to_pci_sysdata(struct msi_desc *desc) |
1131 | { | 1134 | { |
@@ -1285,6 +1288,7 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode, | |||
1285 | domain->bus_token = DOMAIN_BUS_PCI_MSI; | 1288 | domain->bus_token = DOMAIN_BUS_PCI_MSI; |
1286 | return domain; | 1289 | return domain; |
1287 | } | 1290 | } |
1291 | EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain); | ||
1288 | 1292 | ||
1289 | /** | 1293 | /** |
1290 | * pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain | 1294 | * pci_msi_domain_alloc_irqs - Allocate interrupts for @dev in @domain |
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index a32ba753e413..d3f32d6417ef 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c | |||
@@ -9,7 +9,9 @@ | |||
9 | 9 | ||
10 | #include <linux/delay.h> | 10 | #include <linux/delay.h> |
11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
12 | #include <linux/irqdomain.h> | ||
12 | #include <linux/pci.h> | 13 | #include <linux/pci.h> |
14 | #include <linux/msi.h> | ||
13 | #include <linux/pci_hotplug.h> | 15 | #include <linux/pci_hotplug.h> |
14 | #include <linux/module.h> | 16 | #include <linux/module.h> |
15 | #include <linux/pci-aspm.h> | 17 | #include <linux/pci-aspm.h> |
@@ -689,6 +691,46 @@ static struct acpi_bus_type acpi_pci_bus = { | |||
689 | .cleanup = pci_acpi_cleanup, | 691 | .cleanup = pci_acpi_cleanup, |
690 | }; | 692 | }; |
691 | 693 | ||
694 | |||
695 | static struct fwnode_handle *(*pci_msi_get_fwnode_cb)(struct device *dev); | ||
696 | |||
697 | /** | ||
698 | * pci_msi_register_fwnode_provider - Register callback to retrieve fwnode | ||
699 | * @fn: Callback matching a device to a fwnode that identifies a PCI | ||
700 | * MSI domain. | ||
701 | * | ||
702 | * This should be called by irqchip driver, which is the parent of | ||
703 | * the MSI domain to provide callback interface to query fwnode. | ||
704 | */ | ||
705 | void | ||
706 | pci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *)) | ||
707 | { | ||
708 | pci_msi_get_fwnode_cb = fn; | ||
709 | } | ||
710 | |||
711 | /** | ||
712 | * pci_host_bridge_acpi_msi_domain - Retrieve MSI domain of a PCI host bridge | ||
713 | * @bus: The PCI host bridge bus. | ||
714 | * | ||
715 | * This function uses the callback function registered by | ||
716 | * pci_msi_register_fwnode_provider() to retrieve the irq_domain with | ||
717 | * type DOMAIN_BUS_PCI_MSI of the specified host bridge bus. | ||
718 | * This returns NULL on error or when the domain is not found. | ||
719 | */ | ||
720 | struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) | ||
721 | { | ||
722 | struct fwnode_handle *fwnode; | ||
723 | |||
724 | if (!pci_msi_get_fwnode_cb) | ||
725 | return NULL; | ||
726 | |||
727 | fwnode = pci_msi_get_fwnode_cb(&bus->dev); | ||
728 | if (!fwnode) | ||
729 | return NULL; | ||
730 | |||
731 | return irq_find_matching_fwnode(fwnode, DOMAIN_BUS_PCI_MSI); | ||
732 | } | ||
733 | |||
692 | static int __init acpi_pci_init(void) | 734 | static int __init acpi_pci_init(void) |
693 | { | 735 | { |
694 | int ret; | 736 | int ret; |
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index edb1984201e9..553a029e37f1 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c | |||
@@ -672,6 +672,8 @@ static struct irq_domain *pci_host_bridge_msi_domain(struct pci_bus *bus) | |||
672 | * should be called from here. | 672 | * should be called from here. |
673 | */ | 673 | */ |
674 | d = pci_host_bridge_of_msi_domain(bus); | 674 | d = pci_host_bridge_of_msi_domain(bus); |
675 | if (!d) | ||
676 | d = pci_host_bridge_acpi_msi_domain(bus); | ||
675 | 677 | ||
676 | return d; | 678 | return d; |
677 | } | 679 | } |
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index ad16809c8596..cb30edbfe9fc 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h | |||
@@ -195,6 +195,7 @@ extern void disable_irq(unsigned int irq); | |||
195 | extern void disable_percpu_irq(unsigned int irq); | 195 | extern void disable_percpu_irq(unsigned int irq); |
196 | extern void enable_irq(unsigned int irq); | 196 | extern void enable_irq(unsigned int irq); |
197 | extern void enable_percpu_irq(unsigned int irq, unsigned int type); | 197 | extern void enable_percpu_irq(unsigned int irq, unsigned int type); |
198 | extern bool irq_percpu_is_enabled(unsigned int irq); | ||
198 | extern void irq_wake_thread(unsigned int irq, void *dev_id); | 199 | extern void irq_wake_thread(unsigned int irq, void *dev_id); |
199 | 200 | ||
200 | /* The following three functions are for the core kernel use only. */ | 201 | /* The following three functions are for the core kernel use only. */ |
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index bae69e5d693c..9c940263ca23 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h | |||
@@ -103,10 +103,21 @@ struct device_node; | |||
103 | void gic_cascade_irq(unsigned int gic_nr, unsigned int irq); | 103 | void gic_cascade_irq(unsigned int gic_nr, unsigned int irq); |
104 | int gic_cpu_if_down(unsigned int gic_nr); | 104 | int gic_cpu_if_down(unsigned int gic_nr); |
105 | 105 | ||
106 | /* | ||
107 | * Subdrivers that need some preparatory work can initialize their | ||
108 | * chips and call this to register their GICs. | ||
109 | */ | ||
110 | int gic_of_init(struct device_node *node, struct device_node *parent); | ||
111 | |||
112 | /* | ||
113 | * Legacy platforms not converted to DT yet must use this to init | ||
114 | * their GIC | ||
115 | */ | ||
106 | void gic_init(unsigned int nr, int start, | 116 | void gic_init(unsigned int nr, int start, |
107 | void __iomem *dist , void __iomem *cpu); | 117 | void __iomem *dist , void __iomem *cpu); |
108 | 118 | ||
109 | int gicv2m_of_init(struct device_node *node, struct irq_domain *parent); | 119 | int gicv2m_init(struct fwnode_handle *parent_handle, |
120 | struct irq_domain *parent); | ||
110 | 121 | ||
111 | void gic_send_sgi(unsigned int cpu_id, unsigned int irq); | 122 | void gic_send_sgi(unsigned int cpu_id, unsigned int irq); |
112 | int gic_get_cpu_id(unsigned int cpu); | 123 | int gic_get_cpu_id(unsigned int cpu); |
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index a587a33363c7..dcca77c4b9d2 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef _LINUX_IRQDESC_H | 1 | #ifndef _LINUX_IRQDESC_H |
2 | #define _LINUX_IRQDESC_H | 2 | #define _LINUX_IRQDESC_H |
3 | 3 | ||
4 | #include <linux/rcupdate.h> | ||
5 | |||
4 | /* | 6 | /* |
5 | * Core internal functions to deal with irq descriptors | 7 | * Core internal functions to deal with irq descriptors |
6 | */ | 8 | */ |
@@ -40,6 +42,7 @@ struct pt_regs; | |||
40 | * IRQF_NO_SUSPEND set | 42 | * IRQF_NO_SUSPEND set |
41 | * @force_resume_depth: number of irqactions on a irq descriptor with | 43 | * @force_resume_depth: number of irqactions on a irq descriptor with |
42 | * IRQF_FORCE_RESUME set | 44 | * IRQF_FORCE_RESUME set |
45 | * @rcu: rcu head for delayed free | ||
43 | * @dir: /proc/irq/ procfs entry | 46 | * @dir: /proc/irq/ procfs entry |
44 | * @name: flow handler name for /proc/interrupts output | 47 | * @name: flow handler name for /proc/interrupts output |
45 | */ | 48 | */ |
@@ -82,6 +85,9 @@ struct irq_desc { | |||
82 | #ifdef CONFIG_PROC_FS | 85 | #ifdef CONFIG_PROC_FS |
83 | struct proc_dir_entry *dir; | 86 | struct proc_dir_entry *dir; |
84 | #endif | 87 | #endif |
88 | #ifdef CONFIG_SPARSE_IRQ | ||
89 | struct rcu_head rcu; | ||
90 | #endif | ||
85 | int parent_irq; | 91 | int parent_irq; |
86 | struct module *owner; | 92 | struct module *owner; |
87 | const char *name; | 93 | const char *name; |
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index d5e5c5bef28c..f64622ad02c1 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h | |||
@@ -211,6 +211,11 @@ static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node) | |||
211 | return node ? &node->fwnode : NULL; | 211 | return node ? &node->fwnode : NULL; |
212 | } | 212 | } |
213 | 213 | ||
214 | static inline bool is_fwnode_irqchip(struct fwnode_handle *fwnode) | ||
215 | { | ||
216 | return fwnode && fwnode->type == FWNODE_IRQCHIP; | ||
217 | } | ||
218 | |||
214 | static inline struct irq_domain *irq_find_matching_host(struct device_node *node, | 219 | static inline struct irq_domain *irq_find_matching_host(struct device_node *node, |
215 | enum irq_domain_bus_token bus_token) | 220 | enum irq_domain_bus_token bus_token) |
216 | { | 221 | { |
@@ -367,6 +372,9 @@ static inline int irq_domain_alloc_irqs(struct irq_domain *domain, | |||
367 | return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false); | 372 | return __irq_domain_alloc_irqs(domain, -1, nr_irqs, node, arg, false); |
368 | } | 373 | } |
369 | 374 | ||
375 | extern int irq_domain_alloc_irqs_recursive(struct irq_domain *domain, | ||
376 | unsigned int irq_base, | ||
377 | unsigned int nr_irqs, void *arg); | ||
370 | extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, | 378 | extern int irq_domain_set_hwirq_and_chip(struct irq_domain *domain, |
371 | unsigned int virq, | 379 | unsigned int virq, |
372 | irq_hw_number_t hwirq, | 380 | irq_hw_number_t hwirq, |
@@ -410,6 +418,11 @@ static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) | |||
410 | static inline void irq_dispose_mapping(unsigned int virq) { } | 418 | static inline void irq_dispose_mapping(unsigned int virq) { } |
411 | static inline void irq_domain_activate_irq(struct irq_data *data) { } | 419 | static inline void irq_domain_activate_irq(struct irq_data *data) { } |
412 | static inline void irq_domain_deactivate_irq(struct irq_data *data) { } | 420 | static inline void irq_domain_deactivate_irq(struct irq_data *data) { } |
421 | static inline struct irq_domain *irq_find_matching_fwnode( | ||
422 | struct fwnode_handle *fwnode, enum irq_domain_bus_token bus_token) | ||
423 | { | ||
424 | return NULL; | ||
425 | } | ||
413 | #endif /* !CONFIG_IRQ_DOMAIN */ | 426 | #endif /* !CONFIG_IRQ_DOMAIN */ |
414 | 427 | ||
415 | #endif /* _LINUX_IRQDOMAIN_H */ | 428 | #endif /* _LINUX_IRQDOMAIN_H */ |
diff --git a/include/linux/msi.h b/include/linux/msi.h index f71a25e5fd25..1c6342ab8c0e 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h | |||
@@ -174,6 +174,7 @@ struct msi_controller { | |||
174 | #include <asm/msi.h> | 174 | #include <asm/msi.h> |
175 | 175 | ||
176 | struct irq_domain; | 176 | struct irq_domain; |
177 | struct irq_domain_ops; | ||
177 | struct irq_chip; | 178 | struct irq_chip; |
178 | struct device_node; | 179 | struct device_node; |
179 | struct fwnode_handle; | 180 | struct fwnode_handle; |
@@ -279,6 +280,23 @@ struct irq_domain *platform_msi_create_irq_domain(struct fwnode_handle *fwnode, | |||
279 | int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, | 280 | int platform_msi_domain_alloc_irqs(struct device *dev, unsigned int nvec, |
280 | irq_write_msi_msg_t write_msi_msg); | 281 | irq_write_msi_msg_t write_msi_msg); |
281 | void platform_msi_domain_free_irqs(struct device *dev); | 282 | void platform_msi_domain_free_irqs(struct device *dev); |
283 | |||
284 | /* When an MSI domain is used as an intermediate domain */ | ||
285 | int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, | ||
286 | int nvec, msi_alloc_info_t *args); | ||
287 | int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, | ||
288 | int virq, int nvec, msi_alloc_info_t *args); | ||
289 | struct irq_domain * | ||
290 | platform_msi_create_device_domain(struct device *dev, | ||
291 | unsigned int nvec, | ||
292 | irq_write_msi_msg_t write_msi_msg, | ||
293 | const struct irq_domain_ops *ops, | ||
294 | void *host_data); | ||
295 | int platform_msi_domain_alloc(struct irq_domain *domain, unsigned int virq, | ||
296 | unsigned int nr_irqs); | ||
297 | void platform_msi_domain_free(struct irq_domain *domain, unsigned int virq, | ||
298 | unsigned int nvec); | ||
299 | void *platform_msi_get_host_data(struct irq_domain *domain); | ||
282 | #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ | 300 | #endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ |
283 | 301 | ||
284 | #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN | 302 | #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 6ae25aae88fd..d86378c226fb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -1946,6 +1946,16 @@ static inline struct irq_domain * | |||
1946 | pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; } | 1946 | pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; } |
1947 | #endif /* CONFIG_OF */ | 1947 | #endif /* CONFIG_OF */ |
1948 | 1948 | ||
1949 | #ifdef CONFIG_ACPI | ||
1950 | struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus); | ||
1951 | |||
1952 | void | ||
1953 | pci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *)); | ||
1954 | #else | ||
1955 | static inline struct irq_domain * | ||
1956 | pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) { return NULL; } | ||
1957 | #endif | ||
1958 | |||
1949 | #ifdef CONFIG_EEH | 1959 | #ifdef CONFIG_EEH |
1950 | static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) | 1960 | static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) |
1951 | { | 1961 | { |
diff --git a/include/linux/platform_data/irq-renesas-intc-irqpin.h b/include/linux/platform_data/irq-renesas-intc-irqpin.h deleted file mode 100644 index e4cb911066a6..000000000000 --- a/include/linux/platform_data/irq-renesas-intc-irqpin.h +++ /dev/null | |||
@@ -1,29 +0,0 @@ | |||
1 | /* | ||
2 | * Renesas INTC External IRQ Pin Driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Magnus Damm | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #ifndef __IRQ_RENESAS_INTC_IRQPIN_H__ | ||
21 | #define __IRQ_RENESAS_INTC_IRQPIN_H__ | ||
22 | |||
23 | struct renesas_intc_irqpin_config { | ||
24 | unsigned int sense_bitfield_width; | ||
25 | unsigned int irq_base; | ||
26 | bool control_parent; | ||
27 | }; | ||
28 | |||
29 | #endif /* __IRQ_RENESAS_INTC_IRQPIN_H__ */ | ||
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 15206453b12a..5797909f4e5b 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c | |||
@@ -338,7 +338,6 @@ void handle_nested_irq(unsigned int irq) | |||
338 | raw_spin_lock_irq(&desc->lock); | 338 | raw_spin_lock_irq(&desc->lock); |
339 | 339 | ||
340 | desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); | 340 | desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
341 | kstat_incr_irqs_this_cpu(desc); | ||
342 | 341 | ||
343 | action = desc->action; | 342 | action = desc->action; |
344 | if (unlikely(!action || irqd_irq_disabled(&desc->irq_data))) { | 343 | if (unlikely(!action || irqd_irq_disabled(&desc->irq_data))) { |
@@ -346,6 +345,7 @@ void handle_nested_irq(unsigned int irq) | |||
346 | goto out_unlock; | 345 | goto out_unlock; |
347 | } | 346 | } |
348 | 347 | ||
348 | kstat_incr_irqs_this_cpu(desc); | ||
349 | irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS); | 349 | irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS); |
350 | raw_spin_unlock_irq(&desc->lock); | 350 | raw_spin_unlock_irq(&desc->lock); |
351 | 351 | ||
@@ -412,13 +412,13 @@ void handle_simple_irq(struct irq_desc *desc) | |||
412 | goto out_unlock; | 412 | goto out_unlock; |
413 | 413 | ||
414 | desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); | 414 | desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
415 | kstat_incr_irqs_this_cpu(desc); | ||
416 | 415 | ||
417 | if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { | 416 | if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { |
418 | desc->istate |= IRQS_PENDING; | 417 | desc->istate |= IRQS_PENDING; |
419 | goto out_unlock; | 418 | goto out_unlock; |
420 | } | 419 | } |
421 | 420 | ||
421 | kstat_incr_irqs_this_cpu(desc); | ||
422 | handle_irq_event(desc); | 422 | handle_irq_event(desc); |
423 | 423 | ||
424 | out_unlock: | 424 | out_unlock: |
@@ -462,7 +462,6 @@ void handle_level_irq(struct irq_desc *desc) | |||
462 | goto out_unlock; | 462 | goto out_unlock; |
463 | 463 | ||
464 | desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); | 464 | desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
465 | kstat_incr_irqs_this_cpu(desc); | ||
466 | 465 | ||
467 | /* | 466 | /* |
468 | * If its disabled or no action available | 467 | * If its disabled or no action available |
@@ -473,6 +472,7 @@ void handle_level_irq(struct irq_desc *desc) | |||
473 | goto out_unlock; | 472 | goto out_unlock; |
474 | } | 473 | } |
475 | 474 | ||
475 | kstat_incr_irqs_this_cpu(desc); | ||
476 | handle_irq_event(desc); | 476 | handle_irq_event(desc); |
477 | 477 | ||
478 | cond_unmask_irq(desc); | 478 | cond_unmask_irq(desc); |
@@ -532,7 +532,6 @@ void handle_fasteoi_irq(struct irq_desc *desc) | |||
532 | goto out; | 532 | goto out; |
533 | 533 | ||
534 | desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); | 534 | desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
535 | kstat_incr_irqs_this_cpu(desc); | ||
536 | 535 | ||
537 | /* | 536 | /* |
538 | * If its disabled or no action available | 537 | * If its disabled or no action available |
@@ -544,6 +543,7 @@ void handle_fasteoi_irq(struct irq_desc *desc) | |||
544 | goto out; | 543 | goto out; |
545 | } | 544 | } |
546 | 545 | ||
546 | kstat_incr_irqs_this_cpu(desc); | ||
547 | if (desc->istate & IRQS_ONESHOT) | 547 | if (desc->istate & IRQS_ONESHOT) |
548 | mask_irq(desc); | 548 | mask_irq(desc); |
549 | 549 | ||
@@ -950,6 +950,7 @@ void irq_chip_ack_parent(struct irq_data *data) | |||
950 | data = data->parent_data; | 950 | data = data->parent_data; |
951 | data->chip->irq_ack(data); | 951 | data->chip->irq_ack(data); |
952 | } | 952 | } |
953 | EXPORT_SYMBOL_GPL(irq_chip_ack_parent); | ||
953 | 954 | ||
954 | /** | 955 | /** |
955 | * irq_chip_mask_parent - Mask the parent interrupt | 956 | * irq_chip_mask_parent - Mask the parent interrupt |
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 239e2ae2c947..0409da0bcc33 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c | |||
@@ -159,6 +159,7 @@ static struct irq_desc *alloc_desc(int irq, int node, struct module *owner) | |||
159 | 159 | ||
160 | raw_spin_lock_init(&desc->lock); | 160 | raw_spin_lock_init(&desc->lock); |
161 | lockdep_set_class(&desc->lock, &irq_desc_lock_class); | 161 | lockdep_set_class(&desc->lock, &irq_desc_lock_class); |
162 | init_rcu_head(&desc->rcu); | ||
162 | 163 | ||
163 | desc_set_defaults(irq, desc, node, owner); | 164 | desc_set_defaults(irq, desc, node, owner); |
164 | 165 | ||
@@ -171,6 +172,15 @@ err_desc: | |||
171 | return NULL; | 172 | return NULL; |
172 | } | 173 | } |
173 | 174 | ||
175 | static void delayed_free_desc(struct rcu_head *rhp) | ||
176 | { | ||
177 | struct irq_desc *desc = container_of(rhp, struct irq_desc, rcu); | ||
178 | |||
179 | free_masks(desc); | ||
180 | free_percpu(desc->kstat_irqs); | ||
181 | kfree(desc); | ||
182 | } | ||
183 | |||
174 | static void free_desc(unsigned int irq) | 184 | static void free_desc(unsigned int irq) |
175 | { | 185 | { |
176 | struct irq_desc *desc = irq_to_desc(irq); | 186 | struct irq_desc *desc = irq_to_desc(irq); |
@@ -187,9 +197,12 @@ static void free_desc(unsigned int irq) | |||
187 | delete_irq_desc(irq); | 197 | delete_irq_desc(irq); |
188 | mutex_unlock(&sparse_irq_lock); | 198 | mutex_unlock(&sparse_irq_lock); |
189 | 199 | ||
190 | free_masks(desc); | 200 | /* |
191 | free_percpu(desc->kstat_irqs); | 201 | * We free the descriptor, masks and stat fields via RCU. That |
192 | kfree(desc); | 202 | * allows demultiplex interrupts to do rcu based management of |
203 | * the child interrupts. | ||
204 | */ | ||
205 | call_rcu(&desc->rcu, delayed_free_desc); | ||
193 | } | 206 | } |
194 | 207 | ||
195 | static int alloc_descs(unsigned int start, unsigned int cnt, int node, | 208 | static int alloc_descs(unsigned int start, unsigned int cnt, int node, |
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 22aa9612ef7c..8cf95de1ab3f 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c | |||
@@ -60,6 +60,7 @@ struct fwnode_handle *irq_domain_alloc_fwnode(void *data) | |||
60 | fwid->fwnode.type = FWNODE_IRQCHIP; | 60 | fwid->fwnode.type = FWNODE_IRQCHIP; |
61 | return &fwid->fwnode; | 61 | return &fwid->fwnode; |
62 | } | 62 | } |
63 | EXPORT_SYMBOL_GPL(irq_domain_alloc_fwnode); | ||
63 | 64 | ||
64 | /** | 65 | /** |
65 | * irq_domain_free_fwnode - Free a non-OF-backed fwnode_handle | 66 | * irq_domain_free_fwnode - Free a non-OF-backed fwnode_handle |
@@ -70,13 +71,14 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode) | |||
70 | { | 71 | { |
71 | struct irqchip_fwid *fwid; | 72 | struct irqchip_fwid *fwid; |
72 | 73 | ||
73 | if (WARN_ON(fwnode->type != FWNODE_IRQCHIP)) | 74 | if (WARN_ON(!is_fwnode_irqchip(fwnode))) |
74 | return; | 75 | return; |
75 | 76 | ||
76 | fwid = container_of(fwnode, struct irqchip_fwid, fwnode); | 77 | fwid = container_of(fwnode, struct irqchip_fwid, fwnode); |
77 | kfree(fwid->name); | 78 | kfree(fwid->name); |
78 | kfree(fwid); | 79 | kfree(fwid); |
79 | } | 80 | } |
81 | EXPORT_SYMBOL_GPL(irq_domain_free_fwnode); | ||
80 | 82 | ||
81 | /** | 83 | /** |
82 | * __irq_domain_add() - Allocate a new irq_domain data structure | 84 | * __irq_domain_add() - Allocate a new irq_domain data structure |
@@ -1013,6 +1015,7 @@ struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, | |||
1013 | 1015 | ||
1014 | return NULL; | 1016 | return NULL; |
1015 | } | 1017 | } |
1018 | EXPORT_SYMBOL_GPL(irq_domain_get_irq_data); | ||
1016 | 1019 | ||
1017 | /** | 1020 | /** |
1018 | * irq_domain_set_hwirq_and_chip - Set hwirq and irqchip of @virq at @domain | 1021 | * irq_domain_set_hwirq_and_chip - Set hwirq and irqchip of @virq at @domain |
@@ -1125,9 +1128,9 @@ static void irq_domain_free_irqs_recursive(struct irq_domain *domain, | |||
1125 | } | 1128 | } |
1126 | } | 1129 | } |
1127 | 1130 | ||
1128 | static int irq_domain_alloc_irqs_recursive(struct irq_domain *domain, | 1131 | int irq_domain_alloc_irqs_recursive(struct irq_domain *domain, |
1129 | unsigned int irq_base, | 1132 | unsigned int irq_base, |
1130 | unsigned int nr_irqs, void *arg) | 1133 | unsigned int nr_irqs, void *arg) |
1131 | { | 1134 | { |
1132 | int ret = 0; | 1135 | int ret = 0; |
1133 | struct irq_domain *parent = domain->parent; | 1136 | struct irq_domain *parent = domain->parent; |
@@ -1343,6 +1346,7 @@ struct irq_data *irq_domain_get_irq_data(struct irq_domain *domain, | |||
1343 | 1346 | ||
1344 | return (irq_data && irq_data->domain == domain) ? irq_data : NULL; | 1347 | return (irq_data && irq_data->domain == domain) ? irq_data : NULL; |
1345 | } | 1348 | } |
1349 | EXPORT_SYMBOL_GPL(irq_domain_get_irq_data); | ||
1346 | 1350 | ||
1347 | /** | 1351 | /** |
1348 | * irq_domain_set_info - Set the complete data for a @virq in @domain | 1352 | * irq_domain_set_info - Set the complete data for a @virq in @domain |
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 6ead200370da..841187239adc 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c | |||
@@ -1743,6 +1743,31 @@ out: | |||
1743 | } | 1743 | } |
1744 | EXPORT_SYMBOL_GPL(enable_percpu_irq); | 1744 | EXPORT_SYMBOL_GPL(enable_percpu_irq); |
1745 | 1745 | ||
1746 | /** | ||
1747 | * irq_percpu_is_enabled - Check whether the per cpu irq is enabled | ||
1748 | * @irq: Linux irq number to check for | ||
1749 | * | ||
1750 | * Must be called from a non migratable context. Returns the enable | ||
1751 | * state of a per cpu interrupt on the current cpu. | ||
1752 | */ | ||
1753 | bool irq_percpu_is_enabled(unsigned int irq) | ||
1754 | { | ||
1755 | unsigned int cpu = smp_processor_id(); | ||
1756 | struct irq_desc *desc; | ||
1757 | unsigned long flags; | ||
1758 | bool is_enabled; | ||
1759 | |||
1760 | desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU); | ||
1761 | if (!desc) | ||
1762 | return false; | ||
1763 | |||
1764 | is_enabled = cpumask_test_cpu(cpu, desc->percpu_enabled); | ||
1765 | irq_put_desc_unlock(desc, flags); | ||
1766 | |||
1767 | return is_enabled; | ||
1768 | } | ||
1769 | EXPORT_SYMBOL_GPL(irq_percpu_is_enabled); | ||
1770 | |||
1746 | void disable_percpu_irq(unsigned int irq) | 1771 | void disable_percpu_irq(unsigned int irq) |
1747 | { | 1772 | { |
1748 | unsigned int cpu = smp_processor_id(); | 1773 | unsigned int cpu = smp_processor_id(); |
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c index 6b0c0b74a2a1..15b249e7c673 100644 --- a/kernel/irq/msi.c +++ b/kernel/irq/msi.c | |||
@@ -252,6 +252,60 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode, | |||
252 | &msi_domain_ops, info); | 252 | &msi_domain_ops, info); |
253 | } | 253 | } |
254 | 254 | ||
255 | int msi_domain_prepare_irqs(struct irq_domain *domain, struct device *dev, | ||
256 | int nvec, msi_alloc_info_t *arg) | ||
257 | { | ||
258 | struct msi_domain_info *info = domain->host_data; | ||
259 | struct msi_domain_ops *ops = info->ops; | ||
260 | int ret; | ||
261 | |||
262 | ret = ops->msi_check(domain, info, dev); | ||
263 | if (ret == 0) | ||
264 | ret = ops->msi_prepare(domain, dev, nvec, arg); | ||
265 | |||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | int msi_domain_populate_irqs(struct irq_domain *domain, struct device *dev, | ||
270 | int virq, int nvec, msi_alloc_info_t *arg) | ||
271 | { | ||
272 | struct msi_domain_info *info = domain->host_data; | ||
273 | struct msi_domain_ops *ops = info->ops; | ||
274 | struct msi_desc *desc; | ||
275 | int ret = 0; | ||
276 | |||
277 | for_each_msi_entry(desc, dev) { | ||
278 | /* Don't even try the multi-MSI brain damage. */ | ||
279 | if (WARN_ON(!desc->irq || desc->nvec_used != 1)) { | ||
280 | ret = -EINVAL; | ||
281 | break; | ||
282 | } | ||
283 | |||
284 | if (!(desc->irq >= virq && desc->irq < (virq + nvec))) | ||
285 | continue; | ||
286 | |||
287 | ops->set_desc(arg, desc); | ||
288 | /* Assumes the domain mutex is held! */ | ||
289 | ret = irq_domain_alloc_irqs_recursive(domain, virq, 1, arg); | ||
290 | if (ret) | ||
291 | break; | ||
292 | |||
293 | irq_set_msi_desc_off(virq, 0, desc); | ||
294 | } | ||
295 | |||
296 | if (ret) { | ||
297 | /* Mop up the damage */ | ||
298 | for_each_msi_entry(desc, dev) { | ||
299 | if (!(desc->irq >= virq && desc->irq < (virq + nvec))) | ||
300 | continue; | ||
301 | |||
302 | irq_domain_free_irqs_common(domain, desc->irq, 1); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | return ret; | ||
307 | } | ||
308 | |||
255 | /** | 309 | /** |
256 | * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain | 310 | * msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain |
257 | * @domain: The domain to allocate from | 311 | * @domain: The domain to allocate from |
@@ -270,9 +324,7 @@ int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev, | |||
270 | struct msi_desc *desc; | 324 | struct msi_desc *desc; |
271 | int i, ret, virq = -1; | 325 | int i, ret, virq = -1; |
272 | 326 | ||
273 | ret = ops->msi_check(domain, info, dev); | 327 | ret = msi_domain_prepare_irqs(domain, dev, nvec, &arg); |
274 | if (ret == 0) | ||
275 | ret = ops->msi_prepare(domain, dev, nvec, &arg); | ||
276 | if (ret) | 328 | if (ret) |
277 | return ret; | 329 | return ret; |
278 | 330 | ||