summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/irq-gic-v2m.c
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2015-12-29 04:08:45 -0500
committerThomas Gleixner <tglx@linutronix.de>2015-12-29 04:08:45 -0500
commit92b86f92ed0307efbaea3c0e95520551848d3995 (patch)
tree20db0701571353b7e20a9417516616b4b60ebffa /drivers/irqchip/irq-gic-v2m.c
parenta4289dc2ec3a5821076a78ee9678909b4eff297e (diff)
parent0644b3daca28dcb320373ae20069c269c9386304 (diff)
Merge branch 'irq/gic-v2m-acpi' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/core
Pull another round of GIC changes from Marc: ACPI support for GIV-v2m
Diffstat (limited to 'drivers/irqchip/irq-gic-v2m.c')
-rw-r--r--drivers/irqchip/irq-gic-v2m.c163
1 files changed, 141 insertions, 22 deletions
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index ee1e553ee7a6..e2d2d027637d 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -15,9 +15,11 @@
15 15
16#define pr_fmt(fmt) "GICv2m: " fmt 16#define pr_fmt(fmt) "GICv2m: " fmt
17 17
18#include <linux/acpi.h>
18#include <linux/irq.h> 19#include <linux/irq.h>
19#include <linux/irqdomain.h> 20#include <linux/irqdomain.h>
20#include <linux/kernel.h> 21#include <linux/kernel.h>
22#include <linux/msi.h>
21#include <linux/of_address.h> 23#include <linux/of_address.h>
22#include <linux/of_pci.h> 24#include <linux/of_pci.h>
23#include <linux/slab.h> 25#include <linux/slab.h>
@@ -55,7 +57,7 @@ static DEFINE_SPINLOCK(v2m_lock);
55 57
56struct v2m_data { 58struct v2m_data {
57 struct list_head entry; 59 struct list_head entry;
58 struct device_node *node; 60 struct fwnode_handle *fwnode;
59 struct resource res; /* GICv2m resource */ 61 struct resource res; /* GICv2m resource */
60 void __iomem *base; /* GICv2m virt address */ 62 void __iomem *base; /* GICv2m virt address */
61 u32 spi_start; /* The SPI number that MSIs start */ 63 u32 spi_start; /* The SPI number that MSIs start */
@@ -138,6 +140,11 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain,
138 fwspec.param[0] = 0; 140 fwspec.param[0] = 0;
139 fwspec.param[1] = hwirq - 32; 141 fwspec.param[1] = hwirq - 32;
140 fwspec.param[2] = IRQ_TYPE_EDGE_RISING; 142 fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
143 } else if (is_fwnode_irqchip(domain->parent->fwnode)) {
144 fwspec.fwnode = domain->parent->fwnode;
145 fwspec.param_count = 2;
146 fwspec.param[0] = hwirq;
147 fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
141 } else { 148 } else {
142 return -EINVAL; 149 return -EINVAL;
143 } 150 }
@@ -254,7 +261,9 @@ static void gicv2m_teardown(void)
254 list_del(&v2m->entry); 261 list_del(&v2m->entry);
255 kfree(v2m->bm); 262 kfree(v2m->bm);
256 iounmap(v2m->base); 263 iounmap(v2m->base);
257 of_node_put(v2m->node); 264 of_node_put(to_of_node(v2m->fwnode));
265 if (is_fwnode_irqchip(v2m->fwnode))
266 irq_domain_free_fwnode(v2m->fwnode);
258 kfree(v2m); 267 kfree(v2m);
259 } 268 }
260} 269}
@@ -268,7 +277,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
268 if (!v2m) 277 if (!v2m)
269 return 0; 278 return 0;
270 279
271 inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node), 280 inner_domain = irq_domain_create_tree(v2m->fwnode,
272 &gicv2m_domain_ops, v2m); 281 &gicv2m_domain_ops, v2m);
273 if (!inner_domain) { 282 if (!inner_domain) {
274 pr_err("Failed to create GICv2m domain\n"); 283 pr_err("Failed to create GICv2m domain\n");
@@ -277,10 +286,10 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
277 286
278 inner_domain->bus_token = DOMAIN_BUS_NEXUS; 287 inner_domain->bus_token = DOMAIN_BUS_NEXUS;
279 inner_domain->parent = parent; 288 inner_domain->parent = parent;
280 pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node), 289 pci_domain = pci_msi_create_irq_domain(v2m->fwnode,
281 &gicv2m_msi_domain_info, 290 &gicv2m_msi_domain_info,
282 inner_domain); 291 inner_domain);
283 plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node), 292 plat_domain = platform_msi_create_irq_domain(v2m->fwnode,
284 &gicv2m_pmsi_domain_info, 293 &gicv2m_pmsi_domain_info,
285 inner_domain); 294 inner_domain);
286 if (!pci_domain || !plat_domain) { 295 if (!pci_domain || !plat_domain) {
@@ -296,8 +305,9 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
296 return 0; 305 return 0;
297} 306}
298 307
299static int __init gicv2m_init_one(struct device_node *node, 308static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
300 struct irq_domain *parent) 309 u32 spi_start, u32 nr_spis,
310 struct resource *res)
301{ 311{
302 int ret; 312 int ret;
303 struct v2m_data *v2m; 313 struct v2m_data *v2m;
@@ -309,13 +319,9 @@ static int __init gicv2m_init_one(struct device_node *node,
309 } 319 }
310 320
311 INIT_LIST_HEAD(&v2m->entry); 321 INIT_LIST_HEAD(&v2m->entry);
312 v2m->node = node; 322 v2m->fwnode = fwnode;
313 323
314 ret = of_address_to_resource(node, 0, &v2m->res); 324 memcpy(&v2m->res, res, sizeof(struct resource));
315 if (ret) {
316 pr_err("Failed to allocate v2m resource.\n");
317 goto err_free_v2m;
318 }
319 325
320 v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); 326 v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res));
321 if (!v2m->base) { 327 if (!v2m->base) {
@@ -324,10 +330,9 @@ static int __init gicv2m_init_one(struct device_node *node,
324 goto err_free_v2m; 330 goto err_free_v2m;
325 } 331 }
326 332
327 if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) && 333 if (spi_start && nr_spis) {
328 !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) { 334 v2m->spi_start = spi_start;
329 pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n", 335 v2m->nr_spis = nr_spis;
330 v2m->spi_start, v2m->nr_spis);
331 } else { 336 } else {
332 u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); 337 u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
333 338
@@ -359,10 +364,10 @@ static int __init gicv2m_init_one(struct device_node *node,
359 } 364 }
360 365
361 list_add_tail(&v2m->entry, &v2m_nodes); 366 list_add_tail(&v2m->entry, &v2m_nodes);
362 pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name,
363 (unsigned long)v2m->res.start, (unsigned long)v2m->res.end,
364 v2m->spi_start, (v2m->spi_start + v2m->nr_spis));
365 367
368 pr_info("range[%#lx:%#lx], SPI[%d:%d]\n",
369 (unsigned long)res->start, (unsigned long)res->end,
370 v2m->spi_start, (v2m->spi_start + v2m->nr_spis));
366 return 0; 371 return 0;
367 372
368err_iounmap: 373err_iounmap:
@@ -377,17 +382,34 @@ static struct of_device_id gicv2m_device_id[] = {
377 {}, 382 {},
378}; 383};
379 384
380int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) 385static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
386 struct irq_domain *parent)
381{ 387{
382 int ret = 0; 388 int ret = 0;
389 struct device_node *node = to_of_node(parent_handle);
383 struct device_node *child; 390 struct device_node *child;
384 391
385 for (child = of_find_matching_node(node, gicv2m_device_id); child; 392 for (child = of_find_matching_node(node, gicv2m_device_id); child;
386 child = of_find_matching_node(child, gicv2m_device_id)) { 393 child = of_find_matching_node(child, gicv2m_device_id)) {
394 u32 spi_start = 0, nr_spis = 0;
395 struct resource res;
396
387 if (!of_find_property(child, "msi-controller", NULL)) 397 if (!of_find_property(child, "msi-controller", NULL))
388 continue; 398 continue;
389 399
390 ret = gicv2m_init_one(child, parent); 400 ret = of_address_to_resource(child, 0, &res);
401 if (ret) {
402 pr_err("Failed to allocate v2m resource.\n");
403 break;
404 }
405
406 if (!of_property_read_u32(child, "arm,msi-base-spi",
407 &spi_start) &&
408 !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis))
409 pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
410 spi_start, nr_spis);
411
412 ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, &res);
391 if (ret) { 413 if (ret) {
392 of_node_put(child); 414 of_node_put(child);
393 break; 415 break;
@@ -400,3 +422,100 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
400 gicv2m_teardown(); 422 gicv2m_teardown();
401 return ret; 423 return ret;
402} 424}
425
426#ifdef CONFIG_ACPI
427static int acpi_num_msi;
428
429static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
430{
431 struct v2m_data *data;
432
433 if (WARN_ON(acpi_num_msi <= 0))
434 return NULL;
435
436 /* We only return the fwnode of the first MSI frame. */
437 data = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
438 if (!data)
439 return NULL;
440
441 return data->fwnode;
442}
443
444static int __init
445acpi_parse_madt_msi(struct acpi_subtable_header *header,
446 const unsigned long end)
447{
448 int ret;
449 struct resource res;
450 u32 spi_start = 0, nr_spis = 0;
451 struct acpi_madt_generic_msi_frame *m;
452 struct fwnode_handle *fwnode;
453
454 m = (struct acpi_madt_generic_msi_frame *)header;
455 if (BAD_MADT_ENTRY(m, end))
456 return -EINVAL;
457
458 res.start = m->base_address;
459 res.end = m->base_address + SZ_4K;
460
461 if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
462 spi_start = m->spi_base;
463 nr_spis = m->spi_count;
464
465 pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n",
466 spi_start, nr_spis);
467 }
468
469 fwnode = irq_domain_alloc_fwnode((void *)m->base_address);
470 if (!fwnode) {
471 pr_err("Unable to allocate GICv2m domain token\n");
472 return -EINVAL;
473 }
474
475 ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res);
476 if (ret)
477 irq_domain_free_fwnode(fwnode);
478
479 return ret;
480}
481
482static int __init gicv2m_acpi_init(struct irq_domain *parent)
483{
484 int ret;
485
486 if (acpi_num_msi > 0)
487 return 0;
488
489 acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME,
490 acpi_parse_madt_msi, 0);
491
492 if (acpi_num_msi <= 0)
493 goto err_out;
494
495 ret = gicv2m_allocate_domains(parent);
496 if (ret)
497 goto err_out;
498
499 pci_msi_register_fwnode_provider(&gicv2m_get_fwnode);
500
501 return 0;
502
503err_out:
504 gicv2m_teardown();
505 return -EINVAL;
506}
507#else /* CONFIG_ACPI */
508static int __init gicv2m_acpi_init(struct irq_domain *parent)
509{
510 return -EINVAL;
511}
512#endif /* CONFIG_ACPI */
513
514int __init gicv2m_init(struct fwnode_handle *parent_handle,
515 struct irq_domain *parent)
516{
517 if (is_of_node(parent_handle))
518 return gicv2m_of_init(parent_handle, parent);
519
520 return gicv2m_acpi_init(parent);
521}