Skip to content

Commit

Permalink
APCI: irq: Add support for multiple GSI domains
Browse files Browse the repository at this point in the history
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: Marc Zyngier <maz@kernel.org>
Cc: Hanjun Guo <guohanjun@huawei.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Jianmin Lv <lvjianmin@loongson.cn>
Tested-by: Hanjun Guo <guohanjun@huawei.com>
Reviewed-by: Hanjun Guo <guohanjun@huawei.com>
Link: https://lore.kernel.org/r/1658314292-35346-3-git-send-email-lvjianmin@loongson.cn
  • Loading branch information
Marc Zyngier committed Jul 20, 2022
1 parent af6a1cf commit 7327b16
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 28 deletions.
40 changes: 25 additions & 15 deletions drivers/acpi/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

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
Expand All @@ -26,9 +26,10 @@ static struct fwnode_handle *acpi_gsi_domain_id;
*/
int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
{
struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
DOMAIN_BUS_ANY);
struct irq_domain *d;

d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
DOMAIN_BUS_ANY);
*irq = irq_find_mapping(d, gsi);
/*
* *irq == 0 means no mapping, that should
Expand All @@ -53,12 +54,12 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
{
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");
return -EINVAL;
}

fwspec.fwnode = acpi_gsi_domain_id;
fwspec.param[0] = gsi;
fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
fwspec.param_count = 2;
Expand All @@ -73,13 +74,14 @@ EXPORT_SYMBOL_GPL(acpi_register_gsi);
*/
void acpi_unregister_gsi(u32 gsi)
{
struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
DOMAIN_BUS_ANY);
struct irq_domain *d;
int irq;

if (WARN_ON(acpi_irq_model == ACPI_IRQ_MODEL_GIC && gsi < 16))
return;

d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
DOMAIN_BUS_ANY);
irq = irq_find_mapping(d, gsi);
irq_dispose_mapping(irq);
}
Expand All @@ -97,15 +99,16 @@ EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
* The referenced device fwhandle or NULL on failure
*/
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 acpi_device *device;
acpi_handle handle;
acpi_status status;

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);
if (WARN_ON(ACPI_FAILURE(status)))
Expand Down Expand Up @@ -194,7 +197,7 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
ctx->index -= irq->interrupt_count;
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],
irq->triggering, irq->polarity,
irq->shareable, ctx);
Expand All @@ -207,7 +210,8 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
ctx->index -= eirq->interrupt_count;
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],
eirq->triggering, eirq->polarity,
eirq->shareable, ctx);
Expand Down Expand Up @@ -291,10 +295,10 @@ EXPORT_SYMBOL_GPL(acpi_irq_get);
* GSI interrupts
*/
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_gsi_domain_id = fwnode;
acpi_get_gsi_domain_id = fn;
}

/**
Expand All @@ -312,8 +316,14 @@ struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
const struct irq_domain_ops *ops,
void *host_data)
{
struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
DOMAIN_BUS_ANY);
struct irq_domain *d;

/* 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)
return NULL;
Expand Down
18 changes: 12 additions & 6 deletions drivers/irqchip/irq-gic-v3.c
Original file line number Diff line number Diff line change
Expand Up @@ -2360,11 +2360,17 @@ static void __init gic_acpi_setup_kvm_info(void)
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
gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
{
struct acpi_madt_generic_distributor *dist;
struct fwnode_handle *domain_handle;
size_t size;
int i, err;

Expand Down Expand Up @@ -2396,26 +2402,26 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
if (err)
goto out_redist_unmap;

domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
if (!domain_handle) {
gsi_domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
if (!gsi_domain_handle) {
err = -ENOMEM;
goto out_redist_unmap;
}

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)
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))
gic_acpi_setup_kvm_info();

return 0;

out_fwhandle_free:
irq_domain_free_fwnode(domain_handle);
irq_domain_free_fwnode(gsi_domain_handle);
out_redist_unmap:
for (i = 0; i < acpi_data.nr_redist_regions; i++)
if (acpi_data.redist_regs[i].redist_base)
Expand Down
18 changes: 12 additions & 6 deletions drivers/irqchip/irq-gic.c
Original file line number Diff line number Diff line change
Expand Up @@ -1682,11 +1682,17 @@ static void __init gic_acpi_setup_kvm_info(void)
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,
const unsigned long end)
{
struct acpi_madt_generic_distributor *dist;
struct fwnode_handle *domain_handle;
struct gic_chip_data *gic = &gic_data[0];
int count, ret;

Expand Down Expand Up @@ -1724,22 +1730,22 @@ static int __init gic_v2_acpi_init(union acpi_subtable_headers *header,
/*
* Initialize GIC instance zero (no multi-GIC support).
*/
domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
if (!domain_handle) {
gsi_domain_handle = irq_domain_alloc_fwnode(&dist->base_address);
if (!gsi_domain_handle) {
pr_err("Unable to allocate domain handle\n");
gic_teardown(gic);
return -ENOMEM;
}

ret = __gic_init_bases(gic, domain_handle);
ret = __gic_init_bases(gic, gsi_domain_handle);
if (ret) {
pr_err("Failed to initialise GIC\n");
irq_domain_free_fwnode(domain_handle);
irq_domain_free_fwnode(gsi_domain_handle);
gic_teardown(gic);
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))
gicv2m_init(NULL, gic_data[0].domain);
Expand Down
2 changes: 1 addition & 1 deletion include/linux/acpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

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,
unsigned int size,
Expand Down

0 comments on commit 7327b16

Please sign in to comment.