diff options
author | Ard Biesheuvel <ard.biesheuvel@linaro.org> | 2019-05-28 09:36:46 -0400 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2019-05-28 09:55:07 -0400 |
commit | 3d090a36c8c845b77ddea7e3cf9a219650fe322c (patch) | |
tree | 655e49f45d4e6c0928c94b926deaf6e2dc12ccf9 /drivers/irqchip | |
parent | 0444638c0f0641c5239197f0c00a4293949d071e (diff) |
irqchip/exiu: Implement ACPI support
Expose the existing EXIU hierarchical irqchip domain code to permit
the interrupt controller to be used as the irqchip component of a
GPIO controller on ACPI systems, or as the target of ordinary
interrupt resources.
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/irq-sni-exiu.c | 76 |
1 files changed, 68 insertions, 8 deletions
diff --git a/drivers/irqchip/irq-sni-exiu.c b/drivers/irqchip/irq-sni-exiu.c index fef7c2437dfb..30a323a2b332 100644 --- a/drivers/irqchip/irq-sni-exiu.c +++ b/drivers/irqchip/irq-sni-exiu.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
22 | #include <linux/of_irq.h> | 22 | #include <linux/of_irq.h> |
23 | #include <linux/platform_device.h> | ||
23 | 24 | ||
24 | #include <dt-bindings/interrupt-controller/arm-gic.h> | 25 | #include <dt-bindings/interrupt-controller/arm-gic.h> |
25 | 26 | ||
@@ -134,9 +135,13 @@ static int exiu_domain_translate(struct irq_domain *domain, | |||
134 | 135 | ||
135 | *hwirq = fwspec->param[1] - info->spi_base; | 136 | *hwirq = fwspec->param[1] - info->spi_base; |
136 | *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; | 137 | *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; |
137 | return 0; | 138 | } else { |
139 | if (fwspec->param_count != 2) | ||
140 | return -EINVAL; | ||
141 | *hwirq = fwspec->param[0]; | ||
142 | *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; | ||
138 | } | 143 | } |
139 | return -EINVAL; | 144 | return 0; |
140 | } | 145 | } |
141 | 146 | ||
142 | static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq, | 147 | static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq, |
@@ -147,16 +152,21 @@ static int exiu_domain_alloc(struct irq_domain *dom, unsigned int virq, | |||
147 | struct exiu_irq_data *info = dom->host_data; | 152 | struct exiu_irq_data *info = dom->host_data; |
148 | irq_hw_number_t hwirq; | 153 | irq_hw_number_t hwirq; |
149 | 154 | ||
150 | if (fwspec->param_count != 3) | 155 | parent_fwspec = *fwspec; |
151 | return -EINVAL; /* Not GIC compliant */ | 156 | if (is_of_node(dom->parent->fwnode)) { |
152 | if (fwspec->param[0] != GIC_SPI) | 157 | if (fwspec->param_count != 3) |
153 | return -EINVAL; /* No PPI should point to this domain */ | 158 | return -EINVAL; /* Not GIC compliant */ |
159 | if (fwspec->param[0] != GIC_SPI) | ||
160 | return -EINVAL; /* No PPI should point to this domain */ | ||
154 | 161 | ||
162 | hwirq = fwspec->param[1] - info->spi_base; | ||
163 | } else { | ||
164 | hwirq = fwspec->param[0]; | ||
165 | parent_fwspec.param[0] = hwirq + info->spi_base + 32; | ||
166 | } | ||
155 | WARN_ON(nr_irqs != 1); | 167 | WARN_ON(nr_irqs != 1); |
156 | hwirq = fwspec->param[1] - info->spi_base; | ||
157 | irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &exiu_irq_chip, info); | 168 | irq_domain_set_hwirq_and_chip(dom, virq, hwirq, &exiu_irq_chip, info); |
158 | 169 | ||
159 | parent_fwspec = *fwspec; | ||
160 | parent_fwspec.fwnode = dom->parent->fwnode; | 170 | parent_fwspec.fwnode = dom->parent->fwnode; |
161 | return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec); | 171 | return irq_domain_alloc_irqs_parent(dom, virq, nr_irqs, &parent_fwspec); |
162 | } | 172 | } |
@@ -245,3 +255,53 @@ out_unmap: | |||
245 | return -ENOMEM; | 255 | return -ENOMEM; |
246 | } | 256 | } |
247 | IRQCHIP_DECLARE(exiu, "socionext,synquacer-exiu", exiu_dt_init); | 257 | IRQCHIP_DECLARE(exiu, "socionext,synquacer-exiu", exiu_dt_init); |
258 | |||
259 | #ifdef CONFIG_ACPI | ||
260 | static int exiu_acpi_probe(struct platform_device *pdev) | ||
261 | { | ||
262 | struct irq_domain *domain; | ||
263 | struct exiu_irq_data *data; | ||
264 | struct resource *res; | ||
265 | |||
266 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
267 | if (!res) { | ||
268 | dev_err(&pdev->dev, "failed to parse memory resource\n"); | ||
269 | return -ENXIO; | ||
270 | } | ||
271 | |||
272 | data = exiu_init(dev_fwnode(&pdev->dev), res); | ||
273 | if (IS_ERR(data)) | ||
274 | return PTR_ERR(data); | ||
275 | |||
276 | domain = acpi_irq_create_hierarchy(0, NUM_IRQS, dev_fwnode(&pdev->dev), | ||
277 | &exiu_domain_ops, data); | ||
278 | if (!domain) { | ||
279 | dev_err(&pdev->dev, "failed to create IRQ domain\n"); | ||
280 | goto out_unmap; | ||
281 | } | ||
282 | |||
283 | dev_info(&pdev->dev, "%d interrupts forwarded\n", NUM_IRQS); | ||
284 | |||
285 | return 0; | ||
286 | |||
287 | out_unmap: | ||
288 | iounmap(data->base); | ||
289 | kfree(data); | ||
290 | return -ENOMEM; | ||
291 | } | ||
292 | |||
293 | static const struct acpi_device_id exiu_acpi_ids[] = { | ||
294 | { "SCX0008" }, | ||
295 | { /* sentinel */ } | ||
296 | }; | ||
297 | MODULE_DEVICE_TABLE(acpi, exiu_acpi_ids); | ||
298 | |||
299 | static struct platform_driver exiu_driver = { | ||
300 | .driver = { | ||
301 | .name = "exiu", | ||
302 | .acpi_match_table = exiu_acpi_ids, | ||
303 | }, | ||
304 | .probe = exiu_acpi_probe, | ||
305 | }; | ||
306 | builtin_platform_driver(exiu_driver); | ||
307 | #endif | ||