mirror of https://gitee.com/openkylin/linux.git
irqchip/gic-v2m: acpi: Introducing GICv2m ACPI support
This patch introduces gicv2m_acpi_init(), which uses information in MADT GIC MSI frames structure to initialize GICv2m driver. It also exposes gicv2m_init() function, which simplifies callers to a single GICv2m init function. Reviewed-by: Marc Zyngier <marc.zyngier@arm.com> Tested-by: Duc Dang <dhdang@apm.com> Acked-by: Rafael J. Wysocki <rjw@rjwysocki.net> Signed-off-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
This commit is contained in:
parent
4266ab1a8f
commit
0644b3daca
|
@ -15,9 +15,11 @@
|
||||||
|
|
||||||
#define pr_fmt(fmt) "GICv2m: " fmt
|
#define pr_fmt(fmt) "GICv2m: " fmt
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/msi.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_pci.h>
|
#include <linux/of_pci.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
@ -138,6 +140,11 @@ static int gicv2m_irq_gic_domain_alloc(struct irq_domain *domain,
|
||||||
fwspec.param[0] = 0;
|
fwspec.param[0] = 0;
|
||||||
fwspec.param[1] = hwirq - 32;
|
fwspec.param[1] = hwirq - 32;
|
||||||
fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
|
fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
|
||||||
|
} else if (is_fwnode_irqchip(domain->parent->fwnode)) {
|
||||||
|
fwspec.fwnode = domain->parent->fwnode;
|
||||||
|
fwspec.param_count = 2;
|
||||||
|
fwspec.param[0] = hwirq;
|
||||||
|
fwspec.param[1] = IRQ_TYPE_EDGE_RISING;
|
||||||
} else {
|
} else {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -255,6 +262,8 @@ static void gicv2m_teardown(void)
|
||||||
kfree(v2m->bm);
|
kfree(v2m->bm);
|
||||||
iounmap(v2m->base);
|
iounmap(v2m->base);
|
||||||
of_node_put(to_of_node(v2m->fwnode));
|
of_node_put(to_of_node(v2m->fwnode));
|
||||||
|
if (is_fwnode_irqchip(v2m->fwnode))
|
||||||
|
irq_domain_free_fwnode(v2m->fwnode);
|
||||||
kfree(v2m);
|
kfree(v2m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -373,9 +382,11 @@ static struct of_device_id gicv2m_device_id[] = {
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
|
static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
|
||||||
|
struct irq_domain *parent)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
struct device_node *node = to_of_node(parent_handle);
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
|
|
||||||
for (child = of_find_matching_node(node, gicv2m_device_id); child;
|
for (child = of_find_matching_node(node, gicv2m_device_id); child;
|
||||||
|
@ -411,3 +422,100 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
|
||||||
gicv2m_teardown();
|
gicv2m_teardown();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACPI
|
||||||
|
static int acpi_num_msi;
|
||||||
|
|
||||||
|
static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
|
||||||
|
{
|
||||||
|
struct v2m_data *data;
|
||||||
|
|
||||||
|
if (WARN_ON(acpi_num_msi <= 0))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* We only return the fwnode of the first MSI frame. */
|
||||||
|
data = list_first_entry_or_null(&v2m_nodes, struct v2m_data, entry);
|
||||||
|
if (!data)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return data->fwnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init
|
||||||
|
acpi_parse_madt_msi(struct acpi_subtable_header *header,
|
||||||
|
const unsigned long end)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct resource res;
|
||||||
|
u32 spi_start = 0, nr_spis = 0;
|
||||||
|
struct acpi_madt_generic_msi_frame *m;
|
||||||
|
struct fwnode_handle *fwnode;
|
||||||
|
|
||||||
|
m = (struct acpi_madt_generic_msi_frame *)header;
|
||||||
|
if (BAD_MADT_ENTRY(m, end))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
res.start = m->base_address;
|
||||||
|
res.end = m->base_address + SZ_4K;
|
||||||
|
|
||||||
|
if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
|
||||||
|
spi_start = m->spi_base;
|
||||||
|
nr_spis = m->spi_count;
|
||||||
|
|
||||||
|
pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n",
|
||||||
|
spi_start, nr_spis);
|
||||||
|
}
|
||||||
|
|
||||||
|
fwnode = irq_domain_alloc_fwnode((void *)m->base_address);
|
||||||
|
if (!fwnode) {
|
||||||
|
pr_err("Unable to allocate GICv2m domain token\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res);
|
||||||
|
if (ret)
|
||||||
|
irq_domain_free_fwnode(fwnode);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init gicv2m_acpi_init(struct irq_domain *parent)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (acpi_num_msi > 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME,
|
||||||
|
acpi_parse_madt_msi, 0);
|
||||||
|
|
||||||
|
if (acpi_num_msi <= 0)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
ret = gicv2m_allocate_domains(parent);
|
||||||
|
if (ret)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
pci_msi_register_fwnode_provider(&gicv2m_get_fwnode);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_out:
|
||||||
|
gicv2m_teardown();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#else /* CONFIG_ACPI */
|
||||||
|
static int __init gicv2m_acpi_init(struct irq_domain *parent)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_ACPI */
|
||||||
|
|
||||||
|
int __init gicv2m_init(struct fwnode_handle *parent_handle,
|
||||||
|
struct irq_domain *parent)
|
||||||
|
{
|
||||||
|
if (is_of_node(parent_handle))
|
||||||
|
return gicv2m_of_init(parent_handle, parent);
|
||||||
|
|
||||||
|
return gicv2m_acpi_init(parent);
|
||||||
|
}
|
||||||
|
|
|
@ -1234,7 +1234,7 @@ gic_of_init(struct device_node *node, struct device_node *parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
|
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
|
||||||
gicv2m_of_init(node, gic_data[gic_cnt].domain);
|
gicv2m_init(&node->fwnode, gic_data[gic_cnt].domain);
|
||||||
|
|
||||||
gic_cnt++;
|
gic_cnt++;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1359,6 +1359,10 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
|
||||||
__gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
|
__gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_handle);
|
||||||
|
|
||||||
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
|
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_handle);
|
||||||
|
|
||||||
|
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
|
||||||
|
gicv2m_init(NULL, gic_data[0].domain);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
|
IRQCHIP_ACPI_DECLARE(gic_v2, ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
|
||||||
|
|
|
@ -106,7 +106,8 @@ int gic_cpu_if_down(unsigned int gic_nr);
|
||||||
void gic_init(unsigned int nr, int start,
|
void gic_init(unsigned int nr, int start,
|
||||||
void __iomem *dist , void __iomem *cpu);
|
void __iomem *dist , void __iomem *cpu);
|
||||||
|
|
||||||
int gicv2m_of_init(struct device_node *node, struct irq_domain *parent);
|
int gicv2m_init(struct fwnode_handle *parent_handle,
|
||||||
|
struct irq_domain *parent);
|
||||||
|
|
||||||
void gic_send_sgi(unsigned int cpu_id, unsigned int irq);
|
void gic_send_sgi(unsigned int cpu_id, unsigned int irq);
|
||||||
int gic_get_cpu_id(unsigned int cpu);
|
int gic_get_cpu_id(unsigned int cpu);
|
||||||
|
|
Loading…
Reference in New Issue