diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-19 13:58:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-05-19 13:58:45 -0400 |
commit | d9351ea14ddca708d3cb384f828af4bf82fcc772 (patch) | |
tree | 90c5fe9067f1005ce512c63b2e664a670af72b4f /drivers/irqchip | |
parent | 39feaa3ff4453594297574e116a55bd6d5371f37 (diff) | |
parent | fb4e0592654adb31bc6f3a738d6499b816a655d6 (diff) |
Merge branch 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull IRQ chip updates from Ingo Molnar:
"A late irqchips update:
- New TI INTR/INTA set of drivers
- Rewrite of the stm32mp1-exti driver as a platform driver
- Update the IOMMU MSI mapping API to be RT friendly
- A number of cleanups and other low impact fixes"
* 'irq-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (34 commits)
iommu/dma-iommu: Remove iommu_dma_map_msi_msg()
irqchip/gic-v3-mbi: Don't map the MSI page in mbi_compose_m{b, s}i_msg()
irqchip/ls-scfg-msi: Don't map the MSI page in ls_scfg_msi_compose_msg()
irqchip/gic-v3-its: Don't map the MSI page in its_irq_compose_msi_msg()
irqchip/gicv2m: Don't map the MSI page in gicv2m_compose_msi_msg()
iommu/dma-iommu: Split iommu_dma_map_msi_msg() in two parts
genirq/msi: Add a new field in msi_desc to store an IOMMU cookie
arm64: arch_k3: Enable interrupt controller drivers
irqchip/ti-sci-inta: Add msi domain support
soc: ti: Add MSI domain bus support for Interrupt Aggregator
irqchip/ti-sci-inta: Add support for Interrupt Aggregator driver
dt-bindings: irqchip: Introduce TISCI Interrupt Aggregator bindings
irqchip/ti-sci-intr: Add support for Interrupt Router driver
dt-bindings: irqchip: Introduce TISCI Interrupt router bindings
gpio: thunderx: Use the default parent apis for {request,release}_resources
genirq: Introduce irq_chip_{request,release}_resource_parent() apis
firmware: ti_sci: Add helper apis to manage resources
firmware: ti_sci: Add RM mapping table for am654
firmware: ti_sci: Add support for IRQ management
firmware: ti_sci: Add support for RM core ops
...
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Kconfig | 27 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm7038-l1.c | 3 | ||||
-rw-r--r-- | drivers/irqchip/irq-bcm7120-l2.c | 3 | ||||
-rw-r--r-- | drivers/irqchip/irq-brcmstb-l2.c | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-pm.c | 76 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v2m.c | 8 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 84 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3-mbi.c | 10 | ||||
-rw-r--r-- | drivers/irqchip/irq-imx-irqsteer.c | 4 | ||||
-rw-r--r-- | drivers/irqchip/irq-ls-scfg-msi.c | 7 | ||||
-rw-r--r-- | drivers/irqchip/irq-renesas-intc-irqpin.c | 4 | ||||
-rw-r--r-- | drivers/irqchip/irq-stm32-exti.c | 233 | ||||
-rw-r--r-- | drivers/irqchip/irq-ti-sci-inta.c | 615 | ||||
-rw-r--r-- | drivers/irqchip/irq-ti-sci-intr.c | 275 |
15 files changed, 1166 insertions, 187 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index cf7984991062..1c1f3f66dfd3 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -6,7 +6,6 @@ config IRQCHIP | |||
6 | 6 | ||
7 | config ARM_GIC | 7 | config ARM_GIC |
8 | bool | 8 | bool |
9 | select IRQ_DOMAIN | ||
10 | select IRQ_DOMAIN_HIERARCHY | 9 | select IRQ_DOMAIN_HIERARCHY |
11 | select GENERIC_IRQ_MULTI_HANDLER | 10 | select GENERIC_IRQ_MULTI_HANDLER |
12 | select GENERIC_IRQ_EFFECTIVE_AFF_MASK | 11 | select GENERIC_IRQ_EFFECTIVE_AFF_MASK |
@@ -33,7 +32,6 @@ config GIC_NON_BANKED | |||
33 | 32 | ||
34 | config ARM_GIC_V3 | 33 | config ARM_GIC_V3 |
35 | bool | 34 | bool |
36 | select IRQ_DOMAIN | ||
37 | select GENERIC_IRQ_MULTI_HANDLER | 35 | select GENERIC_IRQ_MULTI_HANDLER |
38 | select IRQ_DOMAIN_HIERARCHY | 36 | select IRQ_DOMAIN_HIERARCHY |
39 | select PARTITION_PERCPU | 37 | select PARTITION_PERCPU |
@@ -59,7 +57,6 @@ config ARM_GIC_V3_ITS_FSL_MC | |||
59 | 57 | ||
60 | config ARM_NVIC | 58 | config ARM_NVIC |
61 | bool | 59 | bool |
62 | select IRQ_DOMAIN | ||
63 | select IRQ_DOMAIN_HIERARCHY | 60 | select IRQ_DOMAIN_HIERARCHY |
64 | select GENERIC_IRQ_CHIP | 61 | select GENERIC_IRQ_CHIP |
65 | 62 | ||
@@ -358,7 +355,6 @@ config STM32_EXTI | |||
358 | config QCOM_IRQ_COMBINER | 355 | config QCOM_IRQ_COMBINER |
359 | bool "QCOM IRQ combiner support" | 356 | bool "QCOM IRQ combiner support" |
360 | depends on ARCH_QCOM && ACPI | 357 | depends on ARCH_QCOM && ACPI |
361 | select IRQ_DOMAIN | ||
362 | select IRQ_DOMAIN_HIERARCHY | 358 | select IRQ_DOMAIN_HIERARCHY |
363 | help | 359 | help |
364 | Say yes here to add support for the IRQ combiner devices embedded | 360 | Say yes here to add support for the IRQ combiner devices embedded |
@@ -375,7 +371,6 @@ config IRQ_UNIPHIER_AIDET | |||
375 | config MESON_IRQ_GPIO | 371 | config MESON_IRQ_GPIO |
376 | bool "Meson GPIO Interrupt Multiplexer" | 372 | bool "Meson GPIO Interrupt Multiplexer" |
377 | depends on ARCH_MESON | 373 | depends on ARCH_MESON |
378 | select IRQ_DOMAIN | ||
379 | select IRQ_DOMAIN_HIERARCHY | 374 | select IRQ_DOMAIN_HIERARCHY |
380 | help | 375 | help |
381 | Support Meson SoC Family GPIO Interrupt Multiplexer | 376 | Support Meson SoC Family GPIO Interrupt Multiplexer |
@@ -391,7 +386,6 @@ config GOLDFISH_PIC | |||
391 | config QCOM_PDC | 386 | config QCOM_PDC |
392 | bool "QCOM PDC" | 387 | bool "QCOM PDC" |
393 | depends on ARCH_QCOM | 388 | depends on ARCH_QCOM |
394 | select IRQ_DOMAIN | ||
395 | select IRQ_DOMAIN_HIERARCHY | 389 | select IRQ_DOMAIN_HIERARCHY |
396 | help | 390 | help |
397 | Power Domain Controller driver to manage and configure wakeup | 391 | Power Domain Controller driver to manage and configure wakeup |
@@ -431,6 +425,27 @@ config LS1X_IRQ | |||
431 | help | 425 | help |
432 | Support for the Loongson-1 platform Interrupt Controller. | 426 | Support for the Loongson-1 platform Interrupt Controller. |
433 | 427 | ||
428 | config TI_SCI_INTR_IRQCHIP | ||
429 | bool | ||
430 | depends on TI_SCI_PROTOCOL | ||
431 | select IRQ_DOMAIN_HIERARCHY | ||
432 | help | ||
433 | This enables the irqchip driver support for K3 Interrupt router | ||
434 | over TI System Control Interface available on some new TI's SoCs. | ||
435 | If you wish to use interrupt router irq resources managed by the | ||
436 | TI System Controller, say Y here. Otherwise, say N. | ||
437 | |||
438 | config TI_SCI_INTA_IRQCHIP | ||
439 | bool | ||
440 | depends on TI_SCI_PROTOCOL | ||
441 | select IRQ_DOMAIN_HIERARCHY | ||
442 | select TI_SCI_INTA_MSI_DOMAIN | ||
443 | help | ||
444 | This enables the irqchip driver support for K3 Interrupt aggregator | ||
445 | over TI System Control Interface available on some new TI's SoCs. | ||
446 | If you wish to use interrupt aggregator irq resources managed by the | ||
447 | TI System Controller, say Y here. Otherwise, say N. | ||
448 | |||
434 | endmenu | 449 | endmenu |
435 | 450 | ||
436 | config SIFIVE_PLIC | 451 | config SIFIVE_PLIC |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index f8c66e958a64..606a003a0000 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -98,3 +98,5 @@ obj-$(CONFIG_SIFIVE_PLIC) += irq-sifive-plic.o | |||
98 | obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o | 98 | obj-$(CONFIG_IMX_IRQSTEER) += irq-imx-irqsteer.o |
99 | obj-$(CONFIG_MADERA_IRQ) += irq-madera.o | 99 | obj-$(CONFIG_MADERA_IRQ) += irq-madera.o |
100 | obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o | 100 | obj-$(CONFIG_LS1X_IRQ) += irq-ls1x.o |
101 | obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o | ||
102 | obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o | ||
diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c index 0f6e30e9009d..0acebac1920b 100644 --- a/drivers/irqchip/irq-bcm7038-l1.c +++ b/drivers/irqchip/irq-bcm7038-l1.c | |||
@@ -343,6 +343,9 @@ int __init bcm7038_l1_of_init(struct device_node *dn, | |||
343 | goto out_unmap; | 343 | goto out_unmap; |
344 | } | 344 | } |
345 | 345 | ||
346 | pr_info("registered BCM7038 L1 intc (%pOF, IRQs: %d)\n", | ||
347 | dn, IRQS_PER_WORD * intc->n_words); | ||
348 | |||
346 | return 0; | 349 | return 0; |
347 | 350 | ||
348 | out_unmap: | 351 | out_unmap: |
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 8968e5e93fcb..541bdca9f4af 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c | |||
@@ -318,6 +318,9 @@ static int __init bcm7120_l2_intc_probe(struct device_node *dn, | |||
318 | } | 318 | } |
319 | } | 319 | } |
320 | 320 | ||
321 | pr_info("registered %s intc (%pOF, parent IRQ(s): %d)\n", | ||
322 | intc_name, dn, data->num_parent_irqs); | ||
323 | |||
321 | return 0; | 324 | return 0; |
322 | 325 | ||
323 | out_free_domain: | 326 | out_free_domain: |
diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index 5e4ca139e4ea..a0642b59befa 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c | |||
@@ -264,6 +264,8 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np, | |||
264 | ct->chip.irq_set_wake = irq_gc_set_wake; | 264 | ct->chip.irq_set_wake = irq_gc_set_wake; |
265 | } | 265 | } |
266 | 266 | ||
267 | pr_info("registered L2 intc (%pOF, parent irq: %d)\n", np, parent_irq); | ||
268 | |||
267 | return 0; | 269 | return 0; |
268 | 270 | ||
269 | out_free_domain: | 271 | out_free_domain: |
diff --git a/drivers/irqchip/irq-gic-pm.c b/drivers/irqchip/irq-gic-pm.c index ecafd295c31c..c4aac0977d8a 100644 --- a/drivers/irqchip/irq-gic-pm.c +++ b/drivers/irqchip/irq-gic-pm.c | |||
@@ -19,7 +19,6 @@ | |||
19 | #include <linux/of_irq.h> | 19 | #include <linux/of_irq.h> |
20 | #include <linux/irqchip/arm-gic.h> | 20 | #include <linux/irqchip/arm-gic.h> |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <linux/pm_clock.h> | ||
23 | #include <linux/pm_runtime.h> | 22 | #include <linux/pm_runtime.h> |
24 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
25 | 24 | ||
@@ -28,17 +27,27 @@ struct gic_clk_data { | |||
28 | const char *const *clocks; | 27 | const char *const *clocks; |
29 | }; | 28 | }; |
30 | 29 | ||
30 | struct gic_chip_pm { | ||
31 | struct gic_chip_data *chip_data; | ||
32 | const struct gic_clk_data *clk_data; | ||
33 | struct clk_bulk_data *clks; | ||
34 | }; | ||
35 | |||
31 | static int gic_runtime_resume(struct device *dev) | 36 | static int gic_runtime_resume(struct device *dev) |
32 | { | 37 | { |
33 | struct gic_chip_data *gic = dev_get_drvdata(dev); | 38 | struct gic_chip_pm *chip_pm = dev_get_drvdata(dev); |
39 | struct gic_chip_data *gic = chip_pm->chip_data; | ||
40 | const struct gic_clk_data *data = chip_pm->clk_data; | ||
34 | int ret; | 41 | int ret; |
35 | 42 | ||
36 | ret = pm_clk_resume(dev); | 43 | ret = clk_bulk_prepare_enable(data->num_clocks, chip_pm->clks); |
37 | if (ret) | 44 | if (ret) { |
45 | dev_err(dev, "clk_enable failed: %d\n", ret); | ||
38 | return ret; | 46 | return ret; |
47 | } | ||
39 | 48 | ||
40 | /* | 49 | /* |
41 | * On the very first resume, the pointer to the driver data | 50 | * On the very first resume, the pointer to chip_pm->chip_data |
42 | * will be NULL and this is intentional, because we do not | 51 | * will be NULL and this is intentional, because we do not |
43 | * want to restore the GIC on the very first resume. So if | 52 | * want to restore the GIC on the very first resume. So if |
44 | * the pointer is not valid just return. | 53 | * the pointer is not valid just return. |
@@ -54,35 +63,14 @@ static int gic_runtime_resume(struct device *dev) | |||
54 | 63 | ||
55 | static int gic_runtime_suspend(struct device *dev) | 64 | static int gic_runtime_suspend(struct device *dev) |
56 | { | 65 | { |
57 | struct gic_chip_data *gic = dev_get_drvdata(dev); | 66 | struct gic_chip_pm *chip_pm = dev_get_drvdata(dev); |
67 | struct gic_chip_data *gic = chip_pm->chip_data; | ||
68 | const struct gic_clk_data *data = chip_pm->clk_data; | ||
58 | 69 | ||
59 | gic_dist_save(gic); | 70 | gic_dist_save(gic); |
60 | gic_cpu_save(gic); | 71 | gic_cpu_save(gic); |
61 | 72 | ||
62 | return pm_clk_suspend(dev); | 73 | clk_bulk_disable_unprepare(data->num_clocks, chip_pm->clks); |
63 | } | ||
64 | |||
65 | static int gic_get_clocks(struct device *dev, const struct gic_clk_data *data) | ||
66 | { | ||
67 | unsigned int i; | ||
68 | int ret; | ||
69 | |||
70 | if (!dev || !data) | ||
71 | return -EINVAL; | ||
72 | |||
73 | ret = pm_clk_create(dev); | ||
74 | if (ret) | ||
75 | return ret; | ||
76 | |||
77 | for (i = 0; i < data->num_clocks; i++) { | ||
78 | ret = of_pm_clk_add_clk(dev, data->clocks[i]); | ||
79 | if (ret) { | ||
80 | dev_err(dev, "failed to add clock %s\n", | ||
81 | data->clocks[i]); | ||
82 | pm_clk_destroy(dev); | ||
83 | return ret; | ||
84 | } | ||
85 | } | ||
86 | 74 | ||
87 | return 0; | 75 | return 0; |
88 | } | 76 | } |
@@ -91,8 +79,8 @@ static int gic_probe(struct platform_device *pdev) | |||
91 | { | 79 | { |
92 | struct device *dev = &pdev->dev; | 80 | struct device *dev = &pdev->dev; |
93 | const struct gic_clk_data *data; | 81 | const struct gic_clk_data *data; |
94 | struct gic_chip_data *gic; | 82 | struct gic_chip_pm *chip_pm; |
95 | int ret, irq; | 83 | int ret, irq, i; |
96 | 84 | ||
97 | data = of_device_get_match_data(&pdev->dev); | 85 | data = of_device_get_match_data(&pdev->dev); |
98 | if (!data) { | 86 | if (!data) { |
@@ -100,28 +88,41 @@ static int gic_probe(struct platform_device *pdev) | |||
100 | return -ENODEV; | 88 | return -ENODEV; |
101 | } | 89 | } |
102 | 90 | ||
91 | chip_pm = devm_kzalloc(dev, sizeof(*chip_pm), GFP_KERNEL); | ||
92 | if (!chip_pm) | ||
93 | return -ENOMEM; | ||
94 | |||
103 | irq = irq_of_parse_and_map(dev->of_node, 0); | 95 | irq = irq_of_parse_and_map(dev->of_node, 0); |
104 | if (!irq) { | 96 | if (!irq) { |
105 | dev_err(dev, "no parent interrupt found!\n"); | 97 | dev_err(dev, "no parent interrupt found!\n"); |
106 | return -EINVAL; | 98 | return -EINVAL; |
107 | } | 99 | } |
108 | 100 | ||
109 | ret = gic_get_clocks(dev, data); | 101 | chip_pm->clks = devm_kcalloc(dev, data->num_clocks, |
102 | sizeof(*chip_pm->clks), GFP_KERNEL); | ||
103 | if (!chip_pm->clks) | ||
104 | return -ENOMEM; | ||
105 | |||
106 | for (i = 0; i < data->num_clocks; i++) | ||
107 | chip_pm->clks[i].id = data->clocks[i]; | ||
108 | |||
109 | ret = devm_clk_bulk_get(dev, data->num_clocks, chip_pm->clks); | ||
110 | if (ret) | 110 | if (ret) |
111 | goto irq_dispose; | 111 | goto irq_dispose; |
112 | 112 | ||
113 | chip_pm->clk_data = data; | ||
114 | dev_set_drvdata(dev, chip_pm); | ||
115 | |||
113 | pm_runtime_enable(dev); | 116 | pm_runtime_enable(dev); |
114 | 117 | ||
115 | ret = pm_runtime_get_sync(dev); | 118 | ret = pm_runtime_get_sync(dev); |
116 | if (ret < 0) | 119 | if (ret < 0) |
117 | goto rpm_disable; | 120 | goto rpm_disable; |
118 | 121 | ||
119 | ret = gic_of_init_child(dev, &gic, irq); | 122 | ret = gic_of_init_child(dev, &chip_pm->chip_data, irq); |
120 | if (ret) | 123 | if (ret) |
121 | goto rpm_put; | 124 | goto rpm_put; |
122 | 125 | ||
123 | platform_set_drvdata(pdev, gic); | ||
124 | |||
125 | pm_runtime_put(dev); | 126 | pm_runtime_put(dev); |
126 | 127 | ||
127 | dev_info(dev, "GIC IRQ controller registered\n"); | 128 | dev_info(dev, "GIC IRQ controller registered\n"); |
@@ -132,7 +133,6 @@ rpm_put: | |||
132 | pm_runtime_put_sync(dev); | 133 | pm_runtime_put_sync(dev); |
133 | rpm_disable: | 134 | rpm_disable: |
134 | pm_runtime_disable(dev); | 135 | pm_runtime_disable(dev); |
135 | pm_clk_destroy(dev); | ||
136 | irq_dispose: | 136 | irq_dispose: |
137 | irq_dispose_mapping(irq); | 137 | irq_dispose_mapping(irq); |
138 | 138 | ||
@@ -142,6 +142,8 @@ irq_dispose: | |||
142 | static const struct dev_pm_ops gic_pm_ops = { | 142 | static const struct dev_pm_ops gic_pm_ops = { |
143 | SET_RUNTIME_PM_OPS(gic_runtime_suspend, | 143 | SET_RUNTIME_PM_OPS(gic_runtime_suspend, |
144 | gic_runtime_resume, NULL) | 144 | gic_runtime_resume, NULL) |
145 | SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, | ||
146 | pm_runtime_force_resume) | ||
145 | }; | 147 | }; |
146 | 148 | ||
147 | static const char * const gic400_clocks[] = { | 149 | static const char * const gic400_clocks[] = { |
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index de14e06fd9ec..3c77ab676e54 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c | |||
@@ -110,7 +110,7 @@ static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) | |||
110 | if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) | 110 | if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) |
111 | msg->data -= v2m->spi_offset; | 111 | msg->data -= v2m->spi_offset; |
112 | 112 | ||
113 | iommu_dma_map_msi_msg(data->irq, msg); | 113 | iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); |
114 | } | 114 | } |
115 | 115 | ||
116 | static struct irq_chip gicv2m_irq_chip = { | 116 | static struct irq_chip gicv2m_irq_chip = { |
@@ -167,6 +167,7 @@ static void gicv2m_unalloc_msi(struct v2m_data *v2m, unsigned int hwirq, | |||
167 | static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | 167 | static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, |
168 | unsigned int nr_irqs, void *args) | 168 | unsigned int nr_irqs, void *args) |
169 | { | 169 | { |
170 | msi_alloc_info_t *info = args; | ||
170 | struct v2m_data *v2m = NULL, *tmp; | 171 | struct v2m_data *v2m = NULL, *tmp; |
171 | int hwirq, offset, i, err = 0; | 172 | int hwirq, offset, i, err = 0; |
172 | 173 | ||
@@ -186,6 +187,11 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |||
186 | 187 | ||
187 | hwirq = v2m->spi_start + offset; | 188 | hwirq = v2m->spi_start + offset; |
188 | 189 | ||
190 | err = iommu_dma_prepare_msi(info->desc, | ||
191 | v2m->res.start + V2M_MSI_SETSPI_NS); | ||
192 | if (err) | ||
193 | return err; | ||
194 | |||
189 | for (i = 0; i < nr_irqs; i++) { | 195 | for (i = 0; i < nr_irqs; i++) { |
190 | err = gicv2m_irq_gic_domain_alloc(domain, virq + i, hwirq + i); | 196 | err = gicv2m_irq_gic_domain_alloc(domain, virq + i, hwirq + i); |
191 | if (err) | 197 | if (err) |
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 128ac893d7e4..cfb9b4e5f914 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c | |||
@@ -26,7 +26,6 @@ | |||
26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
27 | #include <linux/irqdomain.h> | 27 | #include <linux/irqdomain.h> |
28 | #include <linux/list.h> | 28 | #include <linux/list.h> |
29 | #include <linux/list_sort.h> | ||
30 | #include <linux/log2.h> | 29 | #include <linux/log2.h> |
31 | #include <linux/memblock.h> | 30 | #include <linux/memblock.h> |
32 | #include <linux/mm.h> | 31 | #include <linux/mm.h> |
@@ -1179,7 +1178,7 @@ static void its_irq_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) | |||
1179 | msg->address_hi = upper_32_bits(addr); | 1178 | msg->address_hi = upper_32_bits(addr); |
1180 | msg->data = its_get_event_id(d); | 1179 | msg->data = its_get_event_id(d); |
1181 | 1180 | ||
1182 | iommu_dma_map_msi_msg(d->irq, msg); | 1181 | iommu_dma_compose_msi_msg(irq_data_get_msi_desc(d), msg); |
1183 | } | 1182 | } |
1184 | 1183 | ||
1185 | static int its_irq_set_irqchip_state(struct irq_data *d, | 1184 | static int its_irq_set_irqchip_state(struct irq_data *d, |
@@ -1465,9 +1464,8 @@ static struct lpi_range *mk_lpi_range(u32 base, u32 span) | |||
1465 | { | 1464 | { |
1466 | struct lpi_range *range; | 1465 | struct lpi_range *range; |
1467 | 1466 | ||
1468 | range = kzalloc(sizeof(*range), GFP_KERNEL); | 1467 | range = kmalloc(sizeof(*range), GFP_KERNEL); |
1469 | if (range) { | 1468 | if (range) { |
1470 | INIT_LIST_HEAD(&range->entry); | ||
1471 | range->base_id = base; | 1469 | range->base_id = base; |
1472 | range->span = span; | 1470 | range->span = span; |
1473 | } | 1471 | } |
@@ -1475,31 +1473,6 @@ static struct lpi_range *mk_lpi_range(u32 base, u32 span) | |||
1475 | return range; | 1473 | return range; |
1476 | } | 1474 | } |
1477 | 1475 | ||
1478 | static int lpi_range_cmp(void *priv, struct list_head *a, struct list_head *b) | ||
1479 | { | ||
1480 | struct lpi_range *ra, *rb; | ||
1481 | |||
1482 | ra = container_of(a, struct lpi_range, entry); | ||
1483 | rb = container_of(b, struct lpi_range, entry); | ||
1484 | |||
1485 | return ra->base_id - rb->base_id; | ||
1486 | } | ||
1487 | |||
1488 | static void merge_lpi_ranges(void) | ||
1489 | { | ||
1490 | struct lpi_range *range, *tmp; | ||
1491 | |||
1492 | list_for_each_entry_safe(range, tmp, &lpi_range_list, entry) { | ||
1493 | if (!list_is_last(&range->entry, &lpi_range_list) && | ||
1494 | (tmp->base_id == (range->base_id + range->span))) { | ||
1495 | tmp->base_id = range->base_id; | ||
1496 | tmp->span += range->span; | ||
1497 | list_del(&range->entry); | ||
1498 | kfree(range); | ||
1499 | } | ||
1500 | } | ||
1501 | } | ||
1502 | |||
1503 | static int alloc_lpi_range(u32 nr_lpis, u32 *base) | 1476 | static int alloc_lpi_range(u32 nr_lpis, u32 *base) |
1504 | { | 1477 | { |
1505 | struct lpi_range *range, *tmp; | 1478 | struct lpi_range *range, *tmp; |
@@ -1529,25 +1502,49 @@ static int alloc_lpi_range(u32 nr_lpis, u32 *base) | |||
1529 | return err; | 1502 | return err; |
1530 | } | 1503 | } |
1531 | 1504 | ||
1505 | static void merge_lpi_ranges(struct lpi_range *a, struct lpi_range *b) | ||
1506 | { | ||
1507 | if (&a->entry == &lpi_range_list || &b->entry == &lpi_range_list) | ||
1508 | return; | ||
1509 | if (a->base_id + a->span != b->base_id) | ||
1510 | return; | ||
1511 | b->base_id = a->base_id; | ||
1512 | b->span += a->span; | ||
1513 | list_del(&a->entry); | ||
1514 | kfree(a); | ||
1515 | } | ||
1516 | |||
1532 | static int free_lpi_range(u32 base, u32 nr_lpis) | 1517 | static int free_lpi_range(u32 base, u32 nr_lpis) |
1533 | { | 1518 | { |
1534 | struct lpi_range *new; | 1519 | struct lpi_range *new, *old; |
1535 | int err = 0; | 1520 | |
1521 | new = mk_lpi_range(base, nr_lpis); | ||
1522 | if (!new) | ||
1523 | return -ENOMEM; | ||
1536 | 1524 | ||
1537 | mutex_lock(&lpi_range_lock); | 1525 | mutex_lock(&lpi_range_lock); |
1538 | 1526 | ||
1539 | new = mk_lpi_range(base, nr_lpis); | 1527 | list_for_each_entry_reverse(old, &lpi_range_list, entry) { |
1540 | if (!new) { | 1528 | if (old->base_id < base) |
1541 | err = -ENOMEM; | 1529 | break; |
1542 | goto out; | ||
1543 | } | 1530 | } |
1531 | /* | ||
1532 | * old is the last element with ->base_id smaller than base, | ||
1533 | * so new goes right after it. If there are no elements with | ||
1534 | * ->base_id smaller than base, &old->entry ends up pointing | ||
1535 | * at the head of the list, and inserting new it the start of | ||
1536 | * the list is the right thing to do in that case as well. | ||
1537 | */ | ||
1538 | list_add(&new->entry, &old->entry); | ||
1539 | /* | ||
1540 | * Now check if we can merge with the preceding and/or | ||
1541 | * following ranges. | ||
1542 | */ | ||
1543 | merge_lpi_ranges(old, new); | ||
1544 | merge_lpi_ranges(new, list_next_entry(new, entry)); | ||
1544 | 1545 | ||
1545 | list_add(&new->entry, &lpi_range_list); | ||
1546 | list_sort(NULL, &lpi_range_list, lpi_range_cmp); | ||
1547 | merge_lpi_ranges(); | ||
1548 | out: | ||
1549 | mutex_unlock(&lpi_range_lock); | 1546 | mutex_unlock(&lpi_range_lock); |
1550 | return err; | 1547 | return 0; |
1551 | } | 1548 | } |
1552 | 1549 | ||
1553 | static int __init its_lpi_init(u32 id_bits) | 1550 | static int __init its_lpi_init(u32 id_bits) |
@@ -2487,7 +2484,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev, | |||
2487 | int err = 0; | 2484 | int err = 0; |
2488 | 2485 | ||
2489 | /* | 2486 | /* |
2490 | * We ignore "dev" entierely, and rely on the dev_id that has | 2487 | * We ignore "dev" entirely, and rely on the dev_id that has |
2491 | * been passed via the scratchpad. This limits this domain's | 2488 | * been passed via the scratchpad. This limits this domain's |
2492 | * usefulness to upper layers that definitely know that they | 2489 | * usefulness to upper layers that definitely know that they |
2493 | * are built on top of the ITS. | 2490 | * are built on top of the ITS. |
@@ -2566,6 +2563,7 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |||
2566 | { | 2563 | { |
2567 | msi_alloc_info_t *info = args; | 2564 | msi_alloc_info_t *info = args; |
2568 | struct its_device *its_dev = info->scratchpad[0].ptr; | 2565 | struct its_device *its_dev = info->scratchpad[0].ptr; |
2566 | struct its_node *its = its_dev->its; | ||
2569 | irq_hw_number_t hwirq; | 2567 | irq_hw_number_t hwirq; |
2570 | int err; | 2568 | int err; |
2571 | int i; | 2569 | int i; |
@@ -2574,6 +2572,10 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |||
2574 | if (err) | 2572 | if (err) |
2575 | return err; | 2573 | return err; |
2576 | 2574 | ||
2575 | err = iommu_dma_prepare_msi(info->desc, its->get_msi_base(its_dev)); | ||
2576 | if (err) | ||
2577 | return err; | ||
2578 | |||
2577 | for (i = 0; i < nr_irqs; i++) { | 2579 | for (i = 0; i < nr_irqs; i++) { |
2578 | err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i); | 2580 | err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i); |
2579 | if (err) | 2581 | if (err) |
diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c index fbfa7ff6deb1..563a9b366294 100644 --- a/drivers/irqchip/irq-gic-v3-mbi.c +++ b/drivers/irqchip/irq-gic-v3-mbi.c | |||
@@ -84,6 +84,7 @@ static void mbi_free_msi(struct mbi_range *mbi, unsigned int hwirq, | |||
84 | static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | 84 | static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, |
85 | unsigned int nr_irqs, void *args) | 85 | unsigned int nr_irqs, void *args) |
86 | { | 86 | { |
87 | msi_alloc_info_t *info = args; | ||
87 | struct mbi_range *mbi = NULL; | 88 | struct mbi_range *mbi = NULL; |
88 | int hwirq, offset, i, err = 0; | 89 | int hwirq, offset, i, err = 0; |
89 | 90 | ||
@@ -104,6 +105,11 @@ static int mbi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |||
104 | 105 | ||
105 | hwirq = mbi->spi_start + offset; | 106 | hwirq = mbi->spi_start + offset; |
106 | 107 | ||
108 | err = iommu_dma_prepare_msi(info->desc, | ||
109 | mbi_phys_base + GICD_SETSPI_NSR); | ||
110 | if (err) | ||
111 | return err; | ||
112 | |||
107 | for (i = 0; i < nr_irqs; i++) { | 113 | for (i = 0; i < nr_irqs; i++) { |
108 | err = mbi_irq_gic_domain_alloc(domain, virq + i, hwirq + i); | 114 | err = mbi_irq_gic_domain_alloc(domain, virq + i, hwirq + i); |
109 | if (err) | 115 | if (err) |
@@ -142,7 +148,7 @@ static void mbi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) | |||
142 | msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR); | 148 | msg[0].address_lo = lower_32_bits(mbi_phys_base + GICD_SETSPI_NSR); |
143 | msg[0].data = data->parent_data->hwirq; | 149 | msg[0].data = data->parent_data->hwirq; |
144 | 150 | ||
145 | iommu_dma_map_msi_msg(data->irq, msg); | 151 | iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); |
146 | } | 152 | } |
147 | 153 | ||
148 | #ifdef CONFIG_PCI_MSI | 154 | #ifdef CONFIG_PCI_MSI |
@@ -202,7 +208,7 @@ static void mbi_compose_mbi_msg(struct irq_data *data, struct msi_msg *msg) | |||
202 | msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); | 208 | msg[1].address_lo = lower_32_bits(mbi_phys_base + GICD_CLRSPI_NSR); |
203 | msg[1].data = data->parent_data->hwirq; | 209 | msg[1].data = data->parent_data->hwirq; |
204 | 210 | ||
205 | iommu_dma_map_msi_msg(data->irq, &msg[1]); | 211 | iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), &msg[1]); |
206 | } | 212 | } |
207 | 213 | ||
208 | /* Platform-MSI specific irqchip */ | 214 | /* Platform-MSI specific irqchip */ |
diff --git a/drivers/irqchip/irq-imx-irqsteer.c b/drivers/irqchip/irq-imx-irqsteer.c index 88df3d00052c..290531ec3d61 100644 --- a/drivers/irqchip/irq-imx-irqsteer.c +++ b/drivers/irqchip/irq-imx-irqsteer.c | |||
@@ -144,7 +144,6 @@ static int imx_irqsteer_probe(struct platform_device *pdev) | |||
144 | { | 144 | { |
145 | struct device_node *np = pdev->dev.of_node; | 145 | struct device_node *np = pdev->dev.of_node; |
146 | struct irqsteer_data *data; | 146 | struct irqsteer_data *data; |
147 | struct resource *res; | ||
148 | u32 irqs_num; | 147 | u32 irqs_num; |
149 | int i, ret; | 148 | int i, ret; |
150 | 149 | ||
@@ -152,8 +151,7 @@ static int imx_irqsteer_probe(struct platform_device *pdev) | |||
152 | if (!data) | 151 | if (!data) |
153 | return -ENOMEM; | 152 | return -ENOMEM; |
154 | 153 | ||
155 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 154 | data->regs = devm_platform_ioremap_resource(pdev, 0); |
156 | data->regs = devm_ioremap_resource(&pdev->dev, res); | ||
157 | if (IS_ERR(data->regs)) { | 155 | if (IS_ERR(data->regs)) { |
158 | dev_err(&pdev->dev, "failed to initialize reg\n"); | 156 | dev_err(&pdev->dev, "failed to initialize reg\n"); |
159 | return PTR_ERR(data->regs); | 157 | return PTR_ERR(data->regs); |
diff --git a/drivers/irqchip/irq-ls-scfg-msi.c b/drivers/irqchip/irq-ls-scfg-msi.c index c671b3212010..669d29105772 100644 --- a/drivers/irqchip/irq-ls-scfg-msi.c +++ b/drivers/irqchip/irq-ls-scfg-msi.c | |||
@@ -100,7 +100,7 @@ static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg) | |||
100 | msg->data |= cpumask_first(mask); | 100 | msg->data |= cpumask_first(mask); |
101 | } | 101 | } |
102 | 102 | ||
103 | iommu_dma_map_msi_msg(data->irq, msg); | 103 | iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg); |
104 | } | 104 | } |
105 | 105 | ||
106 | static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, | 106 | static int ls_scfg_msi_set_affinity(struct irq_data *irq_data, |
@@ -141,6 +141,7 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, | |||
141 | unsigned int nr_irqs, | 141 | unsigned int nr_irqs, |
142 | void *args) | 142 | void *args) |
143 | { | 143 | { |
144 | msi_alloc_info_t *info = args; | ||
144 | struct ls_scfg_msi *msi_data = domain->host_data; | 145 | struct ls_scfg_msi *msi_data = domain->host_data; |
145 | int pos, err = 0; | 146 | int pos, err = 0; |
146 | 147 | ||
@@ -157,6 +158,10 @@ static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain, | |||
157 | if (err) | 158 | if (err) |
158 | return err; | 159 | return err; |
159 | 160 | ||
161 | err = iommu_dma_prepare_msi(info->desc, msi_data->msiir_addr); | ||
162 | if (err) | ||
163 | return err; | ||
164 | |||
160 | irq_domain_set_info(domain, virq, pos, | 165 | irq_domain_set_info(domain, virq, pos, |
161 | &ls_scfg_msi_parent_chip, msi_data, | 166 | &ls_scfg_msi_parent_chip, msi_data, |
162 | handle_simple_irq, NULL, NULL); | 167 | handle_simple_irq, NULL, NULL); |
diff --git a/drivers/irqchip/irq-renesas-intc-irqpin.c b/drivers/irqchip/irq-renesas-intc-irqpin.c index 8c039525703f..04c05a18600c 100644 --- a/drivers/irqchip/irq-renesas-intc-irqpin.c +++ b/drivers/irqchip/irq-renesas-intc-irqpin.c | |||
@@ -389,10 +389,8 @@ static int intc_irqpin_probe(struct platform_device *pdev) | |||
389 | int k; | 389 | int k; |
390 | 390 | ||
391 | p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); | 391 | p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); |
392 | if (!p) { | 392 | if (!p) |
393 | dev_err(dev, "failed to allocate driver data\n"); | ||
394 | return -ENOMEM; | 393 | return -ENOMEM; |
395 | } | ||
396 | 394 | ||
397 | /* deal with driver instance configuration */ | 395 | /* deal with driver instance configuration */ |
398 | of_property_read_u32(dev->of_node, "sense-bitfield-width", | 396 | of_property_read_u32(dev->of_node, "sense-bitfield-width", |
diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c index 7bd1d4cb2e19..e00f2fa27f00 100644 --- a/drivers/irqchip/irq-stm32-exti.c +++ b/drivers/irqchip/irq-stm32-exti.c | |||
@@ -14,8 +14,10 @@ | |||
14 | #include <linux/irqchip.h> | 14 | #include <linux/irqchip.h> |
15 | #include <linux/irqchip/chained_irq.h> | 15 | #include <linux/irqchip/chained_irq.h> |
16 | #include <linux/irqdomain.h> | 16 | #include <linux/irqdomain.h> |
17 | #include <linux/module.h> | ||
17 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
18 | #include <linux/of_irq.h> | 19 | #include <linux/of_irq.h> |
20 | #include <linux/of_platform.h> | ||
19 | #include <linux/syscore_ops.h> | 21 | #include <linux/syscore_ops.h> |
20 | 22 | ||
21 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 23 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
@@ -37,12 +39,6 @@ struct stm32_exti_bank { | |||
37 | 39 | ||
38 | #define UNDEF_REG ~0 | 40 | #define UNDEF_REG ~0 |
39 | 41 | ||
40 | enum stm32_exti_hwspinlock { | ||
41 | HWSPINLOCK_UNKNOWN, | ||
42 | HWSPINLOCK_NONE, | ||
43 | HWSPINLOCK_READY, | ||
44 | }; | ||
45 | |||
46 | struct stm32_desc_irq { | 42 | struct stm32_desc_irq { |
47 | u32 exti; | 43 | u32 exti; |
48 | u32 irq_parent; | 44 | u32 irq_parent; |
@@ -69,8 +65,6 @@ struct stm32_exti_host_data { | |||
69 | void __iomem *base; | 65 | void __iomem *base; |
70 | struct stm32_exti_chip_data *chips_data; | 66 | struct stm32_exti_chip_data *chips_data; |
71 | const struct stm32_exti_drv_data *drv_data; | 67 | const struct stm32_exti_drv_data *drv_data; |
72 | struct device_node *node; | ||
73 | enum stm32_exti_hwspinlock hwlock_state; | ||
74 | struct hwspinlock *hwlock; | 68 | struct hwspinlock *hwlock; |
75 | }; | 69 | }; |
76 | 70 | ||
@@ -285,49 +279,27 @@ static int stm32_exti_set_type(struct irq_data *d, | |||
285 | 279 | ||
286 | static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) | 280 | static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) |
287 | { | 281 | { |
288 | struct stm32_exti_host_data *host_data = chip_data->host_data; | 282 | int ret, timeout = 0; |
289 | struct hwspinlock *hwlock; | ||
290 | int id, ret = 0, timeout = 0; | ||
291 | |||
292 | /* first time, check for hwspinlock availability */ | ||
293 | if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) { | ||
294 | id = of_hwspin_lock_get_id(host_data->node, 0); | ||
295 | if (id >= 0) { | ||
296 | hwlock = hwspin_lock_request_specific(id); | ||
297 | if (hwlock) { | ||
298 | /* found valid hwspinlock */ | ||
299 | host_data->hwlock_state = HWSPINLOCK_READY; | ||
300 | host_data->hwlock = hwlock; | ||
301 | pr_debug("%s hwspinlock = %d\n", __func__, id); | ||
302 | } else { | ||
303 | host_data->hwlock_state = HWSPINLOCK_NONE; | ||
304 | } | ||
305 | } else if (id != -EPROBE_DEFER) { | ||
306 | host_data->hwlock_state = HWSPINLOCK_NONE; | ||
307 | } else { | ||
308 | /* hwspinlock driver shall be ready at that stage */ | ||
309 | ret = -EPROBE_DEFER; | ||
310 | } | ||
311 | } | ||
312 | 283 | ||
313 | if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) { | 284 | if (!chip_data->host_data->hwlock) |
314 | /* | 285 | return 0; |
315 | * Use the x_raw API since we are under spin_lock protection. | 286 | |
316 | * Do not use the x_timeout API because we are under irq_disable | 287 | /* |
317 | * mode (see __setup_irq()) | 288 | * Use the x_raw API since we are under spin_lock protection. |
318 | */ | 289 | * Do not use the x_timeout API because we are under irq_disable |
319 | do { | 290 | * mode (see __setup_irq()) |
320 | ret = hwspin_trylock_raw(host_data->hwlock); | 291 | */ |
321 | if (!ret) | 292 | do { |
322 | return 0; | 293 | ret = hwspin_trylock_raw(chip_data->host_data->hwlock); |
323 | 294 | if (!ret) | |
324 | udelay(HWSPNLCK_RETRY_DELAY); | 295 | return 0; |
325 | timeout += HWSPNLCK_RETRY_DELAY; | 296 | |
326 | } while (timeout < HWSPNLCK_TIMEOUT); | 297 | udelay(HWSPNLCK_RETRY_DELAY); |
327 | 298 | timeout += HWSPNLCK_RETRY_DELAY; | |
328 | if (ret == -EBUSY) | 299 | } while (timeout < HWSPNLCK_TIMEOUT); |
329 | ret = -ETIMEDOUT; | 300 | |
330 | } | 301 | if (ret == -EBUSY) |
302 | ret = -ETIMEDOUT; | ||
331 | 303 | ||
332 | if (ret) | 304 | if (ret) |
333 | pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); | 305 | pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); |
@@ -337,7 +309,7 @@ static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) | |||
337 | 309 | ||
338 | static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) | 310 | static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) |
339 | { | 311 | { |
340 | if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY)) | 312 | if (chip_data->host_data->hwlock) |
341 | hwspin_unlock_raw(chip_data->host_data->hwlock); | 313 | hwspin_unlock_raw(chip_data->host_data->hwlock); |
342 | } | 314 | } |
343 | 315 | ||
@@ -586,8 +558,7 @@ static int stm32_exti_h_set_affinity(struct irq_data *d, | |||
586 | return -EINVAL; | 558 | return -EINVAL; |
587 | } | 559 | } |
588 | 560 | ||
589 | #ifdef CONFIG_PM | 561 | static int __maybe_unused stm32_exti_h_suspend(void) |
590 | static int stm32_exti_h_suspend(void) | ||
591 | { | 562 | { |
592 | struct stm32_exti_chip_data *chip_data; | 563 | struct stm32_exti_chip_data *chip_data; |
593 | int i; | 564 | int i; |
@@ -602,7 +573,7 @@ static int stm32_exti_h_suspend(void) | |||
602 | return 0; | 573 | return 0; |
603 | } | 574 | } |
604 | 575 | ||
605 | static void stm32_exti_h_resume(void) | 576 | static void __maybe_unused stm32_exti_h_resume(void) |
606 | { | 577 | { |
607 | struct stm32_exti_chip_data *chip_data; | 578 | struct stm32_exti_chip_data *chip_data; |
608 | int i; | 579 | int i; |
@@ -616,17 +587,22 @@ static void stm32_exti_h_resume(void) | |||
616 | } | 587 | } |
617 | 588 | ||
618 | static struct syscore_ops stm32_exti_h_syscore_ops = { | 589 | static struct syscore_ops stm32_exti_h_syscore_ops = { |
590 | #ifdef CONFIG_PM_SLEEP | ||
619 | .suspend = stm32_exti_h_suspend, | 591 | .suspend = stm32_exti_h_suspend, |
620 | .resume = stm32_exti_h_resume, | 592 | .resume = stm32_exti_h_resume, |
593 | #endif | ||
621 | }; | 594 | }; |
622 | 595 | ||
623 | static void stm32_exti_h_syscore_init(void) | 596 | static void stm32_exti_h_syscore_init(struct stm32_exti_host_data *host_data) |
624 | { | 597 | { |
598 | stm32_host_data = host_data; | ||
625 | register_syscore_ops(&stm32_exti_h_syscore_ops); | 599 | register_syscore_ops(&stm32_exti_h_syscore_ops); |
626 | } | 600 | } |
627 | #else | 601 | |
628 | static inline void stm32_exti_h_syscore_init(void) {} | 602 | static void stm32_exti_h_syscore_deinit(void) |
629 | #endif | 603 | { |
604 | unregister_syscore_ops(&stm32_exti_h_syscore_ops); | ||
605 | } | ||
630 | 606 | ||
631 | static struct irq_chip stm32_exti_h_chip = { | 607 | static struct irq_chip stm32_exti_h_chip = { |
632 | .name = "stm32-exti-h", | 608 | .name = "stm32-exti-h", |
@@ -683,8 +659,6 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, | |||
683 | return NULL; | 659 | return NULL; |
684 | 660 | ||
685 | host_data->drv_data = dd; | 661 | host_data->drv_data = dd; |
686 | host_data->node = node; | ||
687 | host_data->hwlock_state = HWSPINLOCK_UNKNOWN; | ||
688 | host_data->chips_data = kcalloc(dd->bank_nr, | 662 | host_data->chips_data = kcalloc(dd->bank_nr, |
689 | sizeof(struct stm32_exti_chip_data), | 663 | sizeof(struct stm32_exti_chip_data), |
690 | GFP_KERNEL); | 664 | GFP_KERNEL); |
@@ -711,7 +685,8 @@ free_host_data: | |||
711 | 685 | ||
712 | static struct | 686 | static struct |
713 | stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, | 687 | stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, |
714 | u32 bank_idx) | 688 | u32 bank_idx, |
689 | struct device_node *node) | ||
715 | { | 690 | { |
716 | const struct stm32_exti_bank *stm32_bank; | 691 | const struct stm32_exti_bank *stm32_bank; |
717 | struct stm32_exti_chip_data *chip_data; | 692 | struct stm32_exti_chip_data *chip_data; |
@@ -731,7 +706,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, | |||
731 | writel_relaxed(0, base + stm32_bank->imr_ofst); | 706 | writel_relaxed(0, base + stm32_bank->imr_ofst); |
732 | writel_relaxed(0, base + stm32_bank->emr_ofst); | 707 | writel_relaxed(0, base + stm32_bank->emr_ofst); |
733 | 708 | ||
734 | pr_info("%pOF: bank%d\n", h_data->node, bank_idx); | 709 | pr_info("%pOF: bank%d\n", node, bank_idx); |
735 | 710 | ||
736 | return chip_data; | 711 | return chip_data; |
737 | } | 712 | } |
@@ -771,7 +746,7 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, | |||
771 | struct stm32_exti_chip_data *chip_data; | 746 | struct stm32_exti_chip_data *chip_data; |
772 | 747 | ||
773 | stm32_bank = drv_data->exti_banks[i]; | 748 | stm32_bank = drv_data->exti_banks[i]; |
774 | chip_data = stm32_exti_chip_init(host_data, i); | 749 | chip_data = stm32_exti_chip_init(host_data, i, node); |
775 | 750 | ||
776 | gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); | 751 | gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); |
777 | 752 | ||
@@ -815,50 +790,130 @@ static const struct irq_domain_ops stm32_exti_h_domain_ops = { | |||
815 | .xlate = irq_domain_xlate_twocell, | 790 | .xlate = irq_domain_xlate_twocell, |
816 | }; | 791 | }; |
817 | 792 | ||
818 | static int | 793 | static void stm32_exti_remove_irq(void *data) |
819 | __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, | 794 | { |
820 | struct device_node *node, | 795 | struct irq_domain *domain = data; |
821 | struct device_node *parent) | 796 | |
797 | irq_domain_remove(domain); | ||
798 | } | ||
799 | |||
800 | static int stm32_exti_remove(struct platform_device *pdev) | ||
801 | { | ||
802 | stm32_exti_h_syscore_deinit(); | ||
803 | return 0; | ||
804 | } | ||
805 | |||
806 | static int stm32_exti_probe(struct platform_device *pdev) | ||
822 | { | 807 | { |
808 | int ret, i; | ||
809 | struct device *dev = &pdev->dev; | ||
810 | struct device_node *np = dev->of_node; | ||
823 | struct irq_domain *parent_domain, *domain; | 811 | struct irq_domain *parent_domain, *domain; |
824 | struct stm32_exti_host_data *host_data; | 812 | struct stm32_exti_host_data *host_data; |
825 | int ret, i; | 813 | const struct stm32_exti_drv_data *drv_data; |
814 | struct resource *res; | ||
826 | 815 | ||
827 | parent_domain = irq_find_host(parent); | 816 | host_data = devm_kzalloc(dev, sizeof(*host_data), GFP_KERNEL); |
828 | if (!parent_domain) { | 817 | if (!host_data) |
829 | pr_err("interrupt-parent not found\n"); | 818 | return -ENOMEM; |
830 | return -EINVAL; | 819 | |
820 | /* check for optional hwspinlock which may be not available yet */ | ||
821 | ret = of_hwspin_lock_get_id(np, 0); | ||
822 | if (ret == -EPROBE_DEFER) | ||
823 | /* hwspinlock framework not yet ready */ | ||
824 | return ret; | ||
825 | |||
826 | if (ret >= 0) { | ||
827 | host_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); | ||
828 | if (!host_data->hwlock) { | ||
829 | dev_err(dev, "Failed to request hwspinlock\n"); | ||
830 | return -EINVAL; | ||
831 | } | ||
832 | } else if (ret != -ENOENT) { | ||
833 | /* note: ENOENT is a valid case (means 'no hwspinlock') */ | ||
834 | dev_err(dev, "Failed to get hwspinlock\n"); | ||
835 | return ret; | ||
831 | } | 836 | } |
832 | 837 | ||
833 | host_data = stm32_exti_host_init(drv_data, node); | 838 | /* initialize host_data */ |
834 | if (!host_data) | 839 | drv_data = of_device_get_match_data(dev); |
840 | if (!drv_data) { | ||
841 | dev_err(dev, "no of match data\n"); | ||
842 | return -ENODEV; | ||
843 | } | ||
844 | host_data->drv_data = drv_data; | ||
845 | |||
846 | host_data->chips_data = devm_kcalloc(dev, drv_data->bank_nr, | ||
847 | sizeof(*host_data->chips_data), | ||
848 | GFP_KERNEL); | ||
849 | if (!host_data->chips_data) | ||
835 | return -ENOMEM; | 850 | return -ENOMEM; |
836 | 851 | ||
852 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
853 | host_data->base = devm_ioremap_resource(dev, res); | ||
854 | if (IS_ERR(host_data->base)) { | ||
855 | dev_err(dev, "Unable to map registers\n"); | ||
856 | return PTR_ERR(host_data->base); | ||
857 | } | ||
858 | |||
837 | for (i = 0; i < drv_data->bank_nr; i++) | 859 | for (i = 0; i < drv_data->bank_nr; i++) |
838 | stm32_exti_chip_init(host_data, i); | 860 | stm32_exti_chip_init(host_data, i, np); |
861 | |||
862 | parent_domain = irq_find_host(of_irq_find_parent(np)); | ||
863 | if (!parent_domain) { | ||
864 | dev_err(dev, "GIC interrupt-parent not found\n"); | ||
865 | return -EINVAL; | ||
866 | } | ||
839 | 867 | ||
840 | domain = irq_domain_add_hierarchy(parent_domain, 0, | 868 | domain = irq_domain_add_hierarchy(parent_domain, 0, |
841 | drv_data->bank_nr * IRQS_PER_BANK, | 869 | drv_data->bank_nr * IRQS_PER_BANK, |
842 | node, &stm32_exti_h_domain_ops, | 870 | np, &stm32_exti_h_domain_ops, |
843 | host_data); | 871 | host_data); |
844 | 872 | ||
845 | if (!domain) { | 873 | if (!domain) { |
846 | pr_err("%pOFn: Could not register exti domain.\n", node); | 874 | dev_err(dev, "Could not register exti domain\n"); |
847 | ret = -ENOMEM; | 875 | return -ENOMEM; |
848 | goto out_unmap; | ||
849 | } | 876 | } |
850 | 877 | ||
851 | stm32_exti_h_syscore_init(); | 878 | ret = devm_add_action_or_reset(dev, stm32_exti_remove_irq, domain); |
879 | if (ret) | ||
880 | return ret; | ||
881 | |||
882 | stm32_exti_h_syscore_init(host_data); | ||
852 | 883 | ||
853 | return 0; | 884 | return 0; |
885 | } | ||
854 | 886 | ||
855 | out_unmap: | 887 | /* platform driver only for MP1 */ |
856 | iounmap(host_data->base); | 888 | static const struct of_device_id stm32_exti_ids[] = { |
857 | kfree(host_data->chips_data); | 889 | { .compatible = "st,stm32mp1-exti", .data = &stm32mp1_drv_data}, |
858 | kfree(host_data); | 890 | {}, |
859 | return ret; | 891 | }; |
892 | MODULE_DEVICE_TABLE(of, stm32_exti_ids); | ||
893 | |||
894 | static struct platform_driver stm32_exti_driver = { | ||
895 | .probe = stm32_exti_probe, | ||
896 | .remove = stm32_exti_remove, | ||
897 | .driver = { | ||
898 | .name = "stm32_exti", | ||
899 | .of_match_table = stm32_exti_ids, | ||
900 | }, | ||
901 | }; | ||
902 | |||
903 | static int __init stm32_exti_arch_init(void) | ||
904 | { | ||
905 | return platform_driver_register(&stm32_exti_driver); | ||
860 | } | 906 | } |
861 | 907 | ||
908 | static void __exit stm32_exti_arch_exit(void) | ||
909 | { | ||
910 | return platform_driver_unregister(&stm32_exti_driver); | ||
911 | } | ||
912 | |||
913 | arch_initcall(stm32_exti_arch_init); | ||
914 | module_exit(stm32_exti_arch_exit); | ||
915 | |||
916 | /* no platform driver for F4 and H7 */ | ||
862 | static int __init stm32f4_exti_of_init(struct device_node *np, | 917 | static int __init stm32f4_exti_of_init(struct device_node *np, |
863 | struct device_node *parent) | 918 | struct device_node *parent) |
864 | { | 919 | { |
@@ -874,11 +929,3 @@ static int __init stm32h7_exti_of_init(struct device_node *np, | |||
874 | } | 929 | } |
875 | 930 | ||
876 | IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); | 931 | IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); |
877 | |||
878 | static int __init stm32mp1_exti_of_init(struct device_node *np, | ||
879 | struct device_node *parent) | ||
880 | { | ||
881 | return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent); | ||
882 | } | ||
883 | |||
884 | IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init); | ||
diff --git a/drivers/irqchip/irq-ti-sci-inta.c b/drivers/irqchip/irq-ti-sci-inta.c new file mode 100644 index 000000000000..011b60a49e3f --- /dev/null +++ b/drivers/irqchip/irq-ti-sci-inta.c | |||
@@ -0,0 +1,615 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Texas Instruments' K3 Interrupt Aggregator irqchip driver | ||
4 | * | ||
5 | * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ | ||
6 | * Lokesh Vutla <lokeshvutla@ti.com> | ||
7 | */ | ||
8 | |||
9 | #include <linux/err.h> | ||
10 | #include <linux/io.h> | ||
11 | #include <linux/irqchip.h> | ||
12 | #include <linux/irqdomain.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/msi.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/moduleparam.h> | ||
17 | #include <linux/of_address.h> | ||
18 | #include <linux/of_irq.h> | ||
19 | #include <linux/of_platform.h> | ||
20 | #include <linux/irqchip/chained_irq.h> | ||
21 | #include <linux/soc/ti/ti_sci_inta_msi.h> | ||
22 | #include <linux/soc/ti/ti_sci_protocol.h> | ||
23 | #include <asm-generic/msi.h> | ||
24 | |||
25 | #define TI_SCI_DEV_ID_MASK 0xffff | ||
26 | #define TI_SCI_DEV_ID_SHIFT 16 | ||
27 | #define TI_SCI_IRQ_ID_MASK 0xffff | ||
28 | #define TI_SCI_IRQ_ID_SHIFT 0 | ||
29 | #define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ | ||
30 | (TI_SCI_DEV_ID_MASK)) | ||
31 | #define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) | ||
32 | #define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \ | ||
33 | TI_SCI_DEV_ID_SHIFT) | \ | ||
34 | ((index) & TI_SCI_IRQ_ID_MASK)) | ||
35 | |||
36 | #define MAX_EVENTS_PER_VINT 64 | ||
37 | #define VINT_ENABLE_SET_OFFSET 0x0 | ||
38 | #define VINT_ENABLE_CLR_OFFSET 0x8 | ||
39 | #define VINT_STATUS_OFFSET 0x18 | ||
40 | |||
41 | /** | ||
42 | * struct ti_sci_inta_event_desc - Description of an event coming to | ||
43 | * Interrupt Aggregator. This serves | ||
44 | * as a mapping table for global event, | ||
45 | * hwirq and vint bit. | ||
46 | * @global_event: Global event number corresponding to this event | ||
47 | * @hwirq: Hwirq of the incoming interrupt | ||
48 | * @vint_bit: Corresponding vint bit to which this event is attached. | ||
49 | */ | ||
50 | struct ti_sci_inta_event_desc { | ||
51 | u16 global_event; | ||
52 | u32 hwirq; | ||
53 | u8 vint_bit; | ||
54 | }; | ||
55 | |||
56 | /** | ||
57 | * struct ti_sci_inta_vint_desc - Description of a virtual interrupt coming out | ||
58 | * of Interrupt Aggregator. | ||
59 | * @domain: Pointer to IRQ domain to which this vint belongs. | ||
60 | * @list: List entry for the vint list | ||
61 | * @event_map: Bitmap to manage the allocation of events to vint. | ||
62 | * @events: Array of event descriptors assigned to this vint. | ||
63 | * @parent_virq: Linux IRQ number that gets attached to parent | ||
64 | * @vint_id: TISCI vint ID | ||
65 | */ | ||
66 | struct ti_sci_inta_vint_desc { | ||
67 | struct irq_domain *domain; | ||
68 | struct list_head list; | ||
69 | DECLARE_BITMAP(event_map, MAX_EVENTS_PER_VINT); | ||
70 | struct ti_sci_inta_event_desc events[MAX_EVENTS_PER_VINT]; | ||
71 | unsigned int parent_virq; | ||
72 | u16 vint_id; | ||
73 | }; | ||
74 | |||
75 | /** | ||
76 | * struct ti_sci_inta_irq_domain - Structure representing a TISCI based | ||
77 | * Interrupt Aggregator IRQ domain. | ||
78 | * @sci: Pointer to TISCI handle | ||
79 | * @vint: TISCI resource pointer representing IA inerrupts. | ||
80 | * @global_event: TISCI resource pointer representing global events. | ||
81 | * @vint_list: List of the vints active in the system | ||
82 | * @vint_mutex: Mutex to protect vint_list | ||
83 | * @base: Base address of the memory mapped IO registers | ||
84 | * @pdev: Pointer to platform device. | ||
85 | */ | ||
86 | struct ti_sci_inta_irq_domain { | ||
87 | const struct ti_sci_handle *sci; | ||
88 | struct ti_sci_resource *vint; | ||
89 | struct ti_sci_resource *global_event; | ||
90 | struct list_head vint_list; | ||
91 | /* Mutex to protect vint list */ | ||
92 | struct mutex vint_mutex; | ||
93 | void __iomem *base; | ||
94 | struct platform_device *pdev; | ||
95 | }; | ||
96 | |||
97 | #define to_vint_desc(e, i) container_of(e, struct ti_sci_inta_vint_desc, \ | ||
98 | events[i]) | ||
99 | |||
100 | /** | ||
101 | * ti_sci_inta_irq_handler() - Chained IRQ handler for the vint irqs | ||
102 | * @desc: Pointer to irq_desc corresponding to the irq | ||
103 | */ | ||
104 | static void ti_sci_inta_irq_handler(struct irq_desc *desc) | ||
105 | { | ||
106 | struct ti_sci_inta_vint_desc *vint_desc; | ||
107 | struct ti_sci_inta_irq_domain *inta; | ||
108 | struct irq_domain *domain; | ||
109 | unsigned int virq, bit; | ||
110 | unsigned long val; | ||
111 | |||
112 | vint_desc = irq_desc_get_handler_data(desc); | ||
113 | domain = vint_desc->domain; | ||
114 | inta = domain->host_data; | ||
115 | |||
116 | chained_irq_enter(irq_desc_get_chip(desc), desc); | ||
117 | |||
118 | val = readq_relaxed(inta->base + vint_desc->vint_id * 0x1000 + | ||
119 | VINT_STATUS_OFFSET); | ||
120 | |||
121 | for_each_set_bit(bit, &val, MAX_EVENTS_PER_VINT) { | ||
122 | virq = irq_find_mapping(domain, vint_desc->events[bit].hwirq); | ||
123 | if (virq) | ||
124 | generic_handle_irq(virq); | ||
125 | } | ||
126 | |||
127 | chained_irq_exit(irq_desc_get_chip(desc), desc); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * ti_sci_inta_alloc_parent_irq() - Allocate parent irq to Interrupt aggregator | ||
132 | * @domain: IRQ domain corresponding to Interrupt Aggregator | ||
133 | * | ||
134 | * Return 0 if all went well else corresponding error value. | ||
135 | */ | ||
136 | static struct ti_sci_inta_vint_desc *ti_sci_inta_alloc_parent_irq(struct irq_domain *domain) | ||
137 | { | ||
138 | struct ti_sci_inta_irq_domain *inta = domain->host_data; | ||
139 | struct ti_sci_inta_vint_desc *vint_desc; | ||
140 | struct irq_fwspec parent_fwspec; | ||
141 | unsigned int parent_virq; | ||
142 | u16 vint_id; | ||
143 | |||
144 | vint_id = ti_sci_get_free_resource(inta->vint); | ||
145 | if (vint_id == TI_SCI_RESOURCE_NULL) | ||
146 | return ERR_PTR(-EINVAL); | ||
147 | |||
148 | vint_desc = kzalloc(sizeof(*vint_desc), GFP_KERNEL); | ||
149 | if (!vint_desc) | ||
150 | return ERR_PTR(-ENOMEM); | ||
151 | |||
152 | vint_desc->domain = domain; | ||
153 | vint_desc->vint_id = vint_id; | ||
154 | INIT_LIST_HEAD(&vint_desc->list); | ||
155 | |||
156 | parent_fwspec.fwnode = of_node_to_fwnode(of_irq_find_parent(dev_of_node(&inta->pdev->dev))); | ||
157 | parent_fwspec.param_count = 2; | ||
158 | parent_fwspec.param[0] = inta->pdev->id; | ||
159 | parent_fwspec.param[1] = vint_desc->vint_id; | ||
160 | |||
161 | parent_virq = irq_create_fwspec_mapping(&parent_fwspec); | ||
162 | if (parent_virq <= 0) { | ||
163 | kfree(vint_desc); | ||
164 | return ERR_PTR(parent_virq); | ||
165 | } | ||
166 | vint_desc->parent_virq = parent_virq; | ||
167 | |||
168 | list_add_tail(&vint_desc->list, &inta->vint_list); | ||
169 | irq_set_chained_handler_and_data(vint_desc->parent_virq, | ||
170 | ti_sci_inta_irq_handler, vint_desc); | ||
171 | |||
172 | return vint_desc; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * ti_sci_inta_alloc_event() - Attach an event to a IA vint. | ||
177 | * @vint_desc: Pointer to vint_desc to which the event gets attached | ||
178 | * @free_bit: Bit inside vint to which event gets attached | ||
179 | * @hwirq: hwirq of the input event | ||
180 | * | ||
181 | * Return event_desc pointer if all went ok else appropriate error value. | ||
182 | */ | ||
183 | static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_event(struct ti_sci_inta_vint_desc *vint_desc, | ||
184 | u16 free_bit, | ||
185 | u32 hwirq) | ||
186 | { | ||
187 | struct ti_sci_inta_irq_domain *inta = vint_desc->domain->host_data; | ||
188 | struct ti_sci_inta_event_desc *event_desc; | ||
189 | u16 dev_id, dev_index; | ||
190 | int err; | ||
191 | |||
192 | dev_id = HWIRQ_TO_DEVID(hwirq); | ||
193 | dev_index = HWIRQ_TO_IRQID(hwirq); | ||
194 | |||
195 | event_desc = &vint_desc->events[free_bit]; | ||
196 | event_desc->hwirq = hwirq; | ||
197 | event_desc->vint_bit = free_bit; | ||
198 | event_desc->global_event = ti_sci_get_free_resource(inta->global_event); | ||
199 | if (event_desc->global_event == TI_SCI_RESOURCE_NULL) | ||
200 | return ERR_PTR(-EINVAL); | ||
201 | |||
202 | err = inta->sci->ops.rm_irq_ops.set_event_map(inta->sci, | ||
203 | dev_id, dev_index, | ||
204 | inta->pdev->id, | ||
205 | vint_desc->vint_id, | ||
206 | event_desc->global_event, | ||
207 | free_bit); | ||
208 | if (err) | ||
209 | goto free_global_event; | ||
210 | |||
211 | return event_desc; | ||
212 | free_global_event: | ||
213 | ti_sci_release_resource(inta->global_event, event_desc->global_event); | ||
214 | return ERR_PTR(err); | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * ti_sci_inta_alloc_irq() - Allocate an irq within INTA domain | ||
219 | * @domain: irq_domain pointer corresponding to INTA | ||
220 | * @hwirq: hwirq of the input event | ||
221 | * | ||
222 | * Note: Allocation happens in the following manner: | ||
223 | * - Find a free bit available in any of the vints available in the list. | ||
224 | * - If not found, allocate a vint from the vint pool | ||
225 | * - Attach the free bit to input hwirq. | ||
226 | * Return event_desc if all went ok else appropriate error value. | ||
227 | */ | ||
228 | static struct ti_sci_inta_event_desc *ti_sci_inta_alloc_irq(struct irq_domain *domain, | ||
229 | u32 hwirq) | ||
230 | { | ||
231 | struct ti_sci_inta_irq_domain *inta = domain->host_data; | ||
232 | struct ti_sci_inta_vint_desc *vint_desc = NULL; | ||
233 | struct ti_sci_inta_event_desc *event_desc; | ||
234 | u16 free_bit; | ||
235 | |||
236 | mutex_lock(&inta->vint_mutex); | ||
237 | list_for_each_entry(vint_desc, &inta->vint_list, list) { | ||
238 | free_bit = find_first_zero_bit(vint_desc->event_map, | ||
239 | MAX_EVENTS_PER_VINT); | ||
240 | if (free_bit != MAX_EVENTS_PER_VINT) { | ||
241 | set_bit(free_bit, vint_desc->event_map); | ||
242 | goto alloc_event; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | /* No free bits available. Allocate a new vint */ | ||
247 | vint_desc = ti_sci_inta_alloc_parent_irq(domain); | ||
248 | if (IS_ERR(vint_desc)) { | ||
249 | mutex_unlock(&inta->vint_mutex); | ||
250 | return ERR_PTR(PTR_ERR(vint_desc)); | ||
251 | } | ||
252 | |||
253 | free_bit = find_first_zero_bit(vint_desc->event_map, | ||
254 | MAX_EVENTS_PER_VINT); | ||
255 | set_bit(free_bit, vint_desc->event_map); | ||
256 | |||
257 | alloc_event: | ||
258 | event_desc = ti_sci_inta_alloc_event(vint_desc, free_bit, hwirq); | ||
259 | if (IS_ERR(event_desc)) | ||
260 | clear_bit(free_bit, vint_desc->event_map); | ||
261 | |||
262 | mutex_unlock(&inta->vint_mutex); | ||
263 | return event_desc; | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * ti_sci_inta_free_parent_irq() - Free a parent irq to INTA | ||
268 | * @inta: Pointer to inta domain. | ||
269 | * @vint_desc: Pointer to vint_desc that needs to be freed. | ||
270 | */ | ||
271 | static void ti_sci_inta_free_parent_irq(struct ti_sci_inta_irq_domain *inta, | ||
272 | struct ti_sci_inta_vint_desc *vint_desc) | ||
273 | { | ||
274 | if (find_first_bit(vint_desc->event_map, MAX_EVENTS_PER_VINT) == MAX_EVENTS_PER_VINT) { | ||
275 | list_del(&vint_desc->list); | ||
276 | ti_sci_release_resource(inta->vint, vint_desc->vint_id); | ||
277 | irq_dispose_mapping(vint_desc->parent_virq); | ||
278 | kfree(vint_desc); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * ti_sci_inta_free_irq() - Free an IRQ within INTA domain | ||
284 | * @event_desc: Pointer to event_desc that needs to be freed. | ||
285 | * @hwirq: Hwirq number within INTA domain that needs to be freed | ||
286 | */ | ||
287 | static void ti_sci_inta_free_irq(struct ti_sci_inta_event_desc *event_desc, | ||
288 | u32 hwirq) | ||
289 | { | ||
290 | struct ti_sci_inta_vint_desc *vint_desc; | ||
291 | struct ti_sci_inta_irq_domain *inta; | ||
292 | |||
293 | vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); | ||
294 | inta = vint_desc->domain->host_data; | ||
295 | /* free event irq */ | ||
296 | mutex_lock(&inta->vint_mutex); | ||
297 | inta->sci->ops.rm_irq_ops.free_event_map(inta->sci, | ||
298 | HWIRQ_TO_DEVID(hwirq), | ||
299 | HWIRQ_TO_IRQID(hwirq), | ||
300 | inta->pdev->id, | ||
301 | vint_desc->vint_id, | ||
302 | event_desc->global_event, | ||
303 | event_desc->vint_bit); | ||
304 | |||
305 | clear_bit(event_desc->vint_bit, vint_desc->event_map); | ||
306 | ti_sci_release_resource(inta->global_event, event_desc->global_event); | ||
307 | event_desc->global_event = TI_SCI_RESOURCE_NULL; | ||
308 | event_desc->hwirq = 0; | ||
309 | |||
310 | ti_sci_inta_free_parent_irq(inta, vint_desc); | ||
311 | mutex_unlock(&inta->vint_mutex); | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * ti_sci_inta_request_resources() - Allocate resources for input irq | ||
316 | * @data: Pointer to corresponding irq_data | ||
317 | * | ||
318 | * Note: This is the core api where the actual allocation happens for input | ||
319 | * hwirq. This allocation involves creating a parent irq for vint. | ||
320 | * If this is done in irq_domain_ops.alloc() then a deadlock is reached | ||
321 | * for allocation. So this allocation is being done in request_resources() | ||
322 | * | ||
323 | * Return: 0 if all went well else corresponding error. | ||
324 | */ | ||
325 | static int ti_sci_inta_request_resources(struct irq_data *data) | ||
326 | { | ||
327 | struct ti_sci_inta_event_desc *event_desc; | ||
328 | |||
329 | event_desc = ti_sci_inta_alloc_irq(data->domain, data->hwirq); | ||
330 | if (IS_ERR(event_desc)) | ||
331 | return PTR_ERR(event_desc); | ||
332 | |||
333 | data->chip_data = event_desc; | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * ti_sci_inta_release_resources - Release resources for input irq | ||
340 | * @data: Pointer to corresponding irq_data | ||
341 | * | ||
342 | * Note: Corresponding to request_resources(), all the unmapping and deletion | ||
343 | * of parent vint irqs happens in this api. | ||
344 | */ | ||
345 | static void ti_sci_inta_release_resources(struct irq_data *data) | ||
346 | { | ||
347 | struct ti_sci_inta_event_desc *event_desc; | ||
348 | |||
349 | event_desc = irq_data_get_irq_chip_data(data); | ||
350 | ti_sci_inta_free_irq(event_desc, data->hwirq); | ||
351 | } | ||
352 | |||
353 | /** | ||
354 | * ti_sci_inta_manage_event() - Control the event based on the offset | ||
355 | * @data: Pointer to corresponding irq_data | ||
356 | * @offset: register offset using which event is controlled. | ||
357 | */ | ||
358 | static void ti_sci_inta_manage_event(struct irq_data *data, u32 offset) | ||
359 | { | ||
360 | struct ti_sci_inta_event_desc *event_desc; | ||
361 | struct ti_sci_inta_vint_desc *vint_desc; | ||
362 | struct ti_sci_inta_irq_domain *inta; | ||
363 | |||
364 | event_desc = irq_data_get_irq_chip_data(data); | ||
365 | vint_desc = to_vint_desc(event_desc, event_desc->vint_bit); | ||
366 | inta = data->domain->host_data; | ||
367 | |||
368 | writeq_relaxed(BIT(event_desc->vint_bit), | ||
369 | inta->base + vint_desc->vint_id * 0x1000 + offset); | ||
370 | } | ||
371 | |||
372 | /** | ||
373 | * ti_sci_inta_mask_irq() - Mask an event | ||
374 | * @data: Pointer to corresponding irq_data | ||
375 | */ | ||
376 | static void ti_sci_inta_mask_irq(struct irq_data *data) | ||
377 | { | ||
378 | ti_sci_inta_manage_event(data, VINT_ENABLE_CLR_OFFSET); | ||
379 | } | ||
380 | |||
381 | /** | ||
382 | * ti_sci_inta_unmask_irq() - Unmask an event | ||
383 | * @data: Pointer to corresponding irq_data | ||
384 | */ | ||
385 | static void ti_sci_inta_unmask_irq(struct irq_data *data) | ||
386 | { | ||
387 | ti_sci_inta_manage_event(data, VINT_ENABLE_SET_OFFSET); | ||
388 | } | ||
389 | |||
390 | /** | ||
391 | * ti_sci_inta_ack_irq() - Ack an event | ||
392 | * @data: Pointer to corresponding irq_data | ||
393 | */ | ||
394 | static void ti_sci_inta_ack_irq(struct irq_data *data) | ||
395 | { | ||
396 | /* | ||
397 | * Do not clear the event if hardware is capable of sending | ||
398 | * a down event. | ||
399 | */ | ||
400 | if (irqd_get_trigger_type(data) != IRQF_TRIGGER_HIGH) | ||
401 | ti_sci_inta_manage_event(data, VINT_STATUS_OFFSET); | ||
402 | } | ||
403 | |||
404 | static int ti_sci_inta_set_affinity(struct irq_data *d, | ||
405 | const struct cpumask *mask_val, bool force) | ||
406 | { | ||
407 | return -EINVAL; | ||
408 | } | ||
409 | |||
410 | /** | ||
411 | * ti_sci_inta_set_type() - Update the trigger type of the irq. | ||
412 | * @data: Pointer to corresponding irq_data | ||
413 | * @type: Trigger type as specified by user | ||
414 | * | ||
415 | * Note: This updates the handle_irq callback for level msi. | ||
416 | * | ||
417 | * Return 0 if all went well else appropriate error. | ||
418 | */ | ||
419 | static int ti_sci_inta_set_type(struct irq_data *data, unsigned int type) | ||
420 | { | ||
421 | /* | ||
422 | * .alloc default sets handle_edge_irq. But if the user specifies | ||
423 | * that IRQ is level MSI, then update the handle to handle_level_irq | ||
424 | */ | ||
425 | switch (type & IRQ_TYPE_SENSE_MASK) { | ||
426 | case IRQF_TRIGGER_HIGH: | ||
427 | irq_set_handler_locked(data, handle_level_irq); | ||
428 | return 0; | ||
429 | case IRQF_TRIGGER_RISING: | ||
430 | return 0; | ||
431 | default: | ||
432 | return -EINVAL; | ||
433 | } | ||
434 | |||
435 | return -EINVAL; | ||
436 | } | ||
437 | |||
438 | static struct irq_chip ti_sci_inta_irq_chip = { | ||
439 | .name = "INTA", | ||
440 | .irq_ack = ti_sci_inta_ack_irq, | ||
441 | .irq_mask = ti_sci_inta_mask_irq, | ||
442 | .irq_set_type = ti_sci_inta_set_type, | ||
443 | .irq_unmask = ti_sci_inta_unmask_irq, | ||
444 | .irq_set_affinity = ti_sci_inta_set_affinity, | ||
445 | .irq_request_resources = ti_sci_inta_request_resources, | ||
446 | .irq_release_resources = ti_sci_inta_release_resources, | ||
447 | }; | ||
448 | |||
449 | /** | ||
450 | * ti_sci_inta_irq_domain_free() - Free an IRQ from the IRQ domain | ||
451 | * @domain: Domain to which the irqs belong | ||
452 | * @virq: base linux virtual IRQ to be freed. | ||
453 | * @nr_irqs: Number of continuous irqs to be freed | ||
454 | */ | ||
455 | static void ti_sci_inta_irq_domain_free(struct irq_domain *domain, | ||
456 | unsigned int virq, unsigned int nr_irqs) | ||
457 | { | ||
458 | struct irq_data *data = irq_domain_get_irq_data(domain, virq); | ||
459 | |||
460 | irq_domain_reset_irq_data(data); | ||
461 | } | ||
462 | |||
463 | /** | ||
464 | * ti_sci_inta_irq_domain_alloc() - Allocate Interrupt aggregator IRQs | ||
465 | * @domain: Point to the interrupt aggregator IRQ domain | ||
466 | * @virq: Corresponding Linux virtual IRQ number | ||
467 | * @nr_irqs: Continuous irqs to be allocated | ||
468 | * @data: Pointer to firmware specifier | ||
469 | * | ||
470 | * No actual allocation happens here. | ||
471 | * | ||
472 | * Return 0 if all went well else appropriate error value. | ||
473 | */ | ||
474 | static int ti_sci_inta_irq_domain_alloc(struct irq_domain *domain, | ||
475 | unsigned int virq, unsigned int nr_irqs, | ||
476 | void *data) | ||
477 | { | ||
478 | msi_alloc_info_t *arg = data; | ||
479 | |||
480 | irq_domain_set_info(domain, virq, arg->hwirq, &ti_sci_inta_irq_chip, | ||
481 | NULL, handle_edge_irq, NULL, NULL); | ||
482 | |||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static const struct irq_domain_ops ti_sci_inta_irq_domain_ops = { | ||
487 | .free = ti_sci_inta_irq_domain_free, | ||
488 | .alloc = ti_sci_inta_irq_domain_alloc, | ||
489 | }; | ||
490 | |||
491 | static struct irq_chip ti_sci_inta_msi_irq_chip = { | ||
492 | .name = "MSI-INTA", | ||
493 | .flags = IRQCHIP_SUPPORTS_LEVEL_MSI, | ||
494 | }; | ||
495 | |||
496 | static void ti_sci_inta_msi_set_desc(msi_alloc_info_t *arg, | ||
497 | struct msi_desc *desc) | ||
498 | { | ||
499 | struct platform_device *pdev = to_platform_device(desc->dev); | ||
500 | |||
501 | arg->desc = desc; | ||
502 | arg->hwirq = TO_HWIRQ(pdev->id, desc->inta.dev_index); | ||
503 | } | ||
504 | |||
505 | static struct msi_domain_ops ti_sci_inta_msi_ops = { | ||
506 | .set_desc = ti_sci_inta_msi_set_desc, | ||
507 | }; | ||
508 | |||
509 | static struct msi_domain_info ti_sci_inta_msi_domain_info = { | ||
510 | .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | | ||
511 | MSI_FLAG_LEVEL_CAPABLE), | ||
512 | .ops = &ti_sci_inta_msi_ops, | ||
513 | .chip = &ti_sci_inta_msi_irq_chip, | ||
514 | }; | ||
515 | |||
516 | static int ti_sci_inta_irq_domain_probe(struct platform_device *pdev) | ||
517 | { | ||
518 | struct irq_domain *parent_domain, *domain, *msi_domain; | ||
519 | struct device_node *parent_node, *node; | ||
520 | struct ti_sci_inta_irq_domain *inta; | ||
521 | struct device *dev = &pdev->dev; | ||
522 | struct resource *res; | ||
523 | int ret; | ||
524 | |||
525 | node = dev_of_node(dev); | ||
526 | parent_node = of_irq_find_parent(node); | ||
527 | if (!parent_node) { | ||
528 | dev_err(dev, "Failed to get IRQ parent node\n"); | ||
529 | return -ENODEV; | ||
530 | } | ||
531 | |||
532 | parent_domain = irq_find_host(parent_node); | ||
533 | if (!parent_domain) | ||
534 | return -EPROBE_DEFER; | ||
535 | |||
536 | inta = devm_kzalloc(dev, sizeof(*inta), GFP_KERNEL); | ||
537 | if (!inta) | ||
538 | return -ENOMEM; | ||
539 | |||
540 | inta->pdev = pdev; | ||
541 | inta->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); | ||
542 | if (IS_ERR(inta->sci)) { | ||
543 | ret = PTR_ERR(inta->sci); | ||
544 | if (ret != -EPROBE_DEFER) | ||
545 | dev_err(dev, "ti,sci read fail %d\n", ret); | ||
546 | inta->sci = NULL; | ||
547 | return ret; | ||
548 | } | ||
549 | |||
550 | ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id", &pdev->id); | ||
551 | if (ret) { | ||
552 | dev_err(dev, "missing 'ti,sci-dev-id' property\n"); | ||
553 | return -EINVAL; | ||
554 | } | ||
555 | |||
556 | inta->vint = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, | ||
557 | "ti,sci-rm-range-vint"); | ||
558 | if (IS_ERR(inta->vint)) { | ||
559 | dev_err(dev, "VINT resource allocation failed\n"); | ||
560 | return PTR_ERR(inta->vint); | ||
561 | } | ||
562 | |||
563 | inta->global_event = devm_ti_sci_get_of_resource(inta->sci, dev, pdev->id, | ||
564 | "ti,sci-rm-range-global-event"); | ||
565 | if (IS_ERR(inta->global_event)) { | ||
566 | dev_err(dev, "Global event resource allocation failed\n"); | ||
567 | return PTR_ERR(inta->global_event); | ||
568 | } | ||
569 | |||
570 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
571 | inta->base = devm_ioremap_resource(dev, res); | ||
572 | if (IS_ERR(inta->base)) | ||
573 | return -ENODEV; | ||
574 | |||
575 | domain = irq_domain_add_linear(dev_of_node(dev), | ||
576 | ti_sci_get_num_resources(inta->vint), | ||
577 | &ti_sci_inta_irq_domain_ops, inta); | ||
578 | if (!domain) { | ||
579 | dev_err(dev, "Failed to allocate IRQ domain\n"); | ||
580 | return -ENOMEM; | ||
581 | } | ||
582 | |||
583 | msi_domain = ti_sci_inta_msi_create_irq_domain(of_node_to_fwnode(node), | ||
584 | &ti_sci_inta_msi_domain_info, | ||
585 | domain); | ||
586 | if (!msi_domain) { | ||
587 | irq_domain_remove(domain); | ||
588 | dev_err(dev, "Failed to allocate msi domain\n"); | ||
589 | return -ENOMEM; | ||
590 | } | ||
591 | |||
592 | INIT_LIST_HEAD(&inta->vint_list); | ||
593 | mutex_init(&inta->vint_mutex); | ||
594 | |||
595 | return 0; | ||
596 | } | ||
597 | |||
598 | static const struct of_device_id ti_sci_inta_irq_domain_of_match[] = { | ||
599 | { .compatible = "ti,sci-inta", }, | ||
600 | { /* sentinel */ }, | ||
601 | }; | ||
602 | MODULE_DEVICE_TABLE(of, ti_sci_inta_irq_domain_of_match); | ||
603 | |||
604 | static struct platform_driver ti_sci_inta_irq_domain_driver = { | ||
605 | .probe = ti_sci_inta_irq_domain_probe, | ||
606 | .driver = { | ||
607 | .name = "ti-sci-inta", | ||
608 | .of_match_table = ti_sci_inta_irq_domain_of_match, | ||
609 | }, | ||
610 | }; | ||
611 | module_platform_driver(ti_sci_inta_irq_domain_driver); | ||
612 | |||
613 | MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>"); | ||
614 | MODULE_DESCRIPTION("K3 Interrupt Aggregator driver over TI SCI protocol"); | ||
615 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/irqchip/irq-ti-sci-intr.c b/drivers/irqchip/irq-ti-sci-intr.c new file mode 100644 index 000000000000..59d51a20bbd8 --- /dev/null +++ b/drivers/irqchip/irq-ti-sci-intr.c | |||
@@ -0,0 +1,275 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Texas Instruments' K3 Interrupt Router irqchip driver | ||
4 | * | ||
5 | * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ | ||
6 | * Lokesh Vutla <lokeshvutla@ti.com> | ||
7 | */ | ||
8 | |||
9 | #include <linux/err.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/moduleparam.h> | ||
12 | #include <linux/io.h> | ||
13 | #include <linux/irqchip.h> | ||
14 | #include <linux/irqdomain.h> | ||
15 | #include <linux/of_platform.h> | ||
16 | #include <linux/of_address.h> | ||
17 | #include <linux/of_irq.h> | ||
18 | #include <linux/soc/ti/ti_sci_protocol.h> | ||
19 | |||
20 | #define TI_SCI_DEV_ID_MASK 0xffff | ||
21 | #define TI_SCI_DEV_ID_SHIFT 16 | ||
22 | #define TI_SCI_IRQ_ID_MASK 0xffff | ||
23 | #define TI_SCI_IRQ_ID_SHIFT 0 | ||
24 | #define HWIRQ_TO_DEVID(hwirq) (((hwirq) >> (TI_SCI_DEV_ID_SHIFT)) & \ | ||
25 | (TI_SCI_DEV_ID_MASK)) | ||
26 | #define HWIRQ_TO_IRQID(hwirq) ((hwirq) & (TI_SCI_IRQ_ID_MASK)) | ||
27 | #define TO_HWIRQ(dev, index) ((((dev) & TI_SCI_DEV_ID_MASK) << \ | ||
28 | TI_SCI_DEV_ID_SHIFT) | \ | ||
29 | ((index) & TI_SCI_IRQ_ID_MASK)) | ||
30 | |||
31 | /** | ||
32 | * struct ti_sci_intr_irq_domain - Structure representing a TISCI based | ||
33 | * Interrupt Router IRQ domain. | ||
34 | * @sci: Pointer to TISCI handle | ||
35 | * @dst_irq: TISCI resource pointer representing GIC irq controller. | ||
36 | * @dst_id: TISCI device ID of the GIC irq controller. | ||
37 | * @type: Specifies the trigger type supported by this Interrupt Router | ||
38 | */ | ||
39 | struct ti_sci_intr_irq_domain { | ||
40 | const struct ti_sci_handle *sci; | ||
41 | struct ti_sci_resource *dst_irq; | ||
42 | u32 dst_id; | ||
43 | u32 type; | ||
44 | }; | ||
45 | |||
46 | static struct irq_chip ti_sci_intr_irq_chip = { | ||
47 | .name = "INTR", | ||
48 | .irq_eoi = irq_chip_eoi_parent, | ||
49 | .irq_mask = irq_chip_mask_parent, | ||
50 | .irq_unmask = irq_chip_unmask_parent, | ||
51 | .irq_set_type = irq_chip_set_type_parent, | ||
52 | .irq_retrigger = irq_chip_retrigger_hierarchy, | ||
53 | .irq_set_affinity = irq_chip_set_affinity_parent, | ||
54 | }; | ||
55 | |||
56 | /** | ||
57 | * ti_sci_intr_irq_domain_translate() - Retrieve hwirq and type from | ||
58 | * IRQ firmware specific handler. | ||
59 | * @domain: Pointer to IRQ domain | ||
60 | * @fwspec: Pointer to IRQ specific firmware structure | ||
61 | * @hwirq: IRQ number identified by hardware | ||
62 | * @type: IRQ type | ||
63 | * | ||
64 | * Return 0 if all went ok else appropriate error. | ||
65 | */ | ||
66 | static int ti_sci_intr_irq_domain_translate(struct irq_domain *domain, | ||
67 | struct irq_fwspec *fwspec, | ||
68 | unsigned long *hwirq, | ||
69 | unsigned int *type) | ||
70 | { | ||
71 | struct ti_sci_intr_irq_domain *intr = domain->host_data; | ||
72 | |||
73 | if (fwspec->param_count != 2) | ||
74 | return -EINVAL; | ||
75 | |||
76 | *hwirq = TO_HWIRQ(fwspec->param[0], fwspec->param[1]); | ||
77 | *type = intr->type; | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * ti_sci_intr_irq_domain_free() - Free the specified IRQs from the domain. | ||
84 | * @domain: Domain to which the irqs belong | ||
85 | * @virq: Linux virtual IRQ to be freed. | ||
86 | * @nr_irqs: Number of continuous irqs to be freed | ||
87 | */ | ||
88 | static void ti_sci_intr_irq_domain_free(struct irq_domain *domain, | ||
89 | unsigned int virq, unsigned int nr_irqs) | ||
90 | { | ||
91 | struct ti_sci_intr_irq_domain *intr = domain->host_data; | ||
92 | struct irq_data *data, *parent_data; | ||
93 | u16 dev_id, irq_index; | ||
94 | |||
95 | parent_data = irq_domain_get_irq_data(domain->parent, virq); | ||
96 | data = irq_domain_get_irq_data(domain, virq); | ||
97 | irq_index = HWIRQ_TO_IRQID(data->hwirq); | ||
98 | dev_id = HWIRQ_TO_DEVID(data->hwirq); | ||
99 | |||
100 | intr->sci->ops.rm_irq_ops.free_irq(intr->sci, dev_id, irq_index, | ||
101 | intr->dst_id, parent_data->hwirq); | ||
102 | ti_sci_release_resource(intr->dst_irq, parent_data->hwirq); | ||
103 | irq_domain_free_irqs_parent(domain, virq, 1); | ||
104 | irq_domain_reset_irq_data(data); | ||
105 | } | ||
106 | |||
107 | /** | ||
108 | * ti_sci_intr_alloc_gic_irq() - Allocate GIC specific IRQ | ||
109 | * @domain: Pointer to the interrupt router IRQ domain | ||
110 | * @virq: Corresponding Linux virtual IRQ number | ||
111 | * @hwirq: Corresponding hwirq for the IRQ within this IRQ domain | ||
112 | * | ||
113 | * Returns 0 if all went well else appropriate error pointer. | ||
114 | */ | ||
115 | static int ti_sci_intr_alloc_gic_irq(struct irq_domain *domain, | ||
116 | unsigned int virq, u32 hwirq) | ||
117 | { | ||
118 | struct ti_sci_intr_irq_domain *intr = domain->host_data; | ||
119 | struct irq_fwspec fwspec; | ||
120 | u16 dev_id, irq_index; | ||
121 | u16 dst_irq; | ||
122 | int err; | ||
123 | |||
124 | dev_id = HWIRQ_TO_DEVID(hwirq); | ||
125 | irq_index = HWIRQ_TO_IRQID(hwirq); | ||
126 | |||
127 | dst_irq = ti_sci_get_free_resource(intr->dst_irq); | ||
128 | if (dst_irq == TI_SCI_RESOURCE_NULL) | ||
129 | return -EINVAL; | ||
130 | |||
131 | fwspec.fwnode = domain->parent->fwnode; | ||
132 | fwspec.param_count = 3; | ||
133 | fwspec.param[0] = 0; /* SPI */ | ||
134 | fwspec.param[1] = dst_irq - 32; /* SPI offset */ | ||
135 | fwspec.param[2] = intr->type; | ||
136 | |||
137 | err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); | ||
138 | if (err) | ||
139 | goto err_irqs; | ||
140 | |||
141 | err = intr->sci->ops.rm_irq_ops.set_irq(intr->sci, dev_id, irq_index, | ||
142 | intr->dst_id, dst_irq); | ||
143 | if (err) | ||
144 | goto err_msg; | ||
145 | |||
146 | return 0; | ||
147 | |||
148 | err_msg: | ||
149 | irq_domain_free_irqs_parent(domain, virq, 1); | ||
150 | err_irqs: | ||
151 | ti_sci_release_resource(intr->dst_irq, dst_irq); | ||
152 | return err; | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * ti_sci_intr_irq_domain_alloc() - Allocate Interrupt router IRQs | ||
157 | * @domain: Point to the interrupt router IRQ domain | ||
158 | * @virq: Corresponding Linux virtual IRQ number | ||
159 | * @nr_irqs: Continuous irqs to be allocated | ||
160 | * @data: Pointer to firmware specifier | ||
161 | * | ||
162 | * Return 0 if all went well else appropriate error value. | ||
163 | */ | ||
164 | static int ti_sci_intr_irq_domain_alloc(struct irq_domain *domain, | ||
165 | unsigned int virq, unsigned int nr_irqs, | ||
166 | void *data) | ||
167 | { | ||
168 | struct irq_fwspec *fwspec = data; | ||
169 | unsigned long hwirq; | ||
170 | unsigned int flags; | ||
171 | int err; | ||
172 | |||
173 | err = ti_sci_intr_irq_domain_translate(domain, fwspec, &hwirq, &flags); | ||
174 | if (err) | ||
175 | return err; | ||
176 | |||
177 | err = ti_sci_intr_alloc_gic_irq(domain, virq, hwirq); | ||
178 | if (err) | ||
179 | return err; | ||
180 | |||
181 | irq_domain_set_hwirq_and_chip(domain, virq, hwirq, | ||
182 | &ti_sci_intr_irq_chip, NULL); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static const struct irq_domain_ops ti_sci_intr_irq_domain_ops = { | ||
188 | .free = ti_sci_intr_irq_domain_free, | ||
189 | .alloc = ti_sci_intr_irq_domain_alloc, | ||
190 | .translate = ti_sci_intr_irq_domain_translate, | ||
191 | }; | ||
192 | |||
193 | static int ti_sci_intr_irq_domain_probe(struct platform_device *pdev) | ||
194 | { | ||
195 | struct irq_domain *parent_domain, *domain; | ||
196 | struct ti_sci_intr_irq_domain *intr; | ||
197 | struct device_node *parent_node; | ||
198 | struct device *dev = &pdev->dev; | ||
199 | int ret; | ||
200 | |||
201 | parent_node = of_irq_find_parent(dev_of_node(dev)); | ||
202 | if (!parent_node) { | ||
203 | dev_err(dev, "Failed to get IRQ parent node\n"); | ||
204 | return -ENODEV; | ||
205 | } | ||
206 | |||
207 | parent_domain = irq_find_host(parent_node); | ||
208 | if (!parent_domain) { | ||
209 | dev_err(dev, "Failed to find IRQ parent domain\n"); | ||
210 | return -ENODEV; | ||
211 | } | ||
212 | |||
213 | intr = devm_kzalloc(dev, sizeof(*intr), GFP_KERNEL); | ||
214 | if (!intr) | ||
215 | return -ENOMEM; | ||
216 | |||
217 | ret = of_property_read_u32(dev_of_node(dev), "ti,intr-trigger-type", | ||
218 | &intr->type); | ||
219 | if (ret) { | ||
220 | dev_err(dev, "missing ti,intr-trigger-type property\n"); | ||
221 | return -EINVAL; | ||
222 | } | ||
223 | |||
224 | intr->sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); | ||
225 | if (IS_ERR(intr->sci)) { | ||
226 | ret = PTR_ERR(intr->sci); | ||
227 | if (ret != -EPROBE_DEFER) | ||
228 | dev_err(dev, "ti,sci read fail %d\n", ret); | ||
229 | intr->sci = NULL; | ||
230 | return ret; | ||
231 | } | ||
232 | |||
233 | ret = of_property_read_u32(dev_of_node(dev), "ti,sci-dst-id", | ||
234 | &intr->dst_id); | ||
235 | if (ret) { | ||
236 | dev_err(dev, "missing 'ti,sci-dst-id' property\n"); | ||
237 | return -EINVAL; | ||
238 | } | ||
239 | |||
240 | intr->dst_irq = devm_ti_sci_get_of_resource(intr->sci, dev, | ||
241 | intr->dst_id, | ||
242 | "ti,sci-rm-range-girq"); | ||
243 | if (IS_ERR(intr->dst_irq)) { | ||
244 | dev_err(dev, "Destination irq resource allocation failed\n"); | ||
245 | return PTR_ERR(intr->dst_irq); | ||
246 | } | ||
247 | |||
248 | domain = irq_domain_add_hierarchy(parent_domain, 0, 0, dev_of_node(dev), | ||
249 | &ti_sci_intr_irq_domain_ops, intr); | ||
250 | if (!domain) { | ||
251 | dev_err(dev, "Failed to allocate IRQ domain\n"); | ||
252 | return -ENOMEM; | ||
253 | } | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static const struct of_device_id ti_sci_intr_irq_domain_of_match[] = { | ||
259 | { .compatible = "ti,sci-intr", }, | ||
260 | { /* sentinel */ }, | ||
261 | }; | ||
262 | MODULE_DEVICE_TABLE(of, ti_sci_intr_irq_domain_of_match); | ||
263 | |||
264 | static struct platform_driver ti_sci_intr_irq_domain_driver = { | ||
265 | .probe = ti_sci_intr_irq_domain_probe, | ||
266 | .driver = { | ||
267 | .name = "ti-sci-intr", | ||
268 | .of_match_table = ti_sci_intr_irq_domain_of_match, | ||
269 | }, | ||
270 | }; | ||
271 | module_platform_driver(ti_sci_intr_irq_domain_driver); | ||
272 | |||
273 | MODULE_AUTHOR("Lokesh Vutla <lokeshvutla@ticom>"); | ||
274 | MODULE_DESCRIPTION("K3 Interrupt Router driver over TI SCI protocol"); | ||
275 | MODULE_LICENSE("GPL v2"); | ||