Merge "msm: ocmem: Add support for dumping OCMEM"
diff --git a/arch/arm/mach-msm/include/mach/ocmem.h b/arch/arm/mach-msm/include/mach/ocmem.h
index 904de5e..cb8aae0 100644
--- a/arch/arm/mach-msm/include/mach/ocmem.h
+++ b/arch/arm/mach-msm/include/mach/ocmem.h
@@ -134,6 +134,9 @@
 int ocmem_unmap(int client_id, struct ocmem_buf *buffer,
 			struct ocmem_map_list *list);
 
+int ocmem_dump(int client_id, struct ocmem_buf *buffer,
+				unsigned long dst_phys_addr);
+
 /* Priority Enforcement APIs */
 int ocmem_evict(int client_id);
 
diff --git a/arch/arm/mach-msm/include/mach/ocmem_priv.h b/arch/arm/mach-msm/include/mach/ocmem_priv.h
index 09dfac0..0b30c26 100644
--- a/arch/arm/mach-msm/include/mach/ocmem_priv.h
+++ b/arch/arm/mach-msm/include/mach/ocmem_priv.h
@@ -56,6 +56,8 @@
 	NR_TRANSFER_FAILS,
 	NR_EVICTIONS,
 	NR_RESTORES,
+	NR_DUMP_REQUESTS,
+	NR_DUMP_COMPLETE,
 	NR_OCMEM_ZSTAT_ITEMS,
 };
 
@@ -198,6 +200,7 @@
 int process_evict(int);
 int process_restore(int);
 int process_shrink(int, struct ocmem_handle *, unsigned long);
+int process_dump(int, struct ocmem_handle *, unsigned long);
 int ocmem_rdm_transfer(int, struct ocmem_map_list *,
 				unsigned long, int);
 int ocmem_clear(unsigned long, unsigned long);
diff --git a/arch/arm/mach-msm/ocmem.c b/arch/arm/mach-msm/ocmem.c
index 793fcc5..7829d8d 100644
--- a/arch/arm/mach-msm/ocmem.c
+++ b/arch/arm/mach-msm/ocmem.c
@@ -82,6 +82,8 @@
 	"Transfer failures",
 	"Evictions",
 	"Restorations",
+	"Dump requests",
+	"Dump completed",
 };
 
 struct ocmem_quota_table {
diff --git a/arch/arm/mach-msm/ocmem_api.c b/arch/arm/mach-msm/ocmem_api.c
index 6e094fd..689e015 100644
--- a/arch/arm/mach-msm/ocmem_api.c
+++ b/arch/arm/mach-msm/ocmem_api.c
@@ -399,6 +399,36 @@
 }
 EXPORT_SYMBOL(ocmem_unmap);
 
+int ocmem_dump(int client_id, struct ocmem_buf *buffer,
+			unsigned long dst_phys_addr)
+{
+	int ret = 0;
+	struct ocmem_handle *handle = NULL;
+
+	if (!check_id(client_id)) {
+		pr_err("ocmem: Invalid client id: %d\n", client_id);
+		return -EINVAL;
+	}
+
+	if (!zone_active(client_id)) {
+		pr_err("ocmem: Client id: %s (id: %d) not allowed to use OCMEM\n",
+					get_name(client_id), client_id);
+		return -EINVAL;
+	}
+
+	if (!buffer) {
+		pr_err("ocmem: Invalid buffer\n");
+		return -EINVAL;
+	}
+
+	handle = buffer_to_handle(buffer);
+	mutex_lock(&handle->handle_mutex);
+	ret = process_dump(client_id, handle, dst_phys_addr);
+	mutex_unlock(&handle->handle_mutex);
+	return ret;
+}
+EXPORT_SYMBOL(ocmem_dump);
+
 unsigned long get_max_quota(int client_id)
 {
 	if (!check_id(client_id)) {
diff --git a/arch/arm/mach-msm/ocmem_sched.c b/arch/arm/mach-msm/ocmem_sched.c
index e8854d5..c380c54 100644
--- a/arch/arm/mach-msm/ocmem_sched.c
+++ b/arch/arm/mach-msm/ocmem_sched.c
@@ -65,6 +65,7 @@
 	MAX_OCMEM_PRIO = PRIO_OCMEM + 1,
 };
 
+static void __iomem *ocmem_vaddr;
 static struct list_head sched_queue[MAX_OCMEM_PRIO];
 static struct mutex sched_queue_mutex;
 
@@ -1670,6 +1671,34 @@
 	return -EINVAL;
 }
 
+static int do_dump(struct ocmem_req *req, unsigned long addr)
+{
+
+	void __iomem *req_vaddr;
+	unsigned long offset = 0x0;
+
+	down_write(&req->rw_sem);
+
+	offset = phys_to_offset(req->req_start);
+
+	req_vaddr = ocmem_vaddr + offset;
+
+	if (!req_vaddr)
+		goto err_do_dump;
+
+	pr_debug("Dumping client %s buffer ocmem p: %lx (v: %p) to ddr %lx\n",
+				get_name(req->owner), req->req_start,
+				req_vaddr, addr);
+
+	memcpy((void *)addr, req_vaddr, req->req_sz);
+
+	up_write(&req->rw_sem);
+	return 0;
+err_do_dump:
+	up_write(&req->rw_sem);
+	return -EINVAL;
+}
+
 int process_restore(int id)
 {
 	struct ocmem_req *req = NULL;
@@ -1828,6 +1857,38 @@
 	return -EINVAL;
 }
 
+int process_dump(int id, struct ocmem_handle *handle, unsigned long addr)
+{
+	struct ocmem_req *req = NULL;
+	int rc = 0;
+
+	req = handle_to_req(handle);
+
+	if (!req)
+		return -EINVAL;
+
+	if (!is_mapped(req)) {
+		pr_err("Buffer is not mapped\n");
+		goto dump_error;
+	}
+
+	inc_ocmem_stat(zone_of(req), NR_DUMP_REQUESTS);
+
+	mutex_lock(&sched_mutex);
+	rc = do_dump(req, addr);
+	mutex_unlock(&sched_mutex);
+
+	if (rc < 0)
+		goto dump_error;
+
+	inc_ocmem_stat(zone_of(req), NR_DUMP_COMPLETE);
+	return 0;
+
+dump_error:
+	pr_err("Dumping OCMEM memory failed for client %d\n", id);
+	return -EINVAL;
+}
+
 static void ocmem_sched_wk_func(struct work_struct *work)
 {
 
@@ -1906,6 +1967,7 @@
 	pdata = platform_get_drvdata(pdev);
 	mutex_init(&sched_mutex);
 	mutex_init(&sched_queue_mutex);
+	ocmem_vaddr = pdata->vbase;
 	for (i = MIN_PRIO; i < MAX_OCMEM_PRIO; i++)
 		INIT_LIST_HEAD(&sched_queue[i]);