From d4c7c45c2f406af616ca47f7964f2d4d3a4d6649 Mon Sep 17 00:00:00 2001
From: Taniya Das <tdas@codeaurora.org>
Date: Wed, 2 Jul 2014 15:30:39 +0530
Subject: [PATCH] soc: qcom: peripheral-loader: Skip kernel mapping for dma
 regions

Peripheral loader requesting for dma allocation could have regions which
are either carveout or cma region. In case of a carveout which could be
large area, the allocation could fail due to constraints of vmalloc
space.
To avoid allocation failure, set the dma attribute to NO_KERNEL_MAPPING
for all requested allocations.

When NO_KERNEL_MAPPING is set as an attribute, dma allocation for
carveout will return a dummy allocated address and for a cma region a
valid address will be returned, but the kernel mapping for that address
will not exist.

When peripheral loader requires the firmware data to be copied or the
address region to be zeroed-out, it will perform:

map_fw_mem will invoke dma_remap api which will take care of
	- ioremap the address for carveout and provide a valid virtual
	  address for a requested size.
	- remap the address, update the kernel mapping for cma regions
	  and provide a valid virtual address for a requested size.

unmap_fw_mem will invoke dma_unremap api will take care of
	- iounmap the address for carveout regions for a requested size.
	- remove the kernel mapping for requested size.

Pass the buffer size from firmware driver to peripheral loader for
dma_unremap.

Change-Id: Ic1feecbd604008da62ef7a84b07ab8cad2baee94
Signed-off-by: Taniya Das <tdas@codeaurora.org>
---
 drivers/base/firmware_class.c        | 18 +++++++------
 drivers/soc/qcom/peripheral-loader.c | 40 +++++++++++++++-------------
 drivers/soc/qcom/peripheral-loader.h |  6 ++++-
 include/linux/firmware.h             |  5 ++--
 4 files changed, 39 insertions(+), 30 deletions(-)

diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 5f61235439e0..584e9a2bfdec 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -129,7 +129,7 @@ struct firmware_buf {
 	phys_addr_t dest_addr;
 	size_t dest_size;
 	void * (*map_fw_mem)(phys_addr_t phys, size_t size, void *data);
-	void (*unmap_fw_mem)(void *virt, void *data);
+	void (*unmap_fw_mem)(void *virt, size_t size, void *data);
 	void *map_data;
 #ifdef CONFIG_FW_LOADER_USER_HELPER
 	bool is_paged_buf;
@@ -161,7 +161,7 @@ struct fw_desc {
 	phys_addr_t dest_addr;
 	size_t dest_size;
 	void * (*map_fw_mem)(phys_addr_t phys, size_t size, void *data);
-	void (*unmap_fw_mem)(void *virt, void *data);
+	void (*unmap_fw_mem)(void *virt, size_t size, void *data);
 	void *map_data;
 	struct module *module;
 	void *context;
@@ -336,7 +336,8 @@ static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf
 		return false;
 	if (kernel_read(file, 0, buf, size) != size) {
 		if (fw_buf->dest_addr)
-			fw_buf->unmap_fw_mem(buf, fw_buf->map_data);
+			fw_buf->unmap_fw_mem(buf, fw_buf->dest_size,
+							fw_buf->map_data);
 		else
 			vfree(buf);
 		return false;
@@ -344,7 +345,7 @@ static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf
 	fw_buf->data = buf;
 	fw_buf->size = size;
 	if (fw_buf->dest_addr)
-		fw_buf->unmap_fw_mem(buf, fw_buf->map_data);
+		fw_buf->unmap_fw_mem(buf, fw_buf->size, fw_buf->map_data);
 	return true;
 }
 
@@ -709,7 +710,7 @@ static int __firmware_data_rw(struct firmware_priv *fw_priv, char *buffer,
 		memcpy(fw_buf, buffer, count);
 
 	*offset += count;
-	buf->unmap_fw_mem(fw_buf, buf->map_data);
+	buf->unmap_fw_mem(fw_buf, count, buf->map_data);
 
 out:
 	return retval;
@@ -1271,7 +1272,8 @@ request_firmware_direct(const char *name, struct device *device,
 			phys_addr_t dest_addr, size_t dest_size,
 			void * (*map_fw_mem)(phys_addr_t phys, size_t size,
 						void *data),
-			void (*unmap_fw_mem)(void *virt, void *data),
+			void (*unmap_fw_mem)(void *virt, size_t size,
+						void *data),
 			void *map_data)
 {
 	struct fw_desc desc;
@@ -1339,7 +1341,7 @@ _request_firmware_nowait(
 	void (*cont)(const struct firmware *fw, void *context),
 	bool nocache, phys_addr_t dest_addr, size_t dest_size,
 	void * (*map_fw_mem)(phys_addr_t phys, size_t size, void *data),
-	void (*unmap_fw_mem)(void *virt, void *data),
+	void (*unmap_fw_mem)(void *virt, size_t size, void *data),
 	void *map_data)
 {
 	struct fw_desc *desc;
@@ -1429,7 +1431,7 @@ request_firmware_nowait_direct(
 	void (*cont)(const struct firmware *fw, void *context),
 	phys_addr_t dest_addr, size_t dest_size,
 	void * (*map_fw_mem)(phys_addr_t phys, size_t size, void *data),
-	void (*unmap_fw_mem)(void *virt, void *data),
+	void (*unmap_fw_mem)(void *virt, size_t size, void *data),
 	void *map_data)
 {
 	return _request_firmware_nowait(module, uevent, name, device, gfp,
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index b9853300cb47..06fcccfdd3c1 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -363,7 +363,6 @@ static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr,
 	void *region;
 	size_t size = max_addr - min_addr;
 	size_t aligned_size;
-	DEFINE_DMA_ATTRS(attrs);
 
 	/* Don't reallocate due to fragmentation concerns, just sanity check */
 	if (priv->region) {
@@ -378,9 +377,12 @@ static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr,
 	else
 		aligned_size = ALIGN(size, SZ_1M);
 
-	dma_set_attr(DMA_ATTR_SKIP_ZEROING, &attrs);
+	dma_set_attr(DMA_ATTR_SKIP_ZEROING, &priv->desc->attrs);
+	dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &priv->desc->attrs);
+
 	region = dma_alloc_attrs(priv->desc->dev, aligned_size,
-				&priv->region_start, GFP_KERNEL, &attrs);
+				&priv->region_start, GFP_KERNEL,
+				&priv->desc->attrs);
 
 	if (region == NULL) {
 		pil_err(priv->desc, "Failed to allocate relocatable region of size %zx\n",
@@ -523,29 +525,25 @@ static void pil_release_mmap(struct pil_desc *desc)
 #define IOMAP_SIZE SZ_1M
 
 struct pil_map_fw_info {
-	int relocated;
 	void *region;
+	struct dma_attrs attrs;
 	phys_addr_t base_addr;
+	struct device *dev;
 };
 
 static void *map_fw_mem(phys_addr_t paddr, size_t size, void *data)
 {
 	struct pil_map_fw_info *info = data;
 
-	if (info && info->relocated && info->region)
-		return info->region + (paddr - info->base_addr);
-
-	return ioremap(paddr, size);
+	return dma_remap(info->dev, info->region, paddr, size,
+					&info->attrs);
 }
 
-static void unmap_fw_mem(void *vaddr, void *data)
+static void unmap_fw_mem(void *vaddr, size_t size, void *data)
 {
 	struct pil_map_fw_info *info = data;
 
-	if (info && info->relocated && info->region)
-		return;
-
-	iounmap(vaddr);
+	dma_unremap(info->dev, vaddr, size);
 }
 
 static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
@@ -555,9 +553,10 @@ static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
 	char fw_name[30];
 	int num = seg->num;
 	struct pil_map_fw_info map_fw_info = {
-		.relocated = seg->relocated,
+		.attrs = desc->attrs,
 		.region = desc->priv->region,
 		.base_addr = desc->priv->region_start,
+		.dev = desc->dev,
 	};
 	void *map_data = desc->map_data ? desc->map_data : &map_fw_info;
 
@@ -612,7 +611,7 @@ static int pil_load_seg(struct pil_desc *desc, struct pil_seg *seg)
 
 		memset(buf, 0, size);
 
-		desc->unmap_fw_mem(buf, map_data);
+		desc->unmap_fw_mem(buf, size, map_data);
 
 		count -= orig_size;
 		paddr += orig_size;
@@ -714,6 +713,8 @@ int pil_boot(struct pil_desc *desc)
 		goto release_fw;
 	}
 
+	init_dma_attrs(&desc->attrs);
+
 	ret = pil_init_mmap(desc, mdt);
 	if (ret)
 		goto release_fw;
@@ -765,8 +766,9 @@ out:
 	up_read(&pil_pm_rwsem);
 	if (ret) {
 		if (priv->region) {
-			dma_free_coherent(desc->dev, priv->region_size,
-					priv->region, priv->region_start);
+			dma_free_attrs(desc->dev, priv->region_size,
+					priv->region, priv->region_start,
+					&desc->attrs);
 			priv->region = NULL;
 		}
 		pil_release_mmap(desc);
@@ -796,8 +798,8 @@ void pil_shutdown(struct pil_desc *desc)
 		flush_delayed_work(&priv->proxy);
 
 	if (priv->region) {
-		dma_free_coherent(desc->dev, priv->region_size,
-				priv->region, priv->region_start);
+		dma_free_attrs(desc->dev, priv->region_size,
+				priv->region, priv->region_start, &desc->attrs);
 		priv->region = NULL;
 	}
 }
diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h
index 68b3659e14b3..9cb791fc32ef 100644
--- a/drivers/soc/qcom/peripheral-loader.h
+++ b/drivers/soc/qcom/peripheral-loader.h
@@ -12,6 +12,8 @@
 #ifndef __MSM_PERIPHERAL_LOADER_H
 #define __MSM_PERIPHERAL_LOADER_H
 
+#include <linux/dma-attrs.h>
+
 struct device;
 struct module;
 struct pil_priv;
@@ -25,6 +27,7 @@ struct pil_priv;
  * @proxy_timeout: delay in ms until proxy vote is removed
  * @flags: bitfield for image flags
  * @priv: DON'T USE - internal only
+ * @attrs: DMA attributes to be used during dma allocation.
  * @proxy_unvote_irq: IRQ to trigger a proxy unvote. proxy_timeout
  * is ignored if this is set.
  * @map_fw_mem: Custom function used to map physical address space to virtual.
@@ -41,9 +44,10 @@ struct pil_desc {
 	unsigned long flags;
 #define PIL_SKIP_ENTRY_CHECK	BIT(0)
 	struct pil_priv *priv;
+	struct dma_attrs attrs;
 	unsigned int proxy_unvote_irq;
 	void * (*map_fw_mem)(phys_addr_t phys, size_t size, void *data);
-	void (*unmap_fw_mem)(void *virt, void *data);
+	void (*unmap_fw_mem)(void *virt, size_t size, void *data);
 	void *map_data;
 };
 
diff --git a/include/linux/firmware.h b/include/linux/firmware.h
index 89b23a831ce7..56672339c6cf 100644
--- a/include/linux/firmware.h
+++ b/include/linux/firmware.h
@@ -49,7 +49,8 @@ int request_firmware_direct(const char *name, struct device *device,
 			    phys_addr_t dest_addr, size_t dest_size,
 			    void * (*map_fw_mem)(phys_addr_t phys,
 						 size_t size, void *data),
-			    void (*unmap_fw_mem)(void *virt, void *data),
+			    void (*unmap_fw_mem)(void *virt, size_t size,
+							void *data),
 			    void *data);
 int request_firmware_nowait_direct(
 	struct module *module, bool uevent,
@@ -57,7 +58,7 @@ int request_firmware_nowait_direct(
 	void (*cont)(const struct firmware *fw, void *context),
 	phys_addr_t dest_addr, size_t dest_size,
 	void * (*map_fw_mem)(phys_addr_t phys, size_t size, void *data),
-	void (*unmap_fw_mem)(void *virt, void *data), void *data);
+	void (*unmap_fw_mem)(void *virt, size_t size, void *data), void *data);
 void release_firmware(const struct firmware *fw);
 int cache_firmware(const char *name);
 int uncache_firmware(const char *name);
-- 
GitLab