ACPI core patches for ARM64 are now upstreamed in 4.1. Also PCI support patches for ARM64 ACPI are in progress, I am sending out this RFC to introduce ACPI support for GICv2m. This would allow MSI to work when booting ACPI.
This patch series modify existing IRQ domain and ACPI GSI code to better support ACPI on ARM64.
Summary: * Patch 1,2 introduce the new irq_domain_ops.init_alloc_info(). * Patch 3,4 modify the existing irq_domain_ops.match() to support ACPI. * Patch 5,6 introduce IRQ domain for ARM64 ACPI. * Patch 7 introduces ACPI support for GICv2m.
Due to a large number of prerequisite patches, I have put together a branch on GitHub for review and testing:
https://github.com/ssuthiku/linux.git acpi-pci-msi-rfc1
This branch has been tested on AMD Seattle Platform. Any feedback and comments are appreciated.
Thank you in advance,
Suravee
Suravee Suthikulpanit (7): irqdomain: Introduce irq_domain_ops.init_alloc_info gic: Add gic_init_irq_alloc_info() irqdomain: Introduce irqdomain matching by reference Adopting the new irq_domain_ops.match() function prototype acpi: gsi: Adding irqdomain for ACPI acpi: gsi: Adding ARM64-specific acpi_register_gsi() gicv2m: Introducing ACPI support for GICv2m
arch/arm64/kernel/acpi.c | 62 ++++++++- arch/powerpc/platforms/512x/mpc5121_ads_cpld.c | 4 +- arch/powerpc/platforms/cell/interrupt.c | 4 +- arch/powerpc/platforms/powermac/pic.c | 4 +- arch/powerpc/platforms/ps3/interrupt.c | 4 +- arch/powerpc/sysdev/ehv_pic.c | 4 +- arch/powerpc/sysdev/i8259.c | 4 +- arch/powerpc/sysdev/ipic.c | 4 +- arch/powerpc/sysdev/mpic.c | 4 +- arch/powerpc/sysdev/qe_lib/qe_ic.c | 4 +- arch/powerpc/sysdev/xics/xics-common.c | 4 +- drivers/acpi/gsi.c | 23 +++- drivers/irqchip/irq-gic-v2m.c | 172 ++++++++++++++++++++----- drivers/irqchip/irq-gic.c | 98 ++++++++++++-- drivers/pci/pci-acpi.c | 52 ++++++++ drivers/pci/probe.c | 2 + include/linux/acpi.h | 4 + include/linux/irqchip/arm-gic-acpi.h | 5 +- include/linux/irqchip/arm-gic.h | 11 ++ include/linux/irqdomain.h | 17 ++- include/linux/pci-acpi.h | 2 + kernel/irq/irqdomain.c | 41 ++++-- 22 files changed, 453 insertions(+), 76 deletions(-)
Currently, when calling irq_domain_alloc_irqs, it uses struct of_phandle_args to pass IRQ information. However, this is not appropriate for ACPI since of_phandle_args is specific to DT.
Therefore, this patch introduces a new function pointer, irq_domain_ops.init_alloc_info, which can be used by irqchip to provide a way to initialize irqchip-specific data-structure for allocating IRQ.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com --- include/linux/irqdomain.h | 2 ++ kernel/irq/irqdomain.c | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 744ac0e..39fbcd8 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -72,6 +72,8 @@ struct irq_domain_ops { /* extended V2 interfaces to support hierarchy irq_domains */ int (*alloc)(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs, void *arg); + int (*init_alloc_info)(uint32_t *data, int nr, void *ref, + void **info); void (*free)(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs); void (*activate)(struct irq_domain *d, struct irq_data *irq_data); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index 8c3577f..cc4ef7c 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -470,6 +470,7 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; int virq; + void *info;
domain = irq_data->np ? irq_find_host(irq_data->np) : irq_default_domain; if (!domain) { @@ -496,7 +497,14 @@ unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data) if (virq) return virq;
- virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, irq_data); + if (domain->ops->init_alloc_info) + if (domain->ops->init_alloc_info(irq_data->args, + irq_data->args_count, + irq_data->np, + &info)) + return 0; + + virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, info); if (virq <= 0) return 0; } else {
Adding init function for GIC irq allocation info structure.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com --- drivers/irqchip/irq-gic.c | 67 +++++++++++++++++++++++++++++++++++++++-- include/linux/irqchip/arm-gic.h | 11 +++++++ 2 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 4dd8826..2249c66 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -855,10 +855,13 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, int i, ret; irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; - struct of_phandle_args *irq_data = arg; + struct gic_irq_alloc_info *info = arg; + u32 intspec[3];
- ret = gic_irq_domain_xlate(domain, irq_data->np, irq_data->args, - irq_data->args_count, &hwirq, &type); + intspec[0] = info->gic_int_type; + intspec[1] = info->hwirq; + intspec[2] = info->irq_type; + ret = gic_irq_domain_xlate(domain, info->ref, intspec, 3, &hwirq, &type); if (ret) return ret;
@@ -868,10 +871,68 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, return 0; }
+static int gic_init_irq_alloc_info(uint32_t *data, int nr, void *ref, + void **info) +{ + struct gic_irq_alloc_info *alloc_info; + unsigned int gic_int_type; + unsigned int hwirq; + unsigned int irq_type; + + if (nr != 3) + return -EINVAL; + + gic_int_type = data[0]; + hwirq = data[1]; + irq_type = data[2]; + + alloc_info = kzalloc(sizeof(struct gic_irq_alloc_info), GFP_KERNEL); + if (!alloc_info) + return -ENOMEM; + + if ((irq_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH && + (irq_type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING) + return -EINVAL; + + alloc_info->irq_type = irq_type; + alloc_info->ref = ref; + + /* + * ACPI have no bindings to indicate SPI or PPI, so we + * use different mappings from DT in ACPI. + * + * For FDT + * PPI interrupt: in the range [0, 15]; + * SPI interrupt: in the range [0, 987]; + * + * For ACPI, GSI should be unique so using + * the hwirq directly for the mapping: + * PPI interrupt: in the range [16, 31]; + * SPI interrupt: in the range [32, 1019]; + */ + + if (gic_int_type != GIC_INT_TYPE_NONE) { + alloc_info->gic_int_type = gic_int_type; + alloc_info->hwirq = hwirq; + } else { + if (hwirq < 32) { + alloc_info->gic_int_type = GIC_INT_TYPE_PPI; + alloc_info->hwirq = hwirq - 16; + } else { + alloc_info->gic_int_type = GIC_INT_TYPE_SPI; + alloc_info->hwirq = hwirq - 32; + } + } + + *info = alloc_info; + return 0; +} + static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = { .xlate = gic_irq_domain_xlate, .alloc = gic_irq_domain_alloc, .free = irq_domain_free_irqs_top, + .init_alloc_info = gic_init_irq_alloc_info, };
static const struct irq_domain_ops gic_irq_domain_ops = { diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 9de976b..4d6d6eb 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -89,12 +89,23 @@ #define GICH_MISR_EOI (1 << 0) #define GICH_MISR_U (1 << 1)
+#define GIC_INT_TYPE_SPI 0 +#define GIC_INT_TYPE_PPI 1 +#define GIC_INT_TYPE_NONE ~0U + #ifndef __ASSEMBLY__
#include <linux/irqdomain.h>
struct device_node;
+struct gic_irq_alloc_info { + void *ref; + unsigned int irq_type; + unsigned int gic_int_type; + unsigned int hwirq; +}; + void gic_set_irqchip_flags(unsigned long flags); void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *, u32 offset, struct device_node *);
Currently, in irq_find_host(), irq_domain_ops.match() uses struct device_node pointer to match a particular irq_domain. This will not be able to support ACPI where reference to GICv2m is in the MADT table (i.e the MSI frame).
This patch modifies irq_domain_ops.match(), to allow various types of irq_domain reference. It also introduces enum irq_domain_ref_types.
Along with the new reference type, it also introduces a new function, irq_find_domain(), which can be used to find a particular irqdomain using different reference types.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com --- include/linux/irqdomain.h | 15 +++++++++++++-- kernel/irq/irqdomain.c | 31 ++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 9 deletions(-)
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index 39fbcd8..a91af80 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -45,6 +45,11 @@ struct irq_data; /* Number of irqs reserved for a legacy isa controller */ #define NUM_ISA_INTERRUPTS 16
+enum irq_domain_ref_type { + IRQ_DOMAIN_REF_OF_DEV_NODE = 0, + IRQ_DOMAIN_REF_ACPI_MSI_FRAME, +}; + /** * struct irq_domain_ops - Methods for irq_domain objects * @match: Match an interrupt controller device node to a host, returns @@ -61,7 +66,7 @@ struct irq_data; * to setup the irq_desc when returning from map(). */ struct irq_domain_ops { - int (*match)(struct irq_domain *d, struct device_node *node); + int (*match)(struct irq_domain *d, enum irq_domain_ref_type type, void *data); int (*map)(struct irq_domain *d, unsigned int virq, irq_hw_number_t hw); void (*unmap)(struct irq_domain *d, unsigned int virq); int (*xlate)(struct irq_domain *d, struct device_node *node, @@ -117,7 +122,11 @@ struct irq_domain { unsigned int flags;
/* Optional data */ - struct device_node *of_node; + enum irq_domain_ref_type type; + union { + struct device_node *of_node; + void *acpi_ref; + }; struct irq_domain_chip_generic *gc; #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY struct irq_domain *parent; @@ -165,6 +174,8 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, void *host_data); extern struct irq_domain *irq_find_host(struct device_node *node); extern void irq_set_default_host(struct irq_domain *host); +extern struct irq_domain *irq_find_domain(enum irq_domain_ref_type type, + void *ref);
/** * irq_domain_add_linear() - Allocate and register a linear revmap irq_domain. diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index cc4ef7c..31536d1 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -187,10 +187,11 @@ struct irq_domain *irq_domain_add_legacy(struct device_node *of_node, EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
/** - * irq_find_host() - Locates a domain for a given device node - * @node: device-tree node of the interrupt controller + * irq_find_domain() - Locates a domain for a given a refence pointer + * @type: specify irq domain reference pointer type to be match + * @ref: pointer to the reference data structure to be matched */ -struct irq_domain *irq_find_host(struct device_node *node) +struct irq_domain *irq_find_domain(enum irq_domain_ref_type type, void *ref) { struct irq_domain *h, *found = NULL; int rc; @@ -202,10 +203,16 @@ struct irq_domain *irq_find_host(struct device_node *node) */ mutex_lock(&irq_domain_mutex); list_for_each_entry(h, &irq_domain_list, link) { - if (h->ops->match) - rc = h->ops->match(h, node); - else - rc = (h->of_node != NULL) && (h->of_node == node); + if (h->ops->match) { + rc = h->ops->match(h, type, ref); + } else if (type == IRQ_DOMAIN_REF_OF_DEV_NODE || + type == IRQ_DOMAIN_REF_ACPI_MSI_FRAME) { + /* Here, we just need to compare reference pointer */ + rc = (h->of_node != NULL) && (h->of_node == ref); + } else { + /* For non-DT and non-ACPI reference, must specify match */ + BUG(); + }
if (rc) { found = h; @@ -215,6 +222,16 @@ struct irq_domain *irq_find_host(struct device_node *node) mutex_unlock(&irq_domain_mutex); return found; } +EXPORT_SYMBOL_GPL(irq_find_domain); + +/** + * irq_find_host() - Locates a domain for a given device node + * @node: device-tree node of the interrupt controller + */ +struct irq_domain *irq_find_host(struct device_node *node) +{ + return irq_find_domain(IRQ_DOMAIN_REF_OF_DEV_NODE, node); +} EXPORT_SYMBOL_GPL(irq_find_host);
/**
On 09/07/15 06:44, Suravee Suthikulpanit wrote:
Currently, in irq_find_host(), irq_domain_ops.match() uses struct device_node pointer to match a particular irq_domain. This will not be able to support ACPI where reference to GICv2m is in the MADT table (i.e the MSI frame).
This patch modifies irq_domain_ops.match(), to allow various types of irq_domain reference. It also introduces enum irq_domain_ref_types.
Along with the new reference type, it also introduces a new function, irq_find_domain(), which can be used to find a particular irqdomain using different reference types.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com
Why has this patch been dropped from the v2? It has the best bit, and it is what I asked Hanjun to use.
But I see that none of the two series is using this approach, which is rather annoying.
Where are we going?
Thanks,
M.
Hi Marc,
On 07/17/2015 11:55 PM, Marc Zyngier wrote:
On 09/07/15 06:44, Suravee Suthikulpanit wrote:
Currently, in irq_find_host(), irq_domain_ops.match() uses struct device_node pointer to match a particular irq_domain. This will not be able to support ACPI where reference to GICv2m is in the MADT table (i.e the MSI frame).
This patch modifies irq_domain_ops.match(), to allow various types of irq_domain reference. It also introduces enum irq_domain_ref_types.
Along with the new reference type, it also introduces a new function, irq_find_domain(), which can be used to find a particular irqdomain using different reference types.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com
Why has this patch been dropped from the v2? It has the best bit, and it is what I asked Hanjun to use.
I'm keeping my eyes on this patch and to see if I can reuse this approach on my patch set.
But I see that none of the two series is using this approach, which is rather annoying.
I discussed with Suravee on IRC that I may cherry pick some of patches in this patch set, and he agreed. I'm reworking my patches but it may have further obstacles, I will let you know the progress.
Thanks Hanjun
On 7/17/15 22:55, Marc Zyngier wrote:
On 09/07/15 06:44, Suravee Suthikulpanit wrote:
Currently, in irq_find_host(), irq_domain_ops.match() uses struct device_node pointer to match a particular irq_domain. This will not be able to support ACPI where reference to GICv2m is in the MADT table (i.e the MSI frame).
This patch modifies irq_domain_ops.match(), to allow various types of irq_domain reference. It also introduces enum irq_domain_ref_types.
Along with the new reference type, it also introduces a new function, irq_find_domain(), which can be used to find a particular irqdomain using different reference types.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com
Why has this patch been dropped from the v2? It has the best bit, and it is what I asked Hanjun to use.
But I see that none of the two series is using this approach, which is rather annoying.
Where are we going?
Thanks,
M.
In my V2, I didn't think this was needed with the new irq_domain_bus_token. I'll update this in the V3 and incorporate your changes from:
[PATCH v5 00/19] Per-device MSI domain & platform MSI [PATCH 0/5] Making the generic ACPI GSI layer irqdomain aware
Thanks,
Suravee
Hi Suravee,
On 23/07/15 09:44, Suravee Suthikulpanit wrote:
On 7/17/15 22:55, Marc Zyngier wrote:
On 09/07/15 06:44, Suravee Suthikulpanit wrote:
Currently, in irq_find_host(), irq_domain_ops.match() uses struct device_node pointer to match a particular irq_domain. This will not be able to support ACPI where reference to GICv2m is in the MADT table (i.e the MSI frame).
This patch modifies irq_domain_ops.match(), to allow various types of irq_domain reference. It also introduces enum irq_domain_ref_types.
Along with the new reference type, it also introduces a new function, irq_find_domain(), which can be used to find a particular irqdomain using different reference types.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com
Why has this patch been dropped from the v2? It has the best bit, and it is what I asked Hanjun to use.
But I see that none of the two series is using this approach, which is rather annoying.
Where are we going?
Thanks,
M.
In my V2, I didn't think this was needed with the new irq_domain_bus_token. I'll update this in the V3 and incorporate your changes from:
[PATCH v5 00/19] Per-device MSI domain & platform MSI [PATCH 0/5] Making the generic ACPI GSI layer irqdomain aware
domain_token and bus_token are orthogonal things. One is gives you a namespace, and the other allows different types for a given name.
Anyway, rebasing on top of these series should make it obvious. I've pushed out a branch (irq/gsi-irq-domain-v2) that contains the latest versions of these series.
Please synchronize with Hanjun to produce a *single* series that covers self-probing, GICv2m and GICv3 support.
Thanks,
M.
On 07/24/2015 12:13 AM, Marc Zyngier wrote:
Hi Suravee,
On 23/07/15 09:44, Suravee Suthikulpanit wrote:
On 7/17/15 22:55, Marc Zyngier wrote:
On 09/07/15 06:44, Suravee Suthikulpanit wrote:
Currently, in irq_find_host(), irq_domain_ops.match() uses struct device_node pointer to match a particular irq_domain. This will not be able to support ACPI where reference to GICv2m is in the MADT table (i.e the MSI frame).
This patch modifies irq_domain_ops.match(), to allow various types of irq_domain reference. It also introduces enum irq_domain_ref_types.
Along with the new reference type, it also introduces a new function, irq_find_domain(), which can be used to find a particular irqdomain using different reference types.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com
Why has this patch been dropped from the v2? It has the best bit, and it is what I asked Hanjun to use.
But I see that none of the two series is using this approach, which is rather annoying.
Where are we going?
Thanks,
M.
In my V2, I didn't think this was needed with the new irq_domain_bus_token. I'll update this in the V3 and incorporate your changes from:
[PATCH v5 00/19] Per-device MSI domain & platform MSI [PATCH 0/5] Making the generic ACPI GSI layer irqdomain aware
domain_token and bus_token are orthogonal things. One is gives you a namespace, and the other allows different types for a given name.
Anyway, rebasing on top of these series should make it obvious. I've pushed out a branch (irq/gsi-irq-domain-v2) that contains the latest versions of these series.
Please synchronize with Hanjun to produce a *single* series that covers self-probing, GICv2m and GICv3 support.
Will do, thanks for your great help!
Hanjun
This patch modifies arch-specific irq_domain_ops.match functions to use the new function prototype.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com --- arch/powerpc/platforms/512x/mpc5121_ads_cpld.c | 4 +++- arch/powerpc/platforms/cell/interrupt.c | 4 +++- arch/powerpc/platforms/powermac/pic.c | 4 +++- arch/powerpc/platforms/ps3/interrupt.c | 4 +++- arch/powerpc/sysdev/ehv_pic.c | 4 +++- arch/powerpc/sysdev/i8259.c | 4 +++- arch/powerpc/sysdev/ipic.c | 4 +++- arch/powerpc/sysdev/mpic.c | 4 +++- arch/powerpc/sysdev/qe_lib/qe_ic.c | 4 +++- arch/powerpc/sysdev/xics/xics-common.c | 4 +++- 10 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c index ca3a062..17619e5 100644 --- a/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c +++ b/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c @@ -123,8 +123,10 @@ cpld_pic_cascade(unsigned int irq, struct irq_desc *desc) }
static int -cpld_pic_host_match(struct irq_domain *h, struct device_node *node) +cpld_pic_host_match(struct irq_domain *h, + enum irq_domain_ref_type type, void *node) { + WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); return cpld_pic_node == node; }
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c index 3af8324..178801d 100644 --- a/arch/powerpc/platforms/cell/interrupt.c +++ b/arch/powerpc/platforms/cell/interrupt.c @@ -222,8 +222,10 @@ void iic_request_IPIs(void) #endif /* CONFIG_SMP */
-static int iic_host_match(struct irq_domain *h, struct device_node *node) +static int iic_host_match(struct irq_domain *h, + enum irq_domain_ref_type type, void *node) { + WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); return of_device_is_compatible(node, "IBM,CBEA-Internal-Interrupt-Controller"); } diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index 59cfc9d..2314c13 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -268,8 +268,10 @@ static struct irqaction gatwick_cascade_action = { .name = "cascade", };
-static int pmac_pic_host_match(struct irq_domain *h, struct device_node *node) +static int pmac_pic_host_match(struct irq_domain *h, + enum irq_domain_ref_type type, void *node) { + WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); /* We match all, we don't always have a node anyway */ return 1; } diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index a6c42f3..7235e07 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c @@ -678,8 +678,10 @@ static int ps3_host_map(struct irq_domain *h, unsigned int virq, return 0; }
-static int ps3_host_match(struct irq_domain *h, struct device_node *np) +static int ps3_host_match(struct irq_domain *h, + enum irq_domain_data_type type, void *node) { + WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); /* Match all */ return 1; } diff --git a/arch/powerpc/sysdev/ehv_pic.c b/arch/powerpc/sysdev/ehv_pic.c index 2d20f10..46638cf 100644 --- a/arch/powerpc/sysdev/ehv_pic.c +++ b/arch/powerpc/sysdev/ehv_pic.c @@ -177,8 +177,10 @@ unsigned int ehv_pic_get_irq(void) return irq_linear_revmap(global_ehv_pic->irqhost, irq); }
-static int ehv_pic_host_match(struct irq_domain *h, struct device_node *node) +static int ehv_pic_host_match(struct irq_domain *h, + enum irq_domain_ref_type type, void *node) { + WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); /* Exact match, unless ehv_pic node is NULL */ return h->of_node == NULL || h->of_node == node; } diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c index 31c3347..eec44a6 100644 --- a/arch/powerpc/sysdev/i8259.c +++ b/arch/powerpc/sysdev/i8259.c @@ -162,8 +162,10 @@ static struct resource pic_edgectrl_iores = { .flags = IORESOURCE_BUSY, };
-static int i8259_host_match(struct irq_domain *h, struct device_node *node) +static int i8259_host_match(struct irq_domain *h, + enum irq_domain_ref_type type, void *node) { + WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); return h->of_node == NULL || h->of_node == node; }
diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c index d78f136..77c255f 100644 --- a/arch/powerpc/sysdev/ipic.c +++ b/arch/powerpc/sysdev/ipic.c @@ -671,8 +671,10 @@ static struct irq_chip ipic_edge_irq_chip = { .irq_set_type = ipic_set_irq_type, };
-static int ipic_host_match(struct irq_domain *h, struct device_node *node) +static int ipic_host_match(struct irq_domain *h, + enum irq_domain_ref_type type, void *node) { + WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); /* Exact match, unless ipic node is NULL */ return h->of_node == NULL || h->of_node == node; } diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index c8e7333..aef4cd5 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -1007,8 +1007,10 @@ static struct irq_chip mpic_irq_ht_chip = { #endif /* CONFIG_MPIC_U3_HT_IRQS */
-static int mpic_host_match(struct irq_domain *h, struct device_node *node) +static int mpic_host_match(struct irq_domain *h, + enum irq_domain_ref_type type, void *node) { + WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); /* Exact match, unless mpic node is NULL */ return h->of_node == NULL || h->of_node == node; } diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c index 6512cd8..c0d1787 100644 --- a/arch/powerpc/sysdev/qe_lib/qe_ic.c +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c @@ -244,8 +244,10 @@ static struct irq_chip qe_ic_irq_chip = { .irq_mask_ack = qe_ic_mask_irq, };
-static int qe_ic_host_match(struct irq_domain *h, struct device_node *node) +static int qe_ic_host_match(struct irq_domain *h, + enum irq_domain_ref_type type, void *node) { + WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); /* Exact match, unless qe_ic node is NULL */ return h->of_node == NULL || h->of_node == node; } diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c index 08c248e..36023f0 100644 --- a/arch/powerpc/sysdev/xics/xics-common.c +++ b/arch/powerpc/sysdev/xics/xics-common.c @@ -298,10 +298,12 @@ int xics_get_irq_server(unsigned int virq, const struct cpumask *cpumask, } #endif /* CONFIG_SMP */
-static int xics_host_match(struct irq_domain *h, struct device_node *node) +static int xics_host_match(struct irq_domain *h, + enum irq_domain_ref_type type, void *node) { struct ics *ics;
+ WARN_ON(type != IRQ_DOMAIN_REF_OF_DEV_NODE); list_for_each_entry(ics, &ics_list, link) if (ics->host_match(ics, node)) return 1;
This patch introduces acpi_irq_domain, which can be used to specify irqdomain when booting with ACPI. Currently, it is using irq_default_domain, which is NULL and does not support stacking. Also, irqdomain stacking is needed for supporting MSI.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com --- drivers/acpi/gsi.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/acpi/gsi.c b/drivers/acpi/gsi.c index 38208f2..8fa8a01 100644 --- a/drivers/acpi/gsi.c +++ b/drivers/acpi/gsi.c @@ -14,7 +14,16 @@
enum acpi_irq_model_id acpi_irq_model;
-static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity) +struct irq_domain *acpi_irq_domain = NULL; + +int acpi_gsi_set_domain(struct irq_domain *domain) +{ + acpi_irq_domain = domain; + return 0; +} +EXPORT_SYMBOL_GPL(acpi_gsi_set_domain); + +unsigned int acpi_gsi_get_irq_type(int trigger, int polarity) { switch (polarity) { case ACPI_ACTIVE_LOW: @@ -32,6 +41,8 @@ static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity) return IRQ_TYPE_NONE; } } +EXPORT_SYMBOL_GPL(acpi_gsi_get_irq_type); +
/** * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI @@ -50,7 +61,7 @@ int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) * the mapping corresponding to default domain by passing NULL * as irq_domain parameter */ - *irq = irq_find_mapping(NULL, gsi); + *irq = irq_find_mapping(acpi_irq_domain, gsi); /* * *irq == 0 means no mapping, that should * be reported as a failure @@ -80,7 +91,7 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, * hence always create mapping referring to the default domain * by passing NULL as irq_domain parameter */ - irq = irq_create_mapping(NULL, gsi); + irq = irq_create_mapping(acpi_irq_domain, gsi); if (!irq) return -EINVAL;
@@ -98,7 +109,7 @@ EXPORT_SYMBOL_GPL(acpi_register_gsi); */ void acpi_unregister_gsi(u32 gsi) { - int irq = irq_find_mapping(NULL, gsi); + int irq = irq_find_mapping(acpi_irq_domain, gsi);
irq_dispose_mapping(irq); }
This patch declares acpi_register_gsi() as weak and allows architecture to specify its own acpi_register_gsi().
In ARM64 case, we need to specify GIC_INT_TYPE_XXX when calling irq_domain_ops.init_alloc_info(), which is specific to ARM64.
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com --- arch/arm64/kernel/acpi.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/gsi.c | 4 ++-- include/linux/acpi.h | 4 ++++ 3 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index 19de753..05e9c07 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -20,6 +20,7 @@ #include <linux/cpumask.h> #include <linux/init.h> #include <linux/irq.h> +#include <linux/irqchip/arm-gic.h> #include <linux/irqdomain.h> #include <linux/memblock.h> #include <linux/of_fdt.h> @@ -230,3 +231,50 @@ void __init acpi_gic_init(void)
early_acpi_os_unmap_memory((char *)table, tbl_size); } + +int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, + int polarity) +{ + unsigned int irq; + unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity); + struct irq_data *d = NULL; + + if (acpi_irq_domain && acpi_irq_domain->ops->init_alloc_info) { + void *info; + int err; + uint32_t data[3] = {GIC_INT_TYPE_NONE, gsi, irq_type}; + + err = acpi_irq_domain->ops->init_alloc_info(data, 3, + NULL, &info); + if (err) + return err; + + irq = irq_domain_alloc_irqs(acpi_irq_domain, 1, + dev_to_node(dev), info); + if (irq < 0) + return -ENOSPC; + + d = irq_domain_get_irq_data(acpi_irq_domain, irq); + if (!d) + return -EFAULT; + } else { + /* + * There is no IRQ domain on ACPI, + * hence always create mapping referring to the default domain + * by passing NULL as irq_domain parameter + */ + irq = irq_create_mapping(NULL, gsi); + if (!irq) + return -EINVAL; + } + + /* Set irq type if specified and different than the current one */ + if (irq_type != IRQ_TYPE_NONE && + irq_type != irq_get_trigger_type(irq)) { + if (d) + d->chip->irq_set_type(d, irq_type); + else + irq_set_irq_type(irq, irq_type); + } + return irq; +} diff --git a/drivers/acpi/gsi.c b/drivers/acpi/gsi.c index 8fa8a01..a015d9e 100644 --- a/drivers/acpi/gsi.c +++ b/drivers/acpi/gsi.c @@ -80,8 +80,8 @@ EXPORT_SYMBOL_GPL(acpi_gsi_to_irq); * Returns: a valid linux IRQ number on success * -EINVAL on failure */ -int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, - int polarity) +int __weak acpi_register_gsi(struct device *dev, u32 gsi, int trigger, + int polarity) { unsigned int irq; unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6826ca4..5254d39 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -221,6 +221,10 @@ extern unsigned long acpi_realmode_flags; int acpi_register_gsi (struct device *dev, u32 gsi, int triggering, int polarity); int acpi_gsi_to_irq (u32 gsi, unsigned int *irq); int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi); +unsigned int acpi_gsi_get_irq_type(int trigger, int polarity); + +extern struct irq_domain *acpi_irq_domain; +int acpi_gsi_set_domain(struct irq_domain *domain);
#ifdef CONFIG_X86_IO_APIC extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity);
On Thu, Jul 09, 2015 at 06:44:28AM +0100, Suravee Suthikulpanit wrote:
This patch declares acpi_register_gsi() as weak and allows architecture to specify its own acpi_register_gsi().
In ARM64 case, we need to specify GIC_INT_TYPE_XXX when calling irq_domain_ops.init_alloc_info(), which is specific to ARM64.
I think it is better to define an ACPI hook that allows you to retrieve what interrupt controller type you want to alloc from instead of adding an acpi_register_gsi function that is 99% generic to arm64 code.
I will review this code on the kernel mailing lists, but I am not happy with this patch approach.
Lorenzo
Signed-off-by: Suravee Suthikulpanit Suravee.Suthikulpanit@amd.com
arch/arm64/kernel/acpi.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/gsi.c | 4 ++-- include/linux/acpi.h | 4 ++++ 3 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index 19de753..05e9c07 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -20,6 +20,7 @@ #include <linux/cpumask.h> #include <linux/init.h> #include <linux/irq.h> +#include <linux/irqchip/arm-gic.h> #include <linux/irqdomain.h> #include <linux/memblock.h> #include <linux/of_fdt.h> @@ -230,3 +231,50 @@ void __init acpi_gic_init(void) early_acpi_os_unmap_memory((char *)table, tbl_size); }
+int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
int polarity)
+{
- unsigned int irq;
- unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity);
- struct irq_data *d = NULL;
- if (acpi_irq_domain && acpi_irq_domain->ops->init_alloc_info) {
void *info;
int err;
uint32_t data[3] = {GIC_INT_TYPE_NONE, gsi, irq_type};
err = acpi_irq_domain->ops->init_alloc_info(data, 3,
NULL, &info);
if (err)
return err;
irq = irq_domain_alloc_irqs(acpi_irq_domain, 1,
dev_to_node(dev), info);
if (irq < 0)
return -ENOSPC;
d = irq_domain_get_irq_data(acpi_irq_domain, irq);
if (!d)
return -EFAULT;
- } else {
/*
* There is no IRQ domain on ACPI,
* hence always create mapping referring to the default domain
* by passing NULL as irq_domain parameter
*/
irq = irq_create_mapping(NULL, gsi);
if (!irq)
return -EINVAL;
- }
- /* Set irq type if specified and different than the current one */
- if (irq_type != IRQ_TYPE_NONE &&
irq_type != irq_get_trigger_type(irq)) {
if (d)
d->chip->irq_set_type(d, irq_type);
else
irq_set_irq_type(irq, irq_type);
- }
- return irq;
+} diff --git a/drivers/acpi/gsi.c b/drivers/acpi/gsi.c index 8fa8a01..a015d9e 100644 --- a/drivers/acpi/gsi.c +++ b/drivers/acpi/gsi.c @@ -80,8 +80,8 @@ EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
- Returns: a valid linux IRQ number on success
-EINVAL on failure
*/ -int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
int polarity)
+int __weak acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
int polarity)
{ unsigned int irq; unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6826ca4..5254d39 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -221,6 +221,10 @@ extern unsigned long acpi_realmode_flags; int acpi_register_gsi (struct device *dev, u32 gsi, int triggering, int polarity); int acpi_gsi_to_irq (u32 gsi, unsigned int *irq); int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi); +unsigned int acpi_gsi_get_irq_type(int trigger, int polarity);
+extern struct irq_domain *acpi_irq_domain; +int acpi_gsi_set_domain(struct irq_domain *domain); #ifdef CONFIG_X86_IO_APIC extern int acpi_get_override_irq(u32 gsi, int *trigger, int *polarity); -- 2.1.0
On 07/10/2015 05:19 PM, Lorenzo Pieralisi wrote:
On Thu, Jul 09, 2015 at 06:44:28AM +0100, Suravee Suthikulpanit wrote:
This patch declares acpi_register_gsi() as weak and allows architecture to specify its own acpi_register_gsi().
In ARM64 case, we need to specify GIC_INT_TYPE_XXX when calling irq_domain_ops.init_alloc_info(), which is specific to ARM64.
I think it is better to define an ACPI hook that allows you to retrieve what interrupt controller type you want to alloc from instead of adding an acpi_register_gsi function that is 99% generic to arm64 code.
I will review this code on the kernel mailing lists, but I am not happy with this patch approach.
Lorenzo
Noted, and I have refactor some code and implement the hook. I'll send the new revision soon.
Suravee
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
On Thu, Jul 09, 2015 at 06:44:29AM +0100, Suravee Suthikulpanit wrote:
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;
This is worth investigating also to avoid parsing the MADT entries a gazillions times instead of stashing its entries. I have to fathom why in generic code it is set so late in the boot process (this has to be investigated, there must be a reason and we will probably bump into it - see acpi_early_init()), but this arm64 override is definitely worth a look, I will take this into account.
Lorenzo
On 7/10/15 17:10, Lorenzo Pieralisi wrote:
On Thu, Jul 09, 2015 at 06:44:29AM +0100, Suravee Suthikulpanit wrote:
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;
This is worth investigating also to avoid parsing the MADT entries a gazillions times instead of stashing its entries. I have to fathom why in generic code it is set so late in the boot process (this has to be investigated, there must be a reason and we will probably bump into it - see acpi_early_init()), but this arm64 override is definitely worth a look, I will take this into account.
Lorenzo
That's a good point. Lemme see if we can avoid having to enable this during init_IRQ() phase.
Thanks,
Suravee
Suravee
On 07/10/2015 05:10 PM, Lorenzo Pieralisi wrote:
On Thu, Jul 09, 2015 at 06:44:29AM +0100, Suravee Suthikulpanit wrote:
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;
This is worth investigating also to avoid parsing the MADT entries a gazillions times instead of stashing its entries. I have to fathom why in generic code it is set so late in the boot process (this has to be investigated, there must be a reason and we will probably bump into it - see acpi_early_init()), but this arm64 override is definitely worth a look, I will take this into account.
Lorenzo
So, what if I provide a MADT GIC MSI frame handler structure and a set of utility functions (e.g. acpi_get_msi_frame(index, ...)), which can be called and reused by multiple codes. This should avoid mapping and parsing the MADT multiple times, and also avoid the need to set acpi_gbl_permanent_mmap too early.
Thanks,
Suravee
By the way, the branch on my GitHub still has the V2 of "Introducing per-device MSI domain" from Marc. I noticed that Marc sent out V3 yesterday.
https://lkml.org/lkml/2015/7/7/712
I'll update my branch in the next revision.
Thanks, Suravee
On 7/9/15 12:44, Suravee Suthikulpanit wrote:
ACPI core patches for ARM64 are now upstreamed in 4.1. Also PCI support patches for ARM64 ACPI are in progress, I am sending out this RFC to introduce ACPI support for GICv2m. This would allow MSI to work when booting ACPI.
This patch series modify existing IRQ domain and ACPI GSI code to better support ACPI on ARM64.
Summary:
- Patch 1,2 introduce the new irq_domain_ops.init_alloc_info().
- Patch 3,4 modify the existing irq_domain_ops.match() to support ACPI.
- Patch 5,6 introduce IRQ domain for ARM64 ACPI.
- Patch 7 introduces ACPI support for GICv2m.
Due to a large number of prerequisite patches, I have put together a branch on GitHub for review and testing:
https://github.com/ssuthiku/linux.git acpi-pci-msi-rfc1
This branch has been tested on AMD Seattle Platform. Any feedback and comments are appreciated.
Thank you in advance,
Suravee
Suravee Suthikulpanit (7): irqdomain: Introduce irq_domain_ops.init_alloc_info gic: Add gic_init_irq_alloc_info() irqdomain: Introduce irqdomain matching by reference Adopting the new irq_domain_ops.match() function prototype acpi: gsi: Adding irqdomain for ACPI acpi: gsi: Adding ARM64-specific acpi_register_gsi() gicv2m: Introducing ACPI support for GICv2m
arch/arm64/kernel/acpi.c | 62 ++++++++- arch/powerpc/platforms/512x/mpc5121_ads_cpld.c | 4 +- arch/powerpc/platforms/cell/interrupt.c | 4 +- arch/powerpc/platforms/powermac/pic.c | 4 +- arch/powerpc/platforms/ps3/interrupt.c | 4 +- arch/powerpc/sysdev/ehv_pic.c | 4 +- arch/powerpc/sysdev/i8259.c | 4 +- arch/powerpc/sysdev/ipic.c | 4 +- arch/powerpc/sysdev/mpic.c | 4 +- arch/powerpc/sysdev/qe_lib/qe_ic.c | 4 +- arch/powerpc/sysdev/xics/xics-common.c | 4 +- drivers/acpi/gsi.c | 23 +++- drivers/irqchip/irq-gic-v2m.c | 172 ++++++++++++++++++++----- drivers/irqchip/irq-gic.c | 98 ++++++++++++-- drivers/pci/pci-acpi.c | 52 ++++++++ drivers/pci/probe.c | 2 + include/linux/acpi.h | 4 + include/linux/irqchip/arm-gic-acpi.h | 5 +- include/linux/irqchip/arm-gic.h | 11 ++ include/linux/irqdomain.h | 17 ++- include/linux/pci-acpi.h | 2 + kernel/irq/irqdomain.c | 41 ++++-- 22 files changed, 453 insertions(+), 76 deletions(-)
On 07/09/2015 01:44 PM, Suravee Suthikulpanit wrote:
ACPI core patches for ARM64 are now upstreamed in 4.1. Also PCI support patches for ARM64 ACPI are in progress, I am sending out this RFC to introduce ACPI support for GICv2m. This would allow MSI to work when booting ACPI.
This patch series modify existing IRQ domain and ACPI GSI code to better support ACPI on ARM64.
Summary:
- Patch 1,2 introduce the new irq_domain_ops.init_alloc_info().
- Patch 3,4 modify the existing irq_domain_ops.match() to support ACPI.
- Patch 5,6 introduce IRQ domain for ARM64 ACPI.
- Patch 7 introduces ACPI support for GICv2m.
Due to a large number of prerequisite patches, I have put together a branch on GitHub for review and testing:
https://github.com/ssuthiku/linux.git acpi-pci-msi-rfc1
This branch has been tested on AMD Seattle Platform. Any feedback and comments are appreciated.
Hi Suravee, I updated the GICv2 code, introduced self-probe infrastructure to initialize IRQ controller (similar as IRQCHIP_DECALRE) and also supported stacked irqdomain, I think your patches needs updating, but I will look into your patch after I send out my v3 GICv2/v3 patches
Thanks Hanjun
Thank you in advance,
Suravee
Suravee Suthikulpanit (7): irqdomain: Introduce irq_domain_ops.init_alloc_info gic: Add gic_init_irq_alloc_info() irqdomain: Introduce irqdomain matching by reference Adopting the new irq_domain_ops.match() function prototype acpi: gsi: Adding irqdomain for ACPI acpi: gsi: Adding ARM64-specific acpi_register_gsi() gicv2m: Introducing ACPI support for GICv2m
arch/arm64/kernel/acpi.c | 62 ++++++++- arch/powerpc/platforms/512x/mpc5121_ads_cpld.c | 4 +- arch/powerpc/platforms/cell/interrupt.c | 4 +- arch/powerpc/platforms/powermac/pic.c | 4 +- arch/powerpc/platforms/ps3/interrupt.c | 4 +- arch/powerpc/sysdev/ehv_pic.c | 4 +- arch/powerpc/sysdev/i8259.c | 4 +- arch/powerpc/sysdev/ipic.c | 4 +- arch/powerpc/sysdev/mpic.c | 4 +- arch/powerpc/sysdev/qe_lib/qe_ic.c | 4 +- arch/powerpc/sysdev/xics/xics-common.c | 4 +- drivers/acpi/gsi.c | 23 +++- drivers/irqchip/irq-gic-v2m.c | 172 ++++++++++++++++++++----- drivers/irqchip/irq-gic.c | 98 ++++++++++++-- drivers/pci/pci-acpi.c | 52 ++++++++ drivers/pci/probe.c | 2 + include/linux/acpi.h | 4 + include/linux/irqchip/arm-gic-acpi.h | 5 +- include/linux/irqchip/arm-gic.h | 11 ++ include/linux/irqdomain.h | 17 ++- include/linux/pci-acpi.h | 2 + kernel/irq/irqdomain.c | 41 ++++-- 22 files changed, 453 insertions(+), 76 deletions(-)
On 7/9/15 20:00, Hanjun Guo wrote:
On 07/09/2015 01:44 PM, Suravee Suthikulpanit wrote:
ACPI core patches for ARM64 are now upstreamed in 4.1. Also PCI support patches for ARM64 ACPI are in progress, I am sending out this RFC to introduce ACPI support for GICv2m. This would allow MSI to work when booting ACPI.
This patch series modify existing IRQ domain and ACPI GSI code to better support ACPI on ARM64.
Summary:
- Patch 1,2 introduce the new irq_domain_ops.init_alloc_info().
- Patch 3,4 modify the existing irq_domain_ops.match() to support ACPI.
- Patch 5,6 introduce IRQ domain for ARM64 ACPI.
- Patch 7 introduces ACPI support for GICv2m.
Due to a large number of prerequisite patches, I have put together a branch on GitHub for review and testing:
https://github.com/ssuthiku/linux.git acpi-pci-msi-rfc1
This branch has been tested on AMD Seattle Platform. Any feedback and comments are appreciated.
Hi Suravee, I updated the GICv2 code, introduced self-probe infrastructure to initialize IRQ controller (similar as IRQCHIP_DECALRE) and also supported stacked irqdomain, I think your patches needs updating, but I will look into your patch after I send out my v3 GICv2/v3 patches
Thanks Hanjun
Sure, I'll wait for your v3 before updating my changes.
Suravee