diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2015-12-29 04:08:45 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2015-12-29 04:08:45 -0500 |
commit | 92b86f92ed0307efbaea3c0e95520551848d3995 (patch) | |
tree | 20db0701571353b7e20a9417516616b4b60ebffa /drivers/irqchip/irq-gic-v2m.c | |
parent | a4289dc2ec3a5821076a78ee9678909b4eff297e (diff) | |
parent | 0644b3daca28dcb320373ae20069c269c9386304 (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.c | 163 |
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 | ||
56 | struct v2m_data { | 58 | struct v2m_data { |
57 | struct list_head entry; | 59 | struct list_head entry; |
58 | struct device_node *node; | 60 | struct fwnode_handle *fwnode; |
59 | struct resource res; /* GICv2m resource */ | 61 | struct resource res; /* GICv2m resource */ |
60 | void __iomem *base; /* GICv2m virt address */ | 62 | void __iomem *base; /* GICv2m virt address */ |
61 | u32 spi_start; /* The SPI number that MSIs start */ | 63 | u32 spi_start; /* The SPI number that MSIs start */ |
@@ -138,6 +140,11 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, | |||
138 | fwspec.param[0] = 0; | 140 | fwspec.param[0] = 0; |
139 | fwspec.param[1] = hwirq - 32; | 141 | fwspec.param[1] = hwirq - 32; |
140 | fwspec.param[2] = IRQ_TYPE_EDGE_RISING; | 142 | fwspec.param[2] = IRQ_TYPE_EDGE_RISING; |
143 | } else if (is_fwnode_irqchip(domain->parent->fwnode)) { | ||
144 | fwspec.fwnode = domain->parent->fwnode; | ||
145 | fwspec.param_count = 2; | ||
146 | fwspec.param[0] = hwirq; | ||
147 | fwspec.param[1] = IRQ_TYPE_EDGE_RISING; | ||
141 | } else { | 148 | } else { |
142 | return -EINVAL; | 149 | return -EINVAL; |
143 | } | 150 | } |
@@ -254,7 +261,9 @@ static void gicv2m_teardown(void) | |||
254 | list_del(&v2m->entry); | 261 | list_del(&v2m->entry); |
255 | kfree(v2m->bm); | 262 | kfree(v2m->bm); |
256 | iounmap(v2m->base); | 263 | iounmap(v2m->base); |
257 | of_node_put(v2m->node); | 264 | of_node_put(to_of_node(v2m->fwnode)); |
265 | if (is_fwnode_irqchip(v2m->fwnode)) | ||
266 | irq_domain_free_fwnode(v2m->fwnode); | ||
258 | kfree(v2m); | 267 | kfree(v2m); |
259 | } | 268 | } |
260 | } | 269 | } |
@@ -268,7 +277,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) | |||
268 | if (!v2m) | 277 | if (!v2m) |
269 | return 0; | 278 | return 0; |
270 | 279 | ||
271 | inner_domain = irq_domain_create_tree(of_node_to_fwnode(v2m->node), | 280 | inner_domain = irq_domain_create_tree(v2m->fwnode, |
272 | &gicv2m_domain_ops, v2m); | 281 | &gicv2m_domain_ops, v2m); |
273 | if (!inner_domain) { | 282 | if (!inner_domain) { |
274 | pr_err("Failed to create GICv2m domain\n"); | 283 | pr_err("Failed to create GICv2m domain\n"); |
@@ -277,10 +286,10 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) | |||
277 | 286 | ||
278 | inner_domain->bus_token = DOMAIN_BUS_NEXUS; | 287 | inner_domain->bus_token = DOMAIN_BUS_NEXUS; |
279 | inner_domain->parent = parent; | 288 | inner_domain->parent = parent; |
280 | pci_domain = pci_msi_create_irq_domain(of_node_to_fwnode(v2m->node), | 289 | pci_domain = pci_msi_create_irq_domain(v2m->fwnode, |
281 | &gicv2m_msi_domain_info, | 290 | &gicv2m_msi_domain_info, |
282 | inner_domain); | 291 | inner_domain); |
283 | plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(v2m->node), | 292 | plat_domain = platform_msi_create_irq_domain(v2m->fwnode, |
284 | &gicv2m_pmsi_domain_info, | 293 | &gicv2m_pmsi_domain_info, |
285 | inner_domain); | 294 | inner_domain); |
286 | if (!pci_domain || !plat_domain) { | 295 | if (!pci_domain || !plat_domain) { |
@@ -296,8 +305,9 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) | |||
296 | return 0; | 305 | return 0; |
297 | } | 306 | } |
298 | 307 | ||
299 | static int __init gicv2m_init_one(struct device_node *node, | 308 | static int __init gicv2m_init_one(struct fwnode_handle *fwnode, |
300 | struct irq_domain *parent) | 309 | u32 spi_start, u32 nr_spis, |
310 | struct resource *res) | ||
301 | { | 311 | { |
302 | int ret; | 312 | int ret; |
303 | struct v2m_data *v2m; | 313 | struct v2m_data *v2m; |
@@ -309,13 +319,9 @@ static int __init gicv2m_init_one(struct device_node *node, | |||
309 | } | 319 | } |
310 | 320 | ||
311 | INIT_LIST_HEAD(&v2m->entry); | 321 | INIT_LIST_HEAD(&v2m->entry); |
312 | v2m->node = node; | 322 | v2m->fwnode = fwnode; |
313 | 323 | ||
314 | ret = of_address_to_resource(node, 0, &v2m->res); | 324 | memcpy(&v2m->res, res, sizeof(struct resource)); |
315 | if (ret) { | ||
316 | pr_err("Failed to allocate v2m resource.\n"); | ||
317 | goto err_free_v2m; | ||
318 | } | ||
319 | 325 | ||
320 | v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); | 326 | v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); |
321 | if (!v2m->base) { | 327 | if (!v2m->base) { |
@@ -324,10 +330,9 @@ static int __init gicv2m_init_one(struct device_node *node, | |||
324 | goto err_free_v2m; | 330 | goto err_free_v2m; |
325 | } | 331 | } |
326 | 332 | ||
327 | if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) && | 333 | if (spi_start && nr_spis) { |
328 | !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) { | 334 | v2m->spi_start = spi_start; |
329 | pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n", | 335 | v2m->nr_spis = nr_spis; |
330 | v2m->spi_start, v2m->nr_spis); | ||
331 | } else { | 336 | } else { |
332 | u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); | 337 | u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); |
333 | 338 | ||
@@ -359,10 +364,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 | ||
368 | err_iounmap: | 373 | err_iounmap: |
@@ -377,17 +382,34 @@ static struct of_device_id gicv2m_device_id[] = { | |||
377 | {}, | 382 | {}, |
378 | }; | 383 | }; |
379 | 384 | ||
380 | int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) | 385 | static 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 | ||
427 | static int acpi_num_msi; | ||
428 | |||
429 | static 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 | |||
444 | static int __init | ||
445 | acpi_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 | |||
482 | static int __init gicv2m_acpi_init(struct irq_domain *parent) | ||
483 | { | ||
484 | int ret; | ||
485 | |||
486 | if (acpi_num_msi > 0) | ||
487 | return 0; | ||
488 | |||
489 | acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME, | ||
490 | acpi_parse_madt_msi, 0); | ||
491 | |||
492 | if (acpi_num_msi <= 0) | ||
493 | goto err_out; | ||
494 | |||
495 | ret = gicv2m_allocate_domains(parent); | ||
496 | if (ret) | ||
497 | goto err_out; | ||
498 | |||
499 | pci_msi_register_fwnode_provider(&gicv2m_get_fwnode); | ||
500 | |||
501 | return 0; | ||
502 | |||
503 | err_out: | ||
504 | gicv2m_teardown(); | ||
505 | return -EINVAL; | ||
506 | } | ||
507 | #else /* CONFIG_ACPI */ | ||
508 | static int __init gicv2m_acpi_init(struct irq_domain *parent) | ||
509 | { | ||
510 | return -EINVAL; | ||
511 | } | ||
512 | #endif /* CONFIG_ACPI */ | ||
513 | |||
514 | int __init gicv2m_init(struct fwnode_handle *parent_handle, | ||
515 | struct irq_domain *parent) | ||
516 | { | ||
517 | if (is_of_node(parent_handle)) | ||
518 | return gicv2m_of_init(parent_handle, parent); | ||
519 | |||
520 | return gicv2m_acpi_init(parent); | ||
521 | } | ||