As we use the acpi_irq_domain as the global pointer to the acpi core irq domain, which lead to multi places referring it and the way is not scalable, so introduce a struct gsi_cfg_data to abstract the gsi data for the irqchips then match the irq domain with the gsi.
For a ACPI based irqchip for example a GICD, it defines System Vector Base in the GICD entry, which means the global system interrupt number where this GIC Distributor’s interrupt inputs start, then we can get the irq number supported by reading the register for this GICD, so we can explictly get the gsi's associated irqdomain if the gsi is within the range of gsi supported by this GICD.
For ACPI device in DSDT, it using another mapping of device and hwirq number than DT, in DT, we using the interrupt parent property to get the device node of irqchip, that's why we need the matching function that match the device node with the one associated with the irqdomain. For ACPI, we only can get the gsi which the device is using, no interrupt parent will be used, that because gsi is a flat hwirq number which is unique in the system, then we can get its associated irq domain as I said above, which can be without the token pointer matching the interrupt controller.
the code is arch independent, it also can be expanted for x86 for multi ioapics.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/acpi/gsi.c | 51 ++++++++++++++++++++++++++++++++++++++------ drivers/irqchip/irq-gic-v3.c | 4 +++- drivers/irqchip/irq-gic.c | 7 +++++- include/linux/acpi.h | 10 ++++++++- 4 files changed, 62 insertions(+), 10 deletions(-)
diff --git a/drivers/acpi/gsi.c b/drivers/acpi/gsi.c index 02c8408..7867ec2 100644 --- a/drivers/acpi/gsi.c +++ b/drivers/acpi/gsi.c @@ -15,11 +15,44 @@
enum acpi_irq_model_id acpi_irq_model;
-static struct irq_domain *acpi_irq_domain __read_mostly; +static LIST_HEAD(gsi_cfg_data_list); +static DEFINE_MUTEX(gsi_mutex);
-void set_acpi_irq_domain(struct irq_domain *domain) +int gsi_cfg_data_add(struct irq_domain *domain, u32 gsi_base, u32 gsi_end) { - acpi_irq_domain = domain; + struct gsi_cfg_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->domain = domain; + data->gsi_base = gsi_base; + data->gsi_base = gsi_base; + + mutex_lock(&gsi_mutex); + list_add(&data->list, &gsi_cfg_data_list); + mutex_unlock(&gsi_mutex); + + return 0; +} + +/* Find irqdomain with GSI (hwirq number) */ +static struct irq_domain *acpi_find_irqdomain(u32 gsi) +{ + struct gsi_cfg_data *data; + struct irq_domain *domain = NULL; + + mutex_lock(&gsi_mutex); + list_for_each_entry(data, &gsi_cfg_data_list, list) { + if (gsi >= data->gsi_base && gsi <= data->gsi_end) { + domain = data->domain; + break; + } + } + mutex_unlock(&gsi_mutex); + + return domain; }
static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity) @@ -53,7 +86,9 @@ static unsigned int acpi_gsi_get_irq_type(int trigger, int polarity) */ int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) { - *irq = irq_find_mapping(acpi_irq_domain, gsi); + struct irq_domain *domain = acpi_find_irqdomain(gsi); + + *irq = irq_find_mapping(domain, gsi); /* * *irq == 0 means no mapping, that should * be reported as a failure @@ -77,12 +112,13 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, { int irq; unsigned int irq_type = acpi_gsi_get_irq_type(trigger, polarity); + struct irq_domain *domain = acpi_find_irqdomain(gsi);
- irq = irq_find_mapping(acpi_irq_domain, gsi); + irq = irq_find_mapping(domain, gsi); if (irq > 0) return irq;
- irq = irq_domain_alloc_irqs(acpi_irq_domain, 1, dev_to_node(dev), + irq = irq_domain_alloc_irqs(domain, 1, dev_to_node(dev), &gsi); if (irq <= 0) return -EINVAL; @@ -101,7 +137,8 @@ EXPORT_SYMBOL_GPL(acpi_register_gsi); */ void acpi_unregister_gsi(u32 gsi) { - int irq = irq_find_mapping(acpi_irq_domain, gsi); + struct irq_domain *domain = acpi_find_irqdomain(gsi); + int irq = irq_find_mapping(domain, gsi);
irq_dispose_mapping(irq); } diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index a11c020..5c53af6 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -947,6 +947,7 @@ static LIST_HEAD(redist_list); static struct redist_region *redist_regs __initdata; static u32 nr_redist_regions __initdata; static phys_addr_t dist_phy_base __initdata; +u32 gsi_base __initdata;
static int __init gic_acpi_register_redist(u64 phys_base, u64 size) @@ -1014,6 +1015,7 @@ gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header, if (BAD_MADT_ENTRY(dist, end)) return -EINVAL;
+ gsi_base = dist->global_irq_base; dist_phy_base = dist->base_address; return 0; } @@ -1168,7 +1170,7 @@ init_base: if (err) goto out_release_redist;
- set_acpi_irq_domain(gic_data.domain); + gsi_cfg_data_add(gic_data.domain, gsi_base, gsi_base + gic_data.irq_nr); return 0;
out_release_redist: diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 7f943ef..62e0640 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -1051,6 +1051,7 @@ IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);
#ifdef CONFIG_ACPI static phys_addr_t dist_phy_base, cpu_phy_base __initdata; +u32 gsi_base __initdata;
static int __init gic_acpi_parse_madt_cpu(struct acpi_subtable_header *header, @@ -1089,6 +1090,7 @@ gic_acpi_parse_madt_distributor(struct acpi_subtable_header *header, if (BAD_MADT_ENTRY(dist, end)) return -EINVAL;
+ gsi_base = dist->global_irq_base; dist_phy_base = dist->base_address; return 0; } @@ -1139,7 +1141,10 @@ gic_v2_acpi_init(struct acpi_table_header *table) }
gic_init_bases(0, -1, dist_base, cpu_base, 0, NULL); - set_acpi_irq_domain(gic_data[0].domain); + + /* since we have only one GICD, we can safely use gic_data[0] here */ + gsi_cfg_data_add(gic_data[0].domain, gsi_base, + gsi_base + gic_data[0].gic_irqs);
acpi_irq_model = ACPI_IRQ_MODEL_GIC; return 0; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 6de4b0e..093d60e 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -223,7 +223,15 @@ void acpi_unregister_gsi (u32 gsi);
#ifdef CONFIG_ACPI_GENERIC_GSI struct irq_domain; -void set_acpi_irq_domain(struct irq_domain *domain); + +struct gsi_cfg_data { + struct list_head list; + u32 gsi_base; + u32 gsi_end; + struct irq_domain *domain; +}; + +int gsi_cfg_data_add(struct irq_domain *domain, u32 gsi_base, u32 gsi_end); #endif
struct pci_dev;