diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-08 14:01:13 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-08 14:01:13 -0400 |
commit | 2a1ccd31420a7b1acd6ca37b2bec2d723aa093e4 (patch) | |
tree | 43946f0c4afc7dce86fc055df48d661f8ed3999c | |
parent | e0e86b111bca6bbf746c03ec5cf3e6a61fa3f8e9 (diff) | |
parent | 3a1d24ca9573fbc74a3d32c972c333b161e0e9dc (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 departement provides the usual mixed bag:
Core:
- Further improvements to the irq timings code which aims to predict
the next interrupt for power state selection to achieve better
latency/power balance
- Add interrupt statistics to the core NMI handlers
- The usual small fixes and cleanups
Drivers:
- Support for Renesas RZ/A1, Annapurna Labs FIC, Meson-G12A SoC and
Amazon Gravition AMR/GIC interrupt controllers.
- Rework of the Renesas INTC controller driver
- ACPI support for Socionext SoCs
- Enhancements to the CSKY interrupt controller
- The usual small fixes and cleanups"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (39 commits)
irq/irqdomain: Fix comment typo
genirq: Update irq stats from NMI handlers
irqchip/gic-pm: Remove PM_CLK dependency
irqchip/al-fic: Introduce Amazon's Annapurna Labs Fabric Interrupt Controller Driver
dt-bindings: interrupt-controller: Add Amazon's Annapurna Labs FIC
softirq: Use __this_cpu_write() in takeover_tasklets()
irqchip/mbigen: Stop printing kernel addresses
irqchip/gic: Add dependency for ARM_GIC_MAX_NR
genirq/affinity: Remove unused argument from [__]irq_build_affinity_masks()
genirq/timings: Add selftest for next event computation
genirq/timings: Add selftest for irqs circular buffer
genirq/timings: Add selftest for circular array
genirq/timings: Encapsulate storing function
genirq/timings: Encapsulate timings push
genirq/timings: Optimize the period detection speed
genirq/timings: Fix timings buffer inspection
genirq/timings: Fix next event index function
irqchip/qcom: Use struct_size() in devm_kzalloc()
irqchip/irq-csky-mpintc: Remove unnecessary loop in interrupt handler
dt-bindings: interrupt-controller: Update csky mpintc
...
32 files changed, 1528 insertions, 192 deletions
diff --git a/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.txt b/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.txt new file mode 100644 index 000000000000..4e82fd575cec --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.txt | |||
@@ -0,0 +1,29 @@ | |||
1 | Amazon's Annapurna Labs Fabric Interrupt Controller | ||
2 | |||
3 | Required properties: | ||
4 | |||
5 | - compatible: should be "amazon,al-fic" | ||
6 | - reg: physical base address and size of the registers | ||
7 | - interrupt-controller: identifies the node as an interrupt controller | ||
8 | - #interrupt-cells: must be 2. | ||
9 | First cell defines the index of the interrupt within the controller. | ||
10 | Second cell is used to specify the trigger type and must be one of the | ||
11 | following: | ||
12 | - bits[3:0] trigger type and level flags | ||
13 | 1 = low-to-high edge triggered | ||
14 | 4 = active high level-sensitive | ||
15 | - interrupt-parent: specifies the parent interrupt controller. | ||
16 | - interrupts: describes which input line in the interrupt parent, this | ||
17 | fic's output is connected to. This field property depends on the parent's | ||
18 | binding | ||
19 | |||
20 | Example: | ||
21 | |||
22 | amazon_fic: interrupt-controller@0xfd8a8500 { | ||
23 | compatible = "amazon,al-fic"; | ||
24 | interrupt-controller; | ||
25 | #interrupt-cells = <2>; | ||
26 | reg = <0x0 0xfd8a8500 0x0 0x1000>; | ||
27 | interrupt-parent = <&gic>; | ||
28 | interrupts = <GIC_SPI 0x0 IRQ_TYPE_LEVEL_HIGH>; | ||
29 | }; | ||
diff --git a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt index 1502a51548bb..7d531d5fff29 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.txt | |||
@@ -15,6 +15,7 @@ Required properties: | |||
15 | "amlogic,meson-gxbb-gpio-intc" for GXBB SoCs (S905) or | 15 | "amlogic,meson-gxbb-gpio-intc" for GXBB SoCs (S905) or |
16 | "amlogic,meson-gxl-gpio-intc" for GXL SoCs (S905X, S912) | 16 | "amlogic,meson-gxl-gpio-intc" for GXL SoCs (S905X, S912) |
17 | "amlogic,meson-axg-gpio-intc" for AXG SoCs (A113D, A113X) | 17 | "amlogic,meson-axg-gpio-intc" for AXG SoCs (A113D, A113X) |
18 | "amlogic,meson-g12a-gpio-intc" for G12A SoCs (S905D2, S905X2, S905Y2) | ||
18 | - reg : Specifies base physical address and size of the registers. | 19 | - reg : Specifies base physical address and size of the registers. |
19 | - interrupt-controller : Identifies the node as an interrupt controller. | 20 | - interrupt-controller : Identifies the node as an interrupt controller. |
20 | - #interrupt-cells : Specifies the number of cells needed to encode an | 21 | - #interrupt-cells : Specifies the number of cells needed to encode an |
diff --git a/Documentation/devicetree/bindings/interrupt-controller/csky,mpintc.txt b/Documentation/devicetree/bindings/interrupt-controller/csky,mpintc.txt index ab921f1698fb..e13405355166 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/csky,mpintc.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/csky,mpintc.txt | |||
@@ -6,11 +6,16 @@ C-SKY Multi-processors Interrupt Controller is designed for ck807/ck810/ck860 | |||
6 | SMP soc, and it also could be used in non-SMP system. | 6 | SMP soc, and it also could be used in non-SMP system. |
7 | 7 | ||
8 | Interrupt number definition: | 8 | Interrupt number definition: |
9 | |||
10 | 0-15 : software irq, and we use 15 as our IPI_IRQ. | 9 | 0-15 : software irq, and we use 15 as our IPI_IRQ. |
11 | 16-31 : private irq, and we use 16 as the co-processor timer. | 10 | 16-31 : private irq, and we use 16 as the co-processor timer. |
12 | 31-1024: common irq for soc ip. | 11 | 31-1024: common irq for soc ip. |
13 | 12 | ||
13 | Interrupt triger mode: (Defined in dt-bindings/interrupt-controller/irq.h) | ||
14 | IRQ_TYPE_LEVEL_HIGH (default) | ||
15 | IRQ_TYPE_LEVEL_LOW | ||
16 | IRQ_TYPE_EDGE_RISING | ||
17 | IRQ_TYPE_EDGE_FALLING | ||
18 | |||
14 | ============================= | 19 | ============================= |
15 | intc node bindings definition | 20 | intc node bindings definition |
16 | ============================= | 21 | ============================= |
@@ -26,15 +31,22 @@ intc node bindings definition | |||
26 | - #interrupt-cells | 31 | - #interrupt-cells |
27 | Usage: required | 32 | Usage: required |
28 | Value type: <u32> | 33 | Value type: <u32> |
29 | Definition: must be <1> | 34 | Definition: <2> |
30 | - interrupt-controller: | 35 | - interrupt-controller: |
31 | Usage: required | 36 | Usage: required |
32 | 37 | ||
33 | Examples: | 38 | Examples: ("interrupts = <irq_num IRQ_TYPE_XXX>") |
34 | --------- | 39 | --------- |
40 | #include <dt-bindings/interrupt-controller/irq.h> | ||
35 | 41 | ||
36 | intc: interrupt-controller { | 42 | intc: interrupt-controller { |
37 | compatible = "csky,mpintc"; | 43 | compatible = "csky,mpintc"; |
38 | #interrupt-cells = <1>; | 44 | #interrupt-cells = <2>; |
39 | interrupt-controller; | 45 | interrupt-controller; |
40 | }; | 46 | }; |
47 | |||
48 | device: device-example { | ||
49 | ... | ||
50 | interrupts = <34 IRQ_TYPE_EDGE_RISING>; | ||
51 | interrupt-parent = <&intc>; | ||
52 | }; | ||
diff --git a/Documentation/devicetree/bindings/interrupt-controller/renesas,rza1-irqc.txt b/Documentation/devicetree/bindings/interrupt-controller/renesas,rza1-irqc.txt new file mode 100644 index 000000000000..727b7e4cd6e0 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/renesas,rza1-irqc.txt | |||
@@ -0,0 +1,43 @@ | |||
1 | DT bindings for the Renesas RZ/A1 Interrupt Controller | ||
2 | |||
3 | The RZ/A1 Interrupt Controller is a front-end for the GIC found on Renesas | ||
4 | RZ/A1 and RZ/A2 SoCs: | ||
5 | - IRQ sense select for 8 external interrupts, 1:1-mapped to 8 GIC SPI | ||
6 | interrupts, | ||
7 | - NMI edge select. | ||
8 | |||
9 | Required properties: | ||
10 | - compatible: Must be "renesas,<soctype>-irqc", and "renesas,rza1-irqc" as | ||
11 | fallback. | ||
12 | Examples with soctypes are: | ||
13 | - "renesas,r7s72100-irqc" (RZ/A1H) | ||
14 | - "renesas,r7s9210-irqc" (RZ/A2M) | ||
15 | - #interrupt-cells: Must be 2 (an interrupt index and flags, as defined | ||
16 | in interrupts.txt in this directory) | ||
17 | - #address-cells: Must be zero | ||
18 | - interrupt-controller: Marks the device as an interrupt controller | ||
19 | - reg: Base address and length of the memory resource used by the interrupt | ||
20 | controller | ||
21 | - interrupt-map: Specifies the mapping from external interrupts to GIC | ||
22 | interrupts | ||
23 | - interrupt-map-mask: Must be <7 0> | ||
24 | |||
25 | Example: | ||
26 | |||
27 | irqc: interrupt-controller@fcfef800 { | ||
28 | compatible = "renesas,r7s72100-irqc", "renesas,rza1-irqc"; | ||
29 | #interrupt-cells = <2>; | ||
30 | #address-cells = <0>; | ||
31 | interrupt-controller; | ||
32 | reg = <0xfcfef800 0x6>; | ||
33 | interrupt-map = | ||
34 | <0 0 &gic GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>, | ||
35 | <1 0 &gic GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>, | ||
36 | <2 0 &gic GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>, | ||
37 | <3 0 &gic GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>, | ||
38 | <4 0 &gic GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>, | ||
39 | <5 0 &gic GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>, | ||
40 | <6 0 &gic GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>, | ||
41 | <7 0 &gic GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; | ||
42 | interrupt-map-mask = <7 0>; | ||
43 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index badf7e89b072..bcb37428522a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -1306,6 +1306,12 @@ S: Maintained | |||
1306 | F: Documentation/devicetree/bindings/interrupt-controller/arm,vic.txt | 1306 | F: Documentation/devicetree/bindings/interrupt-controller/arm,vic.txt |
1307 | F: drivers/irqchip/irq-vic.c | 1307 | F: drivers/irqchip/irq-vic.c |
1308 | 1308 | ||
1309 | AMAZON ANNAPURNA LABS FIC DRIVER | ||
1310 | M: Talel Shenhar <talel@amazon.com> | ||
1311 | S: Maintained | ||
1312 | F: Documentation/devicetree/bindings/interrupt-controller/amazon,al-fic.txt | ||
1313 | F: drivers/irqchip/irq-al-fic.c | ||
1314 | |||
1309 | ARM SMMU DRIVERS | 1315 | ARM SMMU DRIVERS |
1310 | M: Will Deacon <will@kernel.org> | 1316 | M: Will Deacon <will@kernel.org> |
1311 | R: Robin Murphy <robin.murphy@arm.com> | 1317 | R: Robin Murphy <robin.murphy@arm.com> |
diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c index 89690a471360..e209081d644b 100644 --- a/drivers/acpi/irq.c +++ b/drivers/acpi/irq.c | |||
@@ -292,3 +292,29 @@ void __init acpi_set_irq_model(enum acpi_irq_model_id model, | |||
292 | acpi_irq_model = model; | 292 | acpi_irq_model = model; |
293 | acpi_gsi_domain_id = fwnode; | 293 | acpi_gsi_domain_id = fwnode; |
294 | } | 294 | } |
295 | |||
296 | /** | ||
297 | * acpi_irq_create_hierarchy - Create a hierarchical IRQ domain with the default | ||
298 | * GSI domain as its parent. | ||
299 | * @flags: Irq domain flags associated with the domain | ||
300 | * @size: Size of the domain. | ||
301 | * @fwnode: Optional fwnode of the interrupt controller | ||
302 | * @ops: Pointer to the interrupt domain callbacks | ||
303 | * @host_data: Controller private data pointer | ||
304 | */ | ||
305 | struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags, | ||
306 | unsigned int size, | ||
307 | struct fwnode_handle *fwnode, | ||
308 | const struct irq_domain_ops *ops, | ||
309 | void *host_data) | ||
310 | { | ||
311 | struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, | ||
312 | DOMAIN_BUS_ANY); | ||
313 | |||
314 | if (!d) | ||
315 | return NULL; | ||
316 | |||
317 | return irq_domain_create_hierarchy(d, flags, size, fwnode, ops, | ||
318 | host_data); | ||
319 | } | ||
320 | EXPORT_SYMBOL_GPL(acpi_irq_create_hierarchy); | ||
diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c index 9bfff171f9fe..8f466993cd24 100644 --- a/drivers/gpio/gpio-mb86s7x.c +++ b/drivers/gpio/gpio-mb86s7x.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Copyright (C) 2015 Linaro Ltd. | 6 | * Copyright (C) 2015 Linaro Ltd. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/acpi.h> | ||
9 | #include <linux/io.h> | 10 | #include <linux/io.h> |
10 | #include <linux/init.h> | 11 | #include <linux/init.h> |
11 | #include <linux/clk.h> | 12 | #include <linux/clk.h> |
@@ -19,6 +20,8 @@ | |||
19 | #include <linux/spinlock.h> | 20 | #include <linux/spinlock.h> |
20 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
21 | 22 | ||
23 | #include "gpiolib.h" | ||
24 | |||
22 | /* | 25 | /* |
23 | * Only first 8bits of a register correspond to each pin, | 26 | * Only first 8bits of a register correspond to each pin, |
24 | * so there are 4 registers for 32 pins. | 27 | * so there are 4 registers for 32 pins. |
@@ -135,6 +138,20 @@ static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned gpio, int value) | |||
135 | spin_unlock_irqrestore(&gchip->lock, flags); | 138 | spin_unlock_irqrestore(&gchip->lock, flags); |
136 | } | 139 | } |
137 | 140 | ||
141 | static int mb86s70_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) | ||
142 | { | ||
143 | int irq, index; | ||
144 | |||
145 | for (index = 0;; index++) { | ||
146 | irq = platform_get_irq(to_platform_device(gc->parent), index); | ||
147 | if (irq <= 0) | ||
148 | break; | ||
149 | if (irq_get_irq_data(irq)->hwirq == offset) | ||
150 | return irq; | ||
151 | } | ||
152 | return -EINVAL; | ||
153 | } | ||
154 | |||
138 | static int mb86s70_gpio_probe(struct platform_device *pdev) | 155 | static int mb86s70_gpio_probe(struct platform_device *pdev) |
139 | { | 156 | { |
140 | struct mb86s70_gpio_chip *gchip; | 157 | struct mb86s70_gpio_chip *gchip; |
@@ -150,13 +167,15 @@ static int mb86s70_gpio_probe(struct platform_device *pdev) | |||
150 | if (IS_ERR(gchip->base)) | 167 | if (IS_ERR(gchip->base)) |
151 | return PTR_ERR(gchip->base); | 168 | return PTR_ERR(gchip->base); |
152 | 169 | ||
153 | gchip->clk = devm_clk_get(&pdev->dev, NULL); | 170 | if (!has_acpi_companion(&pdev->dev)) { |
154 | if (IS_ERR(gchip->clk)) | 171 | gchip->clk = devm_clk_get(&pdev->dev, NULL); |
155 | return PTR_ERR(gchip->clk); | 172 | if (IS_ERR(gchip->clk)) |
173 | return PTR_ERR(gchip->clk); | ||
156 | 174 | ||
157 | ret = clk_prepare_enable(gchip->clk); | 175 | ret = clk_prepare_enable(gchip->clk); |
158 | if (ret) | 176 | if (ret) |
159 | return ret; | 177 | return ret; |
178 | } | ||
160 | 179 | ||
161 | spin_lock_init(&gchip->lock); | 180 | spin_lock_init(&gchip->lock); |
162 | 181 | ||
@@ -172,19 +191,28 @@ static int mb86s70_gpio_probe(struct platform_device *pdev) | |||
172 | gchip->gc.parent = &pdev->dev; | 191 | gchip->gc.parent = &pdev->dev; |
173 | gchip->gc.base = -1; | 192 | gchip->gc.base = -1; |
174 | 193 | ||
194 | if (has_acpi_companion(&pdev->dev)) | ||
195 | gchip->gc.to_irq = mb86s70_gpio_to_irq; | ||
196 | |||
175 | ret = gpiochip_add_data(&gchip->gc, gchip); | 197 | ret = gpiochip_add_data(&gchip->gc, gchip); |
176 | if (ret) { | 198 | if (ret) { |
177 | dev_err(&pdev->dev, "couldn't register gpio driver\n"); | 199 | dev_err(&pdev->dev, "couldn't register gpio driver\n"); |
178 | clk_disable_unprepare(gchip->clk); | 200 | clk_disable_unprepare(gchip->clk); |
201 | return ret; | ||
179 | } | 202 | } |
180 | 203 | ||
181 | return ret; | 204 | if (has_acpi_companion(&pdev->dev)) |
205 | acpi_gpiochip_request_interrupts(&gchip->gc); | ||
206 | |||
207 | return 0; | ||
182 | } | 208 | } |
183 | 209 | ||
184 | static int mb86s70_gpio_remove(struct platform_device *pdev) | 210 | static int mb86s70_gpio_remove(struct platform_device *pdev) |
185 | { | 211 | { |
186 | struct mb86s70_gpio_chip *gchip = platform_get_drvdata(pdev); | 212 | struct mb86s70_gpio_chip *gchip = platform_get_drvdata(pdev); |
187 | 213 | ||
214 | if (has_acpi_companion(&pdev->dev)) | ||
215 | acpi_gpiochip_free_interrupts(&gchip->gc); | ||
188 | gpiochip_remove(&gchip->gc); | 216 | gpiochip_remove(&gchip->gc); |
189 | clk_disable_unprepare(gchip->clk); | 217 | clk_disable_unprepare(gchip->clk); |
190 | 218 | ||
@@ -197,10 +225,19 @@ static const struct of_device_id mb86s70_gpio_dt_ids[] = { | |||
197 | }; | 225 | }; |
198 | MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids); | 226 | MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids); |
199 | 227 | ||
228 | #ifdef CONFIG_ACPI | ||
229 | static const struct acpi_device_id mb86s70_gpio_acpi_ids[] = { | ||
230 | { "SCX0007" }, | ||
231 | { /* sentinel */ } | ||
232 | }; | ||
233 | MODULE_DEVICE_TABLE(acpi, mb86s70_gpio_acpi_ids); | ||
234 | #endif | ||
235 | |||
200 | static struct platform_driver mb86s70_gpio_driver = { | 236 | static struct platform_driver mb86s70_gpio_driver = { |
201 | .driver = { | 237 | .driver = { |
202 | .name = "mb86s70-gpio", | 238 | .name = "mb86s70-gpio", |
203 | .of_match_table = mb86s70_gpio_dt_ids, | 239 | .of_match_table = mb86s70_gpio_dt_ids, |
240 | .acpi_match_table = ACPI_PTR(mb86s70_gpio_acpi_ids), | ||
204 | }, | 241 | }, |
205 | .probe = mb86s70_gpio_probe, | 242 | .probe = mb86s70_gpio_probe, |
206 | .remove = mb86s70_gpio_remove, | 243 | .remove = mb86s70_gpio_remove, |
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 659c5e0fb835..80e10f4e213a 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -15,10 +15,10 @@ config ARM_GIC_PM | |||
15 | bool | 15 | bool |
16 | depends on PM | 16 | depends on PM |
17 | select ARM_GIC | 17 | select ARM_GIC |
18 | select PM_CLK | ||
19 | 18 | ||
20 | config ARM_GIC_MAX_NR | 19 | config ARM_GIC_MAX_NR |
21 | int | 20 | int |
21 | depends on ARM_GIC | ||
22 | default 2 if ARCH_REALVIEW | 22 | default 2 if ARCH_REALVIEW |
23 | default 1 | 23 | default 1 |
24 | 24 | ||
@@ -87,6 +87,14 @@ config ALPINE_MSI | |||
87 | select PCI_MSI | 87 | select PCI_MSI |
88 | select GENERIC_IRQ_CHIP | 88 | select GENERIC_IRQ_CHIP |
89 | 89 | ||
90 | config AL_FIC | ||
91 | bool "Amazon's Annapurna Labs Fabric Interrupt Controller" | ||
92 | depends on OF || COMPILE_TEST | ||
93 | select GENERIC_IRQ_CHIP | ||
94 | select IRQ_DOMAIN | ||
95 | help | ||
96 | Support Amazon's Annapurna Labs Fabric Interrupt Controller. | ||
97 | |||
90 | config ATMEL_AIC_IRQ | 98 | config ATMEL_AIC_IRQ |
91 | bool | 99 | bool |
92 | select GENERIC_IRQ_CHIP | 100 | select GENERIC_IRQ_CHIP |
@@ -217,13 +225,26 @@ config RDA_INTC | |||
217 | select IRQ_DOMAIN | 225 | select IRQ_DOMAIN |
218 | 226 | ||
219 | config RENESAS_INTC_IRQPIN | 227 | config RENESAS_INTC_IRQPIN |
220 | bool | 228 | bool "Renesas INTC External IRQ Pin Support" if COMPILE_TEST |
221 | select IRQ_DOMAIN | 229 | select IRQ_DOMAIN |
230 | help | ||
231 | Enable support for the Renesas Interrupt Controller for external | ||
232 | interrupt pins, as found on SH/R-Mobile and R-Car Gen1 SoCs. | ||
222 | 233 | ||
223 | config RENESAS_IRQC | 234 | config RENESAS_IRQC |
224 | bool | 235 | bool "Renesas R-Mobile APE6 and R-Car IRQC support" if COMPILE_TEST |
225 | select GENERIC_IRQ_CHIP | 236 | select GENERIC_IRQ_CHIP |
226 | select IRQ_DOMAIN | 237 | select IRQ_DOMAIN |
238 | help | ||
239 | Enable support for the Renesas Interrupt Controller for external | ||
240 | devices, as found on R-Mobile APE6, R-Car Gen2, and R-Car Gen3 SoCs. | ||
241 | |||
242 | config RENESAS_RZA1_IRQC | ||
243 | bool "Renesas RZ/A1 IRQC support" if COMPILE_TEST | ||
244 | select IRQ_DOMAIN_HIERARCHY | ||
245 | help | ||
246 | Enable support for the Renesas RZ/A1 Interrupt Controller, to use up | ||
247 | to 8 external interrupts with configurable sense select. | ||
227 | 248 | ||
228 | config ST_IRQCHIP | 249 | config ST_IRQCHIP |
229 | bool | 250 | bool |
@@ -299,8 +320,11 @@ config RENESAS_H8300H_INTC | |||
299 | select IRQ_DOMAIN | 320 | select IRQ_DOMAIN |
300 | 321 | ||
301 | config RENESAS_H8S_INTC | 322 | config RENESAS_H8S_INTC |
302 | bool | 323 | bool "Renesas H8S Interrupt Controller Support" if COMPILE_TEST |
303 | select IRQ_DOMAIN | 324 | select IRQ_DOMAIN |
325 | help | ||
326 | Enable support for the Renesas H8/300 Interrupt Controller, as found | ||
327 | on Renesas H8S SoCs. | ||
304 | 328 | ||
305 | config IMX_GPCV2 | 329 | config IMX_GPCV2 |
306 | bool | 330 | bool |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 606a003a0000..8d0fcec6ab23 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -1,6 +1,7 @@ | |||
1 | # SPDX-License-Identifier: GPL-2.0 | 1 | # SPDX-License-Identifier: GPL-2.0 |
2 | obj-$(CONFIG_IRQCHIP) += irqchip.o | 2 | obj-$(CONFIG_IRQCHIP) += irqchip.o |
3 | 3 | ||
4 | obj-$(CONFIG_AL_FIC) += irq-al-fic.o | ||
4 | obj-$(CONFIG_ALPINE_MSI) += irq-alpine-msi.o | 5 | obj-$(CONFIG_ALPINE_MSI) += irq-alpine-msi.o |
5 | obj-$(CONFIG_ATH79) += irq-ath79-cpu.o | 6 | obj-$(CONFIG_ATH79) += irq-ath79-cpu.o |
6 | obj-$(CONFIG_ATH79) += irq-ath79-misc.o | 7 | obj-$(CONFIG_ATH79) += irq-ath79-misc.o |
@@ -49,6 +50,7 @@ obj-$(CONFIG_JCORE_AIC) += irq-jcore-aic.o | |||
49 | obj-$(CONFIG_RDA_INTC) += irq-rda-intc.o | 50 | obj-$(CONFIG_RDA_INTC) += irq-rda-intc.o |
50 | obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o | 51 | obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o |
51 | obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o | 52 | obj-$(CONFIG_RENESAS_IRQC) += irq-renesas-irqc.o |
53 | obj-$(CONFIG_RENESAS_RZA1_IRQC) += irq-renesas-rza1.o | ||
52 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o | 54 | obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o |
53 | obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o | 55 | obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o |
54 | obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o | 56 | obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o |
diff --git a/drivers/irqchip/irq-al-fic.c b/drivers/irqchip/irq-al-fic.c new file mode 100644 index 000000000000..1a57cee3efab --- /dev/null +++ b/drivers/irqchip/irq-al-fic.c | |||
@@ -0,0 +1,278 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
4 | */ | ||
5 | |||
6 | #include <linux/bitfield.h> | ||
7 | #include <linux/irq.h> | ||
8 | #include <linux/irqchip.h> | ||
9 | #include <linux/irqchip/chained_irq.h> | ||
10 | #include <linux/irqdomain.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/of.h> | ||
13 | #include <linux/of_address.h> | ||
14 | #include <linux/of_irq.h> | ||
15 | |||
16 | /* FIC Registers */ | ||
17 | #define AL_FIC_CAUSE 0x00 | ||
18 | #define AL_FIC_MASK 0x10 | ||
19 | #define AL_FIC_CONTROL 0x28 | ||
20 | |||
21 | #define CONTROL_TRIGGER_RISING BIT(3) | ||
22 | #define CONTROL_MASK_MSI_X BIT(5) | ||
23 | |||
24 | #define NR_FIC_IRQS 32 | ||
25 | |||
26 | MODULE_AUTHOR("Talel Shenhar"); | ||
27 | MODULE_DESCRIPTION("Amazon's Annapurna Labs Interrupt Controller Driver"); | ||
28 | MODULE_LICENSE("GPL v2"); | ||
29 | |||
30 | enum al_fic_state { | ||
31 | AL_FIC_UNCONFIGURED = 0, | ||
32 | AL_FIC_CONFIGURED_LEVEL, | ||
33 | AL_FIC_CONFIGURED_RISING_EDGE, | ||
34 | }; | ||
35 | |||
36 | struct al_fic { | ||
37 | void __iomem *base; | ||
38 | struct irq_domain *domain; | ||
39 | const char *name; | ||
40 | unsigned int parent_irq; | ||
41 | enum al_fic_state state; | ||
42 | }; | ||
43 | |||
44 | static void al_fic_set_trigger(struct al_fic *fic, | ||
45 | struct irq_chip_generic *gc, | ||
46 | enum al_fic_state new_state) | ||
47 | { | ||
48 | irq_flow_handler_t handler; | ||
49 | u32 control = readl_relaxed(fic->base + AL_FIC_CONTROL); | ||
50 | |||
51 | if (new_state == AL_FIC_CONFIGURED_LEVEL) { | ||
52 | handler = handle_level_irq; | ||
53 | control &= ~CONTROL_TRIGGER_RISING; | ||
54 | } else { | ||
55 | handler = handle_edge_irq; | ||
56 | control |= CONTROL_TRIGGER_RISING; | ||
57 | } | ||
58 | gc->chip_types->handler = handler; | ||
59 | fic->state = new_state; | ||
60 | writel_relaxed(control, fic->base + AL_FIC_CONTROL); | ||
61 | } | ||
62 | |||
63 | static int al_fic_irq_set_type(struct irq_data *data, unsigned int flow_type) | ||
64 | { | ||
65 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); | ||
66 | struct al_fic *fic = gc->private; | ||
67 | enum al_fic_state new_state; | ||
68 | int ret = 0; | ||
69 | |||
70 | irq_gc_lock(gc); | ||
71 | |||
72 | if (((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) && | ||
73 | ((flow_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING)) { | ||
74 | pr_debug("fic doesn't support flow type %d\n", flow_type); | ||
75 | ret = -EINVAL; | ||
76 | goto err; | ||
77 | } | ||
78 | |||
79 | new_state = (flow_type & IRQ_TYPE_LEVEL_HIGH) ? | ||
80 | AL_FIC_CONFIGURED_LEVEL : AL_FIC_CONFIGURED_RISING_EDGE; | ||
81 | |||
82 | /* | ||
83 | * A given FIC instance can be either all level or all edge triggered. | ||
84 | * This is generally fixed depending on what pieces of HW it's wired up | ||
85 | * to. | ||
86 | * | ||
87 | * We configure it based on the sensitivity of the first source | ||
88 | * being setup, and reject any subsequent attempt at configuring it in a | ||
89 | * different way. | ||
90 | */ | ||
91 | if (fic->state == AL_FIC_UNCONFIGURED) { | ||
92 | al_fic_set_trigger(fic, gc, new_state); | ||
93 | } else if (fic->state != new_state) { | ||
94 | pr_debug("fic %s state already configured to %d\n", | ||
95 | fic->name, fic->state); | ||
96 | ret = -EINVAL; | ||
97 | goto err; | ||
98 | } | ||
99 | |||
100 | err: | ||
101 | irq_gc_unlock(gc); | ||
102 | |||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | static void al_fic_irq_handler(struct irq_desc *desc) | ||
107 | { | ||
108 | struct al_fic *fic = irq_desc_get_handler_data(desc); | ||
109 | struct irq_domain *domain = fic->domain; | ||
110 | struct irq_chip *irqchip = irq_desc_get_chip(desc); | ||
111 | struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); | ||
112 | unsigned long pending; | ||
113 | unsigned int irq; | ||
114 | u32 hwirq; | ||
115 | |||
116 | chained_irq_enter(irqchip, desc); | ||
117 | |||
118 | pending = readl_relaxed(fic->base + AL_FIC_CAUSE); | ||
119 | pending &= ~gc->mask_cache; | ||
120 | |||
121 | for_each_set_bit(hwirq, &pending, NR_FIC_IRQS) { | ||
122 | irq = irq_find_mapping(domain, hwirq); | ||
123 | generic_handle_irq(irq); | ||
124 | } | ||
125 | |||
126 | chained_irq_exit(irqchip, desc); | ||
127 | } | ||
128 | |||
129 | static int al_fic_register(struct device_node *node, | ||
130 | struct al_fic *fic) | ||
131 | { | ||
132 | struct irq_chip_generic *gc; | ||
133 | int ret; | ||
134 | |||
135 | fic->domain = irq_domain_add_linear(node, | ||
136 | NR_FIC_IRQS, | ||
137 | &irq_generic_chip_ops, | ||
138 | fic); | ||
139 | if (!fic->domain) { | ||
140 | pr_err("fail to add irq domain\n"); | ||
141 | return -ENOMEM; | ||
142 | } | ||
143 | |||
144 | ret = irq_alloc_domain_generic_chips(fic->domain, | ||
145 | NR_FIC_IRQS, | ||
146 | 1, fic->name, | ||
147 | handle_level_irq, | ||
148 | 0, 0, IRQ_GC_INIT_MASK_CACHE); | ||
149 | if (ret) { | ||
150 | pr_err("fail to allocate generic chip (%d)\n", ret); | ||
151 | goto err_domain_remove; | ||
152 | } | ||
153 | |||
154 | gc = irq_get_domain_generic_chip(fic->domain, 0); | ||
155 | gc->reg_base = fic->base; | ||
156 | gc->chip_types->regs.mask = AL_FIC_MASK; | ||
157 | gc->chip_types->regs.ack = AL_FIC_CAUSE; | ||
158 | gc->chip_types->chip.irq_mask = irq_gc_mask_set_bit; | ||
159 | gc->chip_types->chip.irq_unmask = irq_gc_mask_clr_bit; | ||
160 | gc->chip_types->chip.irq_ack = irq_gc_ack_clr_bit; | ||
161 | gc->chip_types->chip.irq_set_type = al_fic_irq_set_type; | ||
162 | gc->chip_types->chip.flags = IRQCHIP_SKIP_SET_WAKE; | ||
163 | gc->private = fic; | ||
164 | |||
165 | irq_set_chained_handler_and_data(fic->parent_irq, | ||
166 | al_fic_irq_handler, | ||
167 | fic); | ||
168 | return 0; | ||
169 | |||
170 | err_domain_remove: | ||
171 | irq_domain_remove(fic->domain); | ||
172 | |||
173 | return ret; | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * al_fic_wire_init() - initialize and configure fic in wire mode | ||
178 | * @of_node: optional pointer to interrupt controller's device tree node. | ||
179 | * @base: mmio to fic register | ||
180 | * @name: name of the fic | ||
181 | * @parent_irq: interrupt of parent | ||
182 | * | ||
183 | * This API will configure the fic hardware to to work in wire mode. | ||
184 | * In wire mode, fic hardware is generating a wire ("wired") interrupt. | ||
185 | * Interrupt can be generated based on positive edge or level - configuration is | ||
186 | * to be determined based on connected hardware to this fic. | ||
187 | */ | ||
188 | static struct al_fic *al_fic_wire_init(struct device_node *node, | ||
189 | void __iomem *base, | ||
190 | const char *name, | ||
191 | unsigned int parent_irq) | ||
192 | { | ||
193 | struct al_fic *fic; | ||
194 | int ret; | ||
195 | u32 control = CONTROL_MASK_MSI_X; | ||
196 | |||
197 | fic = kzalloc(sizeof(*fic), GFP_KERNEL); | ||
198 | if (!fic) | ||
199 | return ERR_PTR(-ENOMEM); | ||
200 | |||
201 | fic->base = base; | ||
202 | fic->parent_irq = parent_irq; | ||
203 | fic->name = name; | ||
204 | |||
205 | /* mask out all interrupts */ | ||
206 | writel_relaxed(0xFFFFFFFF, fic->base + AL_FIC_MASK); | ||
207 | |||
208 | /* clear any pending interrupt */ | ||
209 | writel_relaxed(0, fic->base + AL_FIC_CAUSE); | ||
210 | |||
211 | writel_relaxed(control, fic->base + AL_FIC_CONTROL); | ||
212 | |||
213 | ret = al_fic_register(node, fic); | ||
214 | if (ret) { | ||
215 | pr_err("fail to register irqchip\n"); | ||
216 | goto err_free; | ||
217 | } | ||
218 | |||
219 | pr_debug("%s initialized successfully in Legacy mode (parent-irq=%u)\n", | ||
220 | fic->name, parent_irq); | ||
221 | |||
222 | return fic; | ||
223 | |||
224 | err_free: | ||
225 | kfree(fic); | ||
226 | return ERR_PTR(ret); | ||
227 | } | ||
228 | |||
229 | static int __init al_fic_init_dt(struct device_node *node, | ||
230 | struct device_node *parent) | ||
231 | { | ||
232 | int ret; | ||
233 | void __iomem *base; | ||
234 | unsigned int parent_irq; | ||
235 | struct al_fic *fic; | ||
236 | |||
237 | if (!parent) { | ||
238 | pr_err("%s: unsupported - device require a parent\n", | ||
239 | node->name); | ||
240 | return -EINVAL; | ||
241 | } | ||
242 | |||
243 | base = of_iomap(node, 0); | ||
244 | if (!base) { | ||
245 | pr_err("%s: fail to map memory\n", node->name); | ||
246 | return -ENOMEM; | ||
247 | } | ||
248 | |||
249 | parent_irq = irq_of_parse_and_map(node, 0); | ||
250 | if (!parent_irq) { | ||
251 | pr_err("%s: fail to map irq\n", node->name); | ||
252 | ret = -EINVAL; | ||
253 | goto err_unmap; | ||
254 | } | ||
255 | |||
256 | fic = al_fic_wire_init(node, | ||
257 | base, | ||
258 | node->name, | ||
259 | parent_irq); | ||
260 | if (IS_ERR(fic)) { | ||
261 | pr_err("%s: fail to initialize irqchip (%lu)\n", | ||
262 | node->name, | ||
263 | PTR_ERR(fic)); | ||
264 | ret = PTR_ERR(fic); | ||
265 | goto err_irq_dispose; | ||
266 | } | ||
267 | |||
268 | return 0; | ||
269 | |||
270 | err_irq_dispose: | ||
271 | irq_dispose_mapping(parent_irq); | ||
272 | err_unmap: | ||
273 | iounmap(base); | ||
274 | |||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | IRQCHIP_DECLARE(al_fic, "amazon,al-fic", al_fic_init_dt); | ||
diff --git a/drivers/irqchip/irq-csky-mpintc.c b/drivers/irqchip/irq-csky-mpintc.c index a4c1aacba1ff..a1534edef7fa 100644 --- a/drivers/irqchip/irq-csky-mpintc.c +++ b/drivers/irqchip/irq-csky-mpintc.c | |||
@@ -32,8 +32,8 @@ static void __iomem *INTCL_base; | |||
32 | #define INTCG_CIDSTR 0x1000 | 32 | #define INTCG_CIDSTR 0x1000 |
33 | 33 | ||
34 | #define INTCL_PICTLR 0x0 | 34 | #define INTCL_PICTLR 0x0 |
35 | #define INTCL_CFGR 0x14 | ||
35 | #define INTCL_SIGR 0x60 | 36 | #define INTCL_SIGR 0x60 |
36 | #define INTCL_HPPIR 0x68 | ||
37 | #define INTCL_RDYIR 0x6c | 37 | #define INTCL_RDYIR 0x6c |
38 | #define INTCL_SENR 0xa0 | 38 | #define INTCL_SENR 0xa0 |
39 | #define INTCL_CENR 0xa4 | 39 | #define INTCL_CENR 0xa4 |
@@ -41,21 +41,49 @@ static void __iomem *INTCL_base; | |||
41 | 41 | ||
42 | static DEFINE_PER_CPU(void __iomem *, intcl_reg); | 42 | static DEFINE_PER_CPU(void __iomem *, intcl_reg); |
43 | 43 | ||
44 | static unsigned long *__trigger; | ||
45 | |||
46 | #define IRQ_OFFSET(irq) ((irq < COMM_IRQ_BASE) ? irq : (irq - COMM_IRQ_BASE)) | ||
47 | |||
48 | #define TRIG_BYTE_OFFSET(i) ((((i) * 2) / 32) * 4) | ||
49 | #define TRIG_BIT_OFFSET(i) (((i) * 2) % 32) | ||
50 | |||
51 | #define TRIG_VAL(trigger, irq) (trigger << TRIG_BIT_OFFSET(IRQ_OFFSET(irq))) | ||
52 | #define TRIG_VAL_MSK(irq) (~(3 << TRIG_BIT_OFFSET(IRQ_OFFSET(irq)))) | ||
53 | |||
54 | #define TRIG_BASE(irq) \ | ||
55 | (TRIG_BYTE_OFFSET(IRQ_OFFSET(irq)) + ((irq < COMM_IRQ_BASE) ? \ | ||
56 | (this_cpu_read(intcl_reg) + INTCL_CFGR) : (INTCG_base + INTCG_CICFGR))) | ||
57 | |||
58 | static DEFINE_SPINLOCK(setup_lock); | ||
59 | static void setup_trigger(unsigned long irq, unsigned long trigger) | ||
60 | { | ||
61 | unsigned int tmp; | ||
62 | |||
63 | spin_lock(&setup_lock); | ||
64 | |||
65 | /* setup trigger */ | ||
66 | tmp = readl_relaxed(TRIG_BASE(irq)) & TRIG_VAL_MSK(irq); | ||
67 | |||
68 | writel_relaxed(tmp | TRIG_VAL(trigger, irq), TRIG_BASE(irq)); | ||
69 | |||
70 | spin_unlock(&setup_lock); | ||
71 | } | ||
72 | |||
44 | static void csky_mpintc_handler(struct pt_regs *regs) | 73 | static void csky_mpintc_handler(struct pt_regs *regs) |
45 | { | 74 | { |
46 | void __iomem *reg_base = this_cpu_read(intcl_reg); | 75 | void __iomem *reg_base = this_cpu_read(intcl_reg); |
47 | 76 | ||
48 | do { | 77 | handle_domain_irq(root_domain, |
49 | handle_domain_irq(root_domain, | 78 | readl_relaxed(reg_base + INTCL_RDYIR), regs); |
50 | readl_relaxed(reg_base + INTCL_RDYIR), | ||
51 | regs); | ||
52 | } while (readl_relaxed(reg_base + INTCL_HPPIR) & BIT(31)); | ||
53 | } | 79 | } |
54 | 80 | ||
55 | static void csky_mpintc_enable(struct irq_data *d) | 81 | static void csky_mpintc_enable(struct irq_data *d) |
56 | { | 82 | { |
57 | void __iomem *reg_base = this_cpu_read(intcl_reg); | 83 | void __iomem *reg_base = this_cpu_read(intcl_reg); |
58 | 84 | ||
85 | setup_trigger(d->hwirq, __trigger[d->hwirq]); | ||
86 | |||
59 | writel_relaxed(d->hwirq, reg_base + INTCL_SENR); | 87 | writel_relaxed(d->hwirq, reg_base + INTCL_SENR); |
60 | } | 88 | } |
61 | 89 | ||
@@ -73,6 +101,28 @@ static void csky_mpintc_eoi(struct irq_data *d) | |||
73 | writel_relaxed(d->hwirq, reg_base + INTCL_CACR); | 101 | writel_relaxed(d->hwirq, reg_base + INTCL_CACR); |
74 | } | 102 | } |
75 | 103 | ||
104 | static int csky_mpintc_set_type(struct irq_data *d, unsigned int type) | ||
105 | { | ||
106 | switch (type & IRQ_TYPE_SENSE_MASK) { | ||
107 | case IRQ_TYPE_LEVEL_HIGH: | ||
108 | __trigger[d->hwirq] = 0; | ||
109 | break; | ||
110 | case IRQ_TYPE_LEVEL_LOW: | ||
111 | __trigger[d->hwirq] = 1; | ||
112 | break; | ||
113 | case IRQ_TYPE_EDGE_RISING: | ||
114 | __trigger[d->hwirq] = 2; | ||
115 | break; | ||
116 | case IRQ_TYPE_EDGE_FALLING: | ||
117 | __trigger[d->hwirq] = 3; | ||
118 | break; | ||
119 | default: | ||
120 | return -EINVAL; | ||
121 | } | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
76 | #ifdef CONFIG_SMP | 126 | #ifdef CONFIG_SMP |
77 | static int csky_irq_set_affinity(struct irq_data *d, | 127 | static int csky_irq_set_affinity(struct irq_data *d, |
78 | const struct cpumask *mask_val, | 128 | const struct cpumask *mask_val, |
@@ -116,6 +166,7 @@ static struct irq_chip csky_irq_chip = { | |||
116 | .irq_eoi = csky_mpintc_eoi, | 166 | .irq_eoi = csky_mpintc_eoi, |
117 | .irq_enable = csky_mpintc_enable, | 167 | .irq_enable = csky_mpintc_enable, |
118 | .irq_disable = csky_mpintc_disable, | 168 | .irq_disable = csky_mpintc_disable, |
169 | .irq_set_type = csky_mpintc_set_type, | ||
119 | #ifdef CONFIG_SMP | 170 | #ifdef CONFIG_SMP |
120 | .irq_set_affinity = csky_irq_set_affinity, | 171 | .irq_set_affinity = csky_irq_set_affinity, |
121 | #endif | 172 | #endif |
@@ -136,9 +187,26 @@ static int csky_irqdomain_map(struct irq_domain *d, unsigned int irq, | |||
136 | return 0; | 187 | return 0; |
137 | } | 188 | } |
138 | 189 | ||
190 | static int csky_irq_domain_xlate_cells(struct irq_domain *d, | ||
191 | struct device_node *ctrlr, const u32 *intspec, | ||
192 | unsigned int intsize, unsigned long *out_hwirq, | ||
193 | unsigned int *out_type) | ||
194 | { | ||
195 | if (WARN_ON(intsize < 1)) | ||
196 | return -EINVAL; | ||
197 | |||
198 | *out_hwirq = intspec[0]; | ||
199 | if (intsize > 1) | ||
200 | *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; | ||
201 | else | ||
202 | *out_type = IRQ_TYPE_LEVEL_HIGH; | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
139 | static const struct irq_domain_ops csky_irqdomain_ops = { | 207 | static const struct irq_domain_ops csky_irqdomain_ops = { |
140 | .map = csky_irqdomain_map, | 208 | .map = csky_irqdomain_map, |
141 | .xlate = irq_domain_xlate_onecell, | 209 | .xlate = csky_irq_domain_xlate_cells, |
142 | }; | 210 | }; |
143 | 211 | ||
144 | #ifdef CONFIG_SMP | 212 | #ifdef CONFIG_SMP |
@@ -172,6 +240,10 @@ csky_mpintc_init(struct device_node *node, struct device_node *parent) | |||
172 | if (ret < 0) | 240 | if (ret < 0) |
173 | nr_irq = INTC_IRQS; | 241 | nr_irq = INTC_IRQS; |
174 | 242 | ||
243 | __trigger = kcalloc(nr_irq, sizeof(unsigned long), GFP_KERNEL); | ||
244 | if (__trigger == NULL) | ||
245 | return -ENXIO; | ||
246 | |||
175 | if (INTCG_base == NULL) { | 247 | if (INTCG_base == NULL) { |
176 | INTCG_base = ioremap(mfcr("cr<31, 14>"), | 248 | INTCG_base = ioremap(mfcr("cr<31, 14>"), |
177 | INTCL_SIZE*nr_cpu_ids + INTCG_SIZE); | 249 | INTCL_SIZE*nr_cpu_ids + INTCG_SIZE); |
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 875ac80f690b..7338f90b2f9e 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c | |||
@@ -53,6 +53,7 @@ | |||
53 | 53 | ||
54 | /* List of flags for specific v2m implementation */ | 54 | /* List of flags for specific v2m implementation */ |
55 | #define GICV2M_NEEDS_SPI_OFFSET 0x00000001 | 55 | #define GICV2M_NEEDS_SPI_OFFSET 0x00000001 |
56 | #define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002 | ||
56 | 57 | ||
57 | static LIST_HEAD(v2m_nodes); | 58 | static LIST_HEAD(v2m_nodes); |
58 | static DEFINE_SPINLOCK(v2m_lock); | 59 | static DEFINE_SPINLOCK(v2m_lock); |
@@ -95,15 +96,26 @@ static struct msi_domain_info gicv2m_msi_domain_info = { | |||
95 | .chip = &gicv2m_msi_irq_chip, | 96 | .chip = &gicv2m_msi_irq_chip, |
96 | }; | 97 | }; |
97 | 98 | ||
99 | static phys_addr_t gicv2m_get_msi_addr(struct v2m_data *v2m, int hwirq) | ||
100 | { | ||
101 | if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) | ||
102 | return v2m->res.start | ((hwirq - 32) << 3); | ||
103 | else | ||
104 | return v2m->res.start + V2M_MSI_SETSPI_NS; | ||
105 | } | ||
106 | |||
98 | static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) | 107 | static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) |
99 | { | 108 | { |
100 | struct v2m_data *v2m = irq_data_get_irq_chip_data(data); | 109 | struct v2m_data *v2m = irq_data_get_irq_chip_data(data); |
101 | phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS; | 110 | phys_addr_t addr = gicv2m_get_msi_addr(v2m, data->hwirq); |
102 | 111 | ||
103 | msg->address_hi = upper_32_bits(addr); | 112 | msg->address_hi = upper_32_bits(addr); |
104 | msg->address_lo = lower_32_bits(addr); | 113 | msg->address_lo = lower_32_bits(addr); |
105 | msg->data = data->hwirq; | ||
106 | 114 | ||
115 | if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) | ||
116 | msg->data = 0; | ||
117 | else | ||
118 | msg->data = data->hwirq; | ||
107 | if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) | 119 | if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) |
108 | msg->data -= v2m->spi_offset; | 120 | msg->data -= v2m->spi_offset; |
109 | 121 | ||
@@ -185,7 +197,7 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |||
185 | hwirq = v2m->spi_start + offset; | 197 | hwirq = v2m->spi_start + offset; |
186 | 198 | ||
187 | err = iommu_dma_prepare_msi(info->desc, | 199 | err = iommu_dma_prepare_msi(info->desc, |
188 | v2m->res.start + V2M_MSI_SETSPI_NS); | 200 | gicv2m_get_msi_addr(v2m, hwirq)); |
189 | if (err) | 201 | if (err) |
190 | return err; | 202 | return err; |
191 | 203 | ||
@@ -304,7 +316,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) | |||
304 | 316 | ||
305 | static int __init gicv2m_init_one(struct fwnode_handle *fwnode, | 317 | static int __init gicv2m_init_one(struct fwnode_handle *fwnode, |
306 | u32 spi_start, u32 nr_spis, | 318 | u32 spi_start, u32 nr_spis, |
307 | struct resource *res) | 319 | struct resource *res, u32 flags) |
308 | { | 320 | { |
309 | int ret; | 321 | int ret; |
310 | struct v2m_data *v2m; | 322 | struct v2m_data *v2m; |
@@ -317,6 +329,7 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, | |||
317 | 329 | ||
318 | INIT_LIST_HEAD(&v2m->entry); | 330 | INIT_LIST_HEAD(&v2m->entry); |
319 | v2m->fwnode = fwnode; | 331 | v2m->fwnode = fwnode; |
332 | v2m->flags = flags; | ||
320 | 333 | ||
321 | memcpy(&v2m->res, res, sizeof(struct resource)); | 334 | memcpy(&v2m->res, res, sizeof(struct resource)); |
322 | 335 | ||
@@ -331,7 +344,14 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, | |||
331 | v2m->spi_start = spi_start; | 344 | v2m->spi_start = spi_start; |
332 | v2m->nr_spis = nr_spis; | 345 | v2m->nr_spis = nr_spis; |
333 | } else { | 346 | } else { |
334 | u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); | 347 | u32 typer; |
348 | |||
349 | /* Graviton should always have explicit spi_start/nr_spis */ | ||
350 | if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) { | ||
351 | ret = -EINVAL; | ||
352 | goto err_iounmap; | ||
353 | } | ||
354 | typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); | ||
335 | 355 | ||
336 | v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); | 356 | v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); |
337 | v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer); | 357 | v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer); |
@@ -352,18 +372,21 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, | |||
352 | * | 372 | * |
353 | * Broadom NS2 GICv2m implementation has an erratum where the MSI data | 373 | * Broadom NS2 GICv2m implementation has an erratum where the MSI data |
354 | * is 'spi_number - 32' | 374 | * is 'spi_number - 32' |
375 | * | ||
376 | * Reading that register fails on the Graviton implementation | ||
355 | */ | 377 | */ |
356 | switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) { | 378 | if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) { |
357 | case XGENE_GICV2M_MSI_IIDR: | 379 | switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) { |
358 | v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; | 380 | case XGENE_GICV2M_MSI_IIDR: |
359 | v2m->spi_offset = v2m->spi_start; | 381 | v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; |
360 | break; | 382 | v2m->spi_offset = v2m->spi_start; |
361 | case BCM_NS2_GICV2M_MSI_IIDR: | 383 | break; |
362 | v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; | 384 | case BCM_NS2_GICV2M_MSI_IIDR: |
363 | v2m->spi_offset = 32; | 385 | v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; |
364 | break; | 386 | v2m->spi_offset = 32; |
387 | break; | ||
388 | } | ||
365 | } | 389 | } |
366 | |||
367 | v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long), | 390 | v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long), |
368 | GFP_KERNEL); | 391 | GFP_KERNEL); |
369 | if (!v2m->bm) { | 392 | if (!v2m->bm) { |
@@ -416,7 +439,8 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, | |||
416 | pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n", | 439 | pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n", |
417 | spi_start, nr_spis); | 440 | spi_start, nr_spis); |
418 | 441 | ||
419 | ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, &res); | 442 | ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, |
443 | &res, 0); | ||
420 | if (ret) { | 444 | if (ret) { |
421 | of_node_put(child); | 445 | of_node_put(child); |
422 | break; | 446 | break; |
@@ -448,6 +472,25 @@ static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) | |||
448 | return data->fwnode; | 472 | return data->fwnode; |
449 | } | 473 | } |
450 | 474 | ||
475 | static bool acpi_check_amazon_graviton_quirks(void) | ||
476 | { | ||
477 | static struct acpi_table_madt *madt; | ||
478 | acpi_status status; | ||
479 | bool rc = false; | ||
480 | |||
481 | #define ACPI_AMZN_OEM_ID "AMAZON" | ||
482 | |||
483 | status = acpi_get_table(ACPI_SIG_MADT, 0, | ||
484 | (struct acpi_table_header **)&madt); | ||
485 | |||
486 | if (ACPI_FAILURE(status) || !madt) | ||
487 | return rc; | ||
488 | rc = !memcmp(madt->header.oem_id, ACPI_AMZN_OEM_ID, ACPI_OEM_ID_SIZE); | ||
489 | acpi_put_table((struct acpi_table_header *)madt); | ||
490 | |||
491 | return rc; | ||
492 | } | ||
493 | |||
451 | static int __init | 494 | static int __init |
452 | acpi_parse_madt_msi(union acpi_subtable_headers *header, | 495 | acpi_parse_madt_msi(union acpi_subtable_headers *header, |
453 | const unsigned long end) | 496 | const unsigned long end) |
@@ -457,6 +500,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, | |||
457 | u32 spi_start = 0, nr_spis = 0; | 500 | u32 spi_start = 0, nr_spis = 0; |
458 | struct acpi_madt_generic_msi_frame *m; | 501 | struct acpi_madt_generic_msi_frame *m; |
459 | struct fwnode_handle *fwnode; | 502 | struct fwnode_handle *fwnode; |
503 | u32 flags = 0; | ||
460 | 504 | ||
461 | m = (struct acpi_madt_generic_msi_frame *)header; | 505 | m = (struct acpi_madt_generic_msi_frame *)header; |
462 | if (BAD_MADT_ENTRY(m, end)) | 506 | if (BAD_MADT_ENTRY(m, end)) |
@@ -466,6 +510,13 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, | |||
466 | res.end = m->base_address + SZ_4K - 1; | 510 | res.end = m->base_address + SZ_4K - 1; |
467 | res.flags = IORESOURCE_MEM; | 511 | res.flags = IORESOURCE_MEM; |
468 | 512 | ||
513 | if (acpi_check_amazon_graviton_quirks()) { | ||
514 | pr_info("applying Amazon Graviton quirk\n"); | ||
515 | res.end = res.start + SZ_8K - 1; | ||
516 | flags |= GICV2M_GRAVITON_ADDRESS_ONLY; | ||
517 | gicv2m_msi_domain_info.flags &= ~MSI_FLAG_MULTI_PCI_MSI; | ||
518 | } | ||
519 | |||
469 | if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { | 520 | if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { |
470 | spi_start = m->spi_base; | 521 | spi_start = m->spi_base; |
471 | nr_spis = m->spi_count; | 522 | nr_spis = m->spi_count; |
@@ -480,7 +531,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, | |||
480 | return -EINVAL; | 531 | return -EINVAL; |
481 | } | 532 | } |
482 | 533 | ||
483 | ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res); | 534 | ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res, flags); |
484 | if (ret) | 535 | if (ret) |
485 | irq_domain_free_fwnode(fwnode); | 536 | irq_domain_free_fwnode(fwnode); |
486 | 537 | ||
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index f3e44d6d9255..9bca4896fa6f 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c | |||
@@ -1339,6 +1339,9 @@ static int __init gic_init_bases(void __iomem *dist_base, | |||
1339 | if (gic_dist_supports_lpis()) { | 1339 | if (gic_dist_supports_lpis()) { |
1340 | its_init(handle, &gic_data.rdists, gic_data.domain); | 1340 | its_init(handle, &gic_data.rdists, gic_data.domain); |
1341 | its_cpu_init(); | 1341 | its_cpu_init(); |
1342 | } else { | ||
1343 | if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) | ||
1344 | gicv2m_init(handle, gic_data.domain); | ||
1342 | } | 1345 | } |
1343 | 1346 | ||
1344 | if (gic_prio_masking_enabled()) { | 1347 | if (gic_prio_masking_enabled()) { |
diff --git a/drivers/irqchip/irq-mbigen.c b/drivers/irqchip/irq-mbigen.c index a89c693d5b90..3dd28382d5f5 100644 --- a/drivers/irqchip/irq-mbigen.c +++ b/drivers/irqchip/irq-mbigen.c | |||
@@ -344,8 +344,7 @@ static int mbigen_device_probe(struct platform_device *pdev) | |||
344 | err = -EINVAL; | 344 | err = -EINVAL; |
345 | 345 | ||
346 | if (err) { | 346 | if (err) { |
347 | dev_err(&pdev->dev, "Failed to create mbi-gen@%p irqdomain", | 347 | dev_err(&pdev->dev, "Failed to create mbi-gen irqdomain\n"); |
348 | mgn_chip->base); | ||
349 | return err; | 348 | return err; |
350 | } | 349 | } |
351 | 350 | ||
diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c index 8eb92eb98f54..dcdc23b9dce6 100644 --- a/drivers/irqchip/irq-meson-gpio.c +++ b/drivers/irqchip/irq-meson-gpio.c | |||
@@ -60,6 +60,7 @@ static const struct of_device_id meson_irq_gpio_matches[] = { | |||
60 | { .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params }, | 60 | { .compatible = "amlogic,meson-gxbb-gpio-intc", .data = &gxbb_params }, |
61 | { .compatible = "amlogic,meson-gxl-gpio-intc", .data = &gxl_params }, | 61 | { .compatible = "amlogic,meson-gxl-gpio-intc", .data = &gxl_params }, |
62 | { .compatible = "amlogic,meson-axg-gpio-intc", .data = &axg_params }, | 62 | { .compatible = "amlogic,meson-axg-gpio-intc", .data = &axg_params }, |
63 | { .compatible = "amlogic,meson-g12a-gpio-intc", .data = &axg_params }, | ||
63 | { } | 64 | { } |
64 | }; | 65 | }; |
65 | 66 | ||
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 04c05a18600c..f82bc60a6793 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c | |||
@@ -508,7 +508,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
508 | } | 508 | } |
509 | 509 | ||
510 | irq_chip = &p->irq_chip; | 510 | irq_chip = &p->irq_chip; |
511 | irq_chip->name = name; | 511 | irq_chip->name = "intc-irqpin"; |
512 | irq_chip->parent_device = dev; | ||
512 | irq_chip->irq_mask = disable_fn; | 513 | irq_chip->irq_mask = disable_fn; |
513 | irq_chip->irq_unmask = enable_fn; | 514 | irq_chip->irq_unmask = enable_fn; |
514 | irq_chip->irq_set_type = intc_irqpin_irq_set_type; | 515 | irq_chip->irq_set_type = intc_irqpin_irq_set_type; |
diff --git a/drivers/irqchip/irq-renesas-irqc.c b/drivers/irqchip/irq-renesas-irqc.c index a449a7c839b3..11abc09ef76c 100644 --- a/drivers/irqchip/irq-renesas-irqc.c +++ b/drivers/irqchip/irq-renesas-irqc.c | |||
@@ -7,7 +7,6 @@ | |||
7 | 7 | ||
8 | #include <linux/init.h> | 8 | #include <linux/init.h> |
9 | #include <linux/platform_device.h> | 9 | #include <linux/platform_device.h> |
10 | #include <linux/spinlock.h> | ||
11 | #include <linux/interrupt.h> | 10 | #include <linux/interrupt.h> |
12 | #include <linux/ioport.h> | 11 | #include <linux/ioport.h> |
13 | #include <linux/io.h> | 12 | #include <linux/io.h> |
@@ -48,7 +47,7 @@ struct irqc_priv { | |||
48 | void __iomem *cpu_int_base; | 47 | void __iomem *cpu_int_base; |
49 | struct irqc_irq irq[IRQC_IRQ_MAX]; | 48 | struct irqc_irq irq[IRQC_IRQ_MAX]; |
50 | unsigned int number_of_irqs; | 49 | unsigned int number_of_irqs; |
51 | struct platform_device *pdev; | 50 | struct device *dev; |
52 | struct irq_chip_generic *gc; | 51 | struct irq_chip_generic *gc; |
53 | struct irq_domain *irq_domain; | 52 | struct irq_domain *irq_domain; |
54 | atomic_t wakeup_path; | 53 | atomic_t wakeup_path; |
@@ -61,8 +60,7 @@ static struct irqc_priv *irq_data_to_priv(struct irq_data *data) | |||
61 | 60 | ||
62 | static void irqc_dbg(struct irqc_irq *i, char *str) | 61 | static void irqc_dbg(struct irqc_irq *i, char *str) |
63 | { | 62 | { |
64 | dev_dbg(&i->p->pdev->dev, "%s (%d:%d)\n", | 63 | dev_dbg(i->p->dev, "%s (%d:%d)\n", str, i->requested_irq, i->hw_irq); |
65 | str, i->requested_irq, i->hw_irq); | ||
66 | } | 64 | } |
67 | 65 | ||
68 | static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { | 66 | static unsigned char irqc_sense[IRQ_TYPE_SENSE_MASK + 1] = { |
@@ -125,33 +123,22 @@ static irqreturn_t irqc_irq_handler(int irq, void *dev_id) | |||
125 | 123 | ||
126 | static int irqc_probe(struct platform_device *pdev) | 124 | static int irqc_probe(struct platform_device *pdev) |
127 | { | 125 | { |
126 | struct device *dev = &pdev->dev; | ||
127 | const char *name = dev_name(dev); | ||
128 | struct irqc_priv *p; | 128 | struct irqc_priv *p; |
129 | struct resource *io; | ||
130 | struct resource *irq; | 129 | struct resource *irq; |
131 | const char *name = dev_name(&pdev->dev); | ||
132 | int ret; | 130 | int ret; |
133 | int k; | 131 | int k; |
134 | 132 | ||
135 | p = kzalloc(sizeof(*p), GFP_KERNEL); | 133 | p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); |
136 | if (!p) { | 134 | if (!p) |
137 | dev_err(&pdev->dev, "failed to allocate driver data\n"); | 135 | return -ENOMEM; |
138 | ret = -ENOMEM; | ||
139 | goto err0; | ||
140 | } | ||
141 | 136 | ||
142 | p->pdev = pdev; | 137 | p->dev = dev; |
143 | platform_set_drvdata(pdev, p); | 138 | platform_set_drvdata(pdev, p); |
144 | 139 | ||
145 | pm_runtime_enable(&pdev->dev); | 140 | pm_runtime_enable(dev); |
146 | pm_runtime_get_sync(&pdev->dev); | 141 | pm_runtime_get_sync(dev); |
147 | |||
148 | /* get hold of manadatory IOMEM */ | ||
149 | io = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
150 | if (!io) { | ||
151 | dev_err(&pdev->dev, "not enough IOMEM resources\n"); | ||
152 | ret = -EINVAL; | ||
153 | goto err1; | ||
154 | } | ||
155 | 142 | ||
156 | /* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ | 143 | /* allow any number of IRQs between 1 and IRQC_IRQ_MAX */ |
157 | for (k = 0; k < IRQC_IRQ_MAX; k++) { | 144 | for (k = 0; k < IRQC_IRQ_MAX; k++) { |
@@ -166,42 +153,41 @@ static int irqc_probe(struct platform_device *pdev) | |||
166 | 153 | ||
167 | p->number_of_irqs = k; | 154 | p->number_of_irqs = k; |
168 | if (p->number_of_irqs < 1) { | 155 | if (p->number_of_irqs < 1) { |
169 | dev_err(&pdev->dev, "not enough IRQ resources\n"); | 156 | dev_err(dev, "not enough IRQ resources\n"); |
170 | ret = -EINVAL; | 157 | ret = -EINVAL; |
171 | goto err1; | 158 | goto err_runtime_pm_disable; |
172 | } | 159 | } |
173 | 160 | ||
174 | /* ioremap IOMEM and setup read/write callbacks */ | 161 | /* ioremap IOMEM and setup read/write callbacks */ |
175 | p->iomem = ioremap_nocache(io->start, resource_size(io)); | 162 | p->iomem = devm_platform_ioremap_resource(pdev, 0); |
176 | if (!p->iomem) { | 163 | if (IS_ERR(p->iomem)) { |
177 | dev_err(&pdev->dev, "failed to remap IOMEM\n"); | 164 | ret = PTR_ERR(p->iomem); |
178 | ret = -ENXIO; | 165 | goto err_runtime_pm_disable; |
179 | goto err2; | ||
180 | } | 166 | } |
181 | 167 | ||
182 | p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ | 168 | p->cpu_int_base = p->iomem + IRQC_INT_CPU_BASE(0); /* SYS-SPI */ |
183 | 169 | ||
184 | p->irq_domain = irq_domain_add_linear(pdev->dev.of_node, | 170 | p->irq_domain = irq_domain_add_linear(dev->of_node, p->number_of_irqs, |
185 | p->number_of_irqs, | ||
186 | &irq_generic_chip_ops, p); | 171 | &irq_generic_chip_ops, p); |
187 | if (!p->irq_domain) { | 172 | if (!p->irq_domain) { |
188 | ret = -ENXIO; | 173 | ret = -ENXIO; |
189 | dev_err(&pdev->dev, "cannot initialize irq domain\n"); | 174 | dev_err(dev, "cannot initialize irq domain\n"); |
190 | goto err2; | 175 | goto err_runtime_pm_disable; |
191 | } | 176 | } |
192 | 177 | ||
193 | ret = irq_alloc_domain_generic_chips(p->irq_domain, p->number_of_irqs, | 178 | ret = irq_alloc_domain_generic_chips(p->irq_domain, p->number_of_irqs, |
194 | 1, name, handle_level_irq, | 179 | 1, "irqc", handle_level_irq, |
195 | 0, 0, IRQ_GC_INIT_NESTED_LOCK); | 180 | 0, 0, IRQ_GC_INIT_NESTED_LOCK); |
196 | if (ret) { | 181 | if (ret) { |
197 | dev_err(&pdev->dev, "cannot allocate generic chip\n"); | 182 | dev_err(dev, "cannot allocate generic chip\n"); |
198 | goto err3; | 183 | goto err_remove_domain; |
199 | } | 184 | } |
200 | 185 | ||
201 | p->gc = irq_get_domain_generic_chip(p->irq_domain, 0); | 186 | p->gc = irq_get_domain_generic_chip(p->irq_domain, 0); |
202 | p->gc->reg_base = p->cpu_int_base; | 187 | p->gc->reg_base = p->cpu_int_base; |
203 | p->gc->chip_types[0].regs.enable = IRQC_EN_SET; | 188 | p->gc->chip_types[0].regs.enable = IRQC_EN_SET; |
204 | p->gc->chip_types[0].regs.disable = IRQC_EN_STS; | 189 | p->gc->chip_types[0].regs.disable = IRQC_EN_STS; |
190 | p->gc->chip_types[0].chip.parent_device = dev; | ||
205 | p->gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; | 191 | p->gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; |
206 | p->gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; | 192 | p->gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; |
207 | p->gc->chip_types[0].chip.irq_set_type = irqc_irq_set_type; | 193 | p->gc->chip_types[0].chip.irq_set_type = irqc_irq_set_type; |
@@ -210,46 +196,33 @@ static int irqc_probe(struct platform_device *pdev) | |||
210 | 196 | ||
211 | /* request interrupts one by one */ | 197 | /* request interrupts one by one */ |
212 | for (k = 0; k < p->number_of_irqs; k++) { | 198 | for (k = 0; k < p->number_of_irqs; k++) { |
213 | if (request_irq(p->irq[k].requested_irq, irqc_irq_handler, | 199 | if (devm_request_irq(dev, p->irq[k].requested_irq, |
214 | 0, name, &p->irq[k])) { | 200 | irqc_irq_handler, 0, name, &p->irq[k])) { |
215 | dev_err(&pdev->dev, "failed to request IRQ\n"); | 201 | dev_err(dev, "failed to request IRQ\n"); |
216 | ret = -ENOENT; | 202 | ret = -ENOENT; |
217 | goto err4; | 203 | goto err_remove_domain; |
218 | } | 204 | } |
219 | } | 205 | } |
220 | 206 | ||
221 | dev_info(&pdev->dev, "driving %d irqs\n", p->number_of_irqs); | 207 | dev_info(dev, "driving %d irqs\n", p->number_of_irqs); |
222 | 208 | ||
223 | return 0; | 209 | return 0; |
224 | err4: | ||
225 | while (--k >= 0) | ||
226 | free_irq(p->irq[k].requested_irq, &p->irq[k]); | ||
227 | 210 | ||
228 | err3: | 211 | err_remove_domain: |
229 | irq_domain_remove(p->irq_domain); | 212 | irq_domain_remove(p->irq_domain); |
230 | err2: | 213 | err_runtime_pm_disable: |
231 | iounmap(p->iomem); | 214 | pm_runtime_put(dev); |
232 | err1: | 215 | pm_runtime_disable(dev); |
233 | pm_runtime_put(&pdev->dev); | ||
234 | pm_runtime_disable(&pdev->dev); | ||
235 | kfree(p); | ||
236 | err0: | ||
237 | return ret; | 216 | return ret; |
238 | } | 217 | } |
239 | 218 | ||
240 | static int irqc_remove(struct platform_device *pdev) | 219 | static int irqc_remove(struct platform_device *pdev) |
241 | { | 220 | { |
242 | struct irqc_priv *p = platform_get_drvdata(pdev); | 221 | struct irqc_priv *p = platform_get_drvdata(pdev); |
243 | int k; | ||
244 | |||
245 | for (k = 0; k < p->number_of_irqs; k++) | ||
246 | free_irq(p->irq[k].requested_irq, &p->irq[k]); | ||
247 | 222 | ||
248 | irq_domain_remove(p->irq_domain); | 223 | irq_domain_remove(p->irq_domain); |
249 | iounmap(p->iomem); | ||
250 | pm_runtime_put(&pdev->dev); | 224 | pm_runtime_put(&pdev->dev); |
251 | pm_runtime_disable(&pdev->dev); | 225 | pm_runtime_disable(&pdev->dev); |
252 | kfree(p); | ||
253 | return 0; | 226 | return 0; |
254 | } | 227 | } |
255 | 228 | ||
diff --git a/drivers/irqchip/irq-renesas-rza1.c b/drivers/irqchip/irq-renesas-rza1.c new file mode 100644 index 000000000000..b1f19b210190 --- /dev/null +++ b/drivers/irqchip/irq-renesas-rza1.c | |||
@@ -0,0 +1,283 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Renesas RZ/A1 IRQC Driver | ||
4 | * | ||
5 | * Copyright (C) 2019 Glider bvba | ||
6 | */ | ||
7 | |||
8 | #include <linux/err.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/io.h> | ||
12 | #include <linux/irqdomain.h> | ||
13 | #include <linux/irq.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/of_irq.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/slab.h> | ||
18 | |||
19 | #include <dt-bindings/interrupt-controller/arm-gic.h> | ||
20 | |||
21 | #define IRQC_NUM_IRQ 8 | ||
22 | |||
23 | #define ICR0 0 /* Interrupt Control Register 0 */ | ||
24 | |||
25 | #define ICR0_NMIL BIT(15) /* NMI Input Level (0=low, 1=high) */ | ||
26 | #define ICR0_NMIE BIT(8) /* Edge Select (0=falling, 1=rising) */ | ||
27 | #define ICR0_NMIF BIT(1) /* NMI Interrupt Request */ | ||
28 | |||
29 | #define ICR1 2 /* Interrupt Control Register 1 */ | ||
30 | |||
31 | #define ICR1_IRQS(n, sense) ((sense) << ((n) * 2)) /* IRQ Sense Select */ | ||
32 | #define ICR1_IRQS_LEVEL_LOW 0 | ||
33 | #define ICR1_IRQS_EDGE_FALLING 1 | ||
34 | #define ICR1_IRQS_EDGE_RISING 2 | ||
35 | #define ICR1_IRQS_EDGE_BOTH 3 | ||
36 | #define ICR1_IRQS_MASK(n) ICR1_IRQS((n), 3) | ||
37 | |||
38 | #define IRQRR 4 /* IRQ Interrupt Request Register */ | ||
39 | |||
40 | |||
41 | struct rza1_irqc_priv { | ||
42 | struct device *dev; | ||
43 | void __iomem *base; | ||
44 | struct irq_chip chip; | ||
45 | struct irq_domain *irq_domain; | ||
46 | struct of_phandle_args map[IRQC_NUM_IRQ]; | ||
47 | }; | ||
48 | |||
49 | static struct rza1_irqc_priv *irq_data_to_priv(struct irq_data *data) | ||
50 | { | ||
51 | return data->domain->host_data; | ||
52 | } | ||
53 | |||
54 | static void rza1_irqc_eoi(struct irq_data *d) | ||
55 | { | ||
56 | struct rza1_irqc_priv *priv = irq_data_to_priv(d); | ||
57 | u16 bit = BIT(irqd_to_hwirq(d)); | ||
58 | u16 tmp; | ||
59 | |||
60 | tmp = readw_relaxed(priv->base + IRQRR); | ||
61 | if (tmp & bit) | ||
62 | writew_relaxed(GENMASK(IRQC_NUM_IRQ - 1, 0) & ~bit, | ||
63 | priv->base + IRQRR); | ||
64 | |||
65 | irq_chip_eoi_parent(d); | ||
66 | } | ||
67 | |||
68 | static int rza1_irqc_set_type(struct irq_data *d, unsigned int type) | ||
69 | { | ||
70 | struct rza1_irqc_priv *priv = irq_data_to_priv(d); | ||
71 | unsigned int hw_irq = irqd_to_hwirq(d); | ||
72 | u16 sense, tmp; | ||
73 | |||
74 | switch (type & IRQ_TYPE_SENSE_MASK) { | ||
75 | case IRQ_TYPE_LEVEL_LOW: | ||
76 | sense = ICR1_IRQS_LEVEL_LOW; | ||
77 | break; | ||
78 | |||
79 | case IRQ_TYPE_EDGE_FALLING: | ||
80 | sense = ICR1_IRQS_EDGE_FALLING; | ||
81 | break; | ||
82 | |||
83 | case IRQ_TYPE_EDGE_RISING: | ||
84 | sense = ICR1_IRQS_EDGE_RISING; | ||
85 | break; | ||
86 | |||
87 | case IRQ_TYPE_EDGE_BOTH: | ||
88 | sense = ICR1_IRQS_EDGE_BOTH; | ||
89 | break; | ||
90 | |||
91 | default: | ||
92 | return -EINVAL; | ||
93 | } | ||
94 | |||
95 | tmp = readw_relaxed(priv->base + ICR1); | ||
96 | tmp &= ~ICR1_IRQS_MASK(hw_irq); | ||
97 | tmp |= ICR1_IRQS(hw_irq, sense); | ||
98 | writew_relaxed(tmp, priv->base + ICR1); | ||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int rza1_irqc_alloc(struct irq_domain *domain, unsigned int virq, | ||
103 | unsigned int nr_irqs, void *arg) | ||
104 | { | ||
105 | struct rza1_irqc_priv *priv = domain->host_data; | ||
106 | struct irq_fwspec *fwspec = arg; | ||
107 | unsigned int hwirq = fwspec->param[0]; | ||
108 | struct irq_fwspec spec; | ||
109 | unsigned int i; | ||
110 | int ret; | ||
111 | |||
112 | ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &priv->chip, | ||
113 | priv); | ||
114 | if (ret) | ||
115 | return ret; | ||
116 | |||
117 | spec.fwnode = &priv->dev->of_node->fwnode; | ||
118 | spec.param_count = priv->map[hwirq].args_count; | ||
119 | for (i = 0; i < spec.param_count; i++) | ||
120 | spec.param[i] = priv->map[hwirq].args[i]; | ||
121 | |||
122 | return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &spec); | ||
123 | } | ||
124 | |||
125 | static int rza1_irqc_translate(struct irq_domain *domain, | ||
126 | struct irq_fwspec *fwspec, unsigned long *hwirq, | ||
127 | unsigned int *type) | ||
128 | { | ||
129 | if (fwspec->param_count != 2 || fwspec->param[0] >= IRQC_NUM_IRQ) | ||
130 | return -EINVAL; | ||
131 | |||
132 | *hwirq = fwspec->param[0]; | ||
133 | *type = fwspec->param[1]; | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static const struct irq_domain_ops rza1_irqc_domain_ops = { | ||
138 | .alloc = rza1_irqc_alloc, | ||
139 | .translate = rza1_irqc_translate, | ||
140 | }; | ||
141 | |||
142 | static int rza1_irqc_parse_map(struct rza1_irqc_priv *priv, | ||
143 | struct device_node *gic_node) | ||
144 | { | ||
145 | unsigned int imaplen, i, j, ret; | ||
146 | struct device *dev = priv->dev; | ||
147 | struct device_node *ipar; | ||
148 | const __be32 *imap; | ||
149 | u32 intsize; | ||
150 | |||
151 | imap = of_get_property(dev->of_node, "interrupt-map", &imaplen); | ||
152 | if (!imap) | ||
153 | return -EINVAL; | ||
154 | |||
155 | for (i = 0; i < IRQC_NUM_IRQ; i++) { | ||
156 | if (imaplen < 3) | ||
157 | return -EINVAL; | ||
158 | |||
159 | /* Check interrupt number, ignore sense */ | ||
160 | if (be32_to_cpup(imap) != i) | ||
161 | return -EINVAL; | ||
162 | |||
163 | ipar = of_find_node_by_phandle(be32_to_cpup(imap + 2)); | ||
164 | if (ipar != gic_node) { | ||
165 | of_node_put(ipar); | ||
166 | return -EINVAL; | ||
167 | } | ||
168 | |||
169 | imap += 3; | ||
170 | imaplen -= 3; | ||
171 | |||
172 | ret = of_property_read_u32(ipar, "#interrupt-cells", &intsize); | ||
173 | of_node_put(ipar); | ||
174 | if (ret) | ||
175 | return ret; | ||
176 | |||
177 | if (imaplen < intsize) | ||
178 | return -EINVAL; | ||
179 | |||
180 | priv->map[i].args_count = intsize; | ||
181 | for (j = 0; j < intsize; j++) | ||
182 | priv->map[i].args[j] = be32_to_cpup(imap++); | ||
183 | |||
184 | imaplen -= intsize; | ||
185 | } | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int rza1_irqc_probe(struct platform_device *pdev) | ||
191 | { | ||
192 | struct device *dev = &pdev->dev; | ||
193 | struct device_node *np = dev->of_node; | ||
194 | struct irq_domain *parent = NULL; | ||
195 | struct device_node *gic_node; | ||
196 | struct rza1_irqc_priv *priv; | ||
197 | int ret; | ||
198 | |||
199 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
200 | if (!priv) | ||
201 | return -ENOMEM; | ||
202 | |||
203 | platform_set_drvdata(pdev, priv); | ||
204 | priv->dev = dev; | ||
205 | |||
206 | priv->base = devm_platform_ioremap_resource(pdev, 0); | ||
207 | if (IS_ERR(priv->base)) | ||
208 | return PTR_ERR(priv->base); | ||
209 | |||
210 | gic_node = of_irq_find_parent(np); | ||
211 | if (gic_node) { | ||
212 | parent = irq_find_host(gic_node); | ||
213 | of_node_put(gic_node); | ||
214 | } | ||
215 | |||
216 | if (!parent) { | ||
217 | dev_err(dev, "cannot find parent domain\n"); | ||
218 | return -ENODEV; | ||
219 | } | ||
220 | |||
221 | ret = rza1_irqc_parse_map(priv, gic_node); | ||
222 | if (ret) { | ||
223 | dev_err(dev, "cannot parse %s: %d\n", "interrupt-map", ret); | ||
224 | return ret; | ||
225 | } | ||
226 | |||
227 | priv->chip.name = "rza1-irqc", | ||
228 | priv->chip.irq_mask = irq_chip_mask_parent, | ||
229 | priv->chip.irq_unmask = irq_chip_unmask_parent, | ||
230 | priv->chip.irq_eoi = rza1_irqc_eoi, | ||
231 | priv->chip.irq_retrigger = irq_chip_retrigger_hierarchy, | ||
232 | priv->chip.irq_set_type = rza1_irqc_set_type, | ||
233 | priv->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE; | ||
234 | |||
235 | priv->irq_domain = irq_domain_add_hierarchy(parent, 0, IRQC_NUM_IRQ, | ||
236 | np, &rza1_irqc_domain_ops, | ||
237 | priv); | ||
238 | if (!priv->irq_domain) { | ||
239 | dev_err(dev, "cannot initialize irq domain\n"); | ||
240 | return -ENOMEM; | ||
241 | } | ||
242 | |||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static int rza1_irqc_remove(struct platform_device *pdev) | ||
247 | { | ||
248 | struct rza1_irqc_priv *priv = platform_get_drvdata(pdev); | ||
249 | |||
250 | irq_domain_remove(priv->irq_domain); | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static const struct of_device_id rza1_irqc_dt_ids[] = { | ||
255 | { .compatible = "renesas,rza1-irqc" }, | ||
256 | {}, | ||
257 | }; | ||
258 | MODULE_DEVICE_TABLE(of, rza1_irqc_dt_ids); | ||
259 | |||
260 | static struct platform_driver rza1_irqc_device_driver = { | ||
261 | .probe = rza1_irqc_probe, | ||
262 | .remove = rza1_irqc_remove, | ||
263 | .driver = { | ||
264 | .name = "renesas_rza1_irqc", | ||
265 | .of_match_table = rza1_irqc_dt_ids, | ||
266 | } | ||
267 | }; | ||
268 | |||
269 | static int __init rza1_irqc_init(void) | ||
270 | { | ||
271 | return platform_driver_register(&rza1_irqc_device_driver); | ||
272 | } | ||
273 | postcore_initcall(rza1_irqc_init); | ||
274 | |||
275 | static void __exit rza1_irqc_exit(void) | ||
276 | { | ||
277 | platform_driver_unregister(&rza1_irqc_device_driver); | ||
278 | } | ||
279 | module_exit(rza1_irqc_exit); | ||
280 | |||
281 | MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>"); | ||
282 | MODULE_DESCRIPTION("Renesas RZ/A1 IRQC Driver"); | ||
283 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/irqchip/irq-sni-exiu.c b/drivers/irqchip/irq-sni-exiu.c index 4e983bc6cf93..1d027623c776 100644 --- a/drivers/irqchip/irq-sni-exiu.c +++ b/drivers/irqchip/irq-sni-exiu.c | |||
@@ -2,7 +2,7 @@ | |||
2 | /* | 2 | /* |
3 | * Driver for Socionext External Interrupt Unit (EXIU) | 3 | * Driver for Socionext External Interrupt Unit (EXIU) |
4 | * | 4 | * |
5 | * Copyright (c) 2017 Linaro, Ltd. <ard.biesheuvel@linaro.org> | 5 | * Copyright (c) 2017-2019 Linaro, Ltd. <ard.biesheuvel@linaro.org> |
6 | * | 6 | * |
7 | * Based on irq-tegra.c: | 7 | * Based on irq-tegra.c: |
8 | * Copyright (C) 2011 Google, Inc. | 8 | * Copyright (C) 2011 Google, Inc. |
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/of.h> | 17 | #include <linux/of.h> |
18 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
19 | #include <linux/of_irq.h> | 19 | #include <linux/of_irq.h> |
20 | #include <linux/platform_device.h> | ||
20 | 21 | ||
21 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 22 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
22 | 23 | ||
@@ -131,9 +132,13 @@ static int exiu_domain_translate(struct irq_domain *domain, | |||
131 | 132 | ||
132 | *hwirq = fwspec->param[1] - info->spi_base; | 133 | *hwirq = fwspec->param[1] - info->spi_base; |
133 | *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; | 134 | *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; |
134 | return 0; | 135 | } else { |
136 | if (fwspec->param_count != 2) | ||
137 | return -EINVAL; | ||
138 | *hwirq = fwspec->param[0]; | ||
139 | *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; | ||
135 | } | 140 | } |
136 | return -EINVAL; | 141 | return 0; |
137 | } | 142 | } |
138 | 143 | ||
139 | static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq, | 144 | static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq, |
@@ -144,16 +149,21 @@ static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq, | |||
144 | struct exiu_irq_data *info = dom->host_data; | 149 | struct exiu_irq_data *info = dom->host_data; |
145 | irq_hw_number_t hwirq; | 150 | irq_hw_number_t hwirq; |
146 | 151 | ||
147 | if (fwspec->param_count != 3) | 152 | parent_fwspec = *fwspec; |
148 | return -EINVAL; /* Not GIC compliant */ | 153 | if (is_of_node(dom->parent->fwnode)) { |
149 | if (fwspec->param[0] != GIC_SPI) | 154 | if (fwspec->param_count != 3) |
150 | return -EINVAL; /* No PPI should point to this domain */ | 155 | return -EINVAL; /* Not GIC compliant */ |
156 | if (fwspec->param[0] != GIC_SPI) | ||
157 | return -EINVAL; /* No PPI should point to this domain */ | ||
151 | 158 | ||
159 | hwirq = fwspec->param[1] - info->spi_base; | ||
160 | } else { | ||
161 | hwirq = fwspec->param[0]; | ||
162 | parent_fwspec.param[0] = hwirq + info->spi_base + 32; | ||
163 | } | ||
152 | WARN_ON(nr_irqs != 1); | 164 | WARN_ON(nr_irqs != 1); |
153 | hwirq = fwspec->param[1] - info->spi_base; | ||
154 | irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &exiu_irq_chip, info); | 165 | irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &exiu_irq_chip, info); |
155 | 166 | ||
156 | parent_fwspec = *fwspec; | ||
157 | parent_fwspec.fwnode = dom->parent->fwnode; | 167 | parent_fwspec.fwnode = dom->parent->fwnode; |
158 | return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec); | 168 | return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec); |
159 | } | 169 | } |
@@ -164,35 +174,23 @@ static const struct irq_domain_ops exiu_domain_ops = { | |||
164 | .free = irq_domain_free_irqs_common, | 174 | .free = irq_domain_free_irqs_common, |
165 | }; | 175 | }; |
166 | 176 | ||
167 | static int __init exiu_init(struct device_node *node, | 177 | static struct exiu_irq_data *exiu_init(const struct fwnode_handle *fwnode, |
168 | struct device_node *parent) | 178 | struct resource *res) |
169 | { | 179 | { |
170 | struct irq_domain *parent_domain, *domain; | ||
171 | struct exiu_irq_data *data; | 180 | struct exiu_irq_data *data; |
172 | int err; | 181 | int err; |
173 | 182 | ||
174 | if (!parent) { | ||
175 | pr_err("%pOF: no parent, giving up\n", node); | ||
176 | return -ENODEV; | ||
177 | } | ||
178 | |||
179 | parent_domain = irq_find_host(parent); | ||
180 | if (!parent_domain) { | ||
181 | pr_err("%pOF: unable to obtain parent domain\n", node); | ||
182 | return -ENXIO; | ||
183 | } | ||
184 | |||
185 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 183 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
186 | if (!data) | 184 | if (!data) |
187 | return -ENOMEM; | 185 | return ERR_PTR(-ENOMEM); |
188 | 186 | ||
189 | if (of_property_read_u32(node, "socionext,spi-base", &data->spi_base)) { | 187 | if (fwnode_property_read_u32_array(fwnode, "socionext,spi-base", |
190 | pr_err("%pOF: failed to parse 'spi-base' property\n", node); | 188 | &data->spi_base, 1)) { |
191 | err = -ENODEV; | 189 | err = -ENODEV; |
192 | goto out_free; | 190 | goto out_free; |
193 | } | 191 | } |
194 | 192 | ||
195 | data->base = of_iomap(node, 0); | 193 | data->base = ioremap(res->start, resource_size(res)); |
196 | if (!data->base) { | 194 | if (!data->base) { |
197 | err = -ENODEV; | 195 | err = -ENODEV; |
198 | goto out_free; | 196 | goto out_free; |
@@ -202,11 +200,44 @@ static int __init exiu_init(struct device_node *node, | |||
202 | writel_relaxed(0xFFFFFFFF, data->base + EIREQCLR); | 200 | writel_relaxed(0xFFFFFFFF, data->base + EIREQCLR); |
203 | writel_relaxed(0xFFFFFFFF, data->base + EIMASK); | 201 | writel_relaxed(0xFFFFFFFF, data->base + EIMASK); |
204 | 202 | ||
203 | return data; | ||
204 | |||
205 | out_free: | ||
206 | kfree(data); | ||
207 | return ERR_PTR(err); | ||
208 | } | ||
209 | |||
210 | static int __init exiu_dt_init(struct device_node *node, | ||
211 | struct device_node *parent) | ||
212 | { | ||
213 | struct irq_domain *parent_domain, *domain; | ||
214 | struct exiu_irq_data *data; | ||
215 | struct resource res; | ||
216 | |||
217 | if (!parent) { | ||
218 | pr_err("%pOF: no parent, giving up\n", node); | ||
219 | return -ENODEV; | ||
220 | } | ||
221 | |||
222 | parent_domain = irq_find_host(parent); | ||
223 | if (!parent_domain) { | ||
224 | pr_err("%pOF: unable to obtain parent domain\n", node); | ||
225 | return -ENXIO; | ||
226 | } | ||
227 | |||
228 | if (of_address_to_resource(node, 0, &res)) { | ||
229 | pr_err("%pOF: failed to parse memory resource\n", node); | ||
230 | return -ENXIO; | ||
231 | } | ||
232 | |||
233 | data = exiu_init(of_node_to_fwnode(node), &res); | ||
234 | if (IS_ERR(data)) | ||
235 | return PTR_ERR(data); | ||
236 | |||
205 | domain = irq_domain_add_hierarchy(parent_domain, 0, NUM_IRQS, node, | 237 | domain = irq_domain_add_hierarchy(parent_domain, 0, NUM_IRQS, node, |
206 | &exiu_domain_ops, data); | 238 | &exiu_domain_ops, data); |
207 | if (!domain) { | 239 | if (!domain) { |
208 | pr_err("%pOF: failed to allocate domain\n", node); | 240 | pr_err("%pOF: failed to allocate domain\n", node); |
209 | err = -ENOMEM; | ||
210 | goto out_unmap; | 241 | goto out_unmap; |
211 | } | 242 | } |
212 | 243 | ||
@@ -217,8 +248,57 @@ static int __init exiu_init(struct device_node *node, | |||
217 | 248 | ||
218 | out_unmap: | 249 | out_unmap: |
219 | iounmap(data->base); | 250 | iounmap(data->base); |
220 | out_free: | ||
221 | kfree(data); | 251 | kfree(data); |
222 | return err; | 252 | return -ENOMEM; |
223 | } | 253 | } |
224 | IRQCHIP_DECLARE(exiu, "socionext,synquacer-exiu", exiu_init); | 254 | IRQCHIP_DECLARE(exiu, "socionext,synquacer-exiu", exiu_dt_init); |
255 | |||
256 | #ifdef CONFIG_ACPI | ||
257 | static int exiu_acpi_probe(struct platform_device *pdev) | ||
258 | { | ||
259 | struct irq_domain *domain; | ||
260 | struct exiu_irq_data *data; | ||
261 | struct resource *res; | ||
262 | |||
263 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
264 | if (!res) { | ||
265 | dev_err(&pdev->dev, "failed to parse memory resource\n"); | ||
266 | return -ENXIO; | ||
267 | } | ||
268 | |||
269 | data = exiu_init(dev_fwnode(&pdev->dev), res); | ||
270 | if (IS_ERR(data)) | ||
271 | return PTR_ERR(data); | ||
272 | |||
273 | domain = acpi_irq_create_hierarchy(0, NUM_IRQS, dev_fwnode(&pdev->dev), | ||
274 | &exiu_domain_ops, data); | ||
275 | if (!domain) { | ||
276 | dev_err(&pdev->dev, "failed to create IRQ domain\n"); | ||
277 | goto out_unmap; | ||
278 | } | ||
279 | |||
280 | dev_info(&pdev->dev, "%d interrupts forwarded\n", NUM_IRQS); | ||
281 | |||
282 | return 0; | ||
283 | |||
284 | out_unmap: | ||
285 | iounmap(data->base); | ||
286 | kfree(data); | ||
287 | return -ENOMEM; | ||
288 | } | ||
289 | |||
290 | static const struct acpi_device_id exiu_acpi_ids[] = { | ||
291 | { "SCX0008" }, | ||
292 | { /* sentinel */ } | ||
293 | }; | ||
294 | MODULE_DEVICE_TABLE(acpi, exiu_acpi_ids); | ||
295 | |||
296 | static struct platform_driver exiu_driver = { | ||
297 | .driver = { | ||
298 | .name = "exiu", | ||
299 | .acpi_match_table = exiu_acpi_ids, | ||
300 | }, | ||
301 | .probe = exiu_acpi_probe, | ||
302 | }; | ||
303 | builtin_platform_driver(exiu_driver); | ||
304 | #endif | ||
diff --git a/drivers/irqchip/qcom-irq-combiner.c b/drivers/irqchip/qcom-irq-combiner.c index 067337ab3f20..d88e993aa66d 100644 --- a/drivers/irqchip/qcom-irq-combiner.c +++ b/drivers/irqchip/qcom-irq-combiner.c | |||
@@ -229,7 +229,6 @@ static int get_registers(struct platform_device *pdev, struct combiner *comb) | |||
229 | static int __init combiner_probe(struct platform_device *pdev) | 229 | static int __init combiner_probe(struct platform_device *pdev) |
230 | { | 230 | { |
231 | struct combiner *combiner; | 231 | struct combiner *combiner; |
232 | size_t alloc_sz; | ||
233 | int nregs; | 232 | int nregs; |
234 | int err; | 233 | int err; |
235 | 234 | ||
@@ -239,8 +238,8 @@ static int __init combiner_probe(struct platform_device *pdev) | |||
239 | return -EINVAL; | 238 | return -EINVAL; |
240 | } | 239 | } |
241 | 240 | ||
242 | alloc_sz = sizeof(*combiner) + sizeof(struct combiner_reg) * nregs; | 241 | combiner = devm_kzalloc(&pdev->dev, struct_size(combiner, regs, nregs), |
243 | combiner = devm_kzalloc(&pdev->dev, alloc_sz, GFP_KERNEL); | 242 | GFP_KERNEL); |
244 | if (!combiner) | 243 | if (!combiner) |
245 | return -ENOMEM; | 244 | return -ENOMEM; |
246 | 245 | ||
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 5bcd23e5ccd6..469be6844703 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
@@ -10,6 +10,7 @@ | |||
10 | 10 | ||
11 | #include <linux/errno.h> | 11 | #include <linux/errno.h> |
12 | #include <linux/ioport.h> /* for struct resource */ | 12 | #include <linux/ioport.h> /* for struct resource */ |
13 | #include <linux/irqdomain.h> | ||
13 | #include <linux/resource_ext.h> | 14 | #include <linux/resource_ext.h> |
14 | #include <linux/device.h> | 15 | #include <linux/device.h> |
15 | #include <linux/property.h> | 16 | #include <linux/property.h> |
@@ -314,6 +315,12 @@ int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi); | |||
314 | void acpi_set_irq_model(enum acpi_irq_model_id model, | 315 | void acpi_set_irq_model(enum acpi_irq_model_id model, |
315 | struct fwnode_handle *fwnode); | 316 | struct fwnode_handle *fwnode); |
316 | 317 | ||
318 | struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags, | ||
319 | unsigned int size, | ||
320 | struct fwnode_handle *fwnode, | ||
321 | const struct irq_domain_ops *ops, | ||
322 | void *host_data); | ||
323 | |||
317 | #ifdef CONFIG_X86_IO_APIC | 324 | #ifdef CONFIG_X86_IO_APIC |
318 | extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity); | 325 | extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity); |
319 | #else | 326 | #else |
diff --git a/include/linux/irqchip/arm-gic-common.h b/include/linux/irqchip/arm-gic-common.h index 626283858563..b9850f5f1906 100644 --- a/include/linux/irqchip/arm-gic-common.h +++ b/include/linux/irqchip/arm-gic-common.h | |||
@@ -36,4 +36,9 @@ struct gic_kvm_info { | |||
36 | 36 | ||
37 | const struct gic_kvm_info *gic_get_kvm_info(void); | 37 | const struct gic_kvm_info *gic_get_kvm_info(void); |
38 | 38 | ||
39 | struct irq_domain; | ||
40 | struct fwnode_handle; | ||
41 | int gicv2m_init(struct fwnode_handle *parent_handle, | ||
42 | struct irq_domain *parent); | ||
43 | |||
39 | #endif /* __LINUX_IRQCHIP_ARM_GIC_COMMON_H */ | 44 | #endif /* __LINUX_IRQCHIP_ARM_GIC_COMMON_H */ |
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 316087da1d09..5686711b0f40 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h | |||
@@ -157,9 +157,6 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq); | |||
157 | */ | 157 | */ |
158 | void gic_init(void __iomem *dist , void __iomem *cpu); | 158 | void gic_init(void __iomem *dist , void __iomem *cpu); |
159 | 159 | ||
160 | int gicv2m_init(struct fwnode_handle *parent_handle, | ||
161 | struct irq_domain *parent); | ||
162 | |||
163 | void gic_send_sgi(unsigned int cpu_id, unsigned int irq); | 160 | void gic_send_sgi(unsigned int cpu_id, unsigned int irq); |
164 | int gic_get_cpu_id(unsigned int cpu); | 161 | int gic_get_cpu_id(unsigned int cpu); |
165 | void gic_migrate_target(unsigned int new_cpu_id); | 162 | void gic_migrate_target(unsigned int new_cpu_id); |
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile index ff6e352e3a6c..b4f53717d143 100644 --- a/kernel/irq/Makefile +++ b/kernel/irq/Makefile | |||
@@ -2,6 +2,9 @@ | |||
2 | 2 | ||
3 | obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o | 3 | obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o |
4 | obj-$(CONFIG_IRQ_TIMINGS) += timings.o | 4 | obj-$(CONFIG_IRQ_TIMINGS) += timings.o |
5 | ifeq ($(CONFIG_TEST_IRQ_TIMINGS),y) | ||
6 | CFLAGS_timings.o += -DDEBUG | ||
7 | endif | ||
5 | obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o | 8 | obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o |
6 | obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o | 9 | obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o |
7 | obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o | 10 | obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o |
diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c index f18cd5aa33e8..4352b08ae48d 100644 --- a/kernel/irq/affinity.c +++ b/kernel/irq/affinity.c | |||
@@ -94,8 +94,7 @@ static int get_nodes_in_cpumask(cpumask_var_t *node_to_cpumask, | |||
94 | return nodes; | 94 | return nodes; |
95 | } | 95 | } |
96 | 96 | ||
97 | static int __irq_build_affinity_masks(const struct irq_affinity *affd, | 97 | static int __irq_build_affinity_masks(unsigned int startvec, |
98 | unsigned int startvec, | ||
99 | unsigned int numvecs, | 98 | unsigned int numvecs, |
100 | unsigned int firstvec, | 99 | unsigned int firstvec, |
101 | cpumask_var_t *node_to_cpumask, | 100 | cpumask_var_t *node_to_cpumask, |
@@ -171,8 +170,7 @@ static int __irq_build_affinity_masks(const struct irq_affinity *affd, | |||
171 | * 1) spread present CPU on these vectors | 170 | * 1) spread present CPU on these vectors |
172 | * 2) spread other possible CPUs on these vectors | 171 | * 2) spread other possible CPUs on these vectors |
173 | */ | 172 | */ |
174 | static int irq_build_affinity_masks(const struct irq_affinity *affd, | 173 | static int irq_build_affinity_masks(unsigned int startvec, unsigned int numvecs, |
175 | unsigned int startvec, unsigned int numvecs, | ||
176 | unsigned int firstvec, | 174 | unsigned int firstvec, |
177 | struct irq_affinity_desc *masks) | 175 | struct irq_affinity_desc *masks) |
178 | { | 176 | { |
@@ -197,7 +195,7 @@ static int irq_build_affinity_masks(const struct irq_affinity *affd, | |||
197 | build_node_to_cpumask(node_to_cpumask); | 195 | build_node_to_cpumask(node_to_cpumask); |
198 | 196 | ||
199 | /* Spread on present CPUs starting from affd->pre_vectors */ | 197 | /* Spread on present CPUs starting from affd->pre_vectors */ |
200 | nr_present = __irq_build_affinity_masks(affd, curvec, numvecs, | 198 | nr_present = __irq_build_affinity_masks(curvec, numvecs, |
201 | firstvec, node_to_cpumask, | 199 | firstvec, node_to_cpumask, |
202 | cpu_present_mask, nmsk, masks); | 200 | cpu_present_mask, nmsk, masks); |
203 | 201 | ||
@@ -212,7 +210,7 @@ static int irq_build_affinity_masks(const struct irq_affinity *affd, | |||
212 | else | 210 | else |
213 | curvec = firstvec + nr_present; | 211 | curvec = firstvec + nr_present; |
214 | cpumask_andnot(npresmsk, cpu_possible_mask, cpu_present_mask); | 212 | cpumask_andnot(npresmsk, cpu_possible_mask, cpu_present_mask); |
215 | nr_others = __irq_build_affinity_masks(affd, curvec, numvecs, | 213 | nr_others = __irq_build_affinity_masks(curvec, numvecs, |
216 | firstvec, node_to_cpumask, | 214 | firstvec, node_to_cpumask, |
217 | npresmsk, nmsk, masks); | 215 | npresmsk, nmsk, masks); |
218 | put_online_cpus(); | 216 | put_online_cpus(); |
@@ -295,7 +293,7 @@ irq_create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd) | |||
295 | unsigned int this_vecs = affd->set_size[i]; | 293 | unsigned int this_vecs = affd->set_size[i]; |
296 | int ret; | 294 | int ret; |
297 | 295 | ||
298 | ret = irq_build_affinity_masks(affd, curvec, this_vecs, | 296 | ret = irq_build_affinity_masks(curvec, this_vecs, |
299 | curvec, masks); | 297 | curvec, masks); |
300 | if (ret) { | 298 | if (ret) { |
301 | kfree(masks); | 299 | kfree(masks); |
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 29d6c7d070b4..04c850fb70cb 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c | |||
@@ -748,6 +748,8 @@ void handle_fasteoi_nmi(struct irq_desc *desc) | |||
748 | unsigned int irq = irq_desc_get_irq(desc); | 748 | unsigned int irq = irq_desc_get_irq(desc); |
749 | irqreturn_t res; | 749 | irqreturn_t res; |
750 | 750 | ||
751 | __kstat_incr_irqs_this_cpu(desc); | ||
752 | |||
751 | trace_irq_handler_entry(irq, action); | 753 | trace_irq_handler_entry(irq, action); |
752 | /* | 754 | /* |
753 | * NMIs cannot be shared, there is only one action. | 755 | * NMIs cannot be shared, there is only one action. |
@@ -962,6 +964,8 @@ void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc) | |||
962 | unsigned int irq = irq_desc_get_irq(desc); | 964 | unsigned int irq = irq_desc_get_irq(desc); |
963 | irqreturn_t res; | 965 | irqreturn_t res; |
964 | 966 | ||
967 | __kstat_incr_irqs_this_cpu(desc); | ||
968 | |||
965 | trace_irq_handler_entry(irq, action); | 969 | trace_irq_handler_entry(irq, action); |
966 | res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id)); | 970 | res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id)); |
967 | trace_irq_handler_exit(irq, action, res); | 971 | trace_irq_handler_exit(irq, action, res); |
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 70c3053bc1f6..21f9927ff5ad 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h | |||
@@ -354,6 +354,16 @@ static inline int irq_timing_decode(u64 value, u64 *timestamp) | |||
354 | return value & U16_MAX; | 354 | return value & U16_MAX; |
355 | } | 355 | } |
356 | 356 | ||
357 | static __always_inline void irq_timings_push(u64 ts, int irq) | ||
358 | { | ||
359 | struct irq_timings *timings = this_cpu_ptr(&irq_timings); | ||
360 | |||
361 | timings->values[timings->count & IRQ_TIMINGS_MASK] = | ||
362 | irq_timing_encode(ts, irq); | ||
363 | |||
364 | timings->count++; | ||
365 | } | ||
366 | |||
357 | /* | 367 | /* |
358 | * The function record_irq_time is only called in one place in the | 368 | * The function record_irq_time is only called in one place in the |
359 | * interrupts handler. We want this function always inline so the code | 369 | * interrupts handler. We want this function always inline so the code |
@@ -367,15 +377,8 @@ static __always_inline void record_irq_time(struct irq_desc *desc) | |||
367 | if (!static_branch_likely(&irq_timing_enabled)) | 377 | if (!static_branch_likely(&irq_timing_enabled)) |
368 | return; | 378 | return; |
369 | 379 | ||
370 | if (desc->istate & IRQS_TIMINGS) { | 380 | if (desc->istate & IRQS_TIMINGS) |
371 | struct irq_timings *timings = this_cpu_ptr(&irq_timings); | 381 | irq_timings_push(local_clock(), irq_desc_get_irq(desc)); |
372 | |||
373 | timings->values[timings->count & IRQ_TIMINGS_MASK] = | ||
374 | irq_timing_encode(local_clock(), | ||
375 | irq_desc_get_irq(desc)); | ||
376 | |||
377 | timings->count++; | ||
378 | } | ||
379 | } | 382 | } |
380 | #else | 383 | #else |
381 | static inline void irq_remove_timings(struct irq_desc *desc) {} | 384 | static inline void irq_remove_timings(struct irq_desc *desc) {} |
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index a92b33593b8d..9484e88dabc2 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c | |||
@@ -950,6 +950,11 @@ unsigned int kstat_irqs_cpu(unsigned int irq, int cpu) | |||
950 | *per_cpu_ptr(desc->kstat_irqs, cpu) : 0; | 950 | *per_cpu_ptr(desc->kstat_irqs, cpu) : 0; |
951 | } | 951 | } |
952 | 952 | ||
953 | static bool irq_is_nmi(struct irq_desc *desc) | ||
954 | { | ||
955 | return desc->istate & IRQS_NMI; | ||
956 | } | ||
957 | |||
953 | /** | 958 | /** |
954 | * kstat_irqs - Get the statistics for an interrupt | 959 | * kstat_irqs - Get the statistics for an interrupt |
955 | * @irq: The interrupt number | 960 | * @irq: The interrupt number |
@@ -967,7 +972,8 @@ unsigned int kstat_irqs(unsigned int irq) | |||
967 | if (!desc || !desc->kstat_irqs) | 972 | if (!desc || !desc->kstat_irqs) |
968 | return 0; | 973 | return 0; |
969 | if (!irq_settings_is_per_cpu_devid(desc) && | 974 | if (!irq_settings_is_per_cpu_devid(desc) && |
970 | !irq_settings_is_per_cpu(desc)) | 975 | !irq_settings_is_per_cpu(desc) && |
976 | !irq_is_nmi(desc)) | ||
971 | return desc->tot_count; | 977 | return desc->tot_count; |
972 | 978 | ||
973 | for_each_possible_cpu(cpu) | 979 | for_each_possible_cpu(cpu) |
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index a453e229f99c..3078d0e48bba 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c | |||
@@ -123,7 +123,7 @@ EXPORT_SYMBOL_GPL(irq_domain_free_fwnode); | |||
123 | * @ops: domain callbacks | 123 | * @ops: domain callbacks |
124 | * @host_data: Controller private data pointer | 124 | * @host_data: Controller private data pointer |
125 | * | 125 | * |
126 | * Allocates and initialize and irq_domain structure. | 126 | * Allocates and initializes an irq_domain structure. |
127 | * Returns pointer to IRQ domain, or NULL on failure. | 127 | * Returns pointer to IRQ domain, or NULL on failure. |
128 | */ | 128 | */ |
129 | struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size, | 129 | struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size, |
@@ -139,7 +139,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size, | |||
139 | 139 | ||
140 | domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size), | 140 | domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size), |
141 | GFP_KERNEL, of_node_to_nid(of_node)); | 141 | GFP_KERNEL, of_node_to_nid(of_node)); |
142 | if (WARN_ON(!domain)) | 142 | if (!domain) |
143 | return NULL; | 143 | return NULL; |
144 | 144 | ||
145 | if (fwnode && is_fwnode_irqchip(fwnode)) { | 145 | if (fwnode && is_fwnode_irqchip(fwnode)) { |
diff --git a/kernel/irq/timings.c b/kernel/irq/timings.c index 90c735da15d0..e960d7ce7bcc 100644 --- a/kernel/irq/timings.c +++ b/kernel/irq/timings.c | |||
@@ -1,10 +1,12 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (C) 2016, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> | 2 | // Copyright (C) 2016, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> |
3 | #define pr_fmt(fmt) "irq_timings: " fmt | ||
3 | 4 | ||
4 | #include <linux/kernel.h> | 5 | #include <linux/kernel.h> |
5 | #include <linux/percpu.h> | 6 | #include <linux/percpu.h> |
6 | #include <linux/slab.h> | 7 | #include <linux/slab.h> |
7 | #include <linux/static_key.h> | 8 | #include <linux/static_key.h> |
9 | #include <linux/init.h> | ||
8 | #include <linux/interrupt.h> | 10 | #include <linux/interrupt.h> |
9 | #include <linux/idr.h> | 11 | #include <linux/idr.h> |
10 | #include <linux/irq.h> | 12 | #include <linux/irq.h> |
@@ -261,12 +263,29 @@ void irq_timings_disable(void) | |||
261 | #define EMA_ALPHA_VAL 64 | 263 | #define EMA_ALPHA_VAL 64 |
262 | #define EMA_ALPHA_SHIFT 7 | 264 | #define EMA_ALPHA_SHIFT 7 |
263 | 265 | ||
264 | #define PREDICTION_PERIOD_MIN 2 | 266 | #define PREDICTION_PERIOD_MIN 3 |
265 | #define PREDICTION_PERIOD_MAX 5 | 267 | #define PREDICTION_PERIOD_MAX 5 |
266 | #define PREDICTION_FACTOR 4 | 268 | #define PREDICTION_FACTOR 4 |
267 | #define PREDICTION_MAX 10 /* 2 ^ PREDICTION_MAX useconds */ | 269 | #define PREDICTION_MAX 10 /* 2 ^ PREDICTION_MAX useconds */ |
268 | #define PREDICTION_BUFFER_SIZE 16 /* slots for EMAs, hardly more than 16 */ | 270 | #define PREDICTION_BUFFER_SIZE 16 /* slots for EMAs, hardly more than 16 */ |
269 | 271 | ||
272 | /* | ||
273 | * Number of elements in the circular buffer: If it happens it was | ||
274 | * flushed before, then the number of elements could be smaller than | ||
275 | * IRQ_TIMINGS_SIZE, so the count is used, otherwise the array size is | ||
276 | * used as we wrapped. The index begins from zero when we did not | ||
277 | * wrap. That could be done in a nicer way with the proper circular | ||
278 | * array structure type but with the cost of extra computation in the | ||
279 | * interrupt handler hot path. We choose efficiency. | ||
280 | */ | ||
281 | #define for_each_irqts(i, irqts) \ | ||
282 | for (i = irqts->count < IRQ_TIMINGS_SIZE ? \ | ||
283 | 0 : irqts->count & IRQ_TIMINGS_MASK, \ | ||
284 | irqts->count = min(IRQ_TIMINGS_SIZE, \ | ||
285 | irqts->count); \ | ||
286 | irqts->count > 0; irqts->count--, \ | ||
287 | i = (i + 1) & IRQ_TIMINGS_MASK) | ||
288 | |||
270 | struct irqt_stat { | 289 | struct irqt_stat { |
271 | u64 last_ts; | 290 | u64 last_ts; |
272 | u64 ema_time[PREDICTION_BUFFER_SIZE]; | 291 | u64 ema_time[PREDICTION_BUFFER_SIZE]; |
@@ -297,7 +316,16 @@ static u64 irq_timings_ema_new(u64 value, u64 ema_old) | |||
297 | 316 | ||
298 | static int irq_timings_next_event_index(int *buffer, size_t len, int period_max) | 317 | static int irq_timings_next_event_index(int *buffer, size_t len, int period_max) |
299 | { | 318 | { |
300 | int i; | 319 | int period; |
320 | |||
321 | /* | ||
322 | * Move the beginning pointer to the end minus the max period x 3. | ||
323 | * We are at the point we can begin searching the pattern | ||
324 | */ | ||
325 | buffer = &buffer[len - (period_max * 3)]; | ||
326 | |||
327 | /* Adjust the length to the maximum allowed period x 3 */ | ||
328 | len = period_max * 3; | ||
301 | 329 | ||
302 | /* | 330 | /* |
303 | * The buffer contains the suite of intervals, in a ilog2 | 331 | * The buffer contains the suite of intervals, in a ilog2 |
@@ -306,21 +334,45 @@ static int irq_timings_next_event_index(int *buffer, size_t len, int period_max) | |||
306 | * period beginning at the end of the buffer. We do that for | 334 | * period beginning at the end of the buffer. We do that for |
307 | * each suffix. | 335 | * each suffix. |
308 | */ | 336 | */ |
309 | for (i = period_max; i >= PREDICTION_PERIOD_MIN ; i--) { | 337 | for (period = period_max; period >= PREDICTION_PERIOD_MIN; period--) { |
310 | 338 | ||
311 | int *begin = &buffer[len - (i * 3)]; | 339 | /* |
312 | int *ptr = begin; | 340 | * The first comparison always succeed because the |
341 | * suffix is deduced from the first n-period bytes of | ||
342 | * the buffer and we compare the initial suffix with | ||
343 | * itself, so we can skip the first iteration. | ||
344 | */ | ||
345 | int idx = period; | ||
346 | size_t size = period; | ||
313 | 347 | ||
314 | /* | 348 | /* |
315 | * We look if the suite with period 'i' repeat | 349 | * We look if the suite with period 'i' repeat |
316 | * itself. If it is truncated at the end, as it | 350 | * itself. If it is truncated at the end, as it |
317 | * repeats we can use the period to find out the next | 351 | * repeats we can use the period to find out the next |
318 | * element. | 352 | * element with the modulo. |
319 | */ | 353 | */ |
320 | while (!memcmp(ptr, begin, i * sizeof(*ptr))) { | 354 | while (!memcmp(buffer, &buffer[idx], size * sizeof(int))) { |
321 | ptr += i; | 355 | |
322 | if (ptr >= &buffer[len]) | 356 | /* |
323 | return begin[((i * 3) % i)]; | 357 | * Move the index in a period basis |
358 | */ | ||
359 | idx += size; | ||
360 | |||
361 | /* | ||
362 | * If this condition is reached, all previous | ||
363 | * memcmp were successful, so the period is | ||
364 | * found. | ||
365 | */ | ||
366 | if (idx == len) | ||
367 | return buffer[len % period]; | ||
368 | |||
369 | /* | ||
370 | * If the remaining elements to compare are | ||
371 | * smaller than the period, readjust the size | ||
372 | * of the comparison for the last iteration. | ||
373 | */ | ||
374 | if (len - idx < period) | ||
375 | size = len - idx; | ||
324 | } | 376 | } |
325 | } | 377 | } |
326 | 378 | ||
@@ -380,11 +432,43 @@ static u64 __irq_timings_next_event(struct irqt_stat *irqs, int irq, u64 now) | |||
380 | return irqs->last_ts + irqs->ema_time[index]; | 432 | return irqs->last_ts + irqs->ema_time[index]; |
381 | } | 433 | } |
382 | 434 | ||
435 | static __always_inline int irq_timings_interval_index(u64 interval) | ||
436 | { | ||
437 | /* | ||
438 | * The PREDICTION_FACTOR increase the interval size for the | ||
439 | * array of exponential average. | ||
440 | */ | ||
441 | u64 interval_us = (interval >> 10) / PREDICTION_FACTOR; | ||
442 | |||
443 | return likely(interval_us) ? ilog2(interval_us) : 0; | ||
444 | } | ||
445 | |||
446 | static __always_inline void __irq_timings_store(int irq, struct irqt_stat *irqs, | ||
447 | u64 interval) | ||
448 | { | ||
449 | int index; | ||
450 | |||
451 | /* | ||
452 | * Get the index in the ema table for this interrupt. | ||
453 | */ | ||
454 | index = irq_timings_interval_index(interval); | ||
455 | |||
456 | /* | ||
457 | * Store the index as an element of the pattern in another | ||
458 | * circular array. | ||
459 | */ | ||
460 | irqs->circ_timings[irqs->count & IRQ_TIMINGS_MASK] = index; | ||
461 | |||
462 | irqs->ema_time[index] = irq_timings_ema_new(interval, | ||
463 | irqs->ema_time[index]); | ||
464 | |||
465 | irqs->count++; | ||
466 | } | ||
467 | |||
383 | static inline void irq_timings_store(int irq, struct irqt_stat *irqs, u64 ts) | 468 | static inline void irq_timings_store(int irq, struct irqt_stat *irqs, u64 ts) |
384 | { | 469 | { |
385 | u64 old_ts = irqs->last_ts; | 470 | u64 old_ts = irqs->last_ts; |
386 | u64 interval; | 471 | u64 interval; |
387 | int index; | ||
388 | 472 | ||
389 | /* | 473 | /* |
390 | * The timestamps are absolute time values, we need to compute | 474 | * The timestamps are absolute time values, we need to compute |
@@ -415,24 +499,7 @@ static inline void irq_timings_store(int irq, struct irqt_stat *irqs, u64 ts) | |||
415 | return; | 499 | return; |
416 | } | 500 | } |
417 | 501 | ||
418 | /* | 502 | __irq_timings_store(irq, irqs, interval); |
419 | * Get the index in the ema table for this interrupt. The | ||
420 | * PREDICTION_FACTOR increase the interval size for the array | ||
421 | * of exponential average. | ||
422 | */ | ||
423 | index = likely(interval) ? | ||
424 | ilog2((interval >> 10) / PREDICTION_FACTOR) : 0; | ||
425 | |||
426 | /* | ||
427 | * Store the index as an element of the pattern in another | ||
428 | * circular array. | ||
429 | */ | ||
430 | irqs->circ_timings[irqs->count & IRQ_TIMINGS_MASK] = index; | ||
431 | |||
432 | irqs->ema_time[index] = irq_timings_ema_new(interval, | ||
433 | irqs->ema_time[index]); | ||
434 | |||
435 | irqs->count++; | ||
436 | } | 503 | } |
437 | 504 | ||
438 | /** | 505 | /** |
@@ -493,11 +560,7 @@ u64 irq_timings_next_event(u64 now) | |||
493 | * model while decrementing the counter because we consume the | 560 | * model while decrementing the counter because we consume the |
494 | * data from our circular buffer. | 561 | * data from our circular buffer. |
495 | */ | 562 | */ |
496 | 563 | for_each_irqts(i, irqts) { | |
497 | i = (irqts->count & IRQ_TIMINGS_MASK) - 1; | ||
498 | irqts->count = min(IRQ_TIMINGS_SIZE, irqts->count); | ||
499 | |||
500 | for (; irqts->count > 0; irqts->count--, i = (i + 1) & IRQ_TIMINGS_MASK) { | ||
501 | irq = irq_timing_decode(irqts->values[i], &ts); | 564 | irq = irq_timing_decode(irqts->values[i], &ts); |
502 | s = idr_find(&irqt_stats, irq); | 565 | s = idr_find(&irqt_stats, irq); |
503 | if (s) | 566 | if (s) |
@@ -564,3 +627,325 @@ int irq_timings_alloc(int irq) | |||
564 | 627 | ||
565 | return 0; | 628 | return 0; |
566 | } | 629 | } |
630 | |||
631 | #ifdef CONFIG_TEST_IRQ_TIMINGS | ||
632 | struct timings_intervals { | ||
633 | u64 *intervals; | ||
634 | size_t count; | ||
635 | }; | ||
636 | |||
637 | /* | ||
638 | * Intervals are given in nanosecond base | ||
639 | */ | ||
640 | static u64 intervals0[] __initdata = { | ||
641 | 10000, 50000, 200000, 500000, | ||
642 | 10000, 50000, 200000, 500000, | ||
643 | 10000, 50000, 200000, 500000, | ||
644 | 10000, 50000, 200000, 500000, | ||
645 | 10000, 50000, 200000, 500000, | ||
646 | 10000, 50000, 200000, 500000, | ||
647 | 10000, 50000, 200000, 500000, | ||
648 | 10000, 50000, 200000, 500000, | ||
649 | 10000, 50000, 200000, | ||
650 | }; | ||
651 | |||
652 | static u64 intervals1[] __initdata = { | ||
653 | 223947000, 1240000, 1384000, 1386000, 1386000, | ||
654 | 217416000, 1236000, 1384000, 1386000, 1387000, | ||
655 | 214719000, 1241000, 1386000, 1387000, 1384000, | ||
656 | 213696000, 1234000, 1384000, 1386000, 1388000, | ||
657 | 219904000, 1240000, 1385000, 1389000, 1385000, | ||
658 | 212240000, 1240000, 1386000, 1386000, 1386000, | ||
659 | 214415000, 1236000, 1384000, 1386000, 1387000, | ||
660 | 214276000, 1234000, | ||
661 | }; | ||
662 | |||
663 | static u64 intervals2[] __initdata = { | ||
664 | 4000, 3000, 5000, 100000, | ||
665 | 3000, 3000, 5000, 117000, | ||
666 | 4000, 4000, 5000, 112000, | ||
667 | 4000, 3000, 4000, 110000, | ||
668 | 3000, 5000, 3000, 117000, | ||
669 | 4000, 4000, 5000, 112000, | ||
670 | 4000, 3000, 4000, 110000, | ||
671 | 3000, 4000, 5000, 112000, | ||
672 | 4000, | ||
673 | }; | ||
674 | |||
675 | static u64 intervals3[] __initdata = { | ||
676 | 1385000, 212240000, 1240000, | ||
677 | 1386000, 214415000, 1236000, | ||
678 | 1384000, 214276000, 1234000, | ||
679 | 1386000, 214415000, 1236000, | ||
680 | 1385000, 212240000, 1240000, | ||
681 | 1386000, 214415000, 1236000, | ||
682 | 1384000, 214276000, 1234000, | ||
683 | 1386000, 214415000, 1236000, | ||
684 | 1385000, 212240000, 1240000, | ||
685 | }; | ||
686 | |||
687 | static u64 intervals4[] __initdata = { | ||
688 | 10000, 50000, 10000, 50000, | ||
689 | 10000, 50000, 10000, 50000, | ||
690 | 10000, 50000, 10000, 50000, | ||
691 | 10000, 50000, 10000, 50000, | ||
692 | 10000, 50000, 10000, 50000, | ||
693 | 10000, 50000, 10000, 50000, | ||
694 | 10000, 50000, 10000, 50000, | ||
695 | 10000, 50000, 10000, 50000, | ||
696 | 10000, | ||
697 | }; | ||
698 | |||
699 | static struct timings_intervals tis[] __initdata = { | ||
700 | { intervals0, ARRAY_SIZE(intervals0) }, | ||
701 | { intervals1, ARRAY_SIZE(intervals1) }, | ||
702 | { intervals2, ARRAY_SIZE(intervals2) }, | ||
703 | { intervals3, ARRAY_SIZE(intervals3) }, | ||
704 | { intervals4, ARRAY_SIZE(intervals4) }, | ||
705 | }; | ||
706 | |||
707 | static int __init irq_timings_test_next_index(struct timings_intervals *ti) | ||
708 | { | ||
709 | int _buffer[IRQ_TIMINGS_SIZE]; | ||
710 | int buffer[IRQ_TIMINGS_SIZE]; | ||
711 | int index, start, i, count, period_max; | ||
712 | |||
713 | count = ti->count - 1; | ||
714 | |||
715 | period_max = count > (3 * PREDICTION_PERIOD_MAX) ? | ||
716 | PREDICTION_PERIOD_MAX : count / 3; | ||
717 | |||
718 | /* | ||
719 | * Inject all values except the last one which will be used | ||
720 | * to compare with the next index result. | ||
721 | */ | ||
722 | pr_debug("index suite: "); | ||
723 | |||
724 | for (i = 0; i < count; i++) { | ||
725 | index = irq_timings_interval_index(ti->intervals[i]); | ||
726 | _buffer[i & IRQ_TIMINGS_MASK] = index; | ||
727 | pr_cont("%d ", index); | ||
728 | } | ||
729 | |||
730 | start = count < IRQ_TIMINGS_SIZE ? 0 : | ||
731 | count & IRQ_TIMINGS_MASK; | ||
732 | |||
733 | count = min_t(int, count, IRQ_TIMINGS_SIZE); | ||
734 | |||
735 | for (i = 0; i < count; i++) { | ||
736 | int index = (start + i) & IRQ_TIMINGS_MASK; | ||
737 | buffer[i] = _buffer[index]; | ||
738 | } | ||
739 | |||
740 | index = irq_timings_next_event_index(buffer, count, period_max); | ||
741 | i = irq_timings_interval_index(ti->intervals[ti->count - 1]); | ||
742 | |||
743 | if (index != i) { | ||
744 | pr_err("Expected (%d) and computed (%d) next indexes differ\n", | ||
745 | i, index); | ||
746 | return -EINVAL; | ||
747 | } | ||
748 | |||
749 | return 0; | ||
750 | } | ||
751 | |||
752 | static int __init irq_timings_next_index_selftest(void) | ||
753 | { | ||
754 | int i, ret; | ||
755 | |||
756 | for (i = 0; i < ARRAY_SIZE(tis); i++) { | ||
757 | |||
758 | pr_info("---> Injecting intervals number #%d (count=%zd)\n", | ||
759 | i, tis[i].count); | ||
760 | |||
761 | ret = irq_timings_test_next_index(&tis[i]); | ||
762 | if (ret) | ||
763 | break; | ||
764 | } | ||
765 | |||
766 | return ret; | ||
767 | } | ||
768 | |||
769 | static int __init irq_timings_test_irqs(struct timings_intervals *ti) | ||
770 | { | ||
771 | struct irqt_stat __percpu *s; | ||
772 | struct irqt_stat *irqs; | ||
773 | int i, index, ret, irq = 0xACE5; | ||
774 | |||
775 | ret = irq_timings_alloc(irq); | ||
776 | if (ret) { | ||
777 | pr_err("Failed to allocate irq timings\n"); | ||
778 | return ret; | ||
779 | } | ||
780 | |||
781 | s = idr_find(&irqt_stats, irq); | ||
782 | if (!s) { | ||
783 | ret = -EIDRM; | ||
784 | goto out; | ||
785 | } | ||
786 | |||
787 | irqs = this_cpu_ptr(s); | ||
788 | |||
789 | for (i = 0; i < ti->count; i++) { | ||
790 | |||
791 | index = irq_timings_interval_index(ti->intervals[i]); | ||
792 | pr_debug("%d: interval=%llu ema_index=%d\n", | ||
793 | i, ti->intervals[i], index); | ||
794 | |||
795 | __irq_timings_store(irq, irqs, ti->intervals[i]); | ||
796 | if (irqs->circ_timings[i & IRQ_TIMINGS_MASK] != index) { | ||
797 | pr_err("Failed to store in the circular buffer\n"); | ||
798 | goto out; | ||
799 | } | ||
800 | } | ||
801 | |||
802 | if (irqs->count != ti->count) { | ||
803 | pr_err("Count differs\n"); | ||
804 | goto out; | ||
805 | } | ||
806 | |||
807 | ret = 0; | ||
808 | out: | ||
809 | irq_timings_free(irq); | ||
810 | |||
811 | return ret; | ||
812 | } | ||
813 | |||
814 | static int __init irq_timings_irqs_selftest(void) | ||
815 | { | ||
816 | int i, ret; | ||
817 | |||
818 | for (i = 0; i < ARRAY_SIZE(tis); i++) { | ||
819 | pr_info("---> Injecting intervals number #%d (count=%zd)\n", | ||
820 | i, tis[i].count); | ||
821 | ret = irq_timings_test_irqs(&tis[i]); | ||
822 | if (ret) | ||
823 | break; | ||
824 | } | ||
825 | |||
826 | return ret; | ||
827 | } | ||
828 | |||
829 | static int __init irq_timings_test_irqts(struct irq_timings *irqts, | ||
830 | unsigned count) | ||
831 | { | ||
832 | int start = count >= IRQ_TIMINGS_SIZE ? count - IRQ_TIMINGS_SIZE : 0; | ||
833 | int i, irq, oirq = 0xBEEF; | ||
834 | u64 ots = 0xDEAD, ts; | ||
835 | |||
836 | /* | ||
837 | * Fill the circular buffer by using the dedicated function. | ||
838 | */ | ||
839 | for (i = 0; i < count; i++) { | ||
840 | pr_debug("%d: index=%d, ts=%llX irq=%X\n", | ||
841 | i, i & IRQ_TIMINGS_MASK, ots + i, oirq + i); | ||
842 | |||
843 | irq_timings_push(ots + i, oirq + i); | ||
844 | } | ||
845 | |||
846 | /* | ||
847 | * Compute the first elements values after the index wrapped | ||
848 | * up or not. | ||
849 | */ | ||
850 | ots += start; | ||
851 | oirq += start; | ||
852 | |||
853 | /* | ||
854 | * Test the circular buffer count is correct. | ||
855 | */ | ||
856 | pr_debug("---> Checking timings array count (%d) is right\n", count); | ||
857 | if (WARN_ON(irqts->count != count)) | ||
858 | return -EINVAL; | ||
859 | |||
860 | /* | ||
861 | * Test the macro allowing to browse all the irqts. | ||
862 | */ | ||
863 | pr_debug("---> Checking the for_each_irqts() macro\n"); | ||
864 | for_each_irqts(i, irqts) { | ||
865 | |||
866 | irq = irq_timing_decode(irqts->values[i], &ts); | ||
867 | |||
868 | pr_debug("index=%d, ts=%llX / %llX, irq=%X / %X\n", | ||
869 | i, ts, ots, irq, oirq); | ||
870 | |||
871 | if (WARN_ON(ts != ots || irq != oirq)) | ||
872 | return -EINVAL; | ||
873 | |||
874 | ots++; oirq++; | ||
875 | } | ||
876 | |||
877 | /* | ||
878 | * The circular buffer should have be flushed when browsed | ||
879 | * with for_each_irqts | ||
880 | */ | ||
881 | pr_debug("---> Checking timings array is empty after browsing it\n"); | ||
882 | if (WARN_ON(irqts->count)) | ||
883 | return -EINVAL; | ||
884 | |||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static int __init irq_timings_irqts_selftest(void) | ||
889 | { | ||
890 | struct irq_timings *irqts = this_cpu_ptr(&irq_timings); | ||
891 | int i, ret; | ||
892 | |||
893 | /* | ||
894 | * Test the circular buffer with different number of | ||
895 | * elements. The purpose is to test at the limits (empty, half | ||
896 | * full, full, wrapped with the cursor at the boundaries, | ||
897 | * wrapped several times, etc ... | ||
898 | */ | ||
899 | int count[] = { 0, | ||
900 | IRQ_TIMINGS_SIZE >> 1, | ||
901 | IRQ_TIMINGS_SIZE, | ||
902 | IRQ_TIMINGS_SIZE + (IRQ_TIMINGS_SIZE >> 1), | ||
903 | 2 * IRQ_TIMINGS_SIZE, | ||
904 | (2 * IRQ_TIMINGS_SIZE) + 3, | ||
905 | }; | ||
906 | |||
907 | for (i = 0; i < ARRAY_SIZE(count); i++) { | ||
908 | |||
909 | pr_info("---> Checking the timings with %d/%d values\n", | ||
910 | count[i], IRQ_TIMINGS_SIZE); | ||
911 | |||
912 | ret = irq_timings_test_irqts(irqts, count[i]); | ||
913 | if (ret) | ||
914 | break; | ||
915 | } | ||
916 | |||
917 | return ret; | ||
918 | } | ||
919 | |||
920 | static int __init irq_timings_selftest(void) | ||
921 | { | ||
922 | int ret; | ||
923 | |||
924 | pr_info("------------------- selftest start -----------------\n"); | ||
925 | |||
926 | /* | ||
927 | * At this point, we don't except any subsystem to use the irq | ||
928 | * timings but us, so it should not be enabled. | ||
929 | */ | ||
930 | if (static_branch_unlikely(&irq_timing_enabled)) { | ||
931 | pr_warn("irq timings already initialized, skipping selftest\n"); | ||
932 | return 0; | ||
933 | } | ||
934 | |||
935 | ret = irq_timings_irqts_selftest(); | ||
936 | if (ret) | ||
937 | goto out; | ||
938 | |||
939 | ret = irq_timings_irqs_selftest(); | ||
940 | if (ret) | ||
941 | goto out; | ||
942 | |||
943 | ret = irq_timings_next_index_selftest(); | ||
944 | out: | ||
945 | pr_info("---------- selftest end with %s -----------\n", | ||
946 | ret ? "failure" : "success"); | ||
947 | |||
948 | return ret; | ||
949 | } | ||
950 | early_initcall(irq_timings_selftest); | ||
951 | #endif | ||
diff --git a/kernel/softirq.c b/kernel/softirq.c index a6b81c6b6bff..0427a86743a4 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c | |||
@@ -649,7 +649,7 @@ static int takeover_tasklets(unsigned int cpu) | |||
649 | /* Find end, append list for that CPU. */ | 649 | /* Find end, append list for that CPU. */ |
650 | if (&per_cpu(tasklet_vec, cpu).head != per_cpu(tasklet_vec, cpu).tail) { | 650 | if (&per_cpu(tasklet_vec, cpu).head != per_cpu(tasklet_vec, cpu).tail) { |
651 | *__this_cpu_read(tasklet_vec.tail) = per_cpu(tasklet_vec, cpu).head; | 651 | *__this_cpu_read(tasklet_vec.tail) = per_cpu(tasklet_vec, cpu).head; |
652 | this_cpu_write(tasklet_vec.tail, per_cpu(tasklet_vec, cpu).tail); | 652 | __this_cpu_write(tasklet_vec.tail, per_cpu(tasklet_vec, cpu).tail); |
653 | per_cpu(tasklet_vec, cpu).head = NULL; | 653 | per_cpu(tasklet_vec, cpu).head = NULL; |
654 | per_cpu(tasklet_vec, cpu).tail = &per_cpu(tasklet_vec, cpu).head; | 654 | per_cpu(tasklet_vec, cpu).tail = &per_cpu(tasklet_vec, cpu).head; |
655 | } | 655 | } |
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index b0d71d9293dc..6629cab453e8 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -1870,6 +1870,14 @@ config TEST_PARMAN | |||
1870 | 1870 | ||
1871 | If unsure, say N. | 1871 | If unsure, say N. |
1872 | 1872 | ||
1873 | config TEST_IRQ_TIMINGS | ||
1874 | bool "IRQ timings selftest" | ||
1875 | depends on IRQ_TIMINGS | ||
1876 | help | ||
1877 | Enable this option to test the irq timings code on boot. | ||
1878 | |||
1879 | If unsure, say N. | ||
1880 | |||
1873 | config TEST_LKM | 1881 | config TEST_LKM |
1874 | tristate "Test module loading with 'hello world' module" | 1882 | tristate "Test module loading with 'hello world' module" |
1875 | depends on m | 1883 | depends on m |