Browse Source

qcacmn: Add QDF APIs for SMMU Stage 1 translation

Add QDF APIs for DMA buffer allocation and sharing with SMMU
Stage 1 translation support. When SMMU Stage 1 is enabled,
DMA APIs return IO virtual address(IOVA) instead of physical
address. This IOVA needs to be mapped to physical address by
external module before accessing these buffers.

Change-Id: I88e938d8ebe1f32fdea79e3c3aa8a3638ddfd2b8
CRS-Fixed: 2072953
Himanshu Agarwal 7 years ago
parent
commit
59c25048cd
4 changed files with 508 additions and 2 deletions
  1. 266 0
      qdf/inc/qdf_mem.h
  2. 17 0
      qdf/inc/qdf_types.h
  3. 194 1
      qdf/linux/src/i_qdf_mem.h
  4. 31 1
      qdf/linux/src/i_qdf_types.h

+ 266 - 0
qdf/inc/qdf_mem.h

@@ -390,4 +390,270 @@ void qdf_mem_skb_inc(qdf_size_t size);
  */
 void qdf_mem_skb_dec(qdf_size_t size);
 
+/**
+ * qdf_mem_map_table_alloc() - Allocate shared memory info structure
+ * @num: number of required storage
+ *
+ * Allocate mapping table for DMA memory allocation. This is needed for
+ * IPA-WLAN buffer sharing when SMMU Stage1 Translation is enabled.
+ *
+ * Return: shared memory info storage table pointer
+ */
+static inline qdf_mem_info_t *qdf_mem_map_table_alloc(uint32_t num)
+{
+	qdf_mem_info_t *mem_info_arr;
+
+	mem_info_arr = qdf_mem_malloc(num * sizeof(mem_info_arr[0]));
+	return mem_info_arr;
+}
+
+/**
+ * qdf_update_mem_map_table() - Update DMA memory map info
+ * @osdev: Parent device instance
+ * @mem_info: Pointer to shared memory information
+ * @dma_addr: dma address
+ * @mem_size: memory size allocated
+ *
+ * Store DMA shared memory information
+ *
+ * Return: none
+ */
+static inline void qdf_update_mem_map_table(qdf_device_t osdev,
+					    qdf_mem_info_t *mem_info,
+					    qdf_dma_addr_t dma_addr,
+					    uint32_t mem_size)
+{
+	if (!mem_info) {
+		__qdf_print("%s: NULL mem_info\n", __func__);
+		return;
+	}
+
+	__qdf_update_mem_map_table(osdev, mem_info, dma_addr, mem_size);
+}
+
+/**
+ * qdf_mem_smmu_s1_enabled() - Return SMMU stage 1 translation enable status
+ * @osdev parent device instance
+ *
+ * Return: true if smmu s1 enabled, false if smmu s1 is bypassed
+ */
+static inline bool qdf_mem_smmu_s1_enabled(qdf_device_t osdev)
+{
+	return __qdf_mem_smmu_s1_enabled(osdev);
+}
+
+/**
+ * qdf_mem_paddr_from_dmaaddr() - get actual physical address from dma address
+ * @osdev: Parent device instance
+ * @dma_addr: DMA/IOVA address
+ *
+ * Get actual physical address from dma_addr based on SMMU enablement status.
+ * IF SMMU Stage 1 tranlation is enabled, DMA APIs return IO virtual address
+ * (IOVA) otherwise returns physical address. So get SMMU physical address
+ * mapping from IOVA.
+ *
+ * Return: dmaable physical address
+ */
+static inline qdf_dma_addr_t qdf_mem_paddr_from_dmaaddr(qdf_device_t osdev,
+							qdf_dma_addr_t dma_addr)
+{
+	return __qdf_mem_paddr_from_dmaaddr(osdev, dma_addr);
+}
+
+/**
+ * qdf_os_mem_dma_get_sgtable() - Returns DMA memory scatter gather table
+ * @dev: device instace
+ * @sgt: scatter gather table pointer
+ * @cpu_addr: HLOS virtual address
+ * @dma_addr: dma address
+ * @size: allocated memory size
+ *
+ * Return: physical address
+ */
+static inline int
+qdf_mem_dma_get_sgtable(struct device *dev, void *sgt, void *cpu_addr,
+			qdf_dma_addr_t dma_addr, size_t size)
+{
+	return __qdf_os_mem_dma_get_sgtable(dev, sgt, cpu_addr, dma_addr, size);
+}
+
+/**
+ * qdf_mem_get_dma_addr() - Return dma address based on SMMU translation status.
+ * @osdev: Parent device instance
+ * @mem_info: Pointer to allocated memory information
+ *
+ * Get dma address based on SMMU enablement status. If SMMU Stage 1
+ * tranlation is enabled, DMA APIs return IO virtual address otherwise
+ * returns physical address.
+ *
+ * Return: dma address
+ */
+static inline qdf_dma_addr_t qdf_mem_get_dma_addr(qdf_device_t osdev,
+						  qdf_mem_info_t *mem_info)
+{
+	return __qdf_mem_get_dma_addr(osdev, mem_info);
+}
+
+/**
+ * qdf_mem_get_dma_addr_ptr() - Return DMA address pointer from mem info struct
+ * @osdev: Parent device instance
+ * @mem_info: Pointer to allocated memory information
+ *
+ * Based on smmu stage 1 translation enablement, return corresponding dma
+ * address storage pointer.
+ *
+ * Return: dma address storage pointer
+ */
+static inline qdf_dma_addr_t *qdf_mem_get_dma_addr_ptr(qdf_device_t osdev,
+						       qdf_mem_info_t *mem_info)
+{
+	return __qdf_mem_get_dma_addr_ptr(osdev, mem_info);
+}
+
+
+/**
+ * qdf_mem_get_dma_size() - Return DMA memory size
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ *
+ * Return: DMA memory size
+ */
+static inline uint32_t
+qdf_mem_get_dma_size(qdf_device_t osdev,
+		       qdf_mem_info_t *mem_info)
+{
+	return __qdf_mem_get_dma_size(osdev, mem_info);
+}
+
+/**
+ * qdf_mem_set_dma_size() - Set DMA memory size
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ * @mem_size: memory size allocated
+ *
+ * Return: none
+ */
+static inline void
+qdf_mem_set_dma_size(qdf_device_t osdev,
+		       qdf_mem_info_t *mem_info,
+		       uint32_t mem_size)
+{
+	__qdf_mem_set_dma_size(osdev, mem_info, mem_size);
+}
+
+/**
+ * qdf_mem_get_dma_size() - Return DMA physical address
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ *
+ * Return: DMA physical address
+ */
+static inline qdf_dma_addr_t
+qdf_mem_get_dma_pa(qdf_device_t osdev,
+		     qdf_mem_info_t *mem_info)
+{
+	return __qdf_mem_get_dma_pa(osdev, mem_info);
+}
+
+/**
+ * qdf_mem_set_dma_size() - Set DMA physical address
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ * @dma_pa: DMA phsical address
+ *
+ * Return: none
+ */
+static inline void
+qdf_mem_set_dma_pa(qdf_device_t osdev,
+		     qdf_mem_info_t *mem_info,
+		     qdf_dma_addr_t dma_pa)
+{
+	__qdf_mem_set_dma_pa(osdev, mem_info, dma_pa);
+}
+
+/**
+ * qdf_mem_shared_mem_alloc() - Allocate DMA memory for shared resource
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ * @size: size to be allocated
+ *
+ * Allocate DMA memory which will be shared with external kernel module. This
+ * information is needed for SMMU mapping.
+ *
+ * Return: 0 suceess
+ */
+static inline qdf_shared_mem_t *qdf_mem_shared_mem_alloc(qdf_device_t osdev,
+							 uint32_t size)
+{
+	qdf_shared_mem_t *shared_mem;
+
+	shared_mem = qdf_mem_malloc(sizeof(*shared_mem));
+	if (!shared_mem) {
+		__qdf_print("%s: Unable to allocate memory for shared resource struct\n",
+			    __func__);
+		return NULL;
+	}
+
+	shared_mem->vaddr = qdf_mem_alloc_consistent(osdev, osdev->dev,
+				size, qdf_mem_get_dma_addr_ptr(osdev,
+						&shared_mem->mem_info));
+	if (!shared_mem->vaddr) {
+		__qdf_print("%s; Unable to allocate DMA memory for shared resource\n",
+			    __func__);
+		qdf_mem_free(shared_mem);
+		return NULL;
+	}
+
+	qdf_mem_set_dma_size(osdev, &shared_mem->mem_info, size);
+	qdf_mem_zero(shared_mem->vaddr,
+		     qdf_mem_get_dma_size(osdev, &shared_mem->mem_info));
+	qdf_mem_set_dma_pa(osdev, &shared_mem->mem_info,
+			   qdf_mem_paddr_from_dmaaddr(osdev,
+				qdf_mem_get_dma_addr(osdev,
+						     &shared_mem->mem_info)));
+	qdf_mem_dma_get_sgtable(osdev->dev,
+				(void *)&shared_mem->sgtable,
+				shared_mem->vaddr,
+				qdf_mem_get_dma_addr(osdev,
+						     &shared_mem->mem_info),
+				qdf_mem_get_dma_size(osdev,
+						     &shared_mem->mem_info));
+
+	shared_mem->sgtable.sgl->dma_address =
+		qdf_mem_get_dma_pa(osdev, &shared_mem->mem_info);
+
+	return shared_mem;
+}
+
+/**
+ * qdf_mem_shared_mem_free() - Free shared memory
+ * @osdev: parent device instance
+ * @shared_mem: shared memory information storage
+ *
+ * Free DMA shared memory resource
+ *
+ * Return: None
+ */
+static inline void qdf_mem_shared_mem_free(qdf_device_t osdev,
+					   qdf_shared_mem_t *shared_mem)
+{
+	if (!shared_mem) {
+		__qdf_print("%s: NULL shared mem struct passed\n",
+			    __func__);
+		return;
+	}
+
+	if (shared_mem->vaddr) {
+		qdf_mem_free_consistent(osdev, osdev->dev,
+					qdf_mem_get_dma_size(osdev,
+						&shared_mem->mem_info),
+					shared_mem->vaddr,
+					qdf_mem_get_dma_addr(osdev,
+						&shared_mem->mem_info),
+					qdf_get_dma_mem_context(shared_mem,
+								memctx));
+	}
+	qdf_mem_free(shared_mem);
+}
+
 #endif /* __QDF_MEMORY_H */

