Lines Matching +full:msi +full:- +full:parent

1 // SPDX-License-Identifier: GPL-2.0-only
3 * ARM GIC v2m MSI(-X) support
21 #include <linux/msi.h>
26 #include <linux/irqchip/arm-gic.h>
27 #include <linux/irqchip/arm-gic-common.h>
29 #include "irq-msi-lib.h"
34 * [25:16] lowest SPI assigned to MSI
36 * [9:0] Numer of SPIs assigned to MSI
52 /* APM X-Gene with GICv2m MSI_IIDR register value */
73 unsigned long *bm; /* MSI vector bitmap */
79 if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) in gicv2m_get_msi_addr()
80 return v2m->res.start | ((hwirq - 32) << 3); in gicv2m_get_msi_addr()
82 return v2m->res.start + V2M_MSI_SETSPI_NS; in gicv2m_get_msi_addr()
88 phys_addr_t addr = gicv2m_get_msi_addr(v2m, data->hwirq); in gicv2m_compose_msi_msg()
90 if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) in gicv2m_compose_msi_msg()
91 msg->data = 0; in gicv2m_compose_msi_msg()
93 msg->data = data->hwirq; in gicv2m_compose_msi_msg()
94 if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) in gicv2m_compose_msi_msg()
95 msg->data -= v2m->spi_offset; in gicv2m_compose_msi_msg()
117 if (is_of_node(domain->parent->fwnode)) { in gicv2m_irq_gic_domain_alloc()
118 fwspec.fwnode = domain->parent->fwnode; in gicv2m_irq_gic_domain_alloc()
121 fwspec.param[1] = hwirq - 32; in gicv2m_irq_gic_domain_alloc()
123 } else if (is_fwnode_irqchip(domain->parent->fwnode)) { in gicv2m_irq_gic_domain_alloc()
124 fwspec.fwnode = domain->parent->fwnode; in gicv2m_irq_gic_domain_alloc()
129 return -EINVAL; in gicv2m_irq_gic_domain_alloc()
137 d = irq_domain_get_irq_data(domain->parent, virq); in gicv2m_irq_gic_domain_alloc()
138 d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING); in gicv2m_irq_gic_domain_alloc()
146 bitmap_release_region(v2m->bm, hwirq - v2m->spi_start, in gicv2m_unalloc_msi()
160 offset = bitmap_find_free_region(tmp->bm, tmp->nr_spis, in gicv2m_irq_domain_alloc()
170 return -ENOSPC; in gicv2m_irq_domain_alloc()
172 hwirq = v2m->spi_start + offset; in gicv2m_irq_domain_alloc()
174 err = iommu_dma_prepare_msi(info->desc, in gicv2m_irq_domain_alloc()
202 gicv2m_unalloc_msi(v2m, d->hwirq, nr_irqs); in gicv2m_irq_domain_free()
215 pr_err("Invalid MSI base SPI (base:%u)\n", base); in is_msi_spi_valid()
221 num, V2M_MAX_SPI - V2M_MIN_SPI + 1); in is_msi_spi_valid()
233 list_del(&v2m->entry); in gicv2m_teardown()
234 bitmap_free(v2m->bm); in gicv2m_teardown()
235 iounmap(v2m->base); in gicv2m_teardown()
236 of_node_put(to_of_node(v2m->fwnode)); in gicv2m_teardown()
237 if (is_fwnode_irqchip(v2m->fwnode)) in gicv2m_teardown()
238 irq_domain_free_fwnode(v2m->fwnode); in gicv2m_teardown()
258 .prefix = "GICv2m-",
262 static __init int gicv2m_allocate_domains(struct irq_domain *parent) in gicv2m_allocate_domains() argument
271 inner_domain = irq_domain_create_hierarchy(parent, 0, 0, v2m->fwnode, in gicv2m_allocate_domains()
275 return -ENOMEM; in gicv2m_allocate_domains()
279 inner_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT; in gicv2m_allocate_domains()
280 inner_domain->msi_parent_ops = &gicv2m_msi_parent_ops; in gicv2m_allocate_domains()
293 return -ENOMEM; in gicv2m_init_one()
295 INIT_LIST_HEAD(&v2m->entry); in gicv2m_init_one()
296 v2m->fwnode = fwnode; in gicv2m_init_one()
297 v2m->flags = flags; in gicv2m_init_one()
299 memcpy(&v2m->res, res, sizeof(struct resource)); in gicv2m_init_one()
301 v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); in gicv2m_init_one()
302 if (!v2m->base) { in gicv2m_init_one()
304 ret = -ENOMEM; in gicv2m_init_one()
309 v2m->spi_start = spi_start; in gicv2m_init_one()
310 v2m->nr_spis = nr_spis; in gicv2m_init_one()
315 if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) { in gicv2m_init_one()
316 ret = -EINVAL; in gicv2m_init_one()
319 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); in gicv2m_init_one()
321 v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); in gicv2m_init_one()
322 v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer); in gicv2m_init_one()
325 if (!is_msi_spi_valid(v2m->spi_start, v2m->nr_spis)) { in gicv2m_init_one()
326 ret = -EINVAL; in gicv2m_init_one()
331 * APM X-Gene GICv2m implementation has an erratum where in gicv2m_init_one()
332 * the MSI data needs to be the offset from the spi_start in gicv2m_init_one()
333 * in order to trigger the correct MSI interrupt. This is in gicv2m_init_one()
335 * the MSI data is the absolute value within the range from in gicv2m_init_one()
338 * Broadcom NS2 GICv2m implementation has an erratum where the MSI data in gicv2m_init_one()
339 * is 'spi_number - 32' in gicv2m_init_one()
343 if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) { in gicv2m_init_one()
344 switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) { in gicv2m_init_one()
346 v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; in gicv2m_init_one()
347 v2m->spi_offset = v2m->spi_start; in gicv2m_init_one()
350 v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; in gicv2m_init_one()
351 v2m->spi_offset = 32; in gicv2m_init_one()
355 v2m->bm = bitmap_zalloc(v2m->nr_spis, GFP_KERNEL); in gicv2m_init_one()
356 if (!v2m->bm) { in gicv2m_init_one()
357 ret = -ENOMEM; in gicv2m_init_one()
361 list_add_tail(&v2m->entry, &v2m_nodes); in gicv2m_init_one()
364 v2m->spi_start, (v2m->spi_start + v2m->nr_spis - 1)); in gicv2m_init_one()
368 iounmap(v2m->base); in gicv2m_init_one()
375 { .compatible = "arm,gic-v2m-frame", },
380 struct irq_domain *parent) in gicv2m_of_init() argument
391 if (!of_property_read_bool(child, "msi-controller")) in gicv2m_of_init()
400 if (!of_property_read_u32(child, "arm,msi-base-spi", in gicv2m_of_init()
402 !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis)) in gicv2m_of_init()
406 ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, in gicv2m_of_init()
415 ret = gicv2m_allocate_domains(parent); in gicv2m_of_init()
431 /* We only return the fwnode of the first MSI frame. */ in gicv2m_get_fwnode()
436 return data->fwnode; in gicv2m_get_fwnode()
452 rc = !memcmp(madt->header.oem_id, ACPI_AMZN_OEM_ID, ACPI_OEM_ID_SIZE); in acpi_check_amazon_graviton_quirks()
471 return -EINVAL; in acpi_parse_madt_msi()
473 res.start = m->base_address; in acpi_parse_madt_msi()
474 res.end = m->base_address + SZ_4K - 1; in acpi_parse_madt_msi()
479 res.end = res.start + SZ_8K - 1; in acpi_parse_madt_msi()
484 if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { in acpi_parse_madt_msi()
485 spi_start = m->spi_base; in acpi_parse_madt_msi()
486 nr_spis = m->spi_count; in acpi_parse_madt_msi()
495 return -EINVAL; in acpi_parse_madt_msi()
505 static int __init gicv2m_acpi_init(struct irq_domain *parent) in gicv2m_acpi_init() argument
518 ret = gicv2m_allocate_domains(parent); in gicv2m_acpi_init()
528 return -EINVAL; in gicv2m_acpi_init()
531 static int __init gicv2m_acpi_init(struct irq_domain *parent) in gicv2m_acpi_init() argument
533 return -EINVAL; in gicv2m_acpi_init()
538 struct irq_domain *parent) in gicv2m_init() argument
541 return gicv2m_of_init(parent_handle, parent); in gicv2m_init()
543 return gicv2m_acpi_init(parent); in gicv2m_init()