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);
 
 	}