This patches modifies acpi_gic_init() and gic_v2_acpi_init() to support irqdomain. Previously, it only support irq_default_domain. This change would allow each GIC controller to has its own domain, and to be able to support irqdomain stacking
Also, this patch introduces gicv2m_acpi_init(), which parse MADT table and look for MSI frames. The pointer to each MSI frame in ACPI table is used as a reference when creating GICv2m irqdomain. which can be used as an input when calling irq_find_domain().
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com --- arch/arm64/kernel/acpi.c | 14 ++- drivers/irqchip/irq-gic-v2m.c | 172 ++++++++++++++++++++++++++++------- drivers/irqchip/irq-gic.c | 31 ++++--- drivers/pci/pci-acpi.c | 52 +++++++++++ drivers/pci/probe.c | 2 + include/linux/irqchip/arm-gic-acpi.h | 5 +- include/linux/pci-acpi.h | 2 + 7 files changed, 231 insertions(+), 47 deletions(-)
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index 05e9c07..1182d16 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -213,10 +213,18 @@ void __init acpi_gic_init(void) acpi_status status; acpi_size tbl_size; int err; + struct irq_domain *domain = NULL;
if (acpi_disabled) return;
+ /** + * NOTE: We need to declare this before we initialize the GIC + * so that we can use pointers to MADT table and MSI_FRAME sub-table + * for reference. + */ + acpi_gbl_permanent_mmap = 1; + status = acpi_get_table_with_size(ACPI_SIG_MADT, 0, &table, &tbl_size); if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); @@ -225,10 +233,12 @@ void __init acpi_gic_init(void) return; }
- err = gic_v2_acpi_init(table); - if (err) + err = gic_v2_acpi_init(table, &domain); + if (err || !domain) pr_err("Failed to initialize GIC IRQ controller");
+ acpi_gsi_set_domain(domain); + early_acpi_os_unmap_memory((char *)table, tbl_size); }
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index a76b802..9214172 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 <linux/acpi.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> @@ -22,6 +23,7 @@ #include <linux/of_pci.h> #include <linux/slab.h> #include <linux/spinlock.h> +#include <linux/irqchip/arm-gic.h>
/* * MSI_TYPER: @@ -114,17 +116,20 @@ 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 irq_data *d; int err; + void *info; + struct irq_domain *parent = domain->parent; + uint32_t data[3] = {GIC_INT_TYPE_NONE, hwirq, IRQ_TYPE_EDGE_RISING}; + + if (parent->ops->init_alloc_info) { + err = parent->ops->init_alloc_info(data, 3, parent->of_node, + &info); + if (err) + return 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 = irq_domain_alloc_irqs_parent(domain, virq, 1, &args); + err = irq_domain_alloc_irqs_parent(domain, virq, 1, info); if (err) return err;
@@ -191,7 +196,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, }; @@ -212,8 +217,14 @@ 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; @@ -225,23 +236,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);
@@ -269,19 +274,34 @@ static int __init gicv2m_init_one(struct device_node *node, }
inner_domain->parent = parent; - v2m->domain = pci_msi_create_irq_domain(node, &gicv2m_msi_domain_info, - inner_domain); - if (!v2m->domain) { - pr_err("Failed to create MSI domain\n"); - ret = -ENOMEM; - goto err_free_domains; + 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; }
- spin_lock_init(&v2m->msi_cnt_lock); + v2m->domain->name = gicv2m_msi_domain_name;
- 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)); + spin_lock_init(&v2m->msi_cnt_lock);
return 0;
@@ -311,15 +331,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/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 2249c66..92a1a6a 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -40,7 +40,6 @@ #include <linux/slab.h> #include <linux/irqchip/chained_irq.h> #include <linux/irqchip/arm-gic.h> -#include <linux/irqchip/arm-gic-acpi.h>
#include <asm/cputype.h> #include <asm/irq.h> @@ -1007,7 +1006,11 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, gic_irqs = 1020; gic->gic_irqs = gic_irqs;
- if (node) { /* DT case */ + if (!acpi_disabled) { /* ACPI case */ + gic->domain = irq_domain_add_linear(NULL, gic_irqs, + &gic_irq_domain_hierarchy_ops, + gic); + } else if (node) { /* DT case */ gic->domain = irq_domain_add_linear(node, gic_irqs, &gic_irq_domain_hierarchy_ops, gic); @@ -1054,9 +1057,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, gic_pm_init(gic); }
-#ifdef CONFIG_OF static int gic_cnt __initdata;
+#ifdef CONFIG_OF static int __init gic_of_init(struct device_node *node, struct device_node *parent) { @@ -1148,7 +1151,7 @@ gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header, }
int __init -gic_v2_acpi_init(struct acpi_table_header *table) +gic_v2_acpi_init(struct acpi_table_header *table, struct irq_domain **domain) { void __iomem *cpu_base, *dist_base; int count; @@ -1192,13 +1195,19 @@ gic_v2_acpi_init(struct acpi_table_header *table) return -ENOMEM; }
- /* - * Initialize zero GIC instance (no multi-GIC support). Also, set GIC - * as default IRQ domain to allow for GSI registration and GSI to IRQ - * number translation (see acpi_register_gsi() and acpi_gsi_to_irq()). - */ - gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL); - irq_set_default_host(gic_data[0].domain); + gic_init_bases(gic_cnt, -1, dist_base, cpu_base, 0, NULL); + *domain = gic_data[gic_cnt].domain; + + if (!*domain) { + pr_err("Unable to create domain\n"); + return -EFAULT; + } + + if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) { + gicv2m_acpi_init(table, gic_data[gic_cnt].domain); + } + + gic_cnt++;
acpi_irq_model = ACPI_IRQ_MODEL_GIC; return 0; diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 314a625..ef1bdc5 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -9,6 +9,7 @@
#include <linux/delay.h> #include <linux/init.h> +#include <linux/irqdomain.h> #include <linux/pci.h> #include <linux/pci_hotplug.h> #include <linux/module.h> @@ -681,6 +682,52 @@ static bool pci_acpi_bus_match(struct device *dev) return dev_is_pci(dev); }
+#ifdef CONFIG_PCI_MSI +static int madt_count; + +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_set_phb_acpi_msi_domain(struct pci_bus *bus) +{ + struct irq_domain *domain; + + if (madt_count <= 0) + return; + +#ifdef CONFIG_GENERIC_MSI_IRQ_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 /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */ +} +#endif /* CONFIG_PCI_MSI */ + static struct acpi_bus_type acpi_pci_bus = { .name = "PCI", .match = pci_acpi_bus_match, @@ -707,6 +754,11 @@ static int __init acpi_pci_init(void) if (ret) return 0;
+#ifdef CONFIG_PCI_MSI + madt_count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME, + pci_acpi_parse_madt_msi, 0); +#endif + pci_set_platform_pm(&acpi_pci_platform_pm); acpi_pci_slot_init(); acpiphp_init(); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 20295e6..4295a3b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -11,6 +11,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/cpumask.h> +#include <linux/pci-acpi.h> #include <linux/pci-aspm.h> #include <linux/acpi.h> #include <linux/property.h> @@ -666,6 +667,7 @@ static void pci_set_bus_speed(struct pci_bus *bus) void __weak pcibios_set_phb_msi_domain(struct pci_bus *bus) { pci_set_phb_of_msi_domain(bus); + pci_set_phb_acpi_msi_domain(bus); }
static void pci_set_bus_msi_domain(struct pci_bus *bus) diff --git a/include/linux/irqchip/arm-gic-acpi.h b/include/linux/irqchip/arm-gic-acpi.h index de3419e..78e1061 100644 --- a/include/linux/irqchip/arm-gic-acpi.h +++ b/include/linux/irqchip/arm-gic-acpi.h @@ -12,6 +12,8 @@
#ifdef CONFIG_ACPI
+#include <linux/irqdomain.h> + /* * Hard code here, we can not get memory size from MADT (but FDT does), * Actually no need to do that, because this size can be inferred @@ -22,7 +24,8 @@
struct acpi_table_header;
-int gic_v2_acpi_init(struct acpi_table_header *table); +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); void acpi_gic_init(void); #else static inline void acpi_gic_init(void) { } diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index a76cb6f..26a03aa 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -99,6 +99,7 @@ static inline void acpiphp_enumerate_slots(struct pci_bus *bus) { } static inline void acpiphp_remove_slots(struct pci_bus *bus) { } static inline void acpiphp_check_host_bridge(struct acpi_device *adev) { } #endif +void pci_set_phb_acpi_msi_domain(struct pci_bus *bus);
extern const u8 pci_acpi_dsm_uuid[]; #define DEVICE_LABEL_DSM 0x07 @@ -108,6 +109,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