IORT, IO remapping tables, which is a table to describe the connections of PCI root complex, devices, ITS (msi controller) and SMMU, for example, it presents the topology of which device under which SMMU and(or) ITS.
This patch set first do some cleanup for the ITS dirver, then init the ITS with the information presented by MADT ITS entries, then follows the IORT spec, implementing:
- PCI root complex using ITS as msi controller, mapping its domain (segemnt) number with the MSI controller;
- init the SMMU and use the mapping of PCI and no-PCI device to SMMU to connect them
- No-PCI devices using MSI (connect to ITS) is not covered by this patch set.
please refer to the ARM documentation: http://infocenter.arm.com/help/topic/com.arm.doc.den0049a/DEN0049A_IO_Remapp...
This patch set is based on Tomasz Nowicki's work, but I rework some of the patches significantly, more work is needed for this patch set and I'm still not satisfy with the some of implementation, anyway, I will continue working on that, and at the same time, sending them out for review to see if there are some major problems.
you can get them form git://git.linaro.org/leg/acpi/acpi.git devel
Comments and test are warmly welcomed.
Hanjun Guo (5): irqchip / GICv3: remove gic root node in ITS irqchip / GICv3 / ITS: mark its_init() as __init irqchip/GICv3/ITS: refator ITS dt init code to prepare for ACPI irqchip: gicv3: its: probe ITS in ACPI way ARM64, ACPI, PCI, MSI: I/O Remapping Table (IORT) initial support.
Tomasz Nowicki (2): arm, smmu: Use more generic structure for SMMU master list. ARM64, ACPI, IORT: Bind SMMU driver via IORT table
drivers/acpi/Kconfig | 3 + drivers/acpi/Makefile | 1 + drivers/acpi/iort.c | 536 +++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 2 + drivers/iommu/arm-smmu.c | 290 ++++++++++++++++----- drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-gic-v3-its.c | 188 ++++++++++---- include/linux/iort.h | 63 +++++ 8 files changed, 966 insertions(+), 118 deletions(-) create mode 100644 drivers/acpi/iort.c create mode 100644 include/linux/iort.h
The gic_root_node defined in ITS driver is not actually used, and the ITS driver seems will not use it in the future, so just remove it.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/irqchip/irq-gic-v3-its.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 9687f8a..31c5a27 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -93,7 +93,6 @@ struct its_device {
static LIST_HEAD(its_nodes); static DEFINE_SPINLOCK(its_lock); -static struct device_node *gic_root_node; static struct rdists *gic_rdists;
#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) @@ -1554,8 +1553,6 @@ int its_init(struct device_node *node, struct rdists *rdists, }
gic_rdists = rdists; - gic_root_node = node; - its_alloc_lpi_tables(); its_lpi_init(rdists->id_bits);
On 16 June 2015 at 09:09, Hanjun Guo hanjun.guo@linaro.org wrote:
The gic_root_node defined in ITS driver is not actually used, and the ITS driver seems will not use it in the future, so just remove it.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/irqchip/irq-gic-v3-its.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 9687f8a..31c5a27 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -93,7 +93,6 @@ struct its_device {
static LIST_HEAD(its_nodes); static DEFINE_SPINLOCK(its_lock); -static struct device_node *gic_root_node; static struct rdists *gic_rdists;
#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) @@ -1554,8 +1553,6 @@ int its_init(struct device_node *node, struct rdists *rdists, }
gic_rdists = rdists;
gic_root_node = node;
its_alloc_lpi_tables(); its_lpi_init(rdists->id_bits);
-- 1.9.1
LGTM..
On 06/19/2015 08:30 AM, Ashwin Chaugule wrote:
On 16 June 2015 at 09:09, Hanjun Guo hanjun.guo@linaro.org wrote:
The gic_root_node defined in ITS driver is not actually used, and the ITS driver seems will not use it in the future, so just remove it.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/irqchip/irq-gic-v3-its.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 9687f8a..31c5a27 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -93,7 +93,6 @@ struct its_device {
static LIST_HEAD(its_nodes); static DEFINE_SPINLOCK(its_lock); -static struct device_node *gic_root_node; static struct rdists *gic_rdists;
#define gic_data_rdist() (raw_cpu_ptr(gic_rdists->rdist)) @@ -1554,8 +1553,6 @@ int its_init(struct device_node *node, struct rdists *rdists, }
gic_rdists = rdists;
gic_root_node = node;
its_alloc_lpi_tables(); its_lpi_init(rdists->id_bits);
-- 1.9.1
LGTM..
Yeah, looks downright obvious.
Since the only caller of its_init(), gicv3_init_bases(), is a __init function, so mark its_init() as __init too, then recursively mark the function called as __init.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/irqchip/irq-gic-v3-its.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 31c5a27..6964bf8 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -657,7 +657,7 @@ static int its_chunk_to_lpi(int chunk) return (chunk << IRQS_PER_CHUNK_SHIFT) + 8192; }
-static int its_lpi_init(u32 id_bits) +static int __init its_lpi_init(u32 id_bits) { lpi_chunks = its_lpi_to_chunk(1UL << id_bits);
@@ -1374,7 +1374,8 @@ static int its_force_quiescent(void __iomem *base) } }
-static int its_probe(struct device_node *node, struct irq_domain *parent) +static int __init its_probe(struct device_node *node, + struct irq_domain *parent) { struct resource res; struct its_node *its; @@ -1537,7 +1538,7 @@ static struct of_device_id its_device_id[] = { {}, };
-int its_init(struct device_node *node, struct rdists *rdists, +int __init its_init(struct device_node *node, struct rdists *rdists, struct irq_domain *parent_domain) { struct device_node *np;
On 16 June 2015 at 09:09, Hanjun Guo hanjun.guo@linaro.org wrote:
Since the only caller of its_init(), gicv3_init_bases(), is a __init function, so mark its_init() as __init too, then recursively mark the function called as __init.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
I'm seeing gic_of_init() as the only caller ? 4.1-rc3.
Were you getting section mismatch warnings before this patch? Just curious to know why this change?
Thanks, Ashwin.
drivers/irqchip/irq-gic-v3-its.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 31c5a27..6964bf8 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -657,7 +657,7 @@ static int its_chunk_to_lpi(int chunk) return (chunk << IRQS_PER_CHUNK_SHIFT) + 8192; }
-static int its_lpi_init(u32 id_bits) +static int __init its_lpi_init(u32 id_bits) { lpi_chunks = its_lpi_to_chunk(1UL << id_bits);
@@ -1374,7 +1374,8 @@ static int its_force_quiescent(void __iomem *base) } }
-static int its_probe(struct device_node *node, struct irq_domain *parent) +static int __init its_probe(struct device_node *node,
struct irq_domain *parent)
{ struct resource res; struct its_node *its; @@ -1537,7 +1538,7 @@ static struct of_device_id its_device_id[] = { {}, };
-int its_init(struct device_node *node, struct rdists *rdists, +int __init its_init(struct device_node *node, struct rdists *rdists, struct irq_domain *parent_domain) { struct device_node *np; -- 1.9.1
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org https://lists.linaro.org/mailman/listinfo/linaro-acpi
On 06/19/2015 10:29 PM, Ashwin Chaugule wrote:
On 16 June 2015 at 09:09, Hanjun Guo hanjun.guo@linaro.org wrote:
Since the only caller of its_init(), gicv3_init_bases(), is a __init function, so mark its_init() as __init too, then recursively mark the function called as __init.
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
I'm seeing gic_of_init() as the only caller ? 4.1-rc3.
Ah, sorry, it based on my GICv3 patches so some of the function name was changed.
Were you getting section mismatch warnings before this patch? Just curious to know why this change?
No section mismatch warnings before this patch, it's ok for __init function to call normal function, did I understand your question here?
Thanks Hanjun
Refactor ITS init code to prepare for ACPI
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/irqchip/irq-gic-v3-its.c | 126 +++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 46 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 6964bf8..c3652cd 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1374,44 +1374,71 @@ static int its_force_quiescent(void __iomem *base) } }
-static int __init its_probe(struct device_node *node, - struct irq_domain *parent) +static int __init its_init_domain(struct device_node *node, struct its_node *its, + struct irq_domain *parent) +{ + int err; + + its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); + if (!its->domain) + return -ENOMEM; + + its->domain->parent = parent; + its->msi_chip.domain = pci_msi_create_irq_domain(node, + &its_pci_msi_domain_info, + its->domain); + if (!its->msi_chip.domain) { + err = -ENOMEM; + goto out_free_domains; + } + + if (its->msi_chip.of_node) { + err = of_pci_msi_chip_add(&its->msi_chip); + if (err) + goto out_free_domains; + } + + return 0; + +out_free_domains: + if (its->msi_chip.domain) + irq_domain_remove(its->msi_chip.domain); + if (its->domain) + irq_domain_remove(its->domain); + + return err; +} + +struct its_node * __init its_probe(phys_addr_t phy_base, unsigned long size, + struct device_node *node, + struct irq_domain *parent, + bool is_msi_controller) { - struct resource res; struct its_node *its; void __iomem *its_base; u32 val; u64 baser, tmp; int err;
- err = of_address_to_resource(node, 0, &res); - if (err) { - pr_warn("%s: no regs?\n", node->full_name); - return -ENXIO; - } - - its_base = ioremap(res.start, resource_size(&res)); + its_base = ioremap(phy_base, size); if (!its_base) { - pr_warn("%s: unable to map registers\n", node->full_name); - return -ENOMEM; + pr_warn("Unable to map registers\n"); + return ERR_PTR(-ENOMEM); }
val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK; if (val != 0x30 && val != 0x40) { - pr_warn("%s: no ITS detected, giving up\n", node->full_name); + pr_warn("No ITS detected, giving up\n"); err = -ENODEV; goto out_unmap; }
err = its_force_quiescent(its_base); if (err) { - pr_warn("%s: failed to quiesce, giving up\n", - node->full_name); + pr_warn("Failed to quiesce, giving up\n"); goto out_unmap; }
- pr_info("ITS: %s\n", node->full_name); - its = kzalloc(sizeof(*its), GFP_KERNEL); if (!its) { err = -ENOMEM; @@ -1422,7 +1449,7 @@ static int __init its_probe(struct device_node *node, INIT_LIST_HEAD(&its->entry); INIT_LIST_HEAD(&its->its_device_list); its->base = its_base; - its->phys_base = res.start; + its->phys_base = phy_base; its->msi_chip.of_node = node; its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
@@ -1469,39 +1496,18 @@ static int __init its_probe(struct device_node *node, writeq_relaxed(0, its->base + GITS_CWRITER); writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
- if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { - its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); - if (!its->domain) { - err = -ENOMEM; - goto out_free_tables; - } - - its->domain->parent = parent; - - its->msi_chip.domain = pci_msi_create_irq_domain(node, - &its_pci_msi_domain_info, - its->domain); - if (!its->msi_chip.domain) { - err = -ENOMEM; - goto out_free_domains; - } - - err = of_pci_msi_chip_add(&its->msi_chip); + if (is_msi_controller) { + err = its_init_domain(node, its, parent); if (err) - goto out_free_domains; + goto out_free_tables; }
spin_lock(&its_lock); list_add(&its->entry, &its_nodes); spin_unlock(&its_lock);
- return 0; + return its;
-out_free_domains: - if (its->msi_chip.domain) - irq_domain_remove(its->msi_chip.domain); - if (its->domain) - irq_domain_remove(its->domain); out_free_tables: its_free_tables(its); out_free_cmd: @@ -1510,8 +1516,36 @@ out_free_its: kfree(its); out_unmap: iounmap(its_base); - pr_err("ITS: failed probing %s (%d)\n", node->full_name, err); - return err; + return ERR_PTR(err); +} + +static int __init its_of_probe(struct device_node *node, + struct irq_domain *parent) +{ + struct resource res; + struct its_node *its; + int err; + bool is_msi_controller = false; + + err = of_address_to_resource(node, 0, &res); + if (err) { + pr_warn("%s: no regs?\n", node->full_name); + return -ENXIO; + } + + if (of_property_read_bool(node, "msi-controller")) + is_msi_controller = true; + + its = its_probe(res.start, resource_size(&res), node, parent, + is_msi_controller); + if (IS_ERR(its)) { + err = PTR_ERR(its); + pr_err("ITS: failed probing %s (%d)\n", node->full_name, err); + return err; + } + + pr_info("ITS: %s\n", node->full_name); + return 0; }
static bool gic_rdists_supports_plpis(void) @@ -1545,7 +1579,7 @@ int __init its_init(struct device_node *node, struct rdists *rdists,
for (np = of_find_matching_node(node, its_device_id); np; np = of_find_matching_node(np, its_device_id)) { - its_probe(np, parent_domain); + its_of_probe(np, parent_domain); }
if (list_empty(&its_nodes)) {
On 16 June 2015 at 09:09, Hanjun Guo hanjun.guo@linaro.org wrote:
Refactor ITS init code to prepare for ACPI
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/irqchip/irq-gic-v3-its.c | 126 +++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 46 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 6964bf8..c3652cd 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1374,44 +1374,71 @@ static int its_force_quiescent(void __iomem *base) } }
-static int __init its_probe(struct device_node *node,
struct irq_domain *parent)
+static int __init its_init_domain(struct device_node *node, struct its_node *its,
struct irq_domain *parent)
+{
int err;
its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its);
if (!its->domain)
return -ENOMEM;
its->domain->parent = parent;
its->msi_chip.domain = pci_msi_create_irq_domain(node,
&its_pci_msi_domain_info,
its->domain);
if (!its->msi_chip.domain) {
err = -ENOMEM;
goto out_free_domains;
}
if (its->msi_chip.of_node) {
err = of_pci_msi_chip_add(&its->msi_chip);
if (err)
goto out_free_domains;
}
return 0;
+out_free_domains:
if (its->msi_chip.domain)
irq_domain_remove(its->msi_chip.domain);
if (its->domain)
irq_domain_remove(its->domain);
return err;
+}
+struct its_node * __init its_probe(phys_addr_t phy_base, unsigned long size,
struct device_node *node,
struct irq_domain *parent,
bool is_msi_controller)
{
struct resource res; struct its_node *its; void __iomem *its_base; u32 val; u64 baser, tmp; int err;
err = of_address_to_resource(node, 0, &res);
if (err) {
pr_warn("%s: no regs?\n", node->full_name);
return -ENXIO;
}
its_base = ioremap(res.start, resource_size(&res));
its_base = ioremap(phy_base, size); if (!its_base) {
pr_warn("%s: unable to map registers\n", node->full_name);
return -ENOMEM;
pr_warn("Unable to map registers\n");
return ERR_PTR(-ENOMEM); } val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK; if (val != 0x30 && val != 0x40) {
pr_warn("%s: no ITS detected, giving up\n", node->full_name);
pr_warn("No ITS detected, giving up\n"); err = -ENODEV; goto out_unmap; } err = its_force_quiescent(its_base); if (err) {
pr_warn("%s: failed to quiesce, giving up\n",
node->full_name);
pr_warn("Failed to quiesce, giving up\n"); goto out_unmap; }
pr_info("ITS: %s\n", node->full_name);
its = kzalloc(sizeof(*its), GFP_KERNEL); if (!its) { err = -ENOMEM;
@@ -1422,7 +1449,7 @@ static int __init its_probe(struct device_node *node, INIT_LIST_HEAD(&its->entry); INIT_LIST_HEAD(&its->its_device_list); its->base = its_base;
its->phys_base = res.start;
its->phys_base = phy_base; its->msi_chip.of_node = node; its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
@@ -1469,39 +1496,18 @@ static int __init its_probe(struct device_node *node, writeq_relaxed(0, its->base + GITS_CWRITER); writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) {
its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its);
if (!its->domain) {
err = -ENOMEM;
goto out_free_tables;
}
its->domain->parent = parent;
its->msi_chip.domain = pci_msi_create_irq_domain(node,
&its_pci_msi_domain_info,
its->domain);
if (!its->msi_chip.domain) {
err = -ENOMEM;
goto out_free_domains;
}
err = of_pci_msi_chip_add(&its->msi_chip);
if (is_msi_controller) {
err = its_init_domain(node, its, parent); if (err)
goto out_free_domains;
goto out_free_tables; } spin_lock(&its_lock); list_add(&its->entry, &its_nodes); spin_unlock(&its_lock);
return 0;
return its;
-out_free_domains:
if (its->msi_chip.domain)
irq_domain_remove(its->msi_chip.domain);
if (its->domain)
irq_domain_remove(its->domain);
out_free_tables: its_free_tables(its); out_free_cmd: @@ -1510,8 +1516,36 @@ out_free_its: kfree(its); out_unmap: iounmap(its_base);
pr_err("ITS: failed probing %s (%d)\n", node->full_name, err);
return err;
return ERR_PTR(err);
+}
+static int __init its_of_probe(struct device_node *node,
struct irq_domain *parent)
+{
struct resource res;
struct its_node *its;
int err;
bool is_msi_controller = false;
err = of_address_to_resource(node, 0, &res);
if (err) {
pr_warn("%s: no regs?\n", node->full_name);
return -ENXIO;
}
if (of_property_read_bool(node, "msi-controller"))
is_msi_controller = true;
its = its_probe(res.start, resource_size(&res), node, parent,
is_msi_controller);
Still grokking, but I think we're passing the DT node pointer around too much. It could probably be split up cleaner such that its_probe() is independent of ACPI or DT, but I'll need to get more surrounding context to be sure.
if (IS_ERR(its)) {
err = PTR_ERR(its);
pr_err("ITS: failed probing %s (%d)\n", node->full_name, err);
return err;
}
pr_info("ITS: %s\n", node->full_name);
return 0;
}
static bool gic_rdists_supports_plpis(void) @@ -1545,7 +1579,7 @@ int __init its_init(struct device_node *node, struct rdists *rdists,
for (np = of_find_matching_node(node, its_device_id); np; np = of_find_matching_node(np, its_device_id)) {
its_probe(np, parent_domain);
its_of_probe(np, parent_domain); } if (list_empty(&its_nodes)) {
-- 1.9.1
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org https://lists.linaro.org/mailman/listinfo/linaro-acpi
On 19 June 2015 at 11:00, Ashwin Chaugule ashwin.chaugule@linaro.org wrote:
On 16 June 2015 at 09:09, Hanjun Guo hanjun.guo@linaro.org wrote:
Refactor ITS init code to prepare for ACPI
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/irqchip/irq-gic-v3-its.c | 126 +++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 46 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 6964bf8..c3652cd 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1374,44 +1374,71 @@ static int its_force_quiescent(void __iomem *base) } }
-static int __init its_probe(struct device_node *node,
struct irq_domain *parent)
+static int __init its_init_domain(struct device_node *node, struct its_node *its,
struct irq_domain *parent)
+{
int err;
its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its);
if (!its->domain)
return -ENOMEM;
its->domain->parent = parent;
its->msi_chip.domain = pci_msi_create_irq_domain(node,
&its_pci_msi_domain_info,
its->domain);
if (!its->msi_chip.domain) {
err = -ENOMEM;
goto out_free_domains;
}
if (its->msi_chip.of_node) {
err = of_pci_msi_chip_add(&its->msi_chip);
if (err)
goto out_free_domains;
}
return 0;
+out_free_domains:
if (its->msi_chip.domain)
irq_domain_remove(its->msi_chip.domain);
if (its->domain)
irq_domain_remove(its->domain);
return err;
+}
+struct its_node * __init its_probe(phys_addr_t phy_base, unsigned long size,
struct device_node *node,
struct irq_domain *parent,
bool is_msi_controller)
{
struct resource res; struct its_node *its; void __iomem *its_base; u32 val; u64 baser, tmp; int err;
err = of_address_to_resource(node, 0, &res);
if (err) {
pr_warn("%s: no regs?\n", node->full_name);
return -ENXIO;
}
its_base = ioremap(res.start, resource_size(&res));
its_base = ioremap(phy_base, size); if (!its_base) {
pr_warn("%s: unable to map registers\n", node->full_name);
return -ENOMEM;
pr_warn("Unable to map registers\n");
return ERR_PTR(-ENOMEM); } val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK; if (val != 0x30 && val != 0x40) {
pr_warn("%s: no ITS detected, giving up\n", node->full_name);
pr_warn("No ITS detected, giving up\n"); err = -ENODEV; goto out_unmap; } err = its_force_quiescent(its_base); if (err) {
pr_warn("%s: failed to quiesce, giving up\n",
node->full_name);
pr_warn("Failed to quiesce, giving up\n"); goto out_unmap; }
pr_info("ITS: %s\n", node->full_name);
its = kzalloc(sizeof(*its), GFP_KERNEL); if (!its) { err = -ENOMEM;
@@ -1422,7 +1449,7 @@ static int __init its_probe(struct device_node *node, INIT_LIST_HEAD(&its->entry); INIT_LIST_HEAD(&its->its_device_list); its->base = its_base;
its->phys_base = res.start;
its->phys_base = phy_base; its->msi_chip.of_node = node; its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
@@ -1469,39 +1496,18 @@ static int __init its_probe(struct device_node *node, writeq_relaxed(0, its->base + GITS_CWRITER); writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) {
its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its);
if (!its->domain) {
err = -ENOMEM;
goto out_free_tables;
}
its->domain->parent = parent;
its->msi_chip.domain = pci_msi_create_irq_domain(node,
&its_pci_msi_domain_info,
its->domain);
if (!its->msi_chip.domain) {
err = -ENOMEM;
goto out_free_domains;
}
err = of_pci_msi_chip_add(&its->msi_chip);
if (is_msi_controller) {
err = its_init_domain(node, its, parent); if (err)
goto out_free_domains;
goto out_free_tables; } spin_lock(&its_lock); list_add(&its->entry, &its_nodes); spin_unlock(&its_lock);
return 0;
return its;
-out_free_domains:
if (its->msi_chip.domain)
irq_domain_remove(its->msi_chip.domain);
if (its->domain)
irq_domain_remove(its->domain);
out_free_tables: its_free_tables(its); out_free_cmd: @@ -1510,8 +1516,36 @@ out_free_its: kfree(its); out_unmap: iounmap(its_base);
pr_err("ITS: failed probing %s (%d)\n", node->full_name, err);
return err;
return ERR_PTR(err);
+}
+static int __init its_of_probe(struct device_node *node,
struct irq_domain *parent)
+{
struct resource res;
struct its_node *its;
int err;
bool is_msi_controller = false;
err = of_address_to_resource(node, 0, &res);
if (err) {
pr_warn("%s: no regs?\n", node->full_name);
return -ENXIO;
}
if (of_property_read_bool(node, "msi-controller"))
is_msi_controller = true;
its = its_probe(res.start, resource_size(&res), node, parent,
is_msi_controller);
Still grokking, but I think we're passing the DT node pointer around too much. It could probably be split up cleaner such that its_probe() is independent of ACPI or DT, but I'll need to get more surrounding context to be sure.
IIUC, you're probably going to need an its_init_domain() for ACPI, in which case, you could just call the one you have in this patch directly below its_probe() inside its_of_probe(). That way you dont need to pass the node ptr to its_probe() and keep that function generic.
if (IS_ERR(its)) {
err = PTR_ERR(its);
pr_err("ITS: failed probing %s (%d)\n", node->full_name, err);
return err;
}
pr_info("ITS: %s\n", node->full_name);
return 0;
}
static bool gic_rdists_supports_plpis(void) @@ -1545,7 +1579,7 @@ int __init its_init(struct device_node *node, struct rdists *rdists,
for (np = of_find_matching_node(node, its_device_id); np; np = of_find_matching_node(np, its_device_id)) {
its_probe(np, parent_domain);
its_of_probe(np, parent_domain); } if (list_empty(&its_nodes)) {
-- 1.9.1
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org https://lists.linaro.org/mailman/listinfo/linaro-acpi
On 06/20/2015 01:28 AM, Ashwin Chaugule wrote:
On 19 June 2015 at 11:00, Ashwin Chaugule ashwin.chaugule@linaro.org wrote:
On 16 June 2015 at 09:09, Hanjun Guo hanjun.guo@linaro.org wrote:
Refactor ITS init code to prepare for ACPI
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/irqchip/irq-gic-v3-its.c | 126 +++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 46 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 6964bf8..c3652cd 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1374,44 +1374,71 @@ static int its_force_quiescent(void __iomem *base) } }
-static int __init its_probe(struct device_node *node,
struct irq_domain *parent)
+static int __init its_init_domain(struct device_node *node, struct its_node *its,
struct irq_domain *parent)
+{
int err;
its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its);
if (!its->domain)
return -ENOMEM;
its->domain->parent = parent;
its->msi_chip.domain = pci_msi_create_irq_domain(node,
&its_pci_msi_domain_info,
its->domain);
if (!its->msi_chip.domain) {
err = -ENOMEM;
goto out_free_domains;
}
if (its->msi_chip.of_node) {
err = of_pci_msi_chip_add(&its->msi_chip);
if (err)
goto out_free_domains;
}
return 0;
+out_free_domains:
if (its->msi_chip.domain)
irq_domain_remove(its->msi_chip.domain);
if (its->domain)
irq_domain_remove(its->domain);
return err;
+}
+struct its_node * __init its_probe(phys_addr_t phy_base, unsigned long size,
struct device_node *node,
struct irq_domain *parent,
{bool is_msi_controller)
struct resource res; struct its_node *its; void __iomem *its_base; u32 val; u64 baser, tmp; int err;
err = of_address_to_resource(node, 0, &res);
if (err) {
pr_warn("%s: no regs?\n", node->full_name);
return -ENXIO;
}
its_base = ioremap(res.start, resource_size(&res));
its_base = ioremap(phy_base, size); if (!its_base) {
pr_warn("%s: unable to map registers\n", node->full_name);
return -ENOMEM;
pr_warn("Unable to map registers\n");
return ERR_PTR(-ENOMEM); } val = readl_relaxed(its_base + GITS_PIDR2) & GIC_PIDR2_ARCH_MASK; if (val != 0x30 && val != 0x40) {
pr_warn("%s: no ITS detected, giving up\n", node->full_name);
pr_warn("No ITS detected, giving up\n"); err = -ENODEV; goto out_unmap; } err = its_force_quiescent(its_base); if (err) {
pr_warn("%s: failed to quiesce, giving up\n",
node->full_name);
pr_warn("Failed to quiesce, giving up\n"); goto out_unmap; }
pr_info("ITS: %s\n", node->full_name);
its = kzalloc(sizeof(*its), GFP_KERNEL); if (!its) { err = -ENOMEM;
@@ -1422,7 +1449,7 @@ static int __init its_probe(struct device_node *node, INIT_LIST_HEAD(&its->entry); INIT_LIST_HEAD(&its->its_device_list); its->base = its_base;
its->phys_base = res.start;
its->phys_base = phy_base; its->msi_chip.of_node = node; its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1;
@@ -1469,39 +1496,18 @@ static int __init its_probe(struct device_node *node, writeq_relaxed(0, its->base + GITS_CWRITER); writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR);
if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) {
its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its);
if (!its->domain) {
err = -ENOMEM;
goto out_free_tables;
}
its->domain->parent = parent;
its->msi_chip.domain = pci_msi_create_irq_domain(node,
&its_pci_msi_domain_info,
its->domain);
if (!its->msi_chip.domain) {
err = -ENOMEM;
goto out_free_domains;
}
err = of_pci_msi_chip_add(&its->msi_chip);
if (is_msi_controller) {
err = its_init_domain(node, its, parent); if (err)
goto out_free_domains;
goto out_free_tables; } spin_lock(&its_lock); list_add(&its->entry, &its_nodes); spin_unlock(&its_lock);
return 0;
return its;
-out_free_domains:
if (its->msi_chip.domain)
irq_domain_remove(its->msi_chip.domain);
if (its->domain)
out_free_tables: its_free_tables(its); out_free_cmd:irq_domain_remove(its->domain);
@@ -1510,8 +1516,36 @@ out_free_its: kfree(its); out_unmap: iounmap(its_base);
pr_err("ITS: failed probing %s (%d)\n", node->full_name, err);
return err;
return ERR_PTR(err);
+}
+static int __init its_of_probe(struct device_node *node,
struct irq_domain *parent)
+{
struct resource res;
struct its_node *its;
int err;
bool is_msi_controller = false;
err = of_address_to_resource(node, 0, &res);
if (err) {
pr_warn("%s: no regs?\n", node->full_name);
return -ENXIO;
}
if (of_property_read_bool(node, "msi-controller"))
is_msi_controller = true;
its = its_probe(res.start, resource_size(&res), node, parent,
is_msi_controller);
Still grokking, but I think we're passing the DT node pointer around too much. It could probably be split up cleaner such that its_probe() is independent of ACPI or DT, but I'll need to get more surrounding context to be sure.
IIUC, you're probably going to need an its_init_domain() for ACPI, in which case, you could just call the one you have in this patch directly below its_probe() inside its_of_probe(). That way you dont need to pass the node ptr to its_probe() and keep that function generic.
That will make its_probe() more generic, the side effect is that I must free memories outside of its_probe() when its_init_domain() failed, those memories are allocated inside of its_probe() so seems awkward to free it outside, what do you think?
Thanks Hanjun
Parse ITS entries in MADT table to probe ITS
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/irqchip/irq-gic-v3-its.c | 54 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index c3652cd..fa2e828 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -15,6 +15,7 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */
+#include <linux/acpi.h> #include <linux/bitmap.h> #include <linux/cpu.h> #include <linux/delay.h> @@ -1567,6 +1568,47 @@ int its_cpu_init(void) return 0; }
+#ifdef CONFIG_ACPI +static struct irq_domain *its_parent __initdata; + +static int __init +gic_acpi_parse_madt_its(struct acpi_subtable_header *header, + const unsigned long end) +{ + struct acpi_madt_generic_translator *its_entry; + struct its_node *its; + + if (BAD_MADT_ENTRY(header, end)) + return -EINVAL; + + its_entry = (struct acpi_madt_generic_translator *)header; + + pr_info("ACPI: ITS: ID: 0x%x\n", its_entry->translation_id); + + /* ITS works as msi controller in ACPI case */ + its = its_probe(its_entry->base_address, 2 * SZ_64K, NULL, its_parent, + true); + if (!IS_ERR(its)) + return 0; + + pr_err("ACPI: failed probing num %d ITS\n", its_entry->translation_id); + return PTR_ERR(its); +} + +void __init its_acpi_probe(struct irq_domain *parent_domain) +{ + int count; + + its_parent = parent_domain; + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, + gic_acpi_parse_madt_its, 0); + if (count <= 0) + pr_info("No valid GIC ITS entries exist\n"); +} +#else +static inline void __init its_acpi_probe(struct irq_domain *parent_domain) { } +#endif + static struct of_device_id its_device_id[] = { { .compatible = "arm,gic-v3-its", }, {}, @@ -1575,11 +1617,15 @@ static struct of_device_id its_device_id[] = { int __init its_init(struct device_node *node, struct rdists *rdists, struct irq_domain *parent_domain) { - struct device_node *np; + if (node) { + struct device_node *np;
- for (np = of_find_matching_node(node, its_device_id); np; - np = of_find_matching_node(np, its_device_id)) { - its_of_probe(np, parent_domain); + for (np = of_find_matching_node(node, its_device_id); np; + np = of_find_matching_node(np, its_device_id)) { + its_of_probe(np, parent_domain); + } + } else { + its_acpi_probe(parent_domain); }
if (list_empty(&its_nodes)) {
On 06/16/2015 07:09 AM, Hanjun Guo wrote:
Parse ITS entries in MADT table to probe ITS
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/irqchip/irq-gic-v3-its.c | 54 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index c3652cd..fa2e828 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -15,6 +15,7 @@
- along with this program. If not, see http://www.gnu.org/licenses/.
*/ +#include <linux/acpi.h> #include <linux/bitmap.h> #include <linux/cpu.h> #include <linux/delay.h> @@ -1567,6 +1568,47 @@ int its_cpu_init(void) return 0; } +#ifdef CONFIG_ACPI +static struct irq_domain *its_parent __initdata;
+static int __init +gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
const unsigned long end)
+{
- struct acpi_madt_generic_translator *its_entry;
- struct its_node *its;
- if (BAD_MADT_ENTRY(header, end))
return -EINVAL;
Off topic a bit....
I'm hoping to get rid of BAD_MADT_ENTRY (see the posted RFC) because of this usage -- every routine that traverses the MADT entries has to insert this instead of having it done once.
- its_entry = (struct acpi_madt_generic_translator *)header;
- pr_info("ACPI: ITS: ID: 0x%x\n", its_entry->translation_id);
- /* ITS works as msi controller in ACPI case */
- its = its_probe(its_entry->base_address, 2 * SZ_64K, NULL, its_parent,
true);
- if (!IS_ERR(its))
return 0;
- pr_err("ACPI: failed probing num %d ITS\n", its_entry->translation_id);
- return PTR_ERR(its);
+}
+void __init its_acpi_probe(struct irq_domain *parent_domain) +{
- int count;
- its_parent = parent_domain;
- count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
gic_acpi_parse_madt_its, 0);
- if (count <= 0)
pr_info("No valid GIC ITS entries exist\n");
+} +#else +static inline void __init its_acpi_probe(struct irq_domain *parent_domain) { } +#endif
static struct of_device_id its_device_id[] = { { .compatible = "arm,gic-v3-its", }, {}, @@ -1575,11 +1617,15 @@ static struct of_device_id its_device_id[] = { int __init its_init(struct device_node *node, struct rdists *rdists, struct irq_domain *parent_domain) {
- struct device_node *np;
- if (node) {
struct device_node *np;
- for (np = of_find_matching_node(node, its_device_id); np;
np = of_find_matching_node(np, its_device_id)) {
its_of_probe(np, parent_domain);
for (np = of_find_matching_node(node, its_device_id); np;
np = of_find_matching_node(np, its_device_id)) {
its_of_probe(np, parent_domain);
}
- } else {
}its_acpi_probe(parent_domain);
if (list_empty(&its_nodes)) {
On 07/17/2015 05:21 AM, Al Stone wrote:
On 06/16/2015 07:09 AM, Hanjun Guo wrote:
Parse ITS entries in MADT table to probe ITS
Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/irqchip/irq-gic-v3-its.c | 54 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index c3652cd..fa2e828 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -15,6 +15,7 @@
- along with this program. If not, see http://www.gnu.org/licenses/.
*/
+#include <linux/acpi.h> #include <linux/bitmap.h> #include <linux/cpu.h> #include <linux/delay.h> @@ -1567,6 +1568,47 @@ int its_cpu_init(void) return 0; }
+#ifdef CONFIG_ACPI +static struct irq_domain *its_parent __initdata;
+static int __init +gic_acpi_parse_madt_its(struct acpi_subtable_header *header,
const unsigned long end)
+{
- struct acpi_madt_generic_translator *its_entry;
- struct its_node *its;
- if (BAD_MADT_ENTRY(header, end))
return -EINVAL;
Off topic a bit....
I'm hoping to get rid of BAD_MADT_ENTRY (see the posted RFC) because of this usage -- every routine that traverses the MADT entries has to insert this instead of having it done once.
ok, I will review your patchset :)
Thanks Hanjun
IORT shows representation of IO topology that will be used by ARM based systems. It describes how various components are connected together e.g. which devices are connected to given ITS instance.
This patch implements calls which allow to: - register/remove ITS as MSI chip - parse all IORT nodes and form node tree (for easy lookup) - find ITS (MSI chip) that device is assigned to
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/acpi/Kconfig | 3 + drivers/acpi/Makefile | 1 + drivers/acpi/iort.c | 272 +++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 2 + drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-gic-v3-its.c | 6 +- include/linux/iort.h | 39 ++++++ 7 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/iort.c create mode 100644 include/linux/iort.h
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 212735f..749a8c7 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -57,6 +57,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT config ACPI_CCA_REQUIRED bool
+config IORT_TABLE + bool + config ACPI_SLEEP bool depends on SUSPEND || HIBERNATION diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 576fbb1..b325d08 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_IRQCHIP) += irq.o +obj-$(CONFIG_IORT_TABLE) += iort.o
# processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c new file mode 100644 index 0000000..01692bb --- /dev/null +++ b/drivers/acpi/iort.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2015, Linaro Ltd. + * Author: Tomasz Nowicki tomasz.nowicki@linaro.org + * Author: Hanjun Guo hanjun.guo@linaro.org + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * This file implements early detection/parsing of I/O mapping + * reported to OS through firmware via I/O Remapping Table (IORT) + * IORT document number: ARM DEN 0049A + * + * These routines are used by ITS and PCI host bridge drivers. + */ + +#define pr_fmt(fmt) "ACPI: IORT: " fmt + +#include <linux/acpi.h> +#include <linux/export.h> +#include <linux/iort.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/mutex.h> +#include <linux/slab.h> + +struct iort_its_msi_chip { + struct list_head list; + struct msi_controller *chip; + u32 id; +}; + +typedef acpi_status (*iort_find_node_callback) + (struct acpi_iort_node *node, void *context); + +/* pointer to the mapped IORT table */ +static struct acpi_table_header *iort_table; +static LIST_HEAD(iort_pci_msi_chip_list); +static DEFINE_MUTEX(iort_pci_msi_chip_mutex); + +int iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id) +{ + struct iort_its_msi_chip *its_msi_chip; + + its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL); + if (!its_msi_chip) + return -ENOMEM; + + its_msi_chip->chip = chip; + its_msi_chip->id = its_id; + + mutex_lock(&iort_pci_msi_chip_mutex); + list_add(&its_msi_chip->list, &iort_pci_msi_chip_list); + mutex_unlock(&iort_pci_msi_chip_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(iort_pci_msi_chip_add); + +void iort_pci_msi_chip_remove(struct msi_controller *chip) +{ + struct iort_its_msi_chip *its_msi_chip; + + mutex_lock(&iort_pci_msi_chip_mutex); + list_for_each_entry(its_msi_chip, &iort_pci_msi_chip_list, list) { + if (its_msi_chip->chip == chip) { + list_del(&its_msi_chip->list); + kfree(its_msi_chip); + break; + } + } + mutex_unlock(&iort_pci_msi_chip_mutex); +} +EXPORT_SYMBOL_GPL(iort_pci_msi_chip_remove); + +static struct msi_controller *iort_pci_find_msi_chip_by_its(u32 its_id) +{ + struct iort_its_msi_chip *its_msi_chip; + + mutex_lock(&iort_pci_msi_chip_mutex); + list_for_each_entry(its_msi_chip, &iort_pci_msi_chip_list, list) { + if (its_msi_chip->id == its_id) { + mutex_unlock(&iort_pci_msi_chip_mutex); + return its_msi_chip->chip; + } + } + mutex_unlock(&iort_pci_msi_chip_mutex); + + return NULL; +} + +/** + * iort_find_root_its_node() - Get PCI root complex, device, or SMMU's + * parent ITS node. + * @node: node pointer to PCI root complex, device, or SMMU + * + * Returns: parent ITS node pointer on success + * NULL on failure + */ +static struct acpi_iort_node * +iort_find_parent_its_node(struct acpi_iort_node *node) +{ + struct acpi_iort_id_mapping *id_map; + + if (!node) + return NULL; + + /* Go upstream until find its parent ITS node */ + while (node->type != ACPI_IORT_NODE_ITS_GROUP) { + /* TODO: handle multi ID mapping entries */ + id_map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, + node->mapping_offset); + + /* Firmware bug! */ + if (!id_map->output_reference) { + pr_err(FW_BUG "[node %p type %d] ID map has invalid parent reference\n", + node, node->type); + return NULL; + } + + /* TODO: Components that do not generate MSIs but are connected to an SMMU */ + node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, + id_map->output_reference); + } + + return node; +} + +/** + * iort_scan_node() - scan the IORT and call the handler with specific + * IORT node type + * @type: IORT node type + * @callback: callback with specific node type + * @context: context pass to the callback + * + * Returns: node pointer when the callback succeed + * NULL on failure + */ +static struct acpi_iort_node * +iort_scan_node(enum acpi_iort_node_type type, + iort_find_node_callback callback, void *context) +{ + struct acpi_iort_node *iort_node, *iort_end; + struct acpi_table_iort *iort; + int i; + + if (!iort_table) + return NULL; + + /* + * iort_table and iort both point to the start of IORT table, but + * have different struct types + */ + iort = container_of(iort_table, struct acpi_table_iort, header); + + /* Get the first iort node */ + iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, + iort->node_offset); + + /* pointer to the end of the table */ + iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, + iort_table->length); + + for (i = 0; i < iort->node_count; i++) { + if (iort_node >= iort_end) { + pr_err("iort node pointer overflows, bad table\n"); + return NULL; + } + + if (iort_node->type == type) { + if (ACPI_SUCCESS(callback(iort_node, context))) + return iort_node; + } + + iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, + iort_node->length); + } + + return NULL; +} + +static acpi_status +iort_find_pci_rc_callback(struct acpi_iort_node *node, void *context) +{ + struct acpi_iort_root_complex *pci_rc; + int segment = *(int *)context; + + pci_rc = (struct acpi_iort_root_complex *)node->node_data; + + /* + * It is assumed that PCI segment numbers have a one-to-one mapping + * with root complexes. Each segment number can represent only one + * root complex. + */ + if (pci_rc->pci_segment_number == segment) + return AE_OK; + + return AE_NOT_FOUND; +} + +/** + * iort_find_pci_msi_chip() - find the msi controller with root complex's + * segment number + * @segment: domain number of this pci root complex + * @idx: index of the ITS in the ITS group + * + * Returns: msi controller bind to the root complex with the segment + * NULL on failure + */ +struct msi_controller *iort_find_pci_msi_chip(int segment, unsigned int idx) +{ + struct acpi_iort_its_group *its; + struct acpi_iort_node *node; + struct msi_controller *msi_chip; + + node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, + iort_find_pci_rc_callback, &segment); + if (!node) { + pr_err("can't find node related to PCI host bridge [segment %d]\n", + segment); + return NULL; + } + + node = iort_find_parent_its_node(node); + if (!node) { + pr_err("can't find ITS parent node for PCI host bridge [segment %d]\n", + segment); + return NULL; + } + + /* Move to ITS specific data */ + its = (struct acpi_iort_its_group *)node->node_data; + if (idx > its->its_count) { + pr_err("requested ITS ID index [%d] is greater than available ITS count [%d]\n", + idx, its->its_count); + return NULL; + } + + msi_chip = iort_pci_find_msi_chip_by_its(its->identifiers[idx]); + if (!msi_chip) + pr_err("can not find ITS chip ID:%d, not registered\n", + its->identifiers[idx]); + + return msi_chip; +} +EXPORT_SYMBOL_GPL(iort_find_pci_msi_chip); + +/* Get the remapped IORT table */ +static int __init iort_table_detect(void) +{ + acpi_status status; + + if (acpi_disabled) + return -ENODEV; + + status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table); + if (ACPI_FAILURE(status)) { + const char *msg = acpi_format_exception(status); + pr_err("Failed to get table, %s\n", msg); + return -EINVAL; + } + + return 0; +} +arch_initcall(iort_table_detect); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 22d0cb7..b1f6d04 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -34,6 +34,7 @@ #include <linux/pci-acpi.h> #include <linux/pci-aspm.h> #include <linux/dmar.h> +#include <linux/iort.h> #include <linux/acpi.h> #include <linux/slab.h> #include <linux/dmi.h> @@ -842,6 +843,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, bus = pci_create_root_bus(NULL, busnum, ops->pci_ops, &info->controller, &info->resources); if (bus) { + bus->msi = iort_find_pci_msi_chip(root->segment, 0); pci_scan_child_bus(bus); pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge), acpi_pci_root_release_info, info); diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 0dd64c5..7dddfee 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -26,6 +26,7 @@ config ARM_GIC_V3 config ARM_GIC_V3_ITS bool select PCI_MSI_IRQ_DOMAIN + select IORT_TABLE if (ACPI && PCI_MSI)
config ARM_NVIC bool diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index fa2e828..141c645 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -20,6 +20,7 @@ #include <linux/cpu.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/iort.h> #include <linux/log2.h> #include <linux/mm.h> #include <linux/msi.h> @@ -1588,8 +1589,11 @@ gic_acpi_parse_madt_its(struct acpi_subtable_header *header, /* ITS works as msi controller in ACPI case */ its = its_probe(its_entry->base_address, 2 * SZ_64K, NULL, its_parent, true); - if (!IS_ERR(its)) + if (!IS_ERR(its)) { + iort_pci_msi_chip_add(&its->msi_chip, + its_entry->translation_id); return 0; + }
pr_err("ACPI: failed probing num %d ITS\n", its_entry->translation_id); return PTR_ERR(its); diff --git a/include/linux/iort.h b/include/linux/iort.h new file mode 100644 index 0000000..7b83a03 --- /dev/null +++ b/include/linux/iort.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2015, Linaro Ltd. + * Author: Tomasz Nowicki tomasz.nowicki@linaro.org + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef __IORT_H__ +#define __IORT_H__ + +struct msi_controller; + +#ifdef CONFIG_IORT_TABLE +int iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id); +void iort_pci_msi_chip_remove(struct msi_controller *chip); +struct msi_controller *iort_find_pci_msi_chip(int segment, unsigned int idx); +#else +static inline int +iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id) { return -ENODEV; } + +static inline void +iort_pci_msi_chip_remove(struct msi_controller *chip) { } + +static struct msi_controller * +iort_find_pci_msi_chip(int segment, unsigned int idx) { return NULL; } +#endif + +#endif /* __IORT_H__ */
On 06/16/2015 07:09 AM, Hanjun Guo wrote:
IORT shows representation of IO topology that will be used by ARM based systems. It describes how various components are connected together e.g. which devices are connected to given ITS instance.
This patch implements calls which allow to:
- register/remove ITS as MSI chip
- parse all IORT nodes and form node tree (for easy lookup)
- find ITS (MSI chip) that device is assigned to
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/acpi/Kconfig | 3 + drivers/acpi/Makefile | 1 + drivers/acpi/iort.c | 272 +++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 2 + drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-gic-v3-its.c | 6 +- include/linux/iort.h | 39 ++++++ 7 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/iort.c create mode 100644 include/linux/iort.h
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 212735f..749a8c7 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -57,6 +57,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT config ACPI_CCA_REQUIRED bool +config IORT_TABLE
- bool
config ACPI_SLEEP bool depends on SUSPEND || HIBERNATION diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 576fbb1..b325d08 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_IRQCHIP) += irq.o +obj-$(CONFIG_IORT_TABLE) += iort.o # processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c new file mode 100644 index 0000000..01692bb --- /dev/null +++ b/drivers/acpi/iort.c @@ -0,0 +1,272 @@ +/*
- Copyright (C) 2015, Linaro Ltd.
- Author: Tomasz Nowicki tomasz.nowicki@linaro.org
- Author: Hanjun Guo hanjun.guo@linaro.org
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- This file implements early detection/parsing of I/O mapping
- reported to OS through firmware via I/O Remapping Table (IORT)
- IORT document number: ARM DEN 0049A
- These routines are used by ITS and PCI host bridge drivers.
- */
+#define pr_fmt(fmt) "ACPI: IORT: " fmt
+#include <linux/acpi.h> +#include <linux/export.h> +#include <linux/iort.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/mutex.h> +#include <linux/slab.h>
+struct iort_its_msi_chip {
- struct list_head list;
- struct msi_controller *chip;
- u32 id;
+};
+typedef acpi_status (*iort_find_node_callback)
- (struct acpi_iort_node *node, void *context);
+/* pointer to the mapped IORT table */ +static struct acpi_table_header *iort_table;
Do you really want acpi_table_header? Or do you want acpi_table_iort? It's not clear to me which would be better and avoid the most casts.
+static LIST_HEAD(iort_pci_msi_chip_list); +static DEFINE_MUTEX(iort_pci_msi_chip_mutex);
+int iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id) +{
- struct iort_its_msi_chip *its_msi_chip;
- its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL);
- if (!its_msi_chip)
return -ENOMEM;
- its_msi_chip->chip = chip;
- its_msi_chip->id = its_id;
- mutex_lock(&iort_pci_msi_chip_mutex);
- list_add(&its_msi_chip->list, &iort_pci_msi_chip_list);
- mutex_unlock(&iort_pci_msi_chip_mutex);
The mutex raises a question for me: when is IORT being parsed? If it is only early on (i.e., setup_arch()), we may not even have multiple CPUs to worry about yet and the mutex isn't needed. And if we are that early, do we need to use __init?
If IORT isn't being parsed until after the interpreter is running, then yeah, a mutex is a good idea.
- return 0;
+} +EXPORT_SYMBOL_GPL(iort_pci_msi_chip_add);
+void iort_pci_msi_chip_remove(struct msi_controller *chip) +{
- struct iort_its_msi_chip *its_msi_chip;
- mutex_lock(&iort_pci_msi_chip_mutex);
- list_for_each_entry(its_msi_chip, &iort_pci_msi_chip_list, list) {
if (its_msi_chip->chip == chip) {
list_del(&its_msi_chip->list);
kfree(its_msi_chip);
break;
}
- }
- mutex_unlock(&iort_pci_msi_chip_mutex);
+} +EXPORT_SYMBOL_GPL(iort_pci_msi_chip_remove);
+static struct msi_controller *iort_pci_find_msi_chip_by_its(u32 its_id) +{
- struct iort_its_msi_chip *its_msi_chip;
- mutex_lock(&iort_pci_msi_chip_mutex);
- list_for_each_entry(its_msi_chip, &iort_pci_msi_chip_list, list) {
if (its_msi_chip->id == its_id) {
mutex_unlock(&iort_pci_msi_chip_mutex);
return its_msi_chip->chip;
}
- }
- mutex_unlock(&iort_pci_msi_chip_mutex);
- return NULL;
+}
+/**
- iort_find_root_its_node() - Get PCI root complex, device, or SMMU's
- parent ITS node.
- @node: node pointer to PCI root complex, device, or SMMU
- Returns: parent ITS node pointer on success
NULL on failure
- */
+static struct acpi_iort_node * +iort_find_parent_its_node(struct acpi_iort_node *node) +{
- struct acpi_iort_id_mapping *id_map;
- if (!node)
return NULL;
- /* Go upstream until find its parent ITS node */
- while (node->type != ACPI_IORT_NODE_ITS_GROUP) {
/* TODO: handle multi ID mapping entries */
id_map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
node->mapping_offset);
/* Firmware bug! */
if (!id_map->output_reference) {
pr_err(FW_BUG "[node %p type %d] ID map has invalid parent reference\n",
node, node->type);
return NULL;
}
/* TODO: Components that do not generate MSIs but are connected to an SMMU */
node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
id_map->output_reference);
- }
- return node;
+}
+/**
- iort_scan_node() - scan the IORT and call the handler with specific
- IORT node type
- @type: IORT node type
- @callback: callback with specific node type
- @context: context pass to the callback
- Returns: node pointer when the callback succeed
NULL on failure
- */
+static struct acpi_iort_node * +iort_scan_node(enum acpi_iort_node_type type,
iort_find_node_callback callback, void *context)
+{
- struct acpi_iort_node *iort_node, *iort_end;
- struct acpi_table_iort *iort;
- int i;
- if (!iort_table)
return NULL;
- /*
* iort_table and iort both point to the start of IORT table, but
* have different struct types
*/
- iort = container_of(iort_table, struct acpi_table_iort, header);
- /* Get the first iort node */
- iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
iort->node_offset);
- /* pointer to the end of the table */
- iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
iort_table->length);
- for (i = 0; i < iort->node_count; i++) {
if (iort_node >= iort_end) {
pr_err("iort node pointer overflows, bad table\n");
return NULL;
}
if (iort_node->type == type) {
if (ACPI_SUCCESS(callback(iort_node, context)))
return iort_node;
}
iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
iort_node->length);
- }
- return NULL;
+}
+static acpi_status +iort_find_pci_rc_callback(struct acpi_iort_node *node, void *context) +{
- struct acpi_iort_root_complex *pci_rc;
- int segment = *(int *)context;
- pci_rc = (struct acpi_iort_root_complex *)node->node_data;
- /*
* It is assumed that PCI segment numbers have a one-to-one mapping
* with root complexes. Each segment number can represent only one
* root complex.
*/
- if (pci_rc->pci_segment_number == segment)
return AE_OK;
- return AE_NOT_FOUND;
+}
+/**
- iort_find_pci_msi_chip() - find the msi controller with root complex's
- segment number
- @segment: domain number of this pci root complex
- @idx: index of the ITS in the ITS group
- Returns: msi controller bind to the root complex with the segment
NULL on failure
- */
+struct msi_controller *iort_find_pci_msi_chip(int segment, unsigned int idx) +{
- struct acpi_iort_its_group *its;
- struct acpi_iort_node *node;
- struct msi_controller *msi_chip;
- node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
iort_find_pci_rc_callback, &segment);
- if (!node) {
pr_err("can't find node related to PCI host bridge [segment %d]\n",
segment);
return NULL;
- }
- node = iort_find_parent_its_node(node);
- if (!node) {
pr_err("can't find ITS parent node for PCI host bridge [segment %d]\n",
segment);
return NULL;
- }
- /* Move to ITS specific data */
- its = (struct acpi_iort_its_group *)node->node_data;
- if (idx > its->its_count) {
pr_err("requested ITS ID index [%d] is greater than available ITS count [%d]\n",
idx, its->its_count);
return NULL;
- }
- msi_chip = iort_pci_find_msi_chip_by_its(its->identifiers[idx]);
- if (!msi_chip)
pr_err("can not find ITS chip ID:%d, not registered\n",
its->identifiers[idx]);
- return msi_chip;
+} +EXPORT_SYMBOL_GPL(iort_find_pci_msi_chip);
+/* Get the remapped IORT table */ +static int __init iort_table_detect(void) +{
- acpi_status status;
- if (acpi_disabled)
return -ENODEV;
- status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
- if (ACPI_FAILURE(status)) {
const char *msg = acpi_format_exception(status);
pr_err("Failed to get table, %s\n", msg);
return -EINVAL;
- }
- return 0;
+} +arch_initcall(iort_table_detect); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 22d0cb7..b1f6d04 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -34,6 +34,7 @@ #include <linux/pci-acpi.h> #include <linux/pci-aspm.h> #include <linux/dmar.h> +#include <linux/iort.h> #include <linux/acpi.h> #include <linux/slab.h> #include <linux/dmi.h> @@ -842,6 +843,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, bus = pci_create_root_bus(NULL, busnum, ops->pci_ops, &info->controller, &info->resources); if (bus) {
pci_scan_child_bus(bus); pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge), acpi_pci_root_release_info, info);bus->msi = iort_find_pci_msi_chip(root->segment, 0);
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 0dd64c5..7dddfee 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -26,6 +26,7 @@ config ARM_GIC_V3 config ARM_GIC_V3_ITS bool select PCI_MSI_IRQ_DOMAIN
- select IORT_TABLE if (ACPI && PCI_MSI)
The number of Kconfig options is mind boggling these days :/. Do we need to select on a GICv3 option, too?
config ARM_NVIC bool diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index fa2e828..141c645 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -20,6 +20,7 @@ #include <linux/cpu.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/iort.h> #include <linux/log2.h> #include <linux/mm.h> #include <linux/msi.h> @@ -1588,8 +1589,11 @@ gic_acpi_parse_madt_its(struct acpi_subtable_header *header, /* ITS works as msi controller in ACPI case */ its = its_probe(its_entry->base_address, 2 * SZ_64K, NULL, its_parent, true);
- if (!IS_ERR(its))
- if (!IS_ERR(its)) {
iort_pci_msi_chip_add(&its->msi_chip,
return 0;its_entry->translation_id);
- }
pr_err("ACPI: failed probing num %d ITS\n", its_entry->translation_id); return PTR_ERR(its); diff --git a/include/linux/iort.h b/include/linux/iort.h new file mode 100644 index 0000000..7b83a03 --- /dev/null +++ b/include/linux/iort.h @@ -0,0 +1,39 @@ +/*
- Copyright (C) 2015, Linaro Ltd.
- Author: Tomasz Nowicki tomasz.nowicki@linaro.org
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- Place - Suite 330, Boston, MA 02111-1307 USA.
- */
+#ifndef __IORT_H__ +#define __IORT_H__
+struct msi_controller;
+#ifdef CONFIG_IORT_TABLE +int iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id); +void iort_pci_msi_chip_remove(struct msi_controller *chip); +struct msi_controller *iort_find_pci_msi_chip(int segment, unsigned int idx); +#else +static inline int +iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id) { return -ENODEV; }
+static inline void +iort_pci_msi_chip_remove(struct msi_controller *chip) { }
+static struct msi_controller * +iort_find_pci_msi_chip(int segment, unsigned int idx) { return NULL; } +#endif
+#endif /* __IORT_H__ */
On 07/17/2015 05:32 AM, Al Stone wrote:
On 06/16/2015 07:09 AM, Hanjun Guo wrote:
IORT shows representation of IO topology that will be used by ARM based systems. It describes how various components are connected together e.g. which devices are connected to given ITS instance.
This patch implements calls which allow to:
- register/remove ITS as MSI chip
- parse all IORT nodes and form node tree (for easy lookup)
- find ITS (MSI chip) that device is assigned to
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/acpi/Kconfig | 3 + drivers/acpi/Makefile | 1 + drivers/acpi/iort.c | 272 +++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 2 + drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-gic-v3-its.c | 6 +- include/linux/iort.h | 39 ++++++ 7 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/iort.c create mode 100644 include/linux/iort.h
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 212735f..749a8c7 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -57,6 +57,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT config ACPI_CCA_REQUIRED bool
+config IORT_TABLE
- bool
- config ACPI_SLEEP bool depends on SUSPEND || HIBERNATION
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 576fbb1..b325d08 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_IRQCHIP) += irq.o +obj-$(CONFIG_IORT_TABLE) += iort.o
# processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c new file mode 100644 index 0000000..01692bb --- /dev/null +++ b/drivers/acpi/iort.c @@ -0,0 +1,272 @@ +/*
- Copyright (C) 2015, Linaro Ltd.
- Author: Tomasz Nowicki tomasz.nowicki@linaro.org
- Author: Hanjun Guo hanjun.guo@linaro.org
- This program is free software; you can redistribute it and/or modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
- This program is distributed in the hope it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- This file implements early detection/parsing of I/O mapping
- reported to OS through firmware via I/O Remapping Table (IORT)
- IORT document number: ARM DEN 0049A
- These routines are used by ITS and PCI host bridge drivers.
- */
+#define pr_fmt(fmt) "ACPI: IORT: " fmt
+#include <linux/acpi.h> +#include <linux/export.h> +#include <linux/iort.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/mutex.h> +#include <linux/slab.h>
+struct iort_its_msi_chip {
- struct list_head list;
- struct msi_controller *chip;
Just to let you know, Marc is trying to remove struct msi_controller [1] and we need to rework this patch set based on that patch set.
[1]: https://lkml.org/lkml/2015/7/15/507
- u32 id;
+};
+typedef acpi_status (*iort_find_node_callback)
- (struct acpi_iort_node *node, void *context);
+/* pointer to the mapped IORT table */ +static struct acpi_table_header *iort_table;
Do you really want acpi_table_header? Or do you want acpi_table_iort? It's not clear to me which would be better and avoid the most casts.
yes, we need the pointer to the mem-remapped iort table then no need to remap it later when using it.
+static LIST_HEAD(iort_pci_msi_chip_list); +static DEFINE_MUTEX(iort_pci_msi_chip_mutex);
+int iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id) +{
- struct iort_its_msi_chip *its_msi_chip;
- its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL);
- if (!its_msi_chip)
return -ENOMEM;
- its_msi_chip->chip = chip;
- its_msi_chip->id = its_id;
- mutex_lock(&iort_pci_msi_chip_mutex);
- list_add(&its_msi_chip->list, &iort_pci_msi_chip_list);
- mutex_unlock(&iort_pci_msi_chip_mutex);
The mutex raises a question for me: when is IORT being parsed? If it is only early on (i.e., setup_arch()), we may not even have multiple CPUs to worry about yet and the mutex isn't needed. And if we are that early, do we need to use __init?
If IORT isn't being parsed until after the interpreter is running, then yeah, a mutex is a good idea.
Good question. This iort_pci_msi_chip_add() is only called by __init function and with only one CPU running as I know, I will rework this patch set as I said above, we don't need struct msi_controller anymore, something else will be needed.
- return 0;
+} +EXPORT_SYMBOL_GPL(iort_pci_msi_chip_add);
+void iort_pci_msi_chip_remove(struct msi_controller *chip) +{
- struct iort_its_msi_chip *its_msi_chip;
- mutex_lock(&iort_pci_msi_chip_mutex);
- list_for_each_entry(its_msi_chip, &iort_pci_msi_chip_list, list) {
if (its_msi_chip->chip == chip) {
list_del(&its_msi_chip->list);
kfree(its_msi_chip);
break;
}
- }
- mutex_unlock(&iort_pci_msi_chip_mutex);
+} +EXPORT_SYMBOL_GPL(iort_pci_msi_chip_remove);
+static struct msi_controller *iort_pci_find_msi_chip_by_its(u32 its_id) +{
- struct iort_its_msi_chip *its_msi_chip;
- mutex_lock(&iort_pci_msi_chip_mutex);
- list_for_each_entry(its_msi_chip, &iort_pci_msi_chip_list, list) {
if (its_msi_chip->id == its_id) {
mutex_unlock(&iort_pci_msi_chip_mutex);
return its_msi_chip->chip;
}
- }
- mutex_unlock(&iort_pci_msi_chip_mutex);
- return NULL;
+}
+/**
- iort_find_root_its_node() - Get PCI root complex, device, or SMMU's
- parent ITS node.
- @node: node pointer to PCI root complex, device, or SMMU
- Returns: parent ITS node pointer on success
NULL on failure
- */
+static struct acpi_iort_node * +iort_find_parent_its_node(struct acpi_iort_node *node) +{
- struct acpi_iort_id_mapping *id_map;
- if (!node)
return NULL;
- /* Go upstream until find its parent ITS node */
- while (node->type != ACPI_IORT_NODE_ITS_GROUP) {
/* TODO: handle multi ID mapping entries */
id_map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
node->mapping_offset);
/* Firmware bug! */
if (!id_map->output_reference) {
pr_err(FW_BUG "[node %p type %d] ID map has invalid parent reference\n",
node, node->type);
return NULL;
}
/* TODO: Components that do not generate MSIs but are connected to an SMMU */
node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
id_map->output_reference);
- }
- return node;
+}
+/**
- iort_scan_node() - scan the IORT and call the handler with specific
- IORT node type
- @type: IORT node type
- @callback: callback with specific node type
- @context: context pass to the callback
- Returns: node pointer when the callback succeed
NULL on failure
- */
+static struct acpi_iort_node * +iort_scan_node(enum acpi_iort_node_type type,
iort_find_node_callback callback, void *context)
+{
- struct acpi_iort_node *iort_node, *iort_end;
- struct acpi_table_iort *iort;
- int i;
- if (!iort_table)
return NULL;
- /*
* iort_table and iort both point to the start of IORT table, but
* have different struct types
*/
- iort = container_of(iort_table, struct acpi_table_iort, header);
- /* Get the first iort node */
- iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
iort->node_offset);
- /* pointer to the end of the table */
- iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
iort_table->length);
- for (i = 0; i < iort->node_count; i++) {
if (iort_node >= iort_end) {
pr_err("iort node pointer overflows, bad table\n");
return NULL;
}
if (iort_node->type == type) {
if (ACPI_SUCCESS(callback(iort_node, context)))
return iort_node;
}
iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
iort_node->length);
- }
- return NULL;
+}
+static acpi_status +iort_find_pci_rc_callback(struct acpi_iort_node *node, void *context) +{
- struct acpi_iort_root_complex *pci_rc;
- int segment = *(int *)context;
- pci_rc = (struct acpi_iort_root_complex *)node->node_data;
- /*
* It is assumed that PCI segment numbers have a one-to-one mapping
* with root complexes. Each segment number can represent only one
* root complex.
*/
- if (pci_rc->pci_segment_number == segment)
return AE_OK;
- return AE_NOT_FOUND;
+}
+/**
- iort_find_pci_msi_chip() - find the msi controller with root complex's
- segment number
- @segment: domain number of this pci root complex
- @idx: index of the ITS in the ITS group
- Returns: msi controller bind to the root complex with the segment
NULL on failure
- */
+struct msi_controller *iort_find_pci_msi_chip(int segment, unsigned int idx) +{
- struct acpi_iort_its_group *its;
- struct acpi_iort_node *node;
- struct msi_controller *msi_chip;
- node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
iort_find_pci_rc_callback, &segment);
- if (!node) {
pr_err("can't find node related to PCI host bridge [segment %d]\n",
segment);
return NULL;
- }
- node = iort_find_parent_its_node(node);
- if (!node) {
pr_err("can't find ITS parent node for PCI host bridge [segment %d]\n",
segment);
return NULL;
- }
- /* Move to ITS specific data */
- its = (struct acpi_iort_its_group *)node->node_data;
- if (idx > its->its_count) {
pr_err("requested ITS ID index [%d] is greater than available ITS count [%d]\n",
idx, its->its_count);
return NULL;
- }
- msi_chip = iort_pci_find_msi_chip_by_its(its->identifiers[idx]);
- if (!msi_chip)
pr_err("can not find ITS chip ID:%d, not registered\n",
its->identifiers[idx]);
- return msi_chip;
+} +EXPORT_SYMBOL_GPL(iort_find_pci_msi_chip);
+/* Get the remapped IORT table */ +static int __init iort_table_detect(void) +{
- acpi_status status;
- if (acpi_disabled)
return -ENODEV;
- status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
- if (ACPI_FAILURE(status)) {
const char *msg = acpi_format_exception(status);
pr_err("Failed to get table, %s\n", msg);
return -EINVAL;
- }
- return 0;
+} +arch_initcall(iort_table_detect); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 22d0cb7..b1f6d04 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -34,6 +34,7 @@ #include <linux/pci-acpi.h> #include <linux/pci-aspm.h> #include <linux/dmar.h> +#include <linux/iort.h> #include <linux/acpi.h> #include <linux/slab.h> #include <linux/dmi.h> @@ -842,6 +843,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, bus = pci_create_root_bus(NULL, busnum, ops->pci_ops, &info->controller, &info->resources); if (bus) {
pci_scan_child_bus(bus); pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge), acpi_pci_root_release_info, info);bus->msi = iort_find_pci_msi_chip(root->segment, 0);
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 0dd64c5..7dddfee 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -26,6 +26,7 @@ config ARM_GIC_V3 config ARM_GIC_V3_ITS bool select PCI_MSI_IRQ_DOMAIN
- select IORT_TABLE if (ACPI && PCI_MSI)
The number of Kconfig options is mind boggling these days :/. Do we need to select on a GICv3 option, too?
Seems not, IORT is only connecting to ITS. but as you said, seems ARM_GIC_V3_ITS depends on GICv3 :)
Thanks Hanjun
On 17.07.2015 10:20, Hanjun Guo wrote:
On 07/17/2015 05:32 AM, Al Stone wrote:
On 06/16/2015 07:09 AM, Hanjun Guo wrote:
IORT shows representation of IO topology that will be used by ARM based systems. It describes how various components are connected together e.g. which devices are connected to given ITS instance.
This patch implements calls which allow to:
- register/remove ITS as MSI chip
- parse all IORT nodes and form node tree (for easy lookup)
- find ITS (MSI chip) that device is assigned to
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org Signed-off-by: Hanjun Guo hanjun.guo@linaro.org
drivers/acpi/Kconfig | 3 + drivers/acpi/Makefile | 1 + drivers/acpi/iort.c | 272 +++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 2 + drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-gic-v3-its.c | 6 +- include/linux/iort.h | 39 ++++++ 7 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/iort.c create mode 100644 include/linux/iort.h
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 212735f..749a8c7 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -57,6 +57,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT config ACPI_CCA_REQUIRED bool
+config IORT_TABLE
- bool
- config ACPI_SLEEP bool depends on SUSPEND || HIBERNATION
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 576fbb1..b325d08 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -82,6 +82,7 @@ obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o obj-$(CONFIG_ACPI_BGRT) += bgrt.o obj-$(CONFIG_IRQCHIP) += irq.o +obj-$(CONFIG_IORT_TABLE) += iort.o
# processor has its own "processor." module_param namespace processor-y := processor_driver.o processor_throttling.o diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c new file mode 100644 index 0000000..01692bb --- /dev/null +++ b/drivers/acpi/iort.c @@ -0,0 +1,272 @@ +/*
- Copyright (C) 2015, Linaro Ltd.
- Author: Tomasz Nowicki tomasz.nowicki@linaro.org
- Author: Hanjun Guo hanjun.guo@linaro.org
- This program is free software; you can redistribute it and/or
modify it
- under the terms and conditions of the GNU General Public License,
- version 2, as published by the Free Software Foundation.
- This program is distributed in the hope it will be useful, but
WITHOUT
- ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for
- more details.
- This file implements early detection/parsing of I/O mapping
- reported to OS through firmware via I/O Remapping Table (IORT)
- IORT document number: ARM DEN 0049A
- These routines are used by ITS and PCI host bridge drivers.
- */
+#define pr_fmt(fmt) "ACPI: IORT: " fmt
+#include <linux/acpi.h> +#include <linux/export.h> +#include <linux/iort.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/mutex.h> +#include <linux/slab.h>
+struct iort_its_msi_chip {
- struct list_head list;
- struct msi_controller *chip;
Just to let you know, Marc is trying to remove struct msi_controller [1] and we need to rework this patch set based on that patch set.
- u32 id;
+};
+typedef acpi_status (*iort_find_node_callback)
- (struct acpi_iort_node *node, void *context);
+/* pointer to the mapped IORT table */ +static struct acpi_table_header *iort_table;
Do you really want acpi_table_header? Or do you want acpi_table_iort? It's not clear to me which would be better and avoid the most casts.
yes, we need the pointer to the mem-remapped iort table then no need to remap it later when using it.
+static LIST_HEAD(iort_pci_msi_chip_list); +static DEFINE_MUTEX(iort_pci_msi_chip_mutex);
+int iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id) +{
- struct iort_its_msi_chip *its_msi_chip;
- its_msi_chip = kzalloc(sizeof(*its_msi_chip), GFP_KERNEL);
- if (!its_msi_chip)
return -ENOMEM;
- its_msi_chip->chip = chip;
- its_msi_chip->id = its_id;
- mutex_lock(&iort_pci_msi_chip_mutex);
- list_add(&its_msi_chip->list, &iort_pci_msi_chip_list);
- mutex_unlock(&iort_pci_msi_chip_mutex);
The mutex raises a question for me: when is IORT being parsed? If it is only early on (i.e., setup_arch()), we may not even have multiple CPUs to worry about yet and the mutex isn't needed. And if we are that early, do we need to use __init?
If IORT isn't being parsed until after the interpreter is running, then yeah, a mutex is a good idea.
Good question. This iort_pci_msi_chip_add() is only called by __init function and with only one CPU running as I know, I will rework this patch set as I said above, we don't need struct msi_controller anymore, something else will be needed.
Not sure here, but you may have a look at analogous solution for FDT, I believe mutex is used there as well.
Tomasz
- return 0;
+} +EXPORT_SYMBOL_GPL(iort_pci_msi_chip_add);
+void iort_pci_msi_chip_remove(struct msi_controller *chip) +{
- struct iort_its_msi_chip *its_msi_chip;
- mutex_lock(&iort_pci_msi_chip_mutex);
- list_for_each_entry(its_msi_chip, &iort_pci_msi_chip_list, list) {
if (its_msi_chip->chip == chip) {
list_del(&its_msi_chip->list);
kfree(its_msi_chip);
break;
}
- }
- mutex_unlock(&iort_pci_msi_chip_mutex);
+} +EXPORT_SYMBOL_GPL(iort_pci_msi_chip_remove);
+static struct msi_controller *iort_pci_find_msi_chip_by_its(u32 its_id) +{
- struct iort_its_msi_chip *its_msi_chip;
- mutex_lock(&iort_pci_msi_chip_mutex);
- list_for_each_entry(its_msi_chip, &iort_pci_msi_chip_list, list) {
if (its_msi_chip->id == its_id) {
mutex_unlock(&iort_pci_msi_chip_mutex);
return its_msi_chip->chip;
}
- }
- mutex_unlock(&iort_pci_msi_chip_mutex);
- return NULL;
+}
+/**
- iort_find_root_its_node() - Get PCI root complex, device, or SMMU's
- parent ITS node.
- @node: node pointer to PCI root complex, device, or SMMU
- Returns: parent ITS node pointer on success
NULL on failure
- */
+static struct acpi_iort_node * +iort_find_parent_its_node(struct acpi_iort_node *node) +{
- struct acpi_iort_id_mapping *id_map;
- if (!node)
return NULL;
- /* Go upstream until find its parent ITS node */
- while (node->type != ACPI_IORT_NODE_ITS_GROUP) {
/* TODO: handle multi ID mapping entries */
id_map = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node,
node->mapping_offset);
/* Firmware bug! */
if (!id_map->output_reference) {
pr_err(FW_BUG "[node %p type %d] ID map has invalid
parent reference\n",
node, node->type);
return NULL;
}
/* TODO: Components that do not generate MSIs but are
connected to an SMMU */
node = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
id_map->output_reference);
- }
- return node;
+}
+/**
- iort_scan_node() - scan the IORT and call the handler with specific
- IORT node type
- @type: IORT node type
- @callback: callback with specific node type
- @context: context pass to the callback
- Returns: node pointer when the callback succeed
NULL on failure
- */
+static struct acpi_iort_node * +iort_scan_node(enum acpi_iort_node_type type,
iort_find_node_callback callback, void *context)
+{
- struct acpi_iort_node *iort_node, *iort_end;
- struct acpi_table_iort *iort;
- int i;
- if (!iort_table)
return NULL;
- /*
* iort_table and iort both point to the start of IORT table, but
* have different struct types
*/
- iort = container_of(iort_table, struct acpi_table_iort, header);
- /* Get the first iort node */
- iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort,
iort->node_offset);
- /* pointer to the end of the table */
- iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort_table,
iort_table->length);
- for (i = 0; i < iort->node_count; i++) {
if (iort_node >= iort_end) {
pr_err("iort node pointer overflows, bad table\n");
return NULL;
}
if (iort_node->type == type) {
if (ACPI_SUCCESS(callback(iort_node, context)))
return iort_node;
}
iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
iort_node->length);
- }
- return NULL;
+}
+static acpi_status +iort_find_pci_rc_callback(struct acpi_iort_node *node, void *context) +{
- struct acpi_iort_root_complex *pci_rc;
- int segment = *(int *)context;
- pci_rc = (struct acpi_iort_root_complex *)node->node_data;
- /*
* It is assumed that PCI segment numbers have a one-to-one mapping
* with root complexes. Each segment number can represent only one
* root complex.
*/
- if (pci_rc->pci_segment_number == segment)
return AE_OK;
- return AE_NOT_FOUND;
+}
+/**
- iort_find_pci_msi_chip() - find the msi controller with root
complex's
- segment number
- @segment: domain number of this pci root complex
- @idx: index of the ITS in the ITS group
- Returns: msi controller bind to the root complex with the segment
NULL on failure
- */
+struct msi_controller *iort_find_pci_msi_chip(int segment, unsigned int idx) +{
- struct acpi_iort_its_group *its;
- struct acpi_iort_node *node;
- struct msi_controller *msi_chip;
- node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
iort_find_pci_rc_callback, &segment);
- if (!node) {
pr_err("can't find node related to PCI host bridge [segment
%d]\n",
segment);
return NULL;
- }
- node = iort_find_parent_its_node(node);
- if (!node) {
pr_err("can't find ITS parent node for PCI host bridge
[segment %d]\n",
segment);
return NULL;
- }
- /* Move to ITS specific data */
- its = (struct acpi_iort_its_group *)node->node_data;
- if (idx > its->its_count) {
pr_err("requested ITS ID index [%d] is greater than
available ITS count [%d]\n",
idx, its->its_count);
return NULL;
- }
- msi_chip = iort_pci_find_msi_chip_by_its(its->identifiers[idx]);
- if (!msi_chip)
pr_err("can not find ITS chip ID:%d, not registered\n",
its->identifiers[idx]);
- return msi_chip;
+} +EXPORT_SYMBOL_GPL(iort_find_pci_msi_chip);
+/* Get the remapped IORT table */ +static int __init iort_table_detect(void) +{
- acpi_status status;
- if (acpi_disabled)
return -ENODEV;
- status = acpi_get_table(ACPI_SIG_IORT, 0, &iort_table);
- if (ACPI_FAILURE(status)) {
const char *msg = acpi_format_exception(status);
pr_err("Failed to get table, %s\n", msg);
return -EINVAL;
- }
- return 0;
+} +arch_initcall(iort_table_detect); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 22d0cb7..b1f6d04 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -34,6 +34,7 @@ #include <linux/pci-acpi.h> #include <linux/pci-aspm.h> #include <linux/dmar.h> +#include <linux/iort.h> #include <linux/acpi.h> #include <linux/slab.h> #include <linux/dmi.h> @@ -842,6 +843,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root, bus = pci_create_root_bus(NULL, busnum, ops->pci_ops, &info->controller, &info->resources); if (bus) {
bus->msi = iort_find_pci_msi_chip(root->segment, 0); pci_scan_child_bus(bus); pci_set_host_bridge_release(to_pci_host_bridge(bus->bridge), acpi_pci_root_release_info, info);
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 0dd64c5..7dddfee 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -26,6 +26,7 @@ config ARM_GIC_V3 config ARM_GIC_V3_ITS bool select PCI_MSI_IRQ_DOMAIN
- select IORT_TABLE if (ACPI && PCI_MSI)
The number of Kconfig options is mind boggling these days :/. Do we need to select on a GICv3 option, too?
Seems not, IORT is only connecting to ITS. but as you said, seems ARM_GIC_V3_ITS depends on GICv3 :)
Thanks Hanjun _______________________________________________ Linaro-acpi mailing list Linaro-acpi@lists.linaro.org https://lists.linaro.org/mailman/listinfo/linaro-acpi
From: Tomasz Nowicki tomasz.nowicki@linaro.org
Device structure identifies physical devices uniquely similar to OF device node. However, it could be used without h/w description dependencies (it can work with DT and ACPI).
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/iommu/arm-smmu.c | 88 +++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 45 deletions(-)
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 9f7e1d3..55021ef 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -37,6 +37,7 @@ #include <linux/iopoll.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_platform.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -276,7 +277,7 @@ struct arm_smmu_master_cfg { };
struct arm_smmu_master { - struct device_node *of_node; + struct device *dev; struct rb_node node; struct arm_smmu_master_cfg cfg; }; @@ -380,21 +381,23 @@ static void parse_driver_options(struct arm_smmu_device *smmu) } while (arm_smmu_options[++i].opt); }
-static struct device_node *dev_get_dev_node(struct device *dev) +static struct device *dev_get_dev_node(struct device *dev) { - if (dev_is_pci(dev)) { - struct pci_bus *bus = to_pci_dev(dev)->bus; + struct pci_bus *bus;
- while (!pci_is_root_bus(bus)) - bus = bus->parent; - return bus->bridge->parent->of_node; - } + if (!dev_is_pci(dev)) + return dev; + + bus = to_pci_dev(dev)->bus;
- return dev->of_node; + while (!pci_is_root_bus(bus)) + bus = bus->parent; + + return bus->bridge->parent; }
static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, - struct device_node *dev_node) + struct device *device) { struct rb_node *node = smmu->masters.rb_node;
@@ -403,9 +406,9 @@ static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu,
master = container_of(node, struct arm_smmu_master, node);
- if (dev_node < master->of_node) + if (device < master->dev) node = node->rb_left; - else if (dev_node > master->of_node) + else if (device > master->dev) node = node->rb_right; else return master; @@ -440,9 +443,9 @@ static int insert_smmu_master(struct arm_smmu_device *smmu, = container_of(*new, struct arm_smmu_master, node);
parent = *new; - if (master->of_node < this->of_node) + if (master->dev < this->dev) new = &((*new)->rb_left); - else if (master->of_node > this->of_node) + else if (master->dev > this->dev) new = &((*new)->rb_right); else return -EEXIST; @@ -455,23 +458,25 @@ static int insert_smmu_master(struct arm_smmu_device *smmu,
static int register_smmu_master(struct arm_smmu_device *smmu, struct device *dev, - struct of_phandle_args *masterspec) + struct device *child_dev, + uint32_t streamids[], + int num_streamids) { int i; struct arm_smmu_master *master;
- master = find_smmu_master(smmu, masterspec->np); + master = find_smmu_master(smmu, child_dev); if (master) { dev_err(dev, "rejecting multiple registrations for master device %s\n", - masterspec->np->name); + dev_name(child_dev)); return -EBUSY; }
- if (masterspec->args_count > MAX_MASTER_STREAMIDS) { + if (num_streamids > MAX_MASTER_STREAMIDS) { dev_err(dev, "reached maximum number (%d) of stream IDs for master device %s\n", - MAX_MASTER_STREAMIDS, masterspec->np->name); + MAX_MASTER_STREAMIDS, dev_name(child_dev)); return -ENOSPC; }
@@ -479,17 +484,17 @@ static int register_smmu_master(struct arm_smmu_device *smmu, if (!master) return -ENOMEM;
- master->of_node = masterspec->np; - master->cfg.num_streamids = masterspec->args_count; + master->dev = child_dev; + master->cfg.num_streamids = num_streamids;
for (i = 0; i < master->cfg.num_streamids; ++i) { - u16 streamid = masterspec->args[i]; + u16 streamid = streamids[i];
if (!(smmu->features & ARM_SMMU_FEAT_STREAM_MATCH) && (streamid >= smmu->num_mapping_groups)) { dev_err(dev, "stream ID for master device %s greater than maximum allowed (%d)\n", - masterspec->np->name, smmu->num_mapping_groups); + dev_name(child_dev), smmu->num_mapping_groups); return -ERANGE; } master->cfg.streamids[i] = streamid; @@ -501,11 +506,11 @@ static struct arm_smmu_device *find_smmu_for_device(struct device *dev) { struct arm_smmu_device *smmu; struct arm_smmu_master *master = NULL; - struct device_node *dev_node = dev_get_dev_node(dev); + struct device *device = dev_get_dev_node(dev);
spin_lock(&arm_smmu_devices_lock); list_for_each_entry(smmu, &arm_smmu_devices, list) { - master = find_smmu_master(smmu, dev_node); + master = find_smmu_master(smmu, device); if (master) break; } @@ -1397,7 +1402,7 @@ static int arm_smmu_add_platform_device(struct device *dev) if (!smmu) return -ENODEV;
- master = find_smmu_master(smmu, dev->of_node); + master = find_smmu_master(smmu, dev); if (!master) return -ENODEV;
@@ -1724,7 +1729,6 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) struct resource *res; struct arm_smmu_device *smmu; struct device *dev = &pdev->dev; - struct rb_node *node; struct of_phandle_args masterspec; int num_irqs, i, err;
@@ -1789,11 +1793,20 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) while (!of_parse_phandle_with_args(dev->of_node, "mmu-masters", "#stream-id-cells", i, &masterspec)) { - err = register_smmu_master(smmu, dev, &masterspec); + struct platform_device *device; + + device = of_find_device_by_node(masterspec.np); + of_node_put(masterspec.np); + if (!device || !(&device->dev)) + return -ENODEV; + + err = register_smmu_master(smmu, dev, &device->dev, + masterspec.args, + masterspec.args_count); if (err) { dev_err(dev, "failed to add master %s\n", masterspec.np->name); - goto out_put_masters; + return err; }
i++; @@ -1807,8 +1820,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) dev_err(dev, "found only %d context interrupt(s) but %d required\n", smmu->num_context_irqs, smmu->num_context_banks); - err = -ENODEV; - goto out_put_masters; + return -ENODEV; }
for (i = 0; i < smmu->num_global_irqs; ++i) { @@ -1836,13 +1848,6 @@ out_free_irqs: while (i--) free_irq(smmu->irqs[i], smmu);
-out_put_masters: - for (node = rb_first(&smmu->masters); node; node = rb_next(node)) { - struct arm_smmu_master *master - = container_of(node, struct arm_smmu_master, node); - of_node_put(master->of_node); - } - return err; }
@@ -1851,7 +1856,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev) int i; struct device *dev = &pdev->dev; struct arm_smmu_device *curr, *smmu = NULL; - struct rb_node *node;
spin_lock(&arm_smmu_devices_lock); list_for_each_entry(curr, &arm_smmu_devices, list) { @@ -1866,12 +1870,6 @@ static int arm_smmu_device_remove(struct platform_device *pdev) if (!smmu) return -ENODEV;
- for (node = rb_first(&smmu->masters); node; node = rb_next(node)) { - struct arm_smmu_master *master - = container_of(node, struct arm_smmu_master, node); - of_node_put(master->of_node); - } - if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS)) dev_err(dev, "removing device with active domains!\n");
From: Tomasz Nowicki tomasz.nowicki@linaro.org
This patch first introduce some helper functions in iort.c, these helpers allow to walk up and down on IORT table and find out several SMMU dependencies, properties, topology of IO devices.
After that, using the information presented in SMMU iort node to init SMMU, since SMMU is fully described using static information in IORT table node in contrast to DSDT node which means platform device needs to be created manually. It happens during IORT initialization so that its driver can probe and get resources in normal way. Any fancy SMMU properties are stored in private platform device structure.
Till now driver has bound via DT so this patch is going to extract common code and move DT and ACPI specific code to corresponding functions.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org Signed-off-by: Hanjun Guo hanjun.guo@linaro.org --- drivers/acpi/iort.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++- drivers/iommu/arm-smmu.c | 206 ++++++++++++++++++++++++++++++++---- include/linux/iort.h | 24 +++++ 3 files changed, 474 insertions(+), 24 deletions(-)
diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c index 01692bb..eaad01d 100644 --- a/drivers/acpi/iort.c +++ b/drivers/acpi/iort.c @@ -16,7 +16,7 @@ * reported to OS through firmware via I/O Remapping Table (IORT) * IORT document number: ARM DEN 0049A * - * These routines are used by ITS and PCI host bridge drivers. + * These routines are used by ITS, PCI host bridge and SMMU drivers. */
#define pr_fmt(fmt) "ACPI: IORT: " fmt @@ -29,6 +29,8 @@ #include <linux/module.h> #include <linux/msi.h> #include <linux/mutex.h> +#include <linux/pci.h> +#include <linux/platform_device.h> #include <linux/slab.h>
struct iort_its_msi_chip { @@ -37,6 +39,13 @@ struct iort_its_msi_chip { u32 id; };
+struct iort_priv_ctx { + struct acpi_iort_node *parent; + unsigned int index; +}; + +#define IORT_NODE_TYPE_ANY (-1) + typedef acpi_status (*iort_find_node_callback) (struct acpi_iort_node *node, void *context);
@@ -174,7 +183,8 @@ iort_scan_node(enum acpi_iort_node_type type, return NULL; }
- if (iort_node->type == type) { + if (iort_node->type == type || + iort_node->type == IORT_NODE_TYPE_ANY) { if (ACPI_SUCCESS(callback(iort_node, context))) return iort_node; } @@ -252,6 +262,257 @@ struct msi_controller *iort_find_pci_msi_chip(int segment, unsigned int idx) } EXPORT_SYMBOL_GPL(iort_find_pci_msi_chip);
+static acpi_status +iort_find_node_idx_callback(struct acpi_iort_node *node, + void *context) +{ + unsigned int *count = context; + + if ((*count)--) + return AE_NOT_FOUND; + + return AE_OK; +} + +static struct acpi_iort_node * +iort_find_node(enum acpi_iort_node_type type, unsigned int idx) +{ + unsigned int count = idx; + + if (!iort_table) + return NULL; + return iort_scan_node(type, iort_find_node_idx_callback, &count); +} + +static acpi_status +iort_find_children_idx_callback(struct acpi_iort_node *node, void *context) +{ + struct iort_priv_ctx *info = context; + struct acpi_iort_id_mapping *id; + struct acpi_iort_node *parent; + int i, found = 0; + + /* Move to ID section */ + id = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, + node->mapping_offset); + for (i = 0; i < node->mapping_count; i++) { + parent = ACPI_ADD_PTR(struct acpi_iort_node, iort_table, + id->output_reference); + if (parent == info->parent) { + found = 1; + break; + } + id++; + } + + if (!found || info->index--) + return AE_NOT_FOUND; + + return AE_OK; +} + +struct acpi_iort_node * +iort_find_node_children(struct acpi_iort_node *parent, + unsigned int idx) +{ + struct iort_priv_ctx info; + + info.parent = parent; + info.index = idx; + + return iort_scan_node(IORT_NODE_TYPE_ANY, + iort_find_children_idx_callback, &info); +} +EXPORT_SYMBOL_GPL(iort_find_node_children); + +int +iort_find_endpoint_id(struct acpi_iort_node *node, u32 *streamids) +{ + struct acpi_iort_id_mapping *id; + int i, num_streamids = 0; + + /* Move to ID section */ + id = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, + node->mapping_offset); + /* Hunt for endpoint ID map */ + for (i = 0; i < node->mapping_count && + i < (sizeof(streamids) / sizeof(*streamids)); i++) + if (id[i].flags & ACPI_IORT_ID_SINGLE_MAPPING) + streamids[num_streamids++] = id[i].output_base; + + return num_streamids; +} +EXPORT_SYMBOL_GPL(iort_find_endpoint_id); + +int +iort_map_pcidev_to_streamid(struct pci_dev *pdev, u32 req_id, u32 *stream_id) +{ + struct acpi_iort_node *node; + struct acpi_iort_id_mapping *id; + int i; + int segment = pci_domain_nr(pdev->bus); + + node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX, + iort_find_pci_rc_callback, &segment); + if (!node) { + pr_err("can not find node related to PCI host bridge [segment %d]\n", + segment); + return -ENODEV; + } + + /* Move to ID section */ + id = ACPI_ADD_PTR(struct acpi_iort_id_mapping, node, + node->mapping_offset); + + /* Look for request ID to stream ID map */ + for (i = 0; i < node->mapping_count; i++, id++) { + if (id->flags & ACPI_IORT_ID_SINGLE_MAPPING) + continue; + + if (req_id < id->input_base || + (req_id > id->input_base + id->id_count)) + continue; + + *stream_id = id->output_base + (req_id - id->input_base); + return 0; + } + + return -ENXIO; +} +EXPORT_SYMBOL_GPL(iort_map_pcidev_to_streamid); + +static acpi_status +match_segment(acpi_handle handle, u32 lvl, void *context, void **ret_val) +{ + int *segment = context; + struct acpi_pci_root *root; + struct acpi_device *adev; + struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; + int err; + + if (!acpi_is_root_bridge(handle)) + return AE_OK; + + root = acpi_pci_find_root(handle); + if (!root) + return AE_OK; + + if (root->segment != *segment) + return AE_OK; + + err = acpi_bus_get_device(handle, &adev); + if (err) { + if (ACPI_FAILURE(acpi_get_name(handle, ACPI_FULL_PATHNAME, &string))) + pr_warn("Invalid link device, error %d\n", err); + else { + pr_warn("Invalid link for %s device\n", + (char *)string.pointer); + kfree(string.pointer); + } + return AE_OK; + } + + *ret_val = &adev->dev; + return AE_CTRL_TERMINATE; +} + +struct device * +iort_find_node_device(struct acpi_iort_node *node) +{ + struct acpi_iort_named_component *acpi_dev; + struct acpi_iort_root_complex *pci_rc; + struct acpi_device *adev; + struct device *device = NULL; + acpi_handle handle; + char *device_path; + int segment; + + switch (node->type) { + case ACPI_IORT_NODE_NAMED_COMPONENT: + acpi_dev = (struct acpi_iort_named_component *)node->node_data; + + device_path = acpi_dev->device_name; + if (ACPI_FAILURE(acpi_get_handle(ACPI_ROOT_OBJECT, device_path, + &handle))) { + pr_warn("Failed to find handle for ACPI object %s\n", + device_path); + break; + } + + if (acpi_bus_get_device(handle, &adev)) { + pr_warn("Failed to get device for ACPI object %s\n", + device_path); + break; + } + + device = &adev->dev; + break; + case ACPI_IORT_NODE_PCI_ROOT_COMPLEX: + pci_rc = (struct acpi_iort_root_complex *)node->node_data; + segment = pci_rc->pci_segment_number; + + /* fix me: also need to check PNP0A08 */ + acpi_get_devices("PNP0A03", match_segment, &segment, (void **)&device); + break; + default: + pr_err("can not find device for node type %d\n", node->type); + return NULL; + } + + return device; +} +EXPORT_SYMBOL_GPL(iort_find_node_device); + +static void iort_add_smmu_platform_device(struct acpi_iort_node *node) +{ + struct acpi_iort_smmu *smmu; + struct platform_device_info pdevinfo; + struct platform_device *pdev = NULL; + struct resource resources; + + /* Move to SMMU1/2 specific data */ + smmu = (struct acpi_iort_smmu *)node->node_data; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.parent = NULL; + pdevinfo.name = "arm-smmu"; + pdevinfo.id = PLATFORM_DEVID_AUTO; + + memset(&resources, 0, sizeof(resources)); + resources.start = smmu->base_address; + resources.end = smmu->base_address + smmu->span - 1; + resources.flags = IORESOURCE_MEM; + + pdevinfo.res = &resources; + pdevinfo.num_res = 1; + + pdevinfo.data = &node; + pdevinfo.size_data = sizeof(node); + + pdev = platform_device_register_full(&pdevinfo); + if (IS_ERR(pdev)) + pr_err("platform device creation failed: %ld\n", + PTR_ERR(pdev)); + else + pr_debug("Platform device arm-smmu created\n"); +} + +static int iort_add_smmu_devices(void) +{ + struct acpi_iort_node *iort_node = NULL; + unsigned int idx = 0; + + while (1) { + iort_node = iort_find_node(ACPI_IORT_NODE_SMMU, idx++); + if (!iort_node) + continue; + + iort_add_smmu_platform_device(iort_node); + } + + return 0; +} + /* Get the remapped IORT table */ static int __init iort_table_detect(void) { @@ -267,6 +528,9 @@ static int __init iort_table_detect(void) return -EINVAL; }
+ if(IS_ENABLED(CONFIG_ARM_SMMU)) + iort_add_smmu_devices(); + return 0; } arch_initcall(iort_table_detect); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 55021ef..85946f5 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -28,6 +28,7 @@
#define pr_fmt(fmt) "arm-smmu: " fmt
+#include <linux/acpi.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/err.h> @@ -35,10 +36,13 @@ #include <linux/io.h> #include <linux/iommu.h> #include <linux/iopoll.h> +#include <linux/iort.h> +#include <linux/mm.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/pci.h> +#include <linux/pci-acpi.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/spinlock.h> @@ -384,6 +388,7 @@ static void parse_driver_options(struct arm_smmu_device *smmu) static struct device *dev_get_dev_node(struct device *dev) { struct pci_bus *bus; + struct device *device;
if (!dev_is_pci(dev)) return dev; @@ -393,7 +398,26 @@ static struct device *dev_get_dev_node(struct device *dev) while (!pci_is_root_bus(bus)) bus = bus->parent;
- return bus->bridge->parent; + device = bus->bridge->parent; + if (device) + return device; + +#ifdef CONFIG_ACPI + /* + * ACPI host bridge device is going to be found via ACPI companion + * device + */ + if (!acpi_disabled) { + struct acpi_device *adev; + + if (!acpi_bus_get_device(ACPI_HANDLE(bus->bridge), &adev)) + return &adev->dev; + } +#endif + + pr_warn("Failed to get PCI host bridge device for %s device\n", + dev_name(dev)); + return NULL; }
static struct arm_smmu_master *find_smmu_master(struct arm_smmu_device *smmu, @@ -1341,6 +1365,27 @@ static int __arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data) return 0; /* Continue walking */ }
+static int arm_smmu_get_pci_sid(struct pci_dev *pdev, u16 alias, void *data) +{ + u32 *stream_id = data; + + /* + * We need a way to describe the ID mappings in FDT. + * Assume Stream ID == Requester ID for now. + * + * ACPI is using IORT ID translation map. Each SMMU is represented by + * corresponding SMMU node and its children underneath. + * Children are referring to parent's ID map looking for appropriate + * translation rule. + */ + + /* include domain number with pci device id */ + if (acpi_disabled) + return __arm_smmu_get_pci_sid(pdev, alias, data); + + return iort_map_pcidev_to_streamid(pdev, alias, stream_id); +} + static void __arm_smmu_release_pci_iommudata(void *data) { kfree(data); @@ -1374,11 +1419,7 @@ static int arm_smmu_add_pci_device(struct pci_dev *pdev) goto out_put_group; }
- /* - * Assume Stream ID == Requester ID for now. - * We need a way to describe the ID mappings in FDT. - */ - pci_for_each_dma_alias(pdev, __arm_smmu_get_pci_sid, &sid); + pci_for_each_dma_alias(pdev, arm_smmu_get_pci_sid, &sid); for (i = 0; i < cfg->num_streamids; ++i) if (cfg->streamids[i] == sid) break; @@ -1713,6 +1754,104 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) return 0; }
+#ifdef CONFIG_ACPI +static int arm_smmu_acpi_probe(struct platform_device *pdev, + struct arm_smmu_device *smmu) +{ + struct device *dev = smmu->dev; + struct acpi_iort_node *node = + *(struct acpi_iort_node **)dev_get_platdata(dev); + struct acpi_iort_smmu *iort_smmu; + int num_irqs, i, err, trigger; + u64 *ctx_irq, *glb_irq; + + /* Move to SMMU1/2 specific data */ + iort_smmu = (struct acpi_iort_smmu *)node->node_data; + + smmu->version = (enum arm_smmu_arch_version)iort_smmu->model + 1; + + /* we may have one or two globle irqs, SMMU_NSgIrpt and probaly SMMU_NSgCfgIrpt */ + glb_irq = ACPI_ADD_PTR(u64, node, iort_smmu->global_interrupt_offset); + if (!IORT_IRQ_MASK(glb_irq[1])) /* 0 means not implemented */ + smmu->num_global_irqs = 1; + else + smmu->num_global_irqs = 2; + + smmu->num_context_irqs = iort_smmu->context_interrupt_count; + num_irqs = smmu->num_context_irqs + smmu->num_global_irqs; + + if (!smmu->num_context_irqs) { + dev_err(dev, "found %d interrupts but expected at least %d\n", + num_irqs, smmu->num_global_irqs + 1); + return -ENODEV; + } + + smmu->irqs = devm_kzalloc(dev, sizeof(*smmu->irqs) * num_irqs, + GFP_KERNEL); + if (!smmu->irqs) { + dev_err(dev, "failed to allocate %d irqs\n", num_irqs); + return -ENOMEM; + } + + for (i = 0; i < smmu->num_global_irqs; i++) { + int hw_irq = IORT_IRQ_MASK(glb_irq[i]); + trigger = IORT_IRQ_TRIGGER_MASK(glb_irq[i]); + smmu->irqs[0] = acpi_register_gsi(NULL, hw_irq, trigger, + ACPI_ACTIVE_HIGH); + } + + /* Time for context IRQs */ + ctx_irq = ACPI_ADD_PTR(u64, node, iort_smmu->context_interrupt_offset); + for (; i < smmu->num_context_irqs + smmu->num_global_irqs; i++) { + int hw_irq = IORT_IRQ_MASK(ctx_irq[i]); + trigger = IORT_IRQ_TRIGGER_MASK(ctx_irq[i]); + smmu->irqs[i + 1] = acpi_register_gsi(NULL, hw_irq, trigger, + ACPI_ACTIVE_HIGH); + } + + err = arm_smmu_device_cfg_probe(smmu); + if (err) + return err; + + i = 0; + smmu->masters = RB_ROOT; + while (1) { + struct acpi_iort_node *child; + uint32_t streamids[MAX_MASTER_STREAMIDS]; + struct device *child_dev; + int num_streamids; + + child = iort_find_node_children(node, i); + if (!child) + break; + + child_dev = iort_find_node_device(child); + if (!child_dev) + break; + + num_streamids = iort_find_endpoint_id(child, streamids); + err = register_smmu_master(smmu, dev, child_dev, streamids, + num_streamids); + if (err) { + dev_err(dev, "failed to add master %s\n", + dev_name(child_dev)); + return err; + } + + i++; + } + dev_notice(dev, "registered %d master devices\n", i); + + return 0; +} +#else +static inline int arm_smmu_acpi_probe(struct platform_device *pdev, + struct arm_smmu_device *smmu) +{ + return -ENODEV; +} +#endif + static const struct of_device_id arm_smmu_of_match[] = { { .compatible = "arm,smmu-v1", .data = (void *)ARM_SMMU_V1 }, { .compatible = "arm,smmu-v2", .data = (void *)ARM_SMMU_V2 }, @@ -1723,31 +1862,18 @@ static const struct of_device_id arm_smmu_of_match[] = { }; MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
-static int arm_smmu_device_dt_probe(struct platform_device *pdev) +static int arm_smmu_dt_probe(struct platform_device *pdev, + struct arm_smmu_device *smmu) { const struct of_device_id *of_id; struct resource *res; - struct arm_smmu_device *smmu; struct device *dev = &pdev->dev; struct of_phandle_args masterspec; int num_irqs, i, err;
- smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); - if (!smmu) { - dev_err(dev, "failed to allocate arm_smmu_device\n"); - return -ENOMEM; - } - smmu->dev = dev; - of_id = of_match_node(arm_smmu_of_match, dev->of_node); smmu->version = (enum arm_smmu_arch_version)of_id->data;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - smmu->base = devm_ioremap_resource(dev, res); - if (IS_ERR(smmu->base)) - return PTR_ERR(smmu->base); - smmu->size = resource_size(res); - if (of_property_read_u32(dev->of_node, "#global-interrupts", &smmu->num_global_irqs)) { dev_err(dev, "missing #global-interrupts property\n"); @@ -1813,6 +1939,37 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev) } dev_notice(dev, "registered %d master devices\n", i);
+ return 0; +} + +static int arm_smmu_device_probe(struct platform_device *pdev) +{ + struct resource *res; + struct arm_smmu_device *smmu; + struct device *dev = &pdev->dev; + int i, err; + + smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL); + if (!smmu) { + dev_err(dev, "failed to allocate arm_smmu_device\n"); + return -ENOMEM; + } + smmu->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + smmu->base = devm_ioremap_resource(dev, res); + if (IS_ERR(smmu->base)) + return PTR_ERR(smmu->base); + smmu->size = resource_size(res); + + if (acpi_disabled) + err = arm_smmu_dt_probe(pdev, smmu); + else + err = arm_smmu_acpi_probe(pdev, smmu); + + if (err) + return err; + parse_driver_options(smmu);
if (smmu->version > ARM_SMMU_V1 && @@ -1886,7 +2043,7 @@ static struct platform_driver arm_smmu_driver = { .name = "arm-smmu", .of_match_table = of_match_ptr(arm_smmu_of_match), }, - .probe = arm_smmu_device_dt_probe, + .probe = arm_smmu_device_probe, .remove = arm_smmu_device_remove, };
@@ -1919,6 +2076,11 @@ static int __init arm_smmu_init(void) bus_set_iommu(&amba_bustype, &arm_smmu_ops); #endif
+#ifdef CONFIG_ACPI + if (!acpi_disabled && !iommu_present(&acpi_bus_type)) + bus_set_iommu(&acpi_bus_type, &arm_smmu_ops); +#endif + #ifdef CONFIG_PCI if (!iommu_present(&pci_bus_type)) bus_set_iommu(&pci_bus_type, &arm_smmu_ops); diff --git a/include/linux/iort.h b/include/linux/iort.h index 7b83a03..f62ae09 100644 --- a/include/linux/iort.h +++ b/include/linux/iort.h @@ -21,10 +21,23 @@
struct msi_controller;
+#define IORT_IRQ_MASK(irq) (irq & 0xffffffffULL) +#define IORT_IRQ_TRIGGER_MASK(irq) ((irq >> 32) & 0xffffffffULL) + #ifdef CONFIG_IORT_TABLE int iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id); void iort_pci_msi_chip_remove(struct msi_controller *chip); struct msi_controller *iort_find_pci_msi_chip(int segment, unsigned int idx); + +struct acpi_iort_node * +iort_find_node_children(struct acpi_iort_node *parent, unsigned int idx); + +int iort_find_endpoint_id(struct acpi_iort_node *node, u32 *streamids); + +int iort_map_pcidev_to_streamid(struct pci_dev *pdev, u32 req_id, u32 *stream_id); + +struct device * +iort_find_node_device(struct acpi_iort_node *node); #else static inline int iort_pci_msi_chip_add(struct msi_controller *chip, u32 its_id) { return -ENODEV; } @@ -34,6 +47,17 @@ iort_pci_msi_chip_remove(struct msi_controller *chip) { }
static struct msi_controller * iort_find_pci_msi_chip(int segment, unsigned int idx) { return NULL; } +static inline struct acpi_iort_node * +iort_find_node_children(struct acpi_iort_node *parent, + unsigned int idx) { return NULL; } +static inline int +iort_find_endpoint_id(struct acpi_iort_node *node, u32 *streamids) +{ return AE_ERROR; } +static inline int +iort_map_pcidev_to_streamid(struct pci_dev *pdev, u32 req_id, u32 *stream_id) +{ return AE_ERROR; } +static inline struct device * +iort_find_node_device(struct acpi_iort_node *node) { return NULL; } #endif
#endif /* __IORT_H__ */
On 06/16/2015 07:09 AM, Hanjun Guo wrote:
IORT, IO remapping tables, which is a table to describe the connections of PCI root complex, devices, ITS (msi controller) and SMMU, for example, it presents the topology of which device under which SMMU and(or) ITS.
This patch set first do some cleanup for the ITS dirver, then init the ITS with the information presented by MADT ITS entries, then follows the IORT spec, implementing:
PCI root complex using ITS as msi controller, mapping its domain (segemnt) number with the MSI controller;
init the SMMU and use the mapping of PCI and no-PCI device to SMMU to connect them
No-PCI devices using MSI (connect to ITS) is not covered by this patch set.
please refer to the ARM documentation: http://infocenter.arm.com/help/topic/com.arm.doc.den0049a/DEN0049A_IO_Remapp...
This patch set is based on Tomasz Nowicki's work, but I rework some of the patches significantly, more work is needed for this patch set and I'm still not satisfy with the some of implementation, anyway, I will continue working on that, and at the same time, sending them out for review to see if there are some major problems.
you can get them form git://git.linaro.org/leg/acpi/acpi.git devel
Comments and test are warmly welcomed.
Hanjun Guo (5): irqchip / GICv3: remove gic root node in ITS irqchip / GICv3 / ITS: mark its_init() as __init irqchip/GICv3/ITS: refator ITS dt init code to prepare for ACPI irqchip: gicv3: its: probe ITS in ACPI way ARM64, ACPI, PCI, MSI: I/O Remapping Table (IORT) initial support.
Tomasz Nowicki (2): arm, smmu: Use more generic structure for SMMU master list. ARM64, ACPI, IORT: Bind SMMU driver via IORT table
drivers/acpi/Kconfig | 3 + drivers/acpi/Makefile | 1 + drivers/acpi/iort.c | 536 +++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 2 + drivers/iommu/arm-smmu.c | 290 ++++++++++++++++----- drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-gic-v3-its.c | 188 ++++++++++---- include/linux/iort.h | 63 +++++ 8 files changed, 966 insertions(+), 118 deletions(-) create mode 100644 drivers/acpi/iort.c create mode 100644 include/linux/iort.h
So, in general, these patches seem okay. My primary concern is: can we test them? There's enough subtle stuff here to give me a headache -- especially since there's a connection to virtualization. If we can test it, can we do that on anything other than the Cavium ThunderX that no one else has access to?
On 07/17/2015 05:36 AM, Al Stone wrote:
On 06/16/2015 07:09 AM, Hanjun Guo wrote:
IORT, IO remapping tables, which is a table to describe the connections of PCI root complex, devices, ITS (msi controller) and SMMU, for example, it presents the topology of which device under which SMMU and(or) ITS.
This patch set first do some cleanup for the ITS dirver, then init the ITS with the information presented by MADT ITS entries, then follows the IORT spec, implementing:
PCI root complex using ITS as msi controller, mapping its domain (segemnt) number with the MSI controller;
init the SMMU and use the mapping of PCI and no-PCI device to SMMU to connect them
No-PCI devices using MSI (connect to ITS) is not covered by this patch set.
please refer to the ARM documentation: http://infocenter.arm.com/help/topic/com.arm.doc.den0049a/DEN0049A_IO_Remapp...
This patch set is based on Tomasz Nowicki's work, but I rework some of the patches significantly, more work is needed for this patch set and I'm still not satisfy with the some of implementation, anyway, I will continue working on that, and at the same time, sending them out for review to see if there are some major problems.
you can get them form git://git.linaro.org/leg/acpi/acpi.git devel
Comments and test are warmly welcomed.
Hanjun Guo (5): irqchip / GICv3: remove gic root node in ITS irqchip / GICv3 / ITS: mark its_init() as __init irqchip/GICv3/ITS: refator ITS dt init code to prepare for ACPI irqchip: gicv3: its: probe ITS in ACPI way ARM64, ACPI, PCI, MSI: I/O Remapping Table (IORT) initial support.
Tomasz Nowicki (2): arm, smmu: Use more generic structure for SMMU master list. ARM64, ACPI, IORT: Bind SMMU driver via IORT table
drivers/acpi/Kconfig | 3 + drivers/acpi/Makefile | 1 + drivers/acpi/iort.c | 536 +++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 2 + drivers/iommu/arm-smmu.c | 290 ++++++++++++++++----- drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-gic-v3-its.c | 188 ++++++++++---- include/linux/iort.h | 63 +++++ 8 files changed, 966 insertions(+), 118 deletions(-) create mode 100644 drivers/acpi/iort.c create mode 100644 include/linux/iort.h
So, in general, these patches seem okay. My primary concern is: can we test them? There's enough subtle stuff here to give me a headache -- especially since there's a connection to virtualization. If we can test it, can we do that on anything other than the Cavium ThunderX that no one else has access to?
I'm not sure if FVP base model can test it or not, I will try to find it, I will let you know.
I think for now, Thunder-X is the best platform for testing.
Thanks Hanjun