+ 17 - 0
qdf/inc/qdf_types.h

@@ -157,6 +157,9 @@ typedef __qdf_dma_size_t     qdf_dma_size_t;
  */
 typedef __qdf_dma_context_t qdf_dma_context_t;
 
+typedef __qdf_mem_info_t qdf_mem_info_t;
+typedef __sgtable_t sgtable_t;
+
 /**
  * pointer to net device
  */
@@ -177,6 +180,20 @@ typedef struct qdf_dma_map_info {
 	} dma_segs[QDF_MAX_SCATTER];
 } qdf_dmamap_info_t;
 
+/**
+ * struct qdf_shared_mem - Shared memory resource
+ * @mem_info: memory info struct
+ * @vaddr: virtual address
+ * @sgtable: scatter-gather table
+ * @memctx: dma address
+ */
+typedef struct qdf_shared_mem {
+	qdf_mem_info_t mem_info;
+	void *vaddr;
+	sgtable_t sgtable;
+	qdf_dma_mem_context(memctx);
+} qdf_shared_mem_t;
+
 #define qdf_iomem_t __qdf_iomem_t;
 
 /**

+ 194 - 1
qdf/linux/src/i_qdf_mem.h

@@ -70,6 +70,12 @@
 #endif /* __KERNEL__ */
 #include <qdf_status.h>
 
