net: ipa: define IMEM memory region for IPA
authorAlex Elder <elder@linaro.org>
Mon, 4 May 2020 17:58:58 +0000 (12:58 -0500)
committerDavid S. Miller <davem@davemloft.net>
Mon, 4 May 2020 18:26:55 +0000 (11:26 -0700)
Define a region of IMEM memory available for use by IPA in the
platform configuration data.  Initialize it from ipa_mem_init().
The memory must be mapped for access through an SMMU.

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ipa/ipa.h
drivers/net/ipa/ipa_data-sc7180.c
drivers/net/ipa/ipa_data-sdm845.c
drivers/net/ipa/ipa_data.h
drivers/net/ipa/ipa_mem.c

index 23fb298..32f6dfa 100644 (file)
@@ -47,6 +47,8 @@ struct ipa_interrupt;
  * @mem_offset:                Offset from @mem_virt used for access to IPA memory
  * @mem_size:          Total size (bytes) of memory at @mem_virt
  * @mem:               Array of IPA-local memory region descriptors
+ * @imem_iova:         I/O virtual address of IPA region in IMEM
+ * @imem_size;         Size of IMEM region
  * @zero_addr:         DMA address of preallocated zero-filled memory
  * @zero_virt:         Virtual address of preallocated zero-filled memory
  * @zero_size:         Size (bytes) of preallocated zero-filled memory
@@ -88,6 +90,9 @@ struct ipa {
        u32 mem_size;
        const struct ipa_mem *mem;
 
+       unsigned long imem_iova;
+       size_t imem_size;
+
        dma_addr_t zero_addr;
        void *zero_virt;
        size_t zero_size;
index f97e7e4..e9007d1 100644 (file)
@@ -299,6 +299,8 @@ static const struct ipa_mem ipa_mem_local_data[] = {
 static struct ipa_mem_data ipa_mem_data = {
        .local_count    = ARRAY_SIZE(ipa_mem_local_data),
        .local          = ipa_mem_local_data,
+       .imem_addr      = 0x146a8000,
+       .imem_size      = 0x00002000,
 };
 
 /* Configuration data for the SC7180 SoC. */
index c55507e..c0e2070 100644 (file)
@@ -321,6 +321,8 @@ static const struct ipa_mem ipa_mem_local_data[] = {
 static struct ipa_mem_data ipa_mem_data = {
        .local_count    = ARRAY_SIZE(ipa_mem_local_data),
        .local          = ipa_mem_local_data,
+       .imem_addr      = 0x146bd000,
+       .imem_size      = 0x00002000,
 };
 
 /* Configuration data for the SDM845 SoC. */
index 51d8e5a..69957af 100644 (file)
@@ -245,13 +245,17 @@ struct ipa_resource_data {
 };
 
 /**
- * struct ipa_mem - IPA-local memory region description
+ * struct ipa_mem - description of IPA memory regions
  * @local_count:       number of regions defined in the local[] array
  * @local:             array of IPA-local memory region descriptors
+ * @imem_addr:         physical address of IPA region within IMEM
+ * @imem_size:         size in bytes of IPA IMEM region
  */
 struct ipa_mem_data {
        u32 local_count;
        const struct ipa_mem *local;
+       u32 imem_addr;
+       u32 imem_size;
 };
 
 /**
index fb4de2a..3c09165 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/bitfield.h>
 #include <linux/bug.h>
 #include <linux/dma-mapping.h>
+#include <linux/iommu.h>
 #include <linux/io.h>
 
 #include "ipa.h"
@@ -266,6 +267,79 @@ int ipa_mem_zero_modem(struct ipa *ipa)
        return 0;
 }
 
+/**
+ * ipa_imem_init() - Initialize IMEM memory used by the IPA
+ * @ipa:       IPA pointer
+ * @addr:      Physical address of the IPA region in IMEM
+ * @size:      Size (bytes) of the IPA region in IMEM
+ *
+ * IMEM is a block of shared memory separate from system DRAM, and
+ * a portion of this memory is available for the IPA to use.  The
+ * modem accesses this memory directly, but the IPA accesses it
+ * via the IOMMU, using the AP's credentials.
+ *
+ * If this region exists (size > 0) we map it for read/write access
+ * through the IOMMU using the IPA device.
+ *
+ * Note: @addr and @size are not guaranteed to be page-aligned.
+ */
+static int ipa_imem_init(struct ipa *ipa, unsigned long addr, size_t size)
+{
+       struct device *dev = &ipa->pdev->dev;
+       struct iommu_domain *domain;
+       unsigned long iova;
+       phys_addr_t phys;
+       int ret;
+
+       if (!size)
+               return 0;       /* IMEM memory not used */
+
+       domain = iommu_get_domain_for_dev(dev);
+       if (!domain) {
+               dev_err(dev, "no IOMMU domain found for IMEM\n");
+               return -EINVAL;
+       }
+
+       /* Align the address down and the size up to page boundaries */
+       phys = addr & PAGE_MASK;
+       size = PAGE_ALIGN(size + addr - phys);
+       iova = phys;    /* We just want a direct mapping */
+
+       ret = iommu_map(domain, iova, phys, size, IOMMU_READ | IOMMU_WRITE);
+       if (ret)
+               return ret;
+
+       ipa->imem_iova = iova;
+       ipa->imem_size = size;
+
+       return 0;
+}
+
+static void ipa_imem_exit(struct ipa *ipa)
+{
+       struct iommu_domain *domain;
+       struct device *dev;
+
+       if (!ipa->imem_size)
+               return;
+
+       dev = &ipa->pdev->dev;
+       domain = iommu_get_domain_for_dev(dev);
+       if (domain) {
+               size_t size;
+
+               size = iommu_unmap(domain, ipa->imem_iova, ipa->imem_size);
+               if (size != ipa->imem_size)
+                       dev_warn(dev, "unmapped %zu IMEM bytes, expected %lu\n",
+                                size, ipa->imem_size);
+       } else {
+               dev_err(dev, "couldn't get IPA IOMMU domain for IMEM\n");
+       }
+
+       ipa->imem_size = 0;
+       ipa->imem_iova = 0;
+}
+
 /* Perform memory region-related initialization */
 int ipa_mem_init(struct ipa *ipa, const struct ipa_mem_data *mem_data)
 {
@@ -305,11 +379,21 @@ int ipa_mem_init(struct ipa *ipa, const struct ipa_mem_data *mem_data)
        /* The ipa->mem[] array is indexed by enum ipa_mem_id values */
        ipa->mem = mem_data->local;
 
+       ret = ipa_imem_init(ipa, mem_data->imem_addr, mem_data->imem_size);
+       if (ret)
+               goto err_unmap;
+
        return 0;
+
+err_unmap:
+       memunmap(ipa->mem_virt);
+
+       return ret;
 }
 
 /* Inverse of ipa_mem_init() */
 void ipa_mem_exit(struct ipa *ipa)
 {
+       ipa_imem_exit(ipa);
        memunmap(ipa->mem_virt);
 }