fmem: Add support for reusable virtual mappings

Instead of just keeping the entire heap mapped all the
time, reserve the virtual area and map as needed.

Change-Id: Id9a253541f1462379f3f81611aec92cd760e71c8
Signed-off-by: Laura Abbott <lauraa@codeaurora.org>
diff --git a/drivers/staging/qcache/fmem.c b/drivers/staging/qcache/fmem.c
index 9ab21da..489d27a 100644
--- a/drivers/staging/qcache/fmem.c
+++ b/drivers/staging/qcache/fmem.c
@@ -17,11 +17,36 @@
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include "tmem.h"
+#include <asm/mach/map.h>
 
 struct fmem_data fmem_data;
 enum fmem_state fmem_state;
 static spinlock_t fmem_state_lock;
 
+void *fmem_map_virtual_area(int cacheability)
+{
+	unsigned long addr;
+	const struct mem_type *type;
+	int ret;
+
+	addr = (unsigned long) fmem_data.area->addr;
+	type = get_mem_type(cacheability);
+	ret = ioremap_page_range(addr, addr + fmem_data.size,
+			fmem_data.phys, __pgprot(type->prot_pte));
+	if (ret)
+		return ERR_PTR(ret);
+
+	fmem_data.virt = fmem_data.area->addr;
+
+	return fmem_data.virt;
+}
+
+void fmem_unmap_virtual_area(void)
+{
+	unmap_kernel_range((unsigned long)fmem_data.virt, fmem_data.size);
+	fmem_data.virt = NULL;
+}
+
 static int fmem_probe(struct platform_device *pdev)
 {
 	struct fmem_platform_data *pdata = pdev->dev.platform_data;
@@ -29,13 +54,16 @@
 	if (!pdata->size)
 		return -ENODEV;
 
-	fmem_data.virt = ioremap_cached(pdata->phys, pdata->size);
-	if (!fmem_data.virt)
-		return -ENOMEM;
-
 	fmem_data.phys = pdata->phys;
 	fmem_data.size = pdata->size;
+	fmem_data.area = get_vm_area(pdata->size, VM_IOREMAP);
+	if (!fmem_data.area)
+		return -ENOMEM;
 
+	if (!fmem_map_virtual_area(MT_DEVICE_CACHED)) {
+		remove_vm_area(fmem_data.area->addr);
+		return -ENOMEM;
+	}
 	pr_info("fmem phys %lx virt %p size %lx\n",
 		fmem_data.phys, fmem_data.virt, fmem_data.size);
 
@@ -159,10 +187,18 @@
 		}
 	}
 
-	if (new_state == FMEM_T_STATE)
+	if (new_state == FMEM_T_STATE) {
+		void *v;
+		v = fmem_map_virtual_area(MT_DEVICE_CACHED);
+		if (IS_ERR_OR_NULL(v)) {
+			ret = PTR_ERR(v);
+			goto out;
+		}
 		tmem_enable(true);
-	else
+	} else {
 		tmem_disable();
+		fmem_unmap_virtual_area();
+	}
 
 out_set:
 	fmem_state = new_state;
diff --git a/include/linux/fmem.h b/include/linux/fmem.h
index a88c674..d91f4c1 100644
--- a/include/linux/fmem.h
+++ b/include/linux/fmem.h
@@ -15,6 +15,8 @@
 #ifndef _FMEM_H_
 #define _FMEM_H_
 
+#include <linux/vmalloc.h>
+
 struct fmem_platform_data {
 	unsigned long phys;
 	unsigned long size;
@@ -23,6 +25,7 @@
 struct fmem_data {
 	unsigned long phys;
 	void *virt;
+	struct vm_struct *area;
 	unsigned long size;
 };
 
@@ -38,11 +41,15 @@
 int fmem_set_state(enum fmem_state);
 void lock_fmem_state(void);
 void unlock_fmem_state(void);
+void *fmem_map_virtual_area(int cacheability);
+void fmem_unmap_virtual_area(void);
 #else
 static inline struct fmem_data *fmem_get_info(void) { return NULL; }
 static inline int fmem_set_state(enum fmem_state f) { return -ENODEV; }
 static inline void lock_fmem_state(void) { return; }
 static inline void unlock_fmem_state(void) { return; }
+static inline void *fmem_map_virtual_area(int cacheability) { return NULL; }
+static inline void fmem_unmap_virtual_area(void) { return; }
 #endif