+#ifdef CONFIG_ARM_SMMU
+#include <pld_common.h>
+#include <asm/dma-iommu.h>
+#include <linux/iommu.h>
+#endif
+
 #ifdef __KERNEL__
 typedef struct mempool_elem {
 	STAILQ_ENTRY(mempool_elem) mempool_entry;
@@ -160,7 +166,7 @@ static inline uint32_t __qdf_mem_map_nbytes_single(qdf_device_t osdev,
  * @dir: DMA unmap direction
  * @nbytes: number of bytes to be unmapped.
  *
- * @return - none
+ * Return - none
  */
 static inline void __qdf_mem_unmap_nbytes_single(qdf_device_t osdev,
 						 qdf_dma_addr_t phy_addr,
@@ -204,4 +210,191 @@ static inline int32_t __qdf_mem_cmp(const void *memory1, const void *memory2,
 	return (int32_t) memcmp(memory1, memory2, num_bytes);
 }
 
+/**
+ * __qdf_mem_smmu_s1_enabled() - Return SMMU stage 1 translation enable status
+ * @osdev parent device instance
+ *
+ * Return: true if smmu s1 enabled, false if smmu s1 is bypassed
+ */
+static inline bool __qdf_mem_smmu_s1_enabled(qdf_device_t osdev)
+{
+	return osdev->smmu_s1_enabled;
+}
+
+#ifdef CONFIG_ARM_SMMU
+/**
+ * __qdf_mem_paddr_from_dmaaddr() - get actual physical address from dma_addr
+ * @osdev: parent device instance
+ * @dma_addr: dma_addr
+ *
+ * Get actual physical address from dma_addr based on SMMU enablement status.
+ * IF SMMU Stage 1 tranlation is enabled, DMA APIs return IO virtual address
+ * (IOVA) otherwise returns physical address. So get SMMU physical address
+ * mapping from IOVA.
+ *
+ * Return: dmaable physical address
+ */
+static inline unsigned long
+__qdf_mem_paddr_from_dmaaddr(qdf_device_t osdev,
+			     qdf_dma_addr_t dma_addr)
+{
+	struct dma_iommu_mapping *mapping;
+
+	if (__qdf_mem_smmu_s1_enabled(osdev)) {
+		mapping = pld_smmu_get_mapping(osdev->dev);
+		if (mapping)
+			return iommu_iova_to_phys(mapping->domain, dma_addr);
+	}
+
+	return dma_addr;
+}
+#else
+static inline unsigned long
+__qdf_mem_paddr_from_dmaaddr(qdf_device_t osdev,
+			     qdf_dma_addr_t dma_addr)
+{
+	return dma_addr;
+}
+#endif
+
+/**
+ * __qdf_os_mem_dma_get_sgtable() - Returns DMA memory scatter gather table
+ * @dev: device instace
+ * @sgt: scatter gather table pointer
+ * @cpu_addr: HLOS virtual address
+ * @dma_addr: dma/iova
+ * @size: allocated memory size
+ *
+ * Return: physical address
+ */
+static inline int
+__qdf_os_mem_dma_get_sgtable(struct device *dev, void *sgt, void *cpu_addr,
+			     qdf_dma_addr_t dma_addr, size_t size)
+{
+	return dma_get_sgtable(dev, (struct sg_table *)sgt, cpu_addr, dma_addr,
+				size);
+}
+
+/**
+ * __qdf_mem_get_dma_addr() - Return dma addr based on SMMU translation status
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ *
+ * Based on smmu stage 1 translation enablement status, return corresponding dma
+ * address from qdf_mem_info_t. If stage 1 translation enabled, return
+ * IO virtual address otherwise return physical address.
+ *
+ * Return: dma address
+ */
+static inline qdf_dma_addr_t __qdf_mem_get_dma_addr(qdf_device_t osdev,
+						    qdf_mem_info_t *mem_info)
+{
+	if (__qdf_mem_smmu_s1_enabled(osdev))
+		return (qdf_dma_addr_t)mem_info->iova;
+	else
+		return (qdf_dma_addr_t)mem_info->pa;
+}
+
+/**
+ * __qdf_mem_get_dma_addr_ptr() - Return DMA address storage pointer
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ *
+ * Based on smmu stage 1 translation enablement status, return corresponding
+ * dma address pointer from qdf_mem_info_t structure. If stage 1 translation
+ * enabled, return pointer to IO virtual address otherwise return pointer to
+ * physical address
+ *
+ * Return: dma address storage pointer
+ */
+static inline qdf_dma_addr_t *
+__qdf_mem_get_dma_addr_ptr(qdf_device_t osdev,
+			   qdf_mem_info_t *mem_info)
+{
+	if (__qdf_mem_smmu_s1_enabled(osdev))
+		return (qdf_dma_addr_t *)(&mem_info->iova);
+	else
+		return (qdf_dma_addr_t *)(&mem_info->pa);
+}
+
+/**
+ * __qdf_update_mem_map_table() - Update DMA memory map info
+ * @osdev: Parent device instance
+ * @mem_info: Pointer to shared memory information
+ * @dma_addr: dma address
+ * @mem_size: memory size allocated
+ *
+ * Store DMA shared memory information
+ *
+ * Return: none
+ */
+static inline void __qdf_update_mem_map_table(qdf_device_t osdev,
+					      qdf_mem_info_t *mem_info,
+					      qdf_dma_addr_t dma_addr,
+					      uint32_t mem_size)
+{
+	mem_info->pa = __qdf_mem_paddr_from_dmaaddr(osdev, dma_addr);
+	mem_info->iova = dma_addr;
+	mem_info->size = mem_size;
+}
+
+/**
+ * __qdf_mem_get_dma_size() - Return DMA memory size
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ *
+ * Return: DMA memory size
+ */
+static inline uint32_t
+__qdf_mem_get_dma_size(qdf_device_t osdev,
+		       qdf_mem_info_t *mem_info)
+{
+	return mem_info->size;
+}
+
+/**
+ * __qdf_mem_set_dma_size() - Set DMA memory size
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ * @mem_size: memory size allocated
+ *
+ * Return: none
+ */
+static inline void
+__qdf_mem_set_dma_size(qdf_device_t osdev,
+		       qdf_mem_info_t *mem_info,
+		       uint32_t mem_size)
+{
+	mem_info->size = mem_size;
+}
+
+/**
+ * __qdf_mem_get_dma_size() - Return DMA physical address
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ *
+ * Return: DMA physical address
+ */
+static inline qdf_dma_addr_t
+__qdf_mem_get_dma_pa(qdf_device_t osdev,
+		     qdf_mem_info_t *mem_info)
+{
+	return mem_info->pa;
+}
+
+/**
+ * __qdf_mem_set_dma_size() - Set DMA physical address
+ * @osdev: parent device instance
+ * @mem_info: Pointer to allocated memory information
+ * @dma_pa: DMA phsical address
+ *
+ * Return: none
+ */
+static inline void
+__qdf_mem_set_dma_pa(qdf_device_t osdev,
+		     qdf_mem_info_t *mem_info,
+		     qdf_dma_addr_t dma_pa)
+{
+	mem_info->pa = dma_pa;
+}
 #endif /* __I_QDF_MEM_H */

+ 31 - 1
qdf/linux/src/i_qdf_types.h

@@ -67,6 +67,11 @@
 #include <linux/dma-mapping.h>
 #include <linux/wireless.h>
 #include <linux/if.h>
+#ifdef IPA_OFFLOAD
+#include <linux/ipa.h>
+#endif
+
+typedef struct sg_table __sgtable_t;
 #else
 
 /*
@@ -79,6 +84,9 @@
 typedef unsigned long dma_addr_t;
 #endif
 
+typedef unsigned long phys_addr_t;
+typedef unsigned long __sgtable_t;
+
 #define SIOCGIWAP       0
 #define IWEVCUSTOM      0
 #define IWEVREGISTERED  0
@@ -136,6 +144,24 @@ typedef __be16 __qdf_be16_t;
 typedef __be32 __qdf_be32_t;
 typedef __be64 __qdf_be64_t;
 
+#ifdef IPA_OFFLOAD
+typedef struct ipa_wdi_buffer_info __qdf_mem_info_t;
+#else
+/**
+ * struct __qdf_shared_mem_info - shared mem info struct
+ * @pa : physical address
+ * @iova: i/o virtual address
+ * @size: allocated memory size
+ * @result: status
+ */
+typedef struct __qdf_shared_mem_info {
+	phys_addr_t pa;
+	unsigned long iova;
+	size_t size;
+	int result;
+} __qdf_mem_info_t;
+#endif /* IPA_OFFLOAD */
+
 #define qdf_dma_mem_context(context) dma_addr_t context
 #define qdf_get_dma_mem_context(var, field)   ((qdf_dma_context_t)(var->field))
 
@@ -183,7 +209,10 @@ enum qdf_bus_type {
  * @dev: Pointer to device
  * @res: QDF resource
  * @func: Interrupt handler
- * @mem_pool: array to pointer to mem context
+ * @mem_pool: array of pointers to mem pool context
+ * @bus_type: Bus type
+ * @bid: Bus ID
+ * @smmu_s1_enabled: SMMU S1 enabled or not
  */
 struct __qdf_device {
 	void *drv;
@@ -198,6 +227,7 @@ struct __qdf_device {
 #ifdef CONFIG_MCL
 	const struct hif_bus_id *bid;
 #endif
+	bool smmu_s1_enabled;
 };
 typedef struct __qdf_device *__qdf_device_t;