summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSuravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>2015-08-11 12:10:25 +0700
committerHanjun Guo <hanjun.guo@linaro.org>2015-09-16 22:01:27 +0800
commit3db94d137064699838d557cb66cd8b0a1eaac8e9 (patch)
treee9586e73811d11150e1eb6e9406d2e488154fdfc
parent136a4bacb279b84113c470aa68cc8867ad11b1d0 (diff)
gicv2m: Introducing gicv2m_acpi_init()topic-gic-self-probe-v2
This patch introduces gicv2m_acpi_init(), which uses information in MADT GIC MSI frames structure to initialize GICv2m driver. It also refactors gicv2m_init_one() to handle both DT and ACPI initialization path. Signed-off-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com> [hj: rebase on top of 4.3-rc1 and fix compile warning with CONFIG_ACPI=n] Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
-rw-r--r--drivers/irqchip/irq-gic-v2m.c142
-rw-r--r--drivers/irqchip/irq-gic.c3
-rw-r--r--include/linux/irqchip/arm-gic.h6
3 files changed, 131 insertions, 20 deletions
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index d0fcbf820db0..9d47ac2a6ad4 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -15,7 +15,10 @@
#define pr_fmt(fmt) "GICv2m: " fmt
+#include <linux/acpi.h>
#include <linux/irq.h>
+#include <linux/list.h>
+#include <linux/msi.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
@@ -44,14 +47,18 @@
#define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK)
struct v2m_data {
+ struct list_head list;
spinlock_t msi_cnt_lock;
struct resource res; /* GICv2m resource */
void __iomem *base; /* GICv2m virt address */
u32 spi_start; /* The SPI number that MSIs start */
u32 nr_spis; /* The number of SPIs for MSIs */
unsigned long *bm; /* MSI vector bitmap */
+ void *dom_token;
};
+static LIST_HEAD(v2m_data_list);
+
static void gicv2m_mask_msi_irq(struct irq_data *d)
{
pci_msi_mask_irq(d);
@@ -224,8 +231,9 @@ static struct msi_domain_info gicv2m_pmsi_domain_info = {
.chip = &gicv2m_pmsi_irq_chip,
};
-static int __init gicv2m_init_one(struct device_node *node,
- struct irq_domain *parent)
+static int __init gicv2m_init_one(struct irq_domain *parent,
+ u32 spi_start, u32 nr_spis,
+ struct resource *res, void *token)
{
int ret;
struct v2m_data *v2m;
@@ -237,23 +245,17 @@ static int __init gicv2m_init_one(struct device_node *node,
return -ENOMEM;
}
- ret = of_address_to_resource(node, 0, &v2m->res);
- if (ret) {
- pr_err("Failed to allocate v2m resource.\n");
- goto err_free_v2m;
- }
-
- v2m->base = ioremap(v2m->res.start, resource_size(&v2m->res));
+ v2m->base = ioremap(res->start, resource_size(res));
if (!v2m->base) {
pr_err("Failed to map GICv2m resource\n");
ret = -ENOMEM;
goto err_free_v2m;
}
+ memcpy(&v2m->res, res, sizeof(struct resource));
- if (!of_property_read_u32(node, "arm,msi-base-spi", &v2m->spi_start) &&
- !of_property_read_u32(node, "arm,msi-num-spis", &v2m->nr_spis)) {
- pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n",
- v2m->spi_start, v2m->nr_spis);
+ if (spi_start && nr_spis) {
+ v2m->spi_start = spi_start;
+ v2m->nr_spis = nr_spis;
} else {
u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
@@ -273,7 +275,7 @@ static int __init gicv2m_init_one(struct device_node *node,
goto err_iounmap;
}
- inner_domain = irq_domain_add_tree(node, &gicv2m_domain_ops, v2m);
+ inner_domain = irq_domain_add_tree(token, &gicv2m_domain_ops, v2m);
if (!inner_domain) {
pr_err("Failed to create GICv2m domain\n");
ret = -ENOMEM;
@@ -282,9 +284,10 @@ static int __init gicv2m_init_one(struct device_node *node,
inner_domain->bus_token = DOMAIN_BUS_NEXUS;
inner_domain->parent = parent;
- pci_domain = pci_msi_create_irq_domain(node, &gicv2m_msi_domain_info,
+
+ pci_domain = pci_msi_create_irq_domain(token, &gicv2m_msi_domain_info,
inner_domain);
- plat_domain = platform_msi_create_irq_domain(node,
+ plat_domain = platform_msi_create_irq_domain(token,
&gicv2m_pmsi_domain_info,
inner_domain);
if (!pci_domain || !plat_domain) {
@@ -294,11 +297,12 @@ static int __init gicv2m_init_one(struct device_node *node,
}
spin_lock_init(&v2m->msi_cnt_lock);
+ v2m->dom_token = token;
+ list_add(&v2m->list, &v2m_data_list);
- pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name,
- (unsigned long)v2m->res.start, (unsigned long)v2m->res.end,
+ pr_info("range[%#lx:%#lx], SPI[%d:%d]\n",
+ (unsigned long)res->start, (unsigned long)res->end,
v2m->spi_start, (v2m->spi_start + v2m->nr_spis));
-
return 0;
err_free_domains:
@@ -329,10 +333,25 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
for (child = of_find_matching_node(node, gicv2m_device_id); child;
child = of_find_matching_node(child, gicv2m_device_id)) {
+ u32 spi_start = 0, nr_spis = 0;
+ struct resource res;
+
if (!of_find_property(child, "msi-controller", NULL))
continue;
- ret = gicv2m_init_one(child, parent);
+ ret = of_address_to_resource(child, 0, &res);
+ if (ret) {
+ pr_err("Failed to allocate v2m resource.\n");
+ break;
+ }
+
+ if (!of_property_read_u32(child, "arm,msi-base-spi",
+ &spi_start) &&
+ !of_property_read_u32(child, "arm,msi-num-spis", &nr_spis))
+ pr_info("Overriding V2M MSI_TYPER (base:%u, num:%u)\n",
+ spi_start, nr_spis);
+
+ ret = gicv2m_init_one(parent, spi_start, nr_spis, &res, child);
if (ret) {
of_node_put(node);
break;
@@ -341,3 +360,86 @@ int __init gicv2m_of_init(struct device_node *node, struct irq_domain *parent)
return ret;
}
+
+#ifdef CONFIG_ACPI
+static int acpi_num_msi;
+
+/**
+ * Note:
+ * This is used as a temporary variable since we cannot
+ * pass args into acpi_parse_masdt_msi() when calling
+ * acpi_parse_entries(),
+ */
+struct irq_domain *acpi_parent_domain;
+
+static int __init
+acpi_parse_madt_msi(struct acpi_subtable_header *header,
+ const unsigned long end)
+{
+ struct resource res;
+ u32 spi_start = 0, nr_spis = 0;
+ struct acpi_madt_generic_msi_frame *m;
+
+ 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 + 0x1000;
+
+ 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);
+ }
+
+ return gicv2m_init_one(acpi_parent_domain, spi_start, nr_spis, &res,
+ (void *)(m->base_address));
+}
+
+static int gicv2m_get_acpi_domain_token(struct device *dev,
+ enum irq_domain_bus_token bus_token,
+ void **tok)
+{
+ int ret = -EINVAL;
+ struct v2m_data *data;
+
+ if (acpi_num_msi) {
+ /**
+ * Since ACPI 5.1 currently does not define
+ * a way to associate MSI frame ID to a device,
+ * we can only support single MSI frame (index 0)
+ * at the moment.
+ */
+ data = list_first_entry_or_null(&v2m_data_list,
+ struct v2m_data, list);
+ if (data) {
+ *tok = data->dom_token;
+ ret = 0;
+ }
+ }
+
+ return ret;
+}
+
+int __init gicv2m_acpi_init(struct irq_domain *parent)
+{
+ if (acpi_num_msi > 0)
+ return 0;
+
+ acpi_parent_domain = parent;
+
+ acpi_num_msi = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME,
+ acpi_parse_madt_msi, 0);
+
+ if (acpi_num_msi == 0)
+ pr_debug("No valid ACPI GIC MSI FRAME exist\n");
+ else
+ msi_register_token_provider(&gicv2m_get_acpi_domain_token);
+
+ return 0;
+}
+
+#endif /* CONFIG_ACPI */
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 735fb708f3ca..39e6b0654b25 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -1305,6 +1305,9 @@ static int __init gic_v2_acpi_init(struct acpi_subtable_header *header,
__gic_init_bases(0, -1, dist_base, cpu_base, 0, domain_token);
+ if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
+ gicv2m_acpi_init(gic_data[0].domain);
+
acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, domain_token,
gic_acpi_gsi_desc_populate);
return 0;
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index b8901dfd9e95..d3bc5dc452e6 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -113,6 +113,12 @@ static inline void gic_init(unsigned int nr, int start,
int gicv2m_of_init(struct device_node *node, struct irq_domain *parent);
+#ifdef CONFIG_ACPI
+#include <linux/acpi.h>
+
+int gicv2m_acpi_init(struct irq_domain *parent);
+#endif
+
void gic_send_sgi(unsigned int cpu_id, unsigned int irq);
int gic_get_cpu_id(unsigned int cpu);
void gic_migrate_target(unsigned int new_cpu_id);