From 6082a087207706d5951768d2d48aaa2d21fc2c0d Mon Sep 17 00:00:00 2001 From: Adrian Calianu Date: Tue, 11 Aug 2015 15:09:19 +0200 Subject: [PATCH] DO NOT UPSTREAM YET: Introducing ACPI support for GICv2m From: Suravee Suthikulpanit Date: Tue, 20 Jan 2015 23:21:20 -0600 Signed-off-by: Adrian Calianu --- drivers/irqchip/irq-gic-v2m.c | 193 ++++++++++++++++++++++++++--------- drivers/pci/pci-acpi.c | 55 ++++++---- include/linux/irqchip/arm-gic-acpi.h | 1 + include/linux/pci-acpi.h | 3 + 4 files changed, 185 insertions(+), 67 deletions(-) diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index fdf7065..c32eb55 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -15,6 +15,7 @@ #define pr_fmt(fmt) "GICv2m: " fmt +#include #include #include #include @@ -22,6 +23,7 @@ #include #include #include +#include /* * MSI_TYPER: @@ -45,7 +47,6 @@ struct v2m_data { spinlock_t msi_cnt_lock; - struct msi_controller mchip; struct resource res; /* GICv2m resource */ void __iomem *base; /* GICv2m virt address */ u32 spi_start; /* The SPI number that MSIs start */ @@ -115,17 +116,17 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain, unsigned int virq, irq_hw_number_t hwirq) { - struct of_phandle_args args; + struct gic_irq_alloc_info info; struct irq_data *d; int err; - args.np = domain->parent->of_node; - args.args_count = 3; - args.args[0] = 0; - args.args[1] = hwirq - 32; - args.args[2] = IRQ_TYPE_EDGE_RISING; + err = gic_init_irq_alloc_info(GIC_INT_TYPE_NONE, hwirq, + IRQ_TYPE_EDGE_RISING, + domain->parent->of_node, &info); + if (err) + return err; - err = irq_domain_alloc_irqs_parent(domain, virq, 1, &args); + err = irq_domain_alloc_irqs_parent(domain, virq, 1, &info); if (err) return err; @@ -192,7 +193,7 @@ static void gicv2m_irq_domain_free(struct irq_domain *domain, irq_domain_free_irqs_parent(domain, virq, nr_irqs); } -static const struct irq_domain_ops gicv2m_domain_ops = { +static struct irq_domain_ops gicv2m_domain_ops = { .alloc = gicv2m_irq_domain_alloc, .free = gicv2m_irq_domain_free, }; @@ -213,11 +214,18 @@ static bool is_msi_spi_valid(u32 base, u32 num) return true; } -static int __init gicv2m_init_one(struct device_node *node, - struct irq_domain *parent) +char gicv2m_msi_domain_name[] = "V2M-MSI"; +char gicv2m_domain_name[] = "GICV2M"; + +static int __init gicv2m_init_one(struct irq_domain *parent, + u32 *spi_start, u32 *nr_spis, + struct resource *res, + enum irq_domain_ref_type type, + void *ref) { int ret; struct v2m_data *v2m; + struct irq_domain *inner_domain; v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL); if (!v2m) { @@ -225,23 +233,17 @@ static int __init gicv2m_init_one(struct device_node *node, return -ENOMEM; } - ret = of_address_to_resource(node, 0, &v2m->res); - if (ret) { - pr_err("Failed to allocate v2m resource.\n"); - goto err_free_v2m; - } - - v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res)); + v2m->base = ioremap(res->start, resource_size(res)); if (!v2m->base) { pr_err("Failed to map GICv2m resource\n"); ret = -ENOMEM; goto err_free_v2m; } + memcpy(&v2m->res,res, sizeof(struct resource)); - if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) && - !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) { - pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n", - v2m->spi_start, v2m->nr_spis); + if (*spi_start && *nr_spis) { + v2m->spi_start = *spi_start; + v2m->nr_spis = *nr_spis; } else { u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); @@ -261,43 +263,50 @@ static int __init gicv2m_init_one(struct device_node *node, goto err_iounmap; } - v2m->domain = irq_domain_add_tree(NULL, &gicv2m_domain_ops, v2m); - if (!v2m->domain) { + inner_domain = irq_domain_add_tree(node, &gicv2m_domain_ops, v2m); + if (!inner_domain) { pr_err("Failed to create GICv2m domain\n"); ret = -ENOMEM; goto err_free_bm; } - v2m->domain->parent = parent; - v2m->mchip.of_node = node; - v2m->mchip.domain = pci_msi_create_irq_domain(node, - &gicv2m_msi_domain_info, - v2m->domain); - if (!v2m->mchip.domain) { - pr_err("Failed to create MSI domain\n"); - ret = -ENOMEM; - goto err_free_domains; - } - - spin_lock_init(&v2m->msi_cnt_lock); - - ret = of_pci_msi_chip_add(&v2m->mchip); - if (ret) { - pr_err("Failed to add msi_chip.\n"); - goto err_free_domains; - } - - pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name, - (unsigned long)v2m->res.start, (unsigned long)v2m->res.end, - v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); + inner_domain->bus_token = DOMAIN_BUS_PLATFORM_MSI; + inner_domain->name = gicv2m_domain_name; + + ret = -ENOMEM; + if (type == IRQ_DOMAIN_REF_OF_DEV_NODE) { + v2m->domain = pci_msi_create_irq_domain( + (struct device_node *)ref, + &gicv2m_msi_domain_info, + inner_domain); + if (!v2m->domain) { + pr_err("Failed to create MSI domain\n"); + goto err_free_domains; + } + } else { + v2m->domain = pci_msi_create_irq_domain( NULL, + &gicv2m_msi_domain_info, + inner_domain); + if (!v2m->domain) { + pr_err("Failed to create MSI domain\n"); + goto err_free_domains; + } + + v2m->domain->type = type; + v2m->domain->acpi_ref = ref; + } + + v2m->domain->name = gicv2m_msi_domain_name; + + spin_lock_init(&v2m->msi_cnt_lock); return 0; err_free_domains: - if (v2m->mchip.domain) - irq_domain_remove(v2m->mchip.domain); if (v2m->domain) irq_domain_remove(v2m->domain); + if (inner_domain) + irq_domain_remove(inner_domain); err_free_bm: kfree(v2m->bm); err_iounmap: @@ -319,15 +328,101 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent) for (child = of_find_matching_node(node, gicv2m_device_id); child; child = of_find_matching_node(child, gicv2m_device_id)) { + u32 spi_start = 0, nr_spis = 0; + struct resource res; + if (!of_find_property(child, "msi-controller", NULL)) continue; - ret = gicv2m_init_one(child, parent); + ret = of_address_to_resource(child, 0, &res); + if (ret) { + pr_err("Failed to allocate v2m resource.\n"); + break; + } + + if (!of_property_read_u32(child, "arm,msi-base-spi", &spi_start) && + !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis)) + pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n", + spi_start, nr_spis); + + ret = gicv2m_init_one(parent, &spi_start, &nr_spis, &res, + IRQ_DOMAIN_REF_OF_DEV_NODE, child); + if (ret) { of_node_put(node); break; } + pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", child->name, + (unsigned long)res.start, (unsigned long)res.end, + spi_start, (spi_start + nr_spis)); } return ret; } + +#ifdef CONFIG_ACPI +static struct acpi_madt_generic_msi_frame *msi_frame; + +static int __init +gicv2m_acpi_parse_madt_msi(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_msi_frame *frame; + + frame = (struct acpi_madt_generic_msi_frame *)header; + if (BAD_MADT_ENTRY(frame, end)) + return -EINVAL; + + if (msi_frame) + pr_warn("Only one GIC MSI FRAME supported.\n"); + else + msi_frame = frame; + + return 0; +} + +int __init gicv2m_acpi_init(struct acpi_table_header *table, + struct irq_domain *parent) +{ + int ret = 0; + int count, i; + struct acpi_madt_generic_msi_frame *cur; + + count = acpi_parse_entries(ACPI_SIG_MADT, sizeof(struct acpi_table_madt), + gicv2m_acpi_parse_madt_msi, table, + ACPI_MADT_TYPE_GENERIC_MSI_FRAME, 0); + + if ((count <= 0) || !msi_frame) { + pr_debug("No valid ACPI GIC MSI FRAME exist\n"); + return 0; + } + + for (i = 0, cur = msi_frame; i < count; i++, cur++) { + struct resource res; + u32 spi_start = 0, nr_spis = 0; + + res.start = cur->base_address; + res.end = cur->base_address + 0x1000; + + if (cur->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { + spi_start = cur->spi_base; + nr_spis = cur->spi_count; + + pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n", + spi_start, nr_spis); + } + + ret = gicv2m_init_one(parent, &spi_start, &nr_spis, &res, + IRQ_DOMAIN_REF_ACPI_MSI_FRAME, msi_frame); + if (ret) + break; + + pr_info("MSI frame ID %u: range[%#lx:%#lx], SPI[%d:%d]\n", + cur->msi_frame_id, + (unsigned long)res.start, (unsigned long)res.end, + spi_start, (spi_start + nr_spis)); + } + return ret; +} + +#endif /* CONFIG_ACPI */ diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index fe42097..fa0d8ec 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -717,25 +718,43 @@ static int __init acpi_pci_init(void) arch_initcall(acpi_pci_init); #ifdef CONFIG_PCI_MSI -void pci_acpi_set_phb_msi_domain(struct pci_bus *bus) { +static struct acpi_madt_generic_msi_frame *msi_frame; +static int +pci_acpi_parse_madt_msi(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_msi_frame *frame; + frame = (struct acpi_madt_generic_msi_frame *)header; + if (BAD_MADT_ENTRY(frame, end)) + return -EINVAL; + + /* We currently support one MSI frame only */ + if (!msi_frame) + msi_frame = frame; + + return 0; +} + +void pci_acpi_set_phb_msi_domain(struct pci_bus *bus) +{ + int count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME, + pci_acpi_parse_madt_msi, 0); + if (count > 0) { #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN - u32 msi_frame_id = 0; - int num; - - if (acpi_disabled) - return; - - /** - * Since ACPI 5.1 currently does not define - * a way to associate MSI frame ID to a device, - * we can only support single MSI frame at the moment. - * Therefore, the id 0 is used as a default. - */ - num = msi_get_num_irq_domain(); - if (num <= 0 || num > 1) - return; - - dev_set_msi_domain(&bus->dev, irq_find_acpi_msi_domain(msi_frame_id)); + struct irq_domain *domain; + /** + * Since ACPI 5.1 currently does not define + * a way to associate MSI frame ID to a device, + * we can only support single MSI frame at the moment. + */ + domain = irq_find_domain(IRQ_DOMAIN_REF_ACPI_MSI_FRAME, msi_frame); + if (!domain) { + pr_debug("Fail to find domain for MSI\n"); + return; + } + + dev_set_msi_domain(&bus->dev, domain); #endif + } } #endif /* CONFIG_PCI_MSI */ diff --git a/include/linux/irqchip/arm-gic-acpi.h b/include/linux/irqchip/arm-gic-acpi.h index fa8033b..b02f065 100644 --- a/include/linux/irqchip/arm-gic-acpi.h +++ b/include/linux/irqchip/arm-gic-acpi.h @@ -26,6 +26,7 @@ struct acpi_table_header; void acpi_irq_init(void); int gic_v2_acpi_init(struct acpi_table_header *table, struct irq_domain **domain); +int gicv2m_acpi_init(struct acpi_table_header *table, struct irq_domain *parent); #else static inline void acpi_gic_init(void) { } #endif diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index a965efa..759916d 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -77,6 +77,8 @@ static inline void acpiphp_remove_slots(struct pci_bus *bus) { } static inline void acpiphp_check_host_bridge(struct acpi_device *adev) { } #endif +void pci_acpi_set_phb_msi_domain(struct pci_bus *bus); + extern const u8 pci_acpi_dsm_uuid[]; #define DEVICE_LABEL_DSM 0x07 #define RESET_DELAY_DSM 0x08 @@ -85,6 +87,7 @@ extern const u8 pci_acpi_dsm_uuid[]; #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } +static inline void pci_acpi_set_phb_msi_domain(struct pci_bus *bus) { }; #endif /* CONFIG_ACPI */ #ifdef CONFIG_ACPI_APEI -- 1.9.1