From 471036b2b895789c2305428fd879006468e4a758 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Thu, 10 Dec 2015 08:55:27 -0800 Subject: acpi: pci: Setup MSI domain for ACPI based pci devices This patch introduces pci_msi_register_fwnode_provider() for irqchip to register a callback, to provide a way to determine appropriate MSI domain for a pci device. It also introduces pci_host_bridge_acpi_msi_domain(), which returns the MSI domain of the specified PCI host bridge with DOMAIN_BUS_PCI_MSI bus token. Then, it is assigned to pci device. Reviewed-by: Marc Zyngier Acked-by: Bjorn Helgaas Acked-by: Rafael J. Wysocki Signed-off-by: Suravee Suthikulpanit Signed-off-by: Marc Zyngier --- include/linux/irqdomain.h | 5 +++++ include/linux/pci.h | 10 ++++++++++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index d5e5c5bef28c..a06fedacd955 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -410,6 +410,11 @@ static inline bool irq_domain_is_hierarchy(struct irq_domain *domain) static inline void irq_dispose_mapping(unsigned int virq) { } static inline void irq_domain_activate_irq(struct irq_data *data) { } static inline void irq_domain_deactivate_irq(struct irq_data *data) { } +static inline struct irq_domain *irq_find_matching_fwnode( + struct fwnode_handle *fwnode, enum irq_domain_bus_token bus_token) +{ + return NULL; +} #endif /* !CONFIG_IRQ_DOMAIN */ #endif /* _LINUX_IRQDOMAIN_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 6ae25aae88fd..d86378c226fb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1946,6 +1946,16 @@ static inline struct irq_domain * pci_host_bridge_of_msi_domain(struct pci_bus *bus) { return NULL; } #endif /* CONFIG_OF */ +#ifdef CONFIG_ACPI +struct irq_domain *pci_host_bridge_acpi_msi_domain(struct pci_bus *bus); + +void +pci_msi_register_fwnode_provider(struct fwnode_handle *(*fn)(struct device *)); +#else +static inline struct irq_domain * +pci_host_bridge_acpi_msi_domain(struct pci_bus *bus) { return NULL; } +#endif + #ifdef CONFIG_EEH static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) { -- cgit v1.2.3-58-ga151 From 75aba7b0e9ac416ca53c0c97680b8e9aedf09284 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Thu, 10 Dec 2015 08:55:28 -0800 Subject: irqdomain: Introduce is_fwnode_irqchip helper Since there will be several places checking if fwnode.type is equal FWNODE_IRQCHIP, this patch adds a convenient function for this purpose. Acked-by: Marc Zyngier Signed-off-by: Suravee Suthikulpanit Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic.c | 2 +- include/linux/irqdomain.h | 5 +++++ kernel/irq/irqdomain.c | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index abf2ffaed392..fcd327f49e8e 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -972,7 +972,7 @@ static int gic_irq_domain_translate(struct irq_domain *d, return 0; } - if (fwspec->fwnode->type == FWNODE_IRQCHIP) { + if (is_fwnode_irqchip(fwspec->fwnode)) { if(fwspec->param_count != 2) return -EINVAL; diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index a06fedacd955..d72fabc04437 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -211,6 +211,11 @@ static inline struct fwnode_handle *of_node_to_fwnode(struct device_node *node) return node ? &node->fwnode : NULL; } +static inline bool is_fwnode_irqchip(struct fwnode_handle *fwnode) +{ + return fwnode && fwnode->type == FWNODE_IRQCHIP; +} + static inline struct irq_domain *irq_find_matching_host(struct device_node *node, enum irq_domain_bus_token bus_token) { diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 22aa9612ef7c..7f34d98ebfc4 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -70,7 +70,7 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode) { struct irqchip_fwid *fwid; - if (WARN_ON(fwnode->type != FWNODE_IRQCHIP)) + if (WARN_ON(!is_fwnode_irqchip(fwnode))) return; fwid = container_of(fwnode, struct irqchip_fwid, fwnode); -- cgit v1.2.3-58-ga151 From 0644b3daca28dcb320373ae20069c269c9386304 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Thu, 10 Dec 2015 08:55:30 -0800 Subject: irqchip/gic-v2m: acpi: Introducing GICv2m ACPI support This patch introduces gicv2m_acpi_init(), which uses information in MADT GIC MSI frames structure to initialize GICv2m driver. It also exposes gicv2m_init() function, which simplifies callers to a single GICv2m init function. Reviewed-by: Marc Zyngier Tested-by: Duc Dang Acked-by: Rafael J. Wysocki Signed-off-by: Suravee Suthikulpanit Signed-off-by: Hanjun Guo Signed-off-by: Marc Zyngier --- drivers/irqchip/irq-gic-v2m.c | 110 +++++++++++++++++++++++++++++++++++++++- drivers/irqchip/irq-gic.c | 6 ++- include/linux/irqchip/arm-gic.h | 3 +- 3 files changed, 116 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 779c3906a22e..7e2975df4473 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -15,9 +15,11 @@ #define pr_fmt(fmt) "GICv2m: " fmt +#include #include #include #include +#include #include #include #include @@ -138,6 +140,11 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, fwspec.param[0] = 0; fwspec.param[1] = hwirq - 32; fwspec.param[2] = IRQ_TYPE_EDGE_RISING; + } else if (is_fwnode_irqchip(domain->parent->fwnode)) { + fwspec.fwnode = domain->parent->fwnode; + fwspec.param_count = 2; + fwspec.param[0] = hwirq; + fwspec.param[1] = IRQ_TYPE_EDGE_RISING; } else { return -EINVAL; } @@ -255,6 +262,8 @@ static void gicv2m_teardown(void) kfree(v2m->bm); iounmap(v2m->base); of_node_put(to_of_node(v2m->fwnode)); + if (is_fwnode_irqchip(v2m->fwnode)) + irq_domain_free_fwnode(v2m->fwnode); kfree(v2m); } } @@ -373,9 +382,11 @@ static struct of_device_id gicv2m_device_id[] = { {}, }; -int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) +static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, + struct irq_domain *parent) { int ret = 0; + struct device_node *node = to_of_node(parent_handle); struct device_node *child; for (child = of_find_matching_node(node, gicv2m_device_id); child; @@ -411,3 +422,100 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) gicv2m_teardown(); return ret; } + +#ifdef CONFIG_ACPI +static int acpi_num_msi; + +static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) +{ + struct v2m_data *data; + + if (WARN_ON(acpi_num_msi <= 0)) + return NULL; + + /* We only return the fwnode of the first MSI frame. */ + data = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry); + if (!data) + return NULL; + + return data->fwnode; +} + +static int __init +acpi_parse_madt_msi(struct acpi_subtable_header *header, + const unsigned long end) +{ + int ret; + struct resource res; + u32 spi_start = 0, nr_spis = 0; + struct acpi_madt_generic_msi_frame *m; + struct fwnode_handle *fwnode; + + m = (struct acpi_madt_generic_msi_frame *)header; + if (BAD_MADT_ENTRY(m, end)) + return -EINVAL; + + res.start = m->base_address; + res.end = m->base_address + SZ_4K; + + if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { + spi_start = m->spi_base; + nr_spis = m->spi_count; + + pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n", + spi_start, nr_spis); + } + + fwnode = irq_domain_alloc_fwnode((void *)m->base_address); + if (!fwnode) { + pr_err("Unable to allocate GICv2m domain token\n"); + return -EINVAL; + } + + ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res); + if (ret) + irq_domain_free_fwnode(fwnode); + + return ret; +} + +static int __init gicv2m_acpi_init(struct irq_domain *parent) +{ + int ret; + + if (acpi_num_msi > 0) + return 0; + + acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME, + acpi_parse_madt_msi, 0); + + if (acpi_num_msi <= 0) + goto err_out; + + ret = gicv2m_allocate_domains(parent); + if (ret) + goto err_out; + + pci_msi_register_fwnode_provider(&gicv2m_get_fwnode); + + return 0; + +err_out: + gicv2m_teardown(); + return -EINVAL; +} +#else /* CONFIG_ACPI */ +static int __init gicv2m_acpi_init(struct irq_domain *parent) +{ + return -EINVAL; +} +#endif /* CONFIG_ACPI */ + +int __init gicv2m_init(struct fwnode_handle *parent_handle, + struct irq_domain *parent) +{ + if (is_of_node(parent_handle)) + return gicv2m_of_init(parent_handle, parent); + + return gicv2m_acpi_init(parent); +} diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index fcd327f49e8e..644e8bbe130c 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1234,7 +1234,7 @@ gic_of_init(struct device_node *node, struct device_node *parent) } if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) - gicv2m_of_init(node, gic_data[gic_cnt].domain); + gicv2m_init(&node->fwnode, gic_data[gic_cnt].domain); gic_cnt++; return 0; @@ -1359,6 +1359,10 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header, __gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle); acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle); + + if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) + gicv2m_init(NULL, gic_data[0].domain); + return 0; } IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR, diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index bae69e5d693c..febc6c312e37 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -106,7 +106,8 @@ int gic_cpu_if_down(unsigned int gic_nr); void gic_init(unsigned int nr, int start, void __iomem *dist , void __iomem *cpu); -int gicv2m_of_init(struct device_node *node, struct irq_domain *parent); +int gicv2m_init(struct fwnode_handle *parent_handle, + struct irq_domain *parent); void gic_send_sgi(unsigned int cpu_id, unsigned int irq); int gic_get_cpu_id(unsigned int cpu); -- cgit v1.2.3-58-ga151