msm: iommu: Restructure iommu domains
Each domain iova space is now partitioned via an array
of memory pools. This makes it easier to understand the
partitioning of the iova space for a given domain.
Change-Id: If83d03a4a97eb5db529cd80f1f956a3602cc4ce4
Signed-off-by: Laura Abbott <lauraa@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/iommu_domains.h b/arch/arm/mach-msm/include/mach/iommu_domains.h
index 8ae3b72..28e98de 100644
--- a/arch/arm/mach-msm/include/mach/iommu_domains.h
+++ b/arch/arm/mach-msm/include/mach/iommu_domains.h
@@ -13,26 +13,67 @@
#ifndef _ARCH_IOMMU_DOMAINS_H
#define _ARCH_IOMMU_DOMAINS_H
-/*
- * Nothing in this file is to be used outside of the iommu wrappers.
- * Do NOT try and use anything here in a driver. Doing so is incorrect.
- */
+enum {
+ GLOBAL_DOMAIN,
+ MAX_DOMAINS
+};
-/*
- * These subsytem ids are NOT for public use. Please check the iommu
- * wrapper header for the properly abstracted id to pass in.
- */
+enum {
+ VIDEO_FIRMWARE_POOL,
+ LOW_256MB_POOL,
+ HIGH_POOL,
+};
+
#if defined(CONFIG_MSM_IOMMU)
-extern struct iommu_domain *msm_subsystem_get_domain(int subsys_id);
-extern struct mem_pool *msm_subsystem_get_pool(int subsys_id);
+extern struct iommu_domain *msm_get_iommu_domain(int domain_num);
+
+extern unsigned long msm_allocate_iova_address(unsigned int iommu_domain,
+ unsigned int partition_no,
+ unsigned long size,
+ unsigned long align);
+
+extern void msm_free_iova_address(unsigned long iova,
+ unsigned int iommu_domain,
+ unsigned int partition_no,
+ unsigned long size);
+
+extern unsigned long msm_subsystem_get_domain_no(int subsys_id);
+
+extern unsigned long msm_subsystem_get_partition_no(int subsys_id);
+
+extern int msm_use_iommu(void);
#else
static inline struct iommu_domain
- *msm_subsystem_get_domain(int subsys_id) { return NULL; }
+ *msm_get_iommu_domain(int subsys_id) { return NULL; }
-static inline struct mem_pool
- *msm_subsystem_get_pool(int subsys_id) { return NULL; }
+
+
+static inline unsigned long msm_allocate_iova_address(unsigned int iommu_domain,
+ unsigned int partition_no,
+ unsigned long size,
+ unsigned long align) { return 0; }
+
+static inline void msm_free_iova_address(unsigned long iova,
+ unsigned int iommu_domain,
+ unsigned int partition_no,
+ unsigned long size) { return; }
+
+static inline unsigned long msm_subsystem_get_domain_no(int subsys_id)
+{
+ return 0xFFFFFFFF;
+}
+
+static inline unsigned long msm_subsystem_get_partition_no(int subsys_id)
+{
+ return 0xFFFFFFFF;
+}
+
+static inline int msm_use_iommu(void)
+{
+ return 0;
+}
#endif
#endif
diff --git a/arch/arm/mach-msm/iommu_domains.c b/arch/arm/mach-msm/iommu_domains.c
index 290ba33..e849cdb 100644
--- a/arch/arm/mach-msm/iommu_domains.c
+++ b/arch/arm/mach-msm/iommu_domains.c
@@ -18,24 +18,23 @@
#include <linux/init.h>
#include <mach/iommu.h>
#include <mach/iommu_domains.h>
+#include <mach/socinfo.h>
struct msm_iommu_domain {
- int domain_idx;
- int iova_pool_idx;
+ /* iommu domain to map in */
+ struct iommu_domain *domain;
+ /* total number of allocations from this domain */
+ atomic_t allocation_cnt;
+ /* number of iova pools */
+ int npools;
+ /*
+ * array of gen_pools for allocating iovas.
+ * behavior is undefined if these overlap
+ */
+ struct mem_pool *iova_pools;
+
};
-enum {
- GLOBAL_DOMAIN,
- VIDEO_DOMAIN,
- EMPTY_DOMAIN,
- MAX_DOMAINS
-};
-
-enum {
- GLOBAL_MEMORY_POOL,
- VIDEO_FIRMWARE_POOL,
- VIDEO_ALLOC_POOL,
-};
struct {
char *name;
@@ -114,114 +113,175 @@
/* Video */
{
.name = "vcodec_a_mm1",
- .domain = VIDEO_DOMAIN,
+ .domain = GLOBAL_DOMAIN,
},
/* Video */
{
.name = "vcodec_b_mm2",
- .domain = VIDEO_DOMAIN,
+ .domain = GLOBAL_DOMAIN,
},
/* Video */
{
.name = "vcodec_a_stream",
- .domain = VIDEO_DOMAIN,
+ .domain = GLOBAL_DOMAIN,
},
};
-static struct iommu_domain *msm_iommu_domains[MAX_DOMAINS];
-
-static struct mem_pool msm_iommu_iova_pools[] = {
- [GLOBAL_MEMORY_POOL] = {
- .paddr = SZ_4K,
- .size = SZ_2G - SZ_4K,
- },
+static struct mem_pool global_pools[] = {
+ [VIDEO_FIRMWARE_POOL] =
+ /* Low addresses, intended for video firmware */
+ {
+ .paddr = SZ_128K,
+ .size = SZ_16M - SZ_128K,
+ },
+ [LOW_256MB_POOL] =
/*
- * The video hardware has several constraints:
- * 1) The start address for firmware must be 128K aligned
- * 2) The video firmware must exist at a lower address than
- * all other video allocations
- * 3) Video allocations cannot be more than 256MB away from the
- * firmware
- *
- * Splitting the video pools makes sure that firmware will
- * always be lower than regular allocations and the maximum
- * size of 256MB will be enforced.
+ * Video can only access first 256MB of memory
+ * dedicated pool for such allocations
*/
- [VIDEO_FIRMWARE_POOL] = {
- .paddr = SZ_128K,
- .size = SZ_16M - SZ_128K,
- },
- [VIDEO_ALLOC_POOL] = {
- .paddr = SZ_16M,
- .size = SZ_256M - SZ_16M - SZ_128K,
+ {
+ .paddr = SZ_16M,
+ .size = SZ_256M - SZ_16M,
+ },
+ [HIGH_POOL] =
+ /* Remaining address space up to 2G */
+ {
+ .paddr = SZ_256M,
+ .size = SZ_2G - SZ_256M,
+ }
+};
+
+
+static struct msm_iommu_domain msm_iommu_domains[] = {
+ [GLOBAL_DOMAIN] = {
+ .iova_pools = global_pools,
+ .npools = ARRAY_SIZE(global_pools),
}
};
-static struct msm_iommu_domain msm_iommu_subsystems[] = {
- [MSM_SUBSYSTEM_VIDEO] = {
- .domain_idx = VIDEO_DOMAIN,
- .iova_pool_idx = VIDEO_ALLOC_POOL,
- },
- [MSM_SUBSYSTEM_VIDEO_FWARE] = {
- .domain_idx = VIDEO_DOMAIN,
- .iova_pool_idx = VIDEO_FIRMWARE_POOL,
- },
- [MSM_SUBSYSTEM_CAMERA] = {
- .domain_idx = GLOBAL_DOMAIN,
- .iova_pool_idx = GLOBAL_MEMORY_POOL,
- },
- [MSM_SUBSYSTEM_DISPLAY] = {
- .domain_idx = GLOBAL_DOMAIN,
- .iova_pool_idx = GLOBAL_MEMORY_POOL,
- },
- [MSM_SUBSYSTEM_ROTATOR] = {
- .domain_idx = GLOBAL_DOMAIN,
- .iova_pool_idx = GLOBAL_MEMORY_POOL,
- },
-};
-
-struct iommu_domain *msm_subsystem_get_domain(int subsys_id)
+struct iommu_domain *msm_get_iommu_domain(int domain_num)
{
- int id = msm_iommu_subsystems[subsys_id].domain_idx;
-
- return msm_iommu_domains[id];
+ if (domain_num >= 0 && domain_num < MAX_DOMAINS)
+ return msm_iommu_domains[domain_num].domain;
+ else
+ return NULL;
}
-struct mem_pool *msm_subsystem_get_pool(int subsys_id)
+unsigned long msm_subsystem_get_domain_no(int subsys_id)
{
- int id = msm_iommu_subsystems[subsys_id].iova_pool_idx;
+ return GLOBAL_DOMAIN;
+}
- return &msm_iommu_iova_pools[id];
+unsigned long msm_subsystem_get_partition_no(int subsys_id)
+{
+ switch (subsys_id) {
+ case MSM_SUBSYSTEM_VIDEO_FWARE:
+ return VIDEO_FIRMWARE_POOL;
+ case MSM_SUBSYSTEM_VIDEO:
+ return LOW_256MB_POOL;
+ case MSM_SUBSYSTEM_CAMERA:
+ case MSM_SUBSYSTEM_DISPLAY:
+ case MSM_SUBSYSTEM_ROTATOR:
+ return HIGH_POOL;
+ default:
+ return 0xFFFFFFFF;
+ }
+}
+
+unsigned long msm_allocate_iova_address(unsigned int iommu_domain,
+ unsigned int partition_no,
+ unsigned long size,
+ unsigned long align)
+{
+ struct mem_pool *pool;
+ unsigned long iova;
+
+ if (iommu_domain >= MAX_DOMAINS)
+ return 0;
+
+ if (partition_no >= msm_iommu_domains[iommu_domain].npools)
+ return 0;
+
+ pool = &msm_iommu_domains[iommu_domain].iova_pools[partition_no];
+
+ if (!pool->gpool)
+ return 0;
+
+ iova = gen_pool_alloc_aligned(pool->gpool, size, ilog2(align));
+ if (iova)
+ pool->free -= size;
+
+ return iova;
+}
+
+void msm_free_iova_address(unsigned long iova,
+ unsigned int iommu_domain,
+ unsigned int partition_no,
+ unsigned long size)
+{
+ struct mem_pool *pool;
+
+ if (iommu_domain >= MAX_DOMAINS) {
+ WARN(1, "Invalid domain %d\n", iommu_domain);
+ return;
+ }
+
+ if (partition_no >= msm_iommu_domains[iommu_domain].npools) {
+ WARN(1, "Invalid partition %d for domain %d\n",
+ partition_no, iommu_domain);
+ return;
+ }
+
+ pool = &msm_iommu_domains[iommu_domain].iova_pools[partition_no];
+
+ if (!pool)
+ return;
+
+ pool->free += size;
+ gen_pool_free(pool->gpool, iova, size);
+}
+
+int msm_use_iommu()
+{
+ /*
+ * For now, just detect if the iommu is attached.
+ */
+ return iommu_found();
}
static int __init msm_subsystem_iommu_init(void)
{
- int i;
+ int i, j;
- for (i = 0; i < (ARRAY_SIZE(msm_iommu_domains) - 1); i++)
- msm_iommu_domains[i] = iommu_domain_alloc(0);
-
- for (i = 0; i < ARRAY_SIZE(msm_iommu_iova_pools); i++) {
- mutex_init(&msm_iommu_iova_pools[i].pool_mutex);
- msm_iommu_iova_pools[i].gpool = gen_pool_create(PAGE_SHIFT, -1);
-
- if (!msm_iommu_iova_pools[i].gpool) {
- pr_err("%s: could not allocate iova pool. iommu"
- " programming will not work with iova space"
- " %d\n", __func__, i);
+ for (i = 0; i < ARRAY_SIZE(msm_iommu_domains); i++) {
+ msm_iommu_domains[i].domain = iommu_domain_alloc(0);
+ if (!msm_iommu_domains[i].domain)
continue;
- }
- if (gen_pool_add(msm_iommu_iova_pools[i].gpool,
- msm_iommu_iova_pools[i].paddr,
- msm_iommu_iova_pools[i].size,
- -1)) {
- pr_err("%s: could not add memory to iova pool. iommu"
- " programming will not work with iova space"
- " %d\n", __func__, i);
- gen_pool_destroy(msm_iommu_iova_pools[i].gpool);
- msm_iommu_iova_pools[i].gpool = NULL;
- continue;
+ for (j = 0; j < msm_iommu_domains[i].npools; j++) {
+ struct mem_pool *pool = &msm_iommu_domains[i].
+ iova_pools[j];
+ mutex_init(&pool->pool_mutex);
+ pool->gpool = gen_pool_create(PAGE_SHIFT, -1);
+
+ if (!pool->gpool) {
+ pr_err("%s: domain %d: could not allocate iova"
+ " pool. iommu programming will not work"
+ " with iova space %d\n", __func__,
+ i, j);
+ continue;
+ }
+
+ if (gen_pool_add(pool->gpool, pool->paddr, pool->size,
+ -1)) {
+ pr_err("%s: domain %d: could not add memory to"
+ " iova pool. iommu programming will not"
+ " work with iova space %d\n", __func__,
+ i, j);
+ gen_pool_destroy(pool->gpool);
+ pool->gpool = NULL;
+ continue;
+ }
}
}
@@ -235,15 +295,15 @@
domain_idx = msm_iommu_ctx_names[i].domain;
- if (!msm_iommu_domains[domain_idx])
+ if (!msm_iommu_domains[domain_idx].domain)
continue;
- if (iommu_attach_device(msm_iommu_domains[domain_idx], ctx)) {
- pr_err("%s: could not attach domain %d to context %s."
+ if (iommu_attach_device(msm_iommu_domains[domain_idx].domain,
+ ctx)) {
+ WARN(1, "%s: could not attach domain %d to context %s."
" iommu programming will not occur.\n",
__func__, domain_idx,
msm_iommu_ctx_names[i].name);
- msm_iommu_subsystems[i].domain_idx = EMPTY_DOMAIN;
continue;
}
}
diff --git a/arch/arm/mach-msm/subsystem_map.c b/arch/arm/mach-msm/subsystem_map.c
index 41d8072..11fe26c 100644
--- a/arch/arm/mach-msm/subsystem_map.c
+++ b/arch/arm/mach-msm/subsystem_map.c
@@ -208,58 +208,18 @@
return 0;
}
-static unsigned long allocate_iova_address(unsigned long size,
- int subsys_id,
- unsigned long align)
-{
- struct mem_pool *pool = msm_subsystem_get_pool(subsys_id);
- unsigned long iova;
-
- iova = gen_pool_alloc_aligned(pool->gpool, size, ilog2(align));
- if (iova)
- pool->free -= size;
-
- return iova;
-}
-
-static void free_iova_address(unsigned long iova,
- unsigned long size,
- int subsys_id)
-{
- struct mem_pool *pool = msm_subsystem_get_pool(subsys_id);
-
- pool->free += size;
- gen_pool_free(pool->gpool, iova, size);
-}
-
-static int subsys_validate(int subsys_id)
-{
- struct mem_pool *pool;
- struct iommu_domain *subsys_domain;
-
- if (!msm_subsystem_check_id(subsys_id)) {
- WARN(1, "subsystem id is not valid. Caller should check this.");
- return 0;
- }
-
- pool = msm_subsystem_get_pool(subsys_id);
- subsys_domain = msm_subsystem_get_domain(subsys_id);
-
- return subsys_domain && pool && pool->gpool;
-}
-
phys_addr_t msm_subsystem_check_iova_mapping(int subsys_id, unsigned long iova)
{
struct iommu_domain *subsys_domain;
- if (!subsys_validate(subsys_id))
+ if (!msm_use_iommu())
/*
- * If the subsystem is not valid, assume a phys = iova
- * mapping. Just return the iova in this case.
+ * If there is no iommu, Just return the iova in this case.
*/
return iova;
- subsys_domain = msm_subsystem_get_domain(subsys_id);
+ subsys_domain = msm_get_iommu_domain(msm_subsystem_get_domain_no
+ (subsys_id));
return iommu_iova_to_phys(subsys_domain, iova);
}
@@ -355,15 +315,29 @@
min_align = flags & ~(SZ_4K - 1);
for (i = 0; i < nsubsys; i++) {
- if (!subsys_validate(subsys_ids[i])) {
+ unsigned int domain_no, partition_no;
+
+ if (!msm_use_iommu()) {
buf->iova[i] = phys;
continue;
}
- d = msm_subsystem_get_domain(subsys_ids[i]);
+ d = msm_get_iommu_domain(
+ msm_subsystem_get_domain_no(subsys_ids[i]));
- iova_start = allocate_iova_address(length,
- subsys_ids[i],
+ if (!d) {
+ pr_err("%s: could not get domain for subsystem"
+ " %d\n", __func__, subsys_ids[i]);
+ continue;
+ }
+
+ domain_no = msm_subsystem_get_domain_no(subsys_ids[i]);
+ partition_no = msm_subsystem_get_partition_no(
+ subsys_ids[i]);
+
+ iova_start = msm_allocate_iova_address(domain_no,
+ partition_no,
+ length,
max(min_align, SZ_4K));
if (!iova_start) {
@@ -410,19 +384,26 @@
iommu_unmap(d, temp_va, get_order(SZ_4K));
outdomain:
if (flags & MSM_SUBSYSTEM_MAP_IOVA) {
+ /* Unmap the rest of the current domain, i */
for (j -= SZ_4K, temp_va -= SZ_4K;
j > 0; temp_va -= SZ_4K, j -= SZ_4K)
iommu_unmap(d, temp_va, get_order(SZ_4K));
+ /* Unmap all the other domains */
for (i--; i >= 0; i--) {
- if (!subsys_validate(subsys_ids[i]))
+ unsigned int domain_no, partition_no;
+ if (!msm_use_iommu())
continue;
+ domain_no = msm_subsystem_get_domain_no(subsys_ids[i]);
+ partition_no = msm_subsystem_get_partition_no(
+ subsys_ids[i]);
temp_va = buf->iova[i];
for (j = length; j > 0; j -= SZ_4K,
temp_va += SZ_4K)
iommu_unmap(d, temp_va, get_order(SZ_4K));
- free_iova_address(buf->iova[i], length, subsys_ids[i]);
+ msm_free_iova_address(buf->iova[i], domain_no,
+ partition_no, length);
}
kfree(buf->iova);
@@ -464,25 +445,32 @@
}
if (buf->iova) {
- for (i = 0; i < node->nsubsys; i++) {
- struct iommu_domain *subsys_domain;
+ if (msm_use_iommu())
+ for (i = 0; i < node->nsubsys; i++) {
+ struct iommu_domain *subsys_domain;
+ unsigned int domain_no, partition_no;
- if (!subsys_validate(node->subsystems[i]))
- continue;
+ subsys_domain = msm_get_iommu_domain(
+ msm_subsystem_get_domain_no(
+ node->subsystems[i]));
- subsys_domain = msm_subsystem_get_domain(
+ domain_no = msm_subsystem_get_domain_no(
node->subsystems[i]);
- temp_va = buf->iova[i];
- for (j = node->length; j > 0; j -= SZ_4K,
- temp_va += SZ_4K) {
- ret = iommu_unmap(subsys_domain, temp_va,
+ partition_no = msm_subsystem_get_partition_no(
+ node->subsystems[i]);
+
+ temp_va = buf->iova[i];
+ for (j = node->length; j > 0; j -= SZ_4K,
+ temp_va += SZ_4K) {
+ ret = iommu_unmap(subsys_domain,
+ temp_va,
get_order(SZ_4K));
- WARN(ret, "iommu_unmap returned a non-zero"
- " value.\n");
+ WARN(ret, "iommu_unmap returned a "
+ " non-zero value.\n");
+ }
+ msm_free_iova_address(buf->iova[i], domain_no,
+ partition_no, node->length);
}
- free_iova_address(buf->iova[i], node->length,
- node->subsystems[i]);
- }
kfree(buf->iova);
}