summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMagnus Damm <damm+renesas@opensource.se>2017-10-16 21:29:46 +0900
committerAlex Williamson <alex.williamson@redhat.com>2017-11-06 10:29:39 -0700
commit5fd163416fb7b6592521c39f867d5ae6360e7924 (patch)
treeca16476ac85e91b195ecdbf2547c3849db8515ac
parentfd5140e29a59e04a6c3e8cc56536bda3e60bbf49 (diff)
downloadlinux-5fd163416fb7b6592521c39f867d5ae6360e7924.tar.bz2
iommu/ipmmu-vmsa: Enable multi context support
Add support for up to 8 contexts. Each context is mapped to one domain. One domain is assigned one or more slave devices. Contexts are allocated dynamically and slave devices are grouped together based on which IPMMU device they are connected to. This makes slave devices tied to the same IPMMU device share the same IOVA space. Signed-off-by: Magnus Damm <damm+renesas@opensource.se> Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
-rw-r--r--drivers/iommu/ipmmu-vmsa.c30
1 files changed, 22 insertions, 8 deletions
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index 5db853b92d3b..c70efd80f740 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -37,11 +37,12 @@
#include "io-pgtable.h"
-#define IPMMU_CTX_MAX 1
+#define IPMMU_CTX_MAX 8
struct ipmmu_features {
bool use_ns_alias_offset;
bool has_cache_leaf_nodes;
+ unsigned int number_of_contexts;
};
struct ipmmu_vmsa_device {
@@ -51,6 +52,7 @@ struct ipmmu_vmsa_device {
struct ipmmu_vmsa_device *root;
const struct ipmmu_features *features;
unsigned int num_utlbs;
+ unsigned int num_ctx;
spinlock_t lock; /* Protects ctx and domains[] */
DECLARE_BITMAP(ctx, IPMMU_CTX_MAX);
struct ipmmu_vmsa_domain *domains[IPMMU_CTX_MAX];
@@ -352,11 +354,12 @@ static int ipmmu_domain_allocate_context(struct ipmmu_vmsa_device *mmu,
spin_lock_irqsave(&mmu->lock, flags);
- ret = find_first_zero_bit(mmu->ctx, IPMMU_CTX_MAX);
- if (ret != IPMMU_CTX_MAX) {
+ ret = find_first_zero_bit(mmu->ctx, mmu->num_ctx);
+ if (ret != mmu->num_ctx) {
mmu->domains[ret] = domain;
set_bit(ret, mmu->ctx);
- }
+ } else
+ ret = -EBUSY;
spin_unlock_irqrestore(&mmu->lock, flags);
@@ -409,8 +412,8 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
* Find an unused context.
*/
ret = ipmmu_domain_allocate_context(domain->mmu->root, domain);
- if (ret == IPMMU_CTX_MAX)
- return -EBUSY;
+ if (ret < 0)
+ return ret;
domain->context_id = ret;
@@ -539,7 +542,7 @@ static irqreturn_t ipmmu_irq(int irq, void *dev)
/*
* Check interrupts for all active contexts.
*/
- for (i = 0; i < IPMMU_CTX_MAX; i++) {
+ for (i = 0; i < mmu->num_ctx; i++) {
if (!mmu->domains[i])
continue;
if (ipmmu_domain_irq(mmu->domains[i]) == IRQ_HANDLED)
@@ -624,6 +627,13 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain,
/* The domain hasn't been used yet, initialize it. */
domain->mmu = mmu;
ret = ipmmu_domain_init_context(domain);
+ if (ret < 0) {
+ dev_err(dev, "Unable to initialize IPMMU context\n");
+ domain->mmu = NULL;
+ } else {
+ dev_info(dev, "Using IPMMU context %u\n",
+ domain->context_id);
+ }
} else if (domain->mmu != mmu) {
/*
* Something is wrong, we can't attach two devices using
@@ -853,13 +863,14 @@ static void ipmmu_device_reset(struct ipmmu_vmsa_device *mmu)
unsigned int i;
/* Disable all contexts. */
- for (i = 0; i < 4; ++i)
+ for (i = 0; i < mmu->num_ctx; ++i)
ipmmu_write(mmu, i * IM_CTX_SIZE + IMCTR, 0);
}
static const struct ipmmu_features ipmmu_features_default = {
.use_ns_alias_offset = true,
.has_cache_leaf_nodes = false,
+ .number_of_contexts = 1, /* software only tested with one context */
};
static const struct of_device_id ipmmu_of_ids[] = {
@@ -913,6 +924,9 @@ static int ipmmu_probe(struct platform_device *pdev)
if (mmu->features->use_ns_alias_offset)
mmu->base += IM_NS_ALIAS_OFFSET;
+ mmu->num_ctx = min_t(unsigned int, IPMMU_CTX_MAX,
+ mmu->features->number_of_contexts);
+
irq = platform_get_irq(pdev, 0);
/*