| From 943e6dd529212c6868cdb032f982ab824e6aceab Mon Sep 17 00:00:00 2001 |
| From: Magnus Damm <damm+renesas@opensource.se> |
| Date: Mon, 16 Oct 2017 21:29:36 +0900 |
| Subject: [PATCH 0100/1795] iommu/ipmmu-vmsa: Add optional root device feature |
| |
| Add root device handling to the IPMMU driver by allowing certain |
| DT compat strings to enable has_cache_leaf_nodes that in turn will |
| support both root devices with interrupts and leaf devices that |
| face the actual IPMMU consumer devices. |
| |
| Signed-off-by: Magnus Damm <damm+renesas@opensource.se> |
| Signed-off-by: Alex Williamson <alex.williamson@redhat.com> |
| (cherry picked from commit fd5140e29a59e04a6c3e8cc56536bda3e60bbf49) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> |
| --- |
| drivers/iommu/ipmmu-vmsa.c | 90 +++++++++++++++++++++++++++++++------- |
| 1 file changed, 73 insertions(+), 17 deletions(-) |
| |
| diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c |
| index 5ce7879cb58d..5db853b92d3b 100644 |
| --- a/drivers/iommu/ipmmu-vmsa.c |
| +++ b/drivers/iommu/ipmmu-vmsa.c |
| @@ -41,12 +41,14 @@ |
| |
| struct ipmmu_features { |
| bool use_ns_alias_offset; |
| + bool has_cache_leaf_nodes; |
| }; |
| |
| struct ipmmu_vmsa_device { |
| struct device *dev; |
| void __iomem *base; |
| struct iommu_device iommu; |
| + struct ipmmu_vmsa_device *root; |
| const struct ipmmu_features *features; |
| unsigned int num_utlbs; |
| spinlock_t lock; /* Protects ctx and domains[] */ |
| @@ -198,6 +200,36 @@ static struct ipmmu_vmsa_device *to_ipmmu(struct device *dev) |
| #define IMUASID_ASID0_MASK (0xff << 0) |
| #define IMUASID_ASID0_SHIFT 0 |
| |
| +/* ----------------------------------------------------------------------------- |
| + * Root device handling |
| + */ |
| + |
| +static struct platform_driver ipmmu_driver; |
| + |
| +static bool ipmmu_is_root(struct ipmmu_vmsa_device *mmu) |
| +{ |
| + return mmu->root == mmu; |
| +} |
| + |
| +static int __ipmmu_check_device(struct device *dev, void *data) |
| +{ |
| + struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev); |
| + struct ipmmu_vmsa_device **rootp = data; |
| + |
| + if (ipmmu_is_root(mmu)) |
| + *rootp = mmu; |
| + |
| + return 0; |
| +} |
| + |
| +static struct ipmmu_vmsa_device *ipmmu_find_root(void) |
| +{ |
| + struct ipmmu_vmsa_device *root = NULL; |
| + |
| + return driver_for_each_device(&ipmmu_driver.driver, NULL, &root, |
| + __ipmmu_check_device) == 0 ? root : NULL; |
| +} |
| + |
| /* ----------------------------------------------------------------------------- |
| * Read/Write Access |
| */ |
| @@ -215,13 +247,15 @@ static void ipmmu_write(struct ipmmu_vmsa_device *mmu, unsigned int offset, |
| |
| static u32 ipmmu_ctx_read(struct ipmmu_vmsa_domain *domain, unsigned int reg) |
| { |
| - return ipmmu_read(domain->mmu, domain->context_id * IM_CTX_SIZE + reg); |
| + return ipmmu_read(domain->mmu->root, |
| + domain->context_id * IM_CTX_SIZE + reg); |
| } |
| |
| static void ipmmu_ctx_write(struct ipmmu_vmsa_domain *domain, unsigned int reg, |
| u32 data) |
| { |
| - ipmmu_write(domain->mmu, domain->context_id * IM_CTX_SIZE + reg, data); |
| + ipmmu_write(domain->mmu->root, |
| + domain->context_id * IM_CTX_SIZE + reg, data); |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| @@ -369,12 +403,12 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) |
| * TODO: Add support for coherent walk through CCI with DVM and remove |
| * cache handling. For now, delegate it to the io-pgtable code. |
| */ |
| - domain->cfg.iommu_dev = domain->mmu->dev; |
| + domain->cfg.iommu_dev = domain->mmu->root->dev; |
| |
| /* |
| * Find an unused context. |
| */ |
| - ret = ipmmu_domain_allocate_context(domain->mmu, domain); |
| + ret = ipmmu_domain_allocate_context(domain->mmu->root, domain); |
| if (ret == IPMMU_CTX_MAX) |
| return -EBUSY; |
| |
| @@ -383,7 +417,8 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) |
| domain->iop = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &domain->cfg, |
| domain); |
| if (!domain->iop) { |
| - ipmmu_domain_free_context(domain->mmu, domain->context_id); |
| + ipmmu_domain_free_context(domain->mmu->root, |
| + domain->context_id); |
| return -EINVAL; |
| } |
| |
| @@ -437,7 +472,7 @@ static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain) |
| */ |
| ipmmu_ctx_write(domain, IMCTR, IMCTR_FLUSH); |
| ipmmu_tlb_sync(domain); |
| - ipmmu_domain_free_context(domain->mmu, domain->context_id); |
| + ipmmu_domain_free_context(domain->mmu->root, domain->context_id); |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| @@ -824,6 +859,7 @@ static void ipmmu_device_reset(struct ipmmu_vmsa_device *mmu) |
| |
| static const struct ipmmu_features ipmmu_features_default = { |
| .use_ns_alias_offset = true, |
| + .has_cache_leaf_nodes = false, |
| }; |
| |
| static const struct of_device_id ipmmu_of_ids[] = { |
| @@ -878,19 +914,39 @@ static int ipmmu_probe(struct platform_device *pdev) |
| mmu->base += IM_NS_ALIAS_OFFSET; |
| |
| irq = platform_get_irq(pdev, 0); |
| - if (irq < 0) { |
| - dev_err(&pdev->dev, "no IRQ found\n"); |
| - return irq; |
| - } |
| |
| - ret = devm_request_irq(&pdev->dev, irq, ipmmu_irq, 0, |
| - dev_name(&pdev->dev), mmu); |
| - if (ret < 0) { |
| - dev_err(&pdev->dev, "failed to request IRQ %d\n", irq); |
| - return ret; |
| - } |
| + /* |
| + * Determine if this IPMMU instance is a root device by checking for |
| + * the lack of has_cache_leaf_nodes flag or renesas,ipmmu-main property. |
| + */ |
| + if (!mmu->features->has_cache_leaf_nodes || |
| + !of_find_property(pdev->dev.of_node, "renesas,ipmmu-main", NULL)) |
| + mmu->root = mmu; |
| + else |
| + mmu->root = ipmmu_find_root(); |
| |
| - ipmmu_device_reset(mmu); |
| + /* |
| + * Wait until the root device has been registered for sure. |
| + */ |
| + if (!mmu->root) |
| + return -EPROBE_DEFER; |
| + |
| + /* Root devices have mandatory IRQs */ |
| + if (ipmmu_is_root(mmu)) { |
| + if (irq < 0) { |
| + dev_err(&pdev->dev, "no IRQ found\n"); |
| + return irq; |
| + } |
| + |
| + ret = devm_request_irq(&pdev->dev, irq, ipmmu_irq, 0, |
| + dev_name(&pdev->dev), mmu); |
| + if (ret < 0) { |
| + dev_err(&pdev->dev, "failed to request IRQ %d\n", irq); |
| + return ret; |
| + } |
| + |
| + ipmmu_device_reset(mmu); |
| + } |
| |
| ret = iommu_device_sysfs_add(&mmu->iommu, &pdev->dev, NULL, |
| dev_name(&pdev->dev)); |
| -- |
| 2.19.0 |
| |