Commit 7327b16f authored by Marc Zyngier's avatar Marc Zyngier

APCI: irq: Add support for multiple GSI domains

In an unfortunate departure from the ACPI spec, the LoongArch
architecture split its GSI space across multiple interrupt
controllers.

In order to be able to reuse the core code and prevent
architectures from reinventing an already square wheel, offer
the arch code the ability to register a dispatcher function
that will return the domain fwnode for a given GSI.

The ARM GIC drivers are updated to support this (with a single
domain, as intended).
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Cc: Hanjun Guo <guohanjun@huawei.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: default avatarJianmin Lv <lvjianmin@loongson.cn>
Tested-by: default avatarHanjun Guo <guohanjun@huawei.com>
Reviewed-by: default avatarHanjun Guo <guohanjun@huawei.com>
Link: https://lore.kernel.org/r/1658314292-35346-3-git-send-email-lvjianmin@loongson.cn
parent af6a1cfa
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
enum acpi_irq_model_id acpi_irq_model; enum acpi_irq_model_id acpi_irq_model;
static struct fwnode_handle *acpi_gsi_domain_id; static struct fwnode_handle *(*acpi_get_gsi_domain_id)(u32 gsi);
/** /**
* acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
...@@ -26,9 +26,10 @@ static struct fwnode_handle *acpi_gsi_domain_id; ...@@ -26,9 +26,10 @@ static struct fwnode_handle *acpi_gsi_domain_id;
*/ */
int acpi_gsi_to_irq(u32 gsi, unsigned int *irq) int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
{ {
struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, struct irq_domain *d;
DOMAIN_BUS_ANY);
d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
DOMAIN_BUS_ANY);
*irq = irq_find_mapping(d, gsi); *irq = irq_find_mapping(d, gsi);
/* /*
* *irq == 0 means no mapping, that should * *irq == 0 means no mapping, that should
...@@ -53,12 +54,12 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, ...@@ -53,12 +54,12 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
{ {
struct irq_fwspec fwspec; struct irq_fwspec fwspec;
if (WARN_ON(!acpi_gsi_domain_id)) { fwspec.fwnode = acpi_get_gsi_domain_id(gsi);
if (WARN_ON(!fwspec.fwnode)) {
pr_warn("GSI: No registered irqchip, giving up\n"); pr_warn("GSI: No registered irqchip, giving up\n");
return -EINVAL; return -EINVAL;
} }
fwspec.fwnode = acpi_gsi_domain_id;
fwspec.param[0] = gsi; fwspec.param[0] = gsi;
fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity); fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
fwspec.param_count = 2; fwspec.param_count = 2;
...@@ -73,13 +74,14 @@ EXPORT_SYMBOL_GPL(acpi_register_gsi); ...@@ -73,13 +74,14 @@ EXPORT_SYMBOL_GPL(acpi_register_gsi);
*/ */
void acpi_unregister_gsi(u32 gsi) void acpi_unregister_gsi(u32 gsi)
{ {
struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, struct irq_domain *d;
DOMAIN_BUS_ANY);
int irq; int irq;
if (WARN_ON(acpi_irq_model == ACPI_IRQ_MODEL_GIC && gsi < 16)) if (WARN_ON(acpi_irq_model == ACPI_IRQ_MODEL_GIC && gsi < 16))
return; return;
d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
DOMAIN_BUS_ANY);
irq = irq_find_mapping(d, gsi); irq = irq_find_mapping(d, gsi);
irq_dispose_mapping(irq); irq_dispose_mapping(irq);
} }
...@@ -97,7 +99,8 @@ EXPORT_SYMBOL_GPL(acpi_unregister_gsi); ...@@ -97,7 +99,8 @@ EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
* The referenced device fwhandle or NULL on failure * The referenced device fwhandle or NULL on failure
*/ */
static struct fwnode_handle * static struct fwnode_handle *
acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source) acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source,
u32 gsi)
{ {
struct fwnode_handle *result; struct fwnode_handle *result;
struct acpi_device *device; struct acpi_device *device;
...@@ -105,7 +108,7 @@ acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source) ...@@ -105,7 +108,7 @@ acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
acpi_status status; acpi_status status;
if (!source->string_length) if (!source->string_length)
return acpi_gsi_domain_id; return acpi_get_gsi_domain_id(gsi);
status = acpi_get_handle(NULL, source->string_ptr, &handle); status = acpi_get_handle(NULL, source->string_ptr, &handle);
if (WARN_ON(ACPI_FAILURE(status))) if (WARN_ON(ACPI_FAILURE(status)))
...@@ -194,7 +197,7 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares, ...@@ -194,7 +197,7 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
ctx->index -= irq->interrupt_count; ctx->index -= irq->interrupt_count;
return AE_OK; return AE_OK;
} }
fwnode = acpi_gsi_domain_id; fwnode = acpi_get_gsi_domain_id(irq->interrupts[ctx->index]);
acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index], acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index],
irq->triggering, irq->polarity, irq->triggering, irq->polarity,
irq->shareable, ctx); irq->shareable, ctx);
...@@ -207,7 +210,8 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares, ...@@ -207,7 +210,8 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
ctx->index -= eirq->interrupt_count; ctx->index -= eirq->interrupt_count;
return AE_OK; return AE_OK;
} }
fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source); fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source,
eirq->interrupts[ctx->index]);
acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index], acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],
eirq->triggering, eirq->polarity, eirq->triggering, eirq->polarity,
eirq->shareable, ctx); eirq->shareable, ctx);
...@@ -291,10 +295,10 @@ EXPORT_SYMBOL_GPL(acpi_irq_get); ...@@ -291,10 +295,10 @@ EXPORT_SYMBOL_GPL(acpi_irq_get);
* GSI interrupts * GSI interrupts
*/ */
void __init acpi_set_irq_model(enum acpi_irq_model_id model, void __init acpi_set_irq_model(enum acpi_irq_model_id model,
struct fwnode_handle *fwnode) struct fwnode_handle *(*fn)(u32))
{ {
acpi_irq_model = model; acpi_irq_model = model;
acpi_gsi_domain_id = fwnode; acpi_get_gsi_domain_id = fn;
} }
/** /**
...@@ -312,8 +316,14 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags, ...@@ -312,8 +316,14 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
const struct irq_domain_ops *ops, const struct irq_domain_ops *ops,
void *host_data) void *host_data)
{ {
struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id, struct irq_domain *d;
DOMAIN_BUS_ANY);
/* This only works for the GIC model... */
if (acpi_irq_model != ACPI_IRQ_MODEL_GIC)
return NULL;
d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(0),
DOMAIN_BUS_ANY);
if (!d) if (!d)
return NULL; return NULL;
......
...@@ -2360,11 +2360,17 @@ static void __init gic_acpi_setup_kvm_info(void) ...@@ -2360,11 +2360,17 @@ static void __init gic_acpi_setup_kvm_info(void)
vgic_set_kvm_info(&gic_v3_kvm_info); vgic_set_kvm_info(&gic_v3_kvm_info);
} }
static struct fwnode_handle *gsi_domain_handle;
static struct fwnode_handle *gic_v3_get_gsi_domain_id(u32 gsi)
{
return gsi_domain_handle;
}
static int __init static int __init
gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
{ {
struct acpi_madt_generic_distributor *dist; struct acpi_madt_generic_distributor *dist;
struct fwnode_handle *domain_handle;
size_t size; size_t size;
int i, err; int i, err;
...@@ -2396,18 +2402,18 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) ...@@ -2396,18 +2402,18 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
if (err) if (err)
goto out_redist_unmap; goto out_redist_unmap;
domain_handle = irq_domain_alloc_fwnode(&dist->base_address); gsi_domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
if (!domain_handle) { if (!gsi_domain_handle) {
err = -ENOMEM; err = -ENOMEM;
goto out_redist_unmap; goto out_redist_unmap;
} }
err = gic_init_bases(acpi_data.dist_base, acpi_data.redist_regs, err = gic_init_bases(acpi_data.dist_base, acpi_data.redist_regs,
acpi_data.nr_redist_regions, 0, domain_handle); acpi_data.nr_redist_regions, 0, gsi_domain_handle);
if (err) if (err)
goto out_fwhandle_free; goto out_fwhandle_free;
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle); acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id);
if (static_branch_likely(&supports_deactivate_key)) if (static_branch_likely(&supports_deactivate_key))
gic_acpi_setup_kvm_info(); gic_acpi_setup_kvm_info();
...@@ -2415,7 +2421,7 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end) ...@@ -2415,7 +2421,7 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
return 0; return 0;
out_fwhandle_free: out_fwhandle_free:
irq_domain_free_fwnode(domain_handle); irq_domain_free_fwnode(gsi_domain_handle);
out_redist_unmap: out_redist_unmap:
for (i = 0; i < acpi_data.nr_redist_regions; i++) for (i = 0; i < acpi_data.nr_redist_regions; i++)
if (acpi_data.redist_regs[i].redist_base) if (acpi_data.redist_regs[i].redist_base)
......
...@@ -1682,11 +1682,17 @@ static void __init gic_acpi_setup_kvm_info(void) ...@@ -1682,11 +1682,17 @@ static void __init gic_acpi_setup_kvm_info(void)
vgic_set_kvm_info(&gic_v2_kvm_info); vgic_set_kvm_info(&gic_v2_kvm_info);
} }
static struct fwnode_handle *gsi_domain_handle;
static struct fwnode_handle *gic_v2_get_gsi_domain_id(u32 gsi)
{
return gsi_domain_handle;
}
static int __init gic_v2_acpi_init(union acpi_subtable_headers *header, static int __init gic_v2_acpi_init(union acpi_subtable_headers *header,
const unsigned long end) const unsigned long end)
{ {
struct acpi_madt_generic_distributor *dist; struct acpi_madt_generic_distributor *dist;
struct fwnode_handle *domain_handle;
struct gic_chip_data *gic = &gic_data[0]; struct gic_chip_data *gic = &gic_data[0];
int count, ret; int count, ret;
...@@ -1724,22 +1730,22 @@ static int __init gic_v2_acpi_init(union acpi_subtable_headers *header, ...@@ -1724,22 +1730,22 @@ static int __init gic_v2_acpi_init(union acpi_subtable_headers *header,
/* /*
* Initialize GIC instance zero (no multi-GIC support). * Initialize GIC instance zero (no multi-GIC support).
*/ */
domain_handle = irq_domain_alloc_fwnode(&dist->base_address); gsi_domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
if (!domain_handle) { if (!gsi_domain_handle) {
pr_err("Unable to allocate domain handle\n"); pr_err("Unable to allocate domain handle\n");
gic_teardown(gic); gic_teardown(gic);
return -ENOMEM; return -ENOMEM;
} }
ret = __gic_init_bases(gic, domain_handle); ret = __gic_init_bases(gic, gsi_domain_handle);
if (ret) { if (ret) {
pr_err("Failed to initialise GIC\n"); pr_err("Failed to initialise GIC\n");
irq_domain_free_fwnode(domain_handle); irq_domain_free_fwnode(gsi_domain_handle);
gic_teardown(gic); gic_teardown(gic);
return ret; return ret;
} }
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle); acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v2_get_gsi_domain_id);
if (IS_ENABLED(CONFIG_ARM_GIC_V2M)) if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
gicv2m_init(NULL, gic_data[0].domain); gicv2m_init(NULL, gic_data[0].domain);
......
...@@ -356,7 +356,7 @@ int acpi_gsi_to_irq (u32 gsi, unsigned int *irq); ...@@ -356,7 +356,7 @@ int acpi_gsi_to_irq (u32 gsi, unsigned int *irq);
int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi); int acpi_isa_irq_to_gsi (unsigned isa_irq, u32 *gsi);
void acpi_set_irq_model(enum acpi_irq_model_id model, void acpi_set_irq_model(enum acpi_irq_model_id model,
struct fwnode_handle *fwnode); struct fwnode_handle *(*)(u32));
struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags, struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
unsigned int size, unsigned int size,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment