irqchip/gic-v3-its: Split allocation from initialisation of its_node

In order to pave the way for more fancy quirk handling without making
more of a mess of this terrible driver, split the allocation of the
ITS descriptor (its_node) from the actual probing.

This will allow firmware-specific hooks to be added between these
two points.

Signed-off-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20231006125929.48591-4-lpieralisi@kernel.org
This commit is contained in:
Marc Zyngier 2023-10-06 14:59:27 +02:00
parent 5e5c636c69
commit 9585a495ac
1 changed files with 89 additions and 60 deletions

View File

@ -4952,7 +4952,7 @@ out_unmap:
return NULL; return NULL;
} }
static int its_init_domain(struct fwnode_handle *handle, struct its_node *its) static int its_init_domain(struct its_node *its)
{ {
struct irq_domain *inner_domain; struct irq_domain *inner_domain;
struct msi_domain_info *info; struct msi_domain_info *info;
@ -4966,7 +4966,7 @@ static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
inner_domain = irq_domain_create_hierarchy(its_parent, inner_domain = irq_domain_create_hierarchy(its_parent,
its->msi_domain_flags, 0, its->msi_domain_flags, 0,
handle, &its_domain_ops, its->fwnode_handle, &its_domain_ops,
info); info);
if (!inner_domain) { if (!inner_domain) {
kfree(info); kfree(info);
@ -5017,8 +5017,7 @@ static int its_init_vpe_domain(void)
return 0; return 0;
} }
static int __init its_compute_its_list_map(struct resource *res, static int __init its_compute_its_list_map(struct its_node *its)
void __iomem *its_base)
{ {
int its_number; int its_number;
u32 ctlr; u32 ctlr;
@ -5032,15 +5031,15 @@ static int __init its_compute_its_list_map(struct resource *res,
its_number = find_first_zero_bit(&its_list_map, GICv4_ITS_LIST_MAX); its_number = find_first_zero_bit(&its_list_map, GICv4_ITS_LIST_MAX);
if (its_number >= GICv4_ITS_LIST_MAX) { if (its_number >= GICv4_ITS_LIST_MAX) {
pr_err("ITS@%pa: No ITSList entry available!\n", pr_err("ITS@%pa: No ITSList entry available!\n",
&res->start); &its->phys_base);
return -EINVAL; return -EINVAL;
} }
ctlr = readl_relaxed(its_base + GITS_CTLR); ctlr = readl_relaxed(its->base + GITS_CTLR);
ctlr &= ~GITS_CTLR_ITS_NUMBER; ctlr &= ~GITS_CTLR_ITS_NUMBER;
ctlr |= its_number << GITS_CTLR_ITS_NUMBER_SHIFT; ctlr |= its_number << GITS_CTLR_ITS_NUMBER_SHIFT;
writel_relaxed(ctlr, its_base + GITS_CTLR); writel_relaxed(ctlr, its->base + GITS_CTLR);
ctlr = readl_relaxed(its_base + GITS_CTLR); ctlr = readl_relaxed(its->base + GITS_CTLR);
if ((ctlr & GITS_CTLR_ITS_NUMBER) != (its_number << GITS_CTLR_ITS_NUMBER_SHIFT)) { if ((ctlr & GITS_CTLR_ITS_NUMBER) != (its_number << GITS_CTLR_ITS_NUMBER_SHIFT)) {
its_number = ctlr & GITS_CTLR_ITS_NUMBER; its_number = ctlr & GITS_CTLR_ITS_NUMBER;
its_number >>= GITS_CTLR_ITS_NUMBER_SHIFT; its_number >>= GITS_CTLR_ITS_NUMBER_SHIFT;
@ -5048,75 +5047,50 @@ static int __init its_compute_its_list_map(struct resource *res,
if (test_and_set_bit(its_number, &its_list_map)) { if (test_and_set_bit(its_number, &its_list_map)) {
pr_err("ITS@%pa: Duplicate ITSList entry %d\n", pr_err("ITS@%pa: Duplicate ITSList entry %d\n",
&res->start, its_number); &its->phys_base, its_number);
return -EINVAL; return -EINVAL;
} }
return its_number; return its_number;
} }
static int __init its_probe_one(struct resource *res, static int __init its_probe_one(struct its_node *its)
struct fwnode_handle *handle, int numa_node)
{ {
struct its_node *its; u64 baser, tmp;
void __iomem *its_base;
u64 baser, tmp, typer;
struct page *page; struct page *page;
u32 ctlr; u32 ctlr;
int err; int err;
its_base = its_map_one(res, &err);
if (!its_base)
return err;
pr_info("ITS %pR\n", res);
its = kzalloc(sizeof(*its), GFP_KERNEL);
if (!its) {
err = -ENOMEM;
goto out_unmap;
}
raw_spin_lock_init(&its->lock);
mutex_init(&its->dev_alloc_lock);
INIT_LIST_HEAD(&its->entry);
INIT_LIST_HEAD(&its->its_device_list);
typer = gic_read_typer(its_base + GITS_TYPER);
its->typer = typer;
its->base = its_base;
its->phys_base = res->start;
if (is_v4(its)) { if (is_v4(its)) {
if (!(typer & GITS_TYPER_VMOVP)) { if (!(its->typer & GITS_TYPER_VMOVP)) {
err = its_compute_its_list_map(res, its_base); err = its_compute_its_list_map(its);
if (err < 0) if (err < 0)
goto out_free_its; goto out;
its->list_nr = err; its->list_nr = err;
pr_info("ITS@%pa: Using ITS number %d\n", pr_info("ITS@%pa: Using ITS number %d\n",
&res->start, err); &its->phys_base, err);
} else { } else {
pr_info("ITS@%pa: Single VMOVP capable\n", &res->start); pr_info("ITS@%pa: Single VMOVP capable\n", &its->phys_base);
} }
if (is_v4_1(its)) { if (is_v4_1(its)) {
u32 svpet = FIELD_GET(GITS_TYPER_SVPET, typer); u32 svpet = FIELD_GET(GITS_TYPER_SVPET, its->typer);
its->sgir_base = ioremap(res->start + SZ_128K, SZ_64K); its->sgir_base = ioremap(its->phys_base + SZ_128K, SZ_64K);
if (!its->sgir_base) { if (!its->sgir_base) {
err = -ENOMEM; err = -ENOMEM;
goto out_free_its; goto out;
} }
its->mpidr = readl_relaxed(its_base + GITS_MPIDR); its->mpidr = readl_relaxed(its->base + GITS_MPIDR);
pr_info("ITS@%pa: Using GICv4.1 mode %08x %08x\n", pr_info("ITS@%pa: Using GICv4.1 mode %08x %08x\n",
&res->start, its->mpidr, svpet); &its->phys_base, its->mpidr, svpet);
} }
} }
its->numa_node = numa_node;
page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO, page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO,
get_order(ITS_CMD_QUEUE_SZ)); get_order(ITS_CMD_QUEUE_SZ));
if (!page) { if (!page) {
@ -5125,12 +5099,9 @@ static int __init its_probe_one(struct resource *res,
} }
its->cmd_base = (void *)page_address(page); its->cmd_base = (void *)page_address(page);
its->cmd_write = its->cmd_base; its->cmd_write = its->cmd_base;
its->fwnode_handle = handle;
its->get_msi_base = its_irq_get_msi_base; its->get_msi_base = its_irq_get_msi_base;
its->msi_domain_flags = IRQ_DOMAIN_FLAG_ISOLATED_MSI; its->msi_domain_flags = IRQ_DOMAIN_FLAG_ISOLATED_MSI;
its_enable_quirks(its);
err = its_alloc_tables(its); err = its_alloc_tables(its);
if (err) if (err)
goto out_free_cmd; goto out_free_cmd;
@ -5174,7 +5145,7 @@ static int __init its_probe_one(struct resource *res,
ctlr |= GITS_CTLR_ImDe; ctlr |= GITS_CTLR_ImDe;
writel_relaxed(ctlr, its->base + GITS_CTLR); writel_relaxed(ctlr, its->base + GITS_CTLR);
err = its_init_domain(handle, its); err = its_init_domain(its);
if (err) if (err)
goto out_free_tables; goto out_free_tables;
@ -5191,11 +5162,8 @@ out_free_cmd:
out_unmap_sgir: out_unmap_sgir:
if (its->sgir_base) if (its->sgir_base)
iounmap(its->sgir_base); iounmap(its->sgir_base);
out_free_its: out:
kfree(its); pr_err("ITS@%pa: failed probing (%d)\n", &its->phys_base, err);
out_unmap:
iounmap(its_base);
pr_err("ITS@%pa: failed probing (%d)\n", &res->start, err);
return err; return err;
} }
@ -5356,10 +5324,53 @@ static const struct of_device_id its_device_id[] = {
{}, {},
}; };
static struct its_node __init *its_node_init(struct resource *res,
struct fwnode_handle *handle, int numa_node)
{
void __iomem *its_base;
struct its_node *its;
int err;
its_base = its_map_one(res, &err);
if (!its_base)
return NULL;
pr_info("ITS %pR\n", res);
its = kzalloc(sizeof(*its), GFP_KERNEL);
if (!its)
goto out_unmap;
raw_spin_lock_init(&its->lock);
mutex_init(&its->dev_alloc_lock);
INIT_LIST_HEAD(&its->entry);
INIT_LIST_HEAD(&its->its_device_list);
its->typer = gic_read_typer(its_base + GITS_TYPER);
its->base = its_base;
its->phys_base = res->start;
its->numa_node = numa_node;
its->fwnode_handle = handle;
return its;
out_unmap:
iounmap(its_base);
return NULL;
}
static void its_node_destroy(struct its_node *its)
{
iounmap(its->base);
kfree(its);
}
static int __init its_of_probe(struct device_node *node) static int __init its_of_probe(struct device_node *node)
{ {
struct device_node *np; struct device_node *np;
struct resource res; struct resource res;
int err;
/* /*
* Make sure *all* the ITS are reset before we probe any, as * Make sure *all* the ITS are reset before we probe any, as
@ -5369,8 +5380,6 @@ static int __init its_of_probe(struct device_node *node)
*/ */
for (np = of_find_matching_node(node, its_device_id); np; for (np = of_find_matching_node(node, its_device_id); np;
np = of_find_matching_node(np, its_device_id)) { np = of_find_matching_node(np, its_device_id)) {
int err;
if (!of_device_is_available(np) || if (!of_device_is_available(np) ||
!of_property_read_bool(np, "msi-controller") || !of_property_read_bool(np, "msi-controller") ||
of_address_to_resource(np, 0, &res)) of_address_to_resource(np, 0, &res))
@ -5383,6 +5392,8 @@ static int __init its_of_probe(struct device_node *node)
for (np = of_find_matching_node(node, its_device_id); np; for (np = of_find_matching_node(node, its_device_id); np;
np = of_find_matching_node(np, its_device_id)) { np = of_find_matching_node(np, its_device_id)) {
struct its_node *its;
if (!of_device_is_available(np)) if (!of_device_is_available(np))
continue; continue;
if (!of_property_read_bool(np, "msi-controller")) { if (!of_property_read_bool(np, "msi-controller")) {
@ -5396,7 +5407,17 @@ static int __init its_of_probe(struct device_node *node)
continue; continue;
} }
its_probe_one(&res, &np->fwnode, of_node_to_nid(np));
its = its_node_init(&res, &np->fwnode, of_node_to_nid(np));
if (!its)
return -ENOMEM;
its_enable_quirks(its);
err = its_probe_one(its);
if (err) {
its_node_destroy(its);
return err;
}
} }
return 0; return 0;
} }
@ -5508,6 +5529,7 @@ static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
{ {
struct acpi_madt_generic_translator *its_entry; struct acpi_madt_generic_translator *its_entry;
struct fwnode_handle *dom_handle; struct fwnode_handle *dom_handle;
struct its_node *its;
struct resource res; struct resource res;
int err; int err;
@ -5532,11 +5554,18 @@ static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
goto dom_err; goto dom_err;
} }
err = its_probe_one(&res, dom_handle, its = its_node_init(&res, dom_handle,
acpi_get_its_numa_node(its_entry->translation_id)); acpi_get_its_numa_node(its_entry->translation_id));
if (!its) {
err = -ENOMEM;
goto node_err;
}
err = its_probe_one(its);
if (!err) if (!err)
return 0; return 0;
node_err:
iort_deregister_domain_token(its_entry->translation_id); iort_deregister_domain_token(its_entry->translation_id);
dom_err: dom_err:
irq_domain_free_fwnode(dom_handle); irq_domain_free_fwnode(dom_handle);