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