msm: ADSPRPC: Add support for SMMU enabled targets

Map / unmap the buffers being passed to the remote processor based
on the SMMU presence.

Change-Id: I79906b3cc382616cd381d5161d504aca8b04f667
Signed-off-by: Mitchel Humpherys <mitchelh@codeaurora.org>
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 51578e0..719a06f 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -26,10 +26,13 @@
 #include <linux/msm_ion.h>
 #include <mach/msm_smd.h>
 #include <mach/ion.h>
+#include <mach/iommu_domains.h>
 #include <linux/scatterlist.h>
 #include <linux/fs.h>
 #include <linux/uaccess.h>
 #include <linux/device.h>
+#include <linux/of.h>
+#include <linux/iommu.h>
 
 #ifndef ION_ADSPRPC_HEAP_ID
 #define ION_ADSPRPC_HEAP_ID ION_AUDIO_HEAP_ID
@@ -84,8 +87,9 @@
 {
 	struct vm_area_struct *vma;
 	uint32_t start = buf_page_start(addr);
+	uint32_t end = buf_page_start((void *)((uint32_t)addr + sz - 1));
 	uint32_t len = nr_pages << PAGE_SHIFT;
-	unsigned long pfn;
+	unsigned long pfn, pfnend;
 	int n = -1, err = 0;
 
 	VERIFY(err, 0 != access_ok(access ? VERIFY_WRITE : VERIFY_READ,
@@ -102,6 +106,12 @@
 	VERIFY(err, 0 == follow_pfn(vma, start, &pfn));
 	if (err)
 		goto bail;
+	VERIFY(err, 0 == follow_pfn(vma, end, &pfnend));
+	if (err)
+		goto bail;
+	VERIFY(err, (pfn + nr_pages - 1) == pfnend);
+	if (err)
+		goto bail;
 	VERIFY(err, nr_elems > 0);
 	if (err)
 		goto bail;
@@ -124,6 +134,13 @@
 	int last;
 };
 
+struct fastrpc_smmu {
+	struct iommu_group *group;
+	struct iommu_domain *domain;
+	int domain_id;
+	bool enabled;
+};
+
 struct fastrpc_apps {
 	smd_channel_t *chan;
 	struct smq_context_list clst;
@@ -132,6 +149,7 @@
 	struct cdev cdev;
 	struct class *class;
 	struct device *dev;
+	struct fastrpc_smmu smmu;
 	dev_t dev_no;
 	spinlock_t wrlock;
 	spinlock_t hlock;
@@ -172,7 +190,12 @@
 {
 	struct fastrpc_apps *me = &gfa;
 
-	if (buf->handle) {
+	if (!IS_ERR_OR_NULL(buf->handle)) {
+		if (me->smmu.enabled && buf->phys) {
+			ion_unmap_iommu(me->iclient, buf->handle,
+					me->smmu.domain_id, 0);
+			buf->phys = 0;
+		}
 		if (buf->virt) {
 			ion_unmap_kernel(me->iclient, buf->handle);
 			buf->virt = 0;
@@ -185,7 +208,7 @@
 static void free_map(struct fastrpc_mmap *map)
 {
 	struct fastrpc_apps *me = &gfa;
-	if (map->handle) {
+	if (!IS_ERR_OR_NULL(map->handle)) {
 		if (map->virt) {
 			ion_unmap_kernel(me->iclient, map->handle);
 			map->virt = 0;
@@ -197,26 +220,39 @@
 
 static int alloc_mem(struct fastrpc_buf *buf)
 {
+	struct fastrpc_apps *me = &gfa;
 	struct ion_client *clnt = gfa.iclient;
 	struct sg_table *sg;
 	int err = 0;
+	unsigned int heap;
+	unsigned long len;
 	buf->handle = 0;
 	buf->virt = 0;
-	buf->handle = ion_alloc(clnt, buf->size, SZ_4K,
-				ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+	heap = me->smmu.enabled ? ION_HEAP(ION_IOMMU_HEAP_ID) :
+		ION_HEAP(ION_ADSP_HEAP_ID) | ION_HEAP(ION_AUDIO_HEAP_ID);
+	buf->handle = ion_alloc(clnt, buf->size, SZ_4K, heap, 0);
 	VERIFY(err, 0 == IS_ERR_OR_NULL(buf->handle));
 	if (err)
 		goto bail;
 	VERIFY(err, 0 != (buf->virt = ion_map_kernel(clnt, buf->handle)));
 	if (err)
 		goto bail;
-	VERIFY(err, 0 != (sg = ion_sg_table(clnt, buf->handle)));
-	if (err)
-		goto bail;
-	VERIFY(err, 1 == sg->nents);
-	if (err)
-		goto bail;
-	buf->phys = sg_dma_address(sg->sgl);
+	if (me->smmu.enabled) {
+		len = buf->size;
+		VERIFY(err, 0 == ion_map_iommu(clnt, buf->handle,
+					me->smmu.domain_id, 0, SZ_4K, 0,
+					&buf->phys, &len, 0, 0));
+		if (err)
+			goto bail;
+	} else {
+		VERIFY(err, 0 != (sg = ion_sg_table(clnt, buf->handle)));
+		if (err)
+			goto bail;
+		VERIFY(err, 1 == sg->nents);
+		if (err)
+			goto bail;
+		buf->phys = sg_dma_address(sg->sgl);
+	}
  bail:
 	if (err && !IS_ERR_OR_NULL(buf->handle))
 		free_mem(buf);
@@ -572,6 +608,8 @@
 {
 	int err = 0;
 	struct fastrpc_apps *me = &gfa;
+	struct device_node *node;
+	bool enabled = 0;
 
 	if (me->chan == 0) {
 		int i;
@@ -588,6 +626,22 @@
 		VERIFY(err, 0 == IS_ERR_OR_NULL(me->iclient));
 		if (err)
 			goto ion_bail;
+		node = of_find_compatible_node(NULL, NULL,
+						"qcom,msm-audio-ion");
+		if (node)
+			enabled = of_property_read_bool(node,
+						"qcom,smmu-enabled");
+		if (enabled)
+			me->smmu.group = iommu_group_find("lpass_audio");
+		if (me->smmu.group)
+			me->smmu.domain = iommu_group_get_iommudata(
+							me->smmu.group);
+		if (!IS_ERR_OR_NULL(me->smmu.domain)) {
+			me->smmu.domain_id = msm_find_domain_no(
+							me->smmu.domain);
+			if (me->smmu.domain_id >= 0)
+				me->smmu.enabled = enabled;
+		}
 		VERIFY(err, 0 == smd_named_open_on_edge(FASTRPC_SMD_GUID,
 						SMD_APPS_QDSP, &me->chan,
 						me, smd_event_handler));
@@ -704,6 +758,12 @@
 
 	sc = invoke->sc;
 	obuf.handle = 0;
+	if (me->smmu.enabled) {
+		VERIFY(err, 0 == iommu_attach_group(me->smmu.domain,
+							me->smmu.group));
+		if (err)
+			return err;
+	}
 	if (REMOTE_SCALARS_LENGTH(sc)) {
 		VERIFY(err, 0 == get_dev(me, &dev));
 		if (err)
@@ -743,6 +803,8 @@
 	}
 	context_free(ctx);
 
+	if (me->smmu.enabled)
+		iommu_detach_group(me->smmu.domain, me->smmu.group);
 	for (i = 0, b = abufs; i < nbufs; ++i, ++b)
 		free_mem(b);
 
@@ -1093,6 +1155,7 @@
 	struct fastrpc_apps *me = &gfa;
 	int err = 0;
 
+	memset(me, 0, sizeof(*me));
 	VERIFY(err, 0 == fastrpc_init());
 	if (err)
 		goto fastrpc_bail;