|  | // SPDX-License-Identifier: GPL-2.0 | 
|  |  | 
|  | /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. | 
|  | * Copyright (C) 2019-2021 Linaro Ltd. | 
|  | */ | 
|  |  | 
|  | #include <linux/types.h> | 
|  | #include <linux/bitfield.h> | 
|  | #include <linux/bug.h> | 
|  | #include <linux/dma-mapping.h> | 
|  | #include <linux/iommu.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/soc/qcom/smem.h> | 
|  |  | 
|  | #include "ipa.h" | 
|  | #include "ipa_reg.h" | 
|  | #include "ipa_data.h" | 
|  | #include "ipa_cmd.h" | 
|  | #include "ipa_mem.h" | 
|  | #include "ipa_table.h" | 
|  | #include "gsi_trans.h" | 
|  |  | 
|  | /* "Canary" value placed between memory regions to detect overflow */ | 
|  | #define IPA_MEM_CANARY_VAL		cpu_to_le32(0xdeadbeef) | 
|  |  | 
|  | /* SMEM host id representing the modem. */ | 
|  | #define QCOM_SMEM_HOST_MODEM	1 | 
|  |  | 
|  | const struct ipa_mem *ipa_mem_find(struct ipa *ipa, enum ipa_mem_id mem_id) | 
|  | { | 
|  | u32 i; | 
|  |  | 
|  | for (i = 0; i < ipa->mem_count; i++) { | 
|  | const struct ipa_mem *mem = &ipa->mem[i]; | 
|  |  | 
|  | if (mem->id == mem_id) | 
|  | return mem; | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Add an immediate command to a transaction that zeroes a memory region */ | 
|  | static void | 
|  | ipa_mem_zero_region_add(struct gsi_trans *trans, enum ipa_mem_id mem_id) | 
|  | { | 
|  | struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi); | 
|  | const struct ipa_mem *mem = ipa_mem_find(ipa, mem_id); | 
|  | dma_addr_t addr = ipa->zero_addr; | 
|  |  | 
|  | if (!mem->size) | 
|  | return; | 
|  |  | 
|  | ipa_cmd_dma_shared_mem_add(trans, mem->offset, mem->size, addr, true); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ipa_mem_setup() - Set up IPA AP and modem shared memory areas | 
|  | * @ipa:	IPA pointer | 
|  | * | 
|  | * Set up the shared memory regions in IPA local memory.  This involves | 
|  | * zero-filling memory regions, and in the case of header memory, telling | 
|  | * the IPA where it's located. | 
|  | * | 
|  | * This function performs the initial setup of this memory.  If the modem | 
|  | * crashes, its regions are re-zeroed in ipa_mem_zero_modem(). | 
|  | * | 
|  | * The AP informs the modem where its portions of memory are located | 
|  | * in a QMI exchange that occurs at modem startup. | 
|  | * | 
|  | * There is no need for a matching ipa_mem_teardown() function. | 
|  | * | 
|  | * Return:	0 if successful, or a negative error code | 
|  | */ | 
|  | int ipa_mem_setup(struct ipa *ipa) | 
|  | { | 
|  | dma_addr_t addr = ipa->zero_addr; | 
|  | const struct ipa_mem *mem; | 
|  | struct gsi_trans *trans; | 
|  | u32 offset; | 
|  | u16 size; | 
|  | u32 val; | 
|  |  | 
|  | /* Get a transaction to define the header memory region and to zero | 
|  | * the processing context and modem memory regions. | 
|  | */ | 
|  | trans = ipa_cmd_trans_alloc(ipa, 4); | 
|  | if (!trans) { | 
|  | dev_err(&ipa->pdev->dev, "no transaction for memory setup\n"); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | /* Initialize IPA-local header memory.  The AP header region, if | 
|  | * present, is contiguous with and follows the modem header region, | 
|  | * and they are initialized together. | 
|  | */ | 
|  | mem = ipa_mem_find(ipa, IPA_MEM_MODEM_HEADER); | 
|  | offset = mem->offset; | 
|  | size = mem->size; | 
|  | mem = ipa_mem_find(ipa, IPA_MEM_AP_HEADER); | 
|  | if (mem) | 
|  | size += mem->size; | 
|  |  | 
|  | ipa_cmd_hdr_init_local_add(trans, offset, size, addr); | 
|  |  | 
|  | ipa_mem_zero_region_add(trans, IPA_MEM_MODEM_PROC_CTX); | 
|  | ipa_mem_zero_region_add(trans, IPA_MEM_AP_PROC_CTX); | 
|  | ipa_mem_zero_region_add(trans, IPA_MEM_MODEM); | 
|  |  | 
|  | gsi_trans_commit_wait(trans); | 
|  |  | 
|  | /* Tell the hardware where the processing context area is located */ | 
|  | mem = ipa_mem_find(ipa, IPA_MEM_MODEM_PROC_CTX); | 
|  | offset = ipa->mem_offset + mem->offset; | 
|  | val = proc_cntxt_base_addr_encoded(ipa->version, offset); | 
|  | iowrite32(val, ipa->reg_virt + IPA_REG_LOCAL_PKT_PROC_CNTXT_OFFSET); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Is the given memory region ID is valid for the current IPA version? */ | 
|  | static bool ipa_mem_id_valid(struct ipa *ipa, enum ipa_mem_id mem_id) | 
|  | { | 
|  | enum ipa_version version = ipa->version; | 
|  |  | 
|  | switch (mem_id) { | 
|  | case IPA_MEM_UC_SHARED: | 
|  | case IPA_MEM_UC_INFO: | 
|  | case IPA_MEM_V4_FILTER_HASHED: | 
|  | case IPA_MEM_V4_FILTER: | 
|  | case IPA_MEM_V6_FILTER_HASHED: | 
|  | case IPA_MEM_V6_FILTER: | 
|  | case IPA_MEM_V4_ROUTE_HASHED: | 
|  | case IPA_MEM_V4_ROUTE: | 
|  | case IPA_MEM_V6_ROUTE_HASHED: | 
|  | case IPA_MEM_V6_ROUTE: | 
|  | case IPA_MEM_MODEM_HEADER: | 
|  | case IPA_MEM_AP_HEADER: | 
|  | case IPA_MEM_MODEM_PROC_CTX: | 
|  | case IPA_MEM_AP_PROC_CTX: | 
|  | case IPA_MEM_MODEM: | 
|  | case IPA_MEM_UC_EVENT_RING: | 
|  | case IPA_MEM_PDN_CONFIG: | 
|  | case IPA_MEM_STATS_QUOTA_MODEM: | 
|  | case IPA_MEM_STATS_QUOTA_AP: | 
|  | case IPA_MEM_END_MARKER:	/* pseudo region */ | 
|  | break; | 
|  |  | 
|  | case IPA_MEM_STATS_TETHERING: | 
|  | case IPA_MEM_STATS_DROP: | 
|  | if (version < IPA_VERSION_4_0) | 
|  | return false; | 
|  | break; | 
|  |  | 
|  | case IPA_MEM_STATS_V4_FILTER: | 
|  | case IPA_MEM_STATS_V6_FILTER: | 
|  | case IPA_MEM_STATS_V4_ROUTE: | 
|  | case IPA_MEM_STATS_V6_ROUTE: | 
|  | if (version < IPA_VERSION_4_0 || version > IPA_VERSION_4_2) | 
|  | return false; | 
|  | break; | 
|  |  | 
|  | case IPA_MEM_NAT_TABLE: | 
|  | case IPA_MEM_STATS_FILTER_ROUTE: | 
|  | if (version < IPA_VERSION_4_5) | 
|  | return false; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Must the given memory region be present in the configuration? */ | 
|  | static bool ipa_mem_id_required(struct ipa *ipa, enum ipa_mem_id mem_id) | 
|  | { | 
|  | switch (mem_id) { | 
|  | case IPA_MEM_UC_SHARED: | 
|  | case IPA_MEM_UC_INFO: | 
|  | case IPA_MEM_V4_FILTER_HASHED: | 
|  | case IPA_MEM_V4_FILTER: | 
|  | case IPA_MEM_V6_FILTER_HASHED: | 
|  | case IPA_MEM_V6_FILTER: | 
|  | case IPA_MEM_V4_ROUTE_HASHED: | 
|  | case IPA_MEM_V4_ROUTE: | 
|  | case IPA_MEM_V6_ROUTE_HASHED: | 
|  | case IPA_MEM_V6_ROUTE: | 
|  | case IPA_MEM_MODEM_HEADER: | 
|  | case IPA_MEM_MODEM_PROC_CTX: | 
|  | case IPA_MEM_AP_PROC_CTX: | 
|  | case IPA_MEM_MODEM: | 
|  | return true; | 
|  |  | 
|  | case IPA_MEM_PDN_CONFIG: | 
|  | case IPA_MEM_STATS_QUOTA_MODEM: | 
|  | case IPA_MEM_STATS_TETHERING: | 
|  | return ipa->version >= IPA_VERSION_4_0; | 
|  |  | 
|  | default: | 
|  | return false;		/* Anything else is optional */ | 
|  | } | 
|  | } | 
|  |  | 
|  | static bool ipa_mem_valid_one(struct ipa *ipa, const struct ipa_mem *mem) | 
|  | { | 
|  | struct device *dev = &ipa->pdev->dev; | 
|  | enum ipa_mem_id mem_id = mem->id; | 
|  | u16 size_multiple; | 
|  |  | 
|  | /* Make sure the memory region is valid for this version of IPA */ | 
|  | if (!ipa_mem_id_valid(ipa, mem_id)) { | 
|  | dev_err(dev, "region id %u not valid\n", mem_id); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!mem->size && !mem->canary_count) { | 
|  | dev_err(dev, "empty memory region %u\n", mem_id); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Other than modem memory, sizes must be a multiple of 8 */ | 
|  | size_multiple = mem_id == IPA_MEM_MODEM ? 4 : 8; | 
|  | if (mem->size % size_multiple) | 
|  | dev_err(dev, "region %u size not a multiple of %u bytes\n", | 
|  | mem_id, size_multiple); | 
|  | else if (mem->offset % 8) | 
|  | dev_err(dev, "region %u offset not 8-byte aligned\n", mem_id); | 
|  | else if (mem->offset < mem->canary_count * sizeof(__le32)) | 
|  | dev_err(dev, "region %u offset too small for %hu canaries\n", | 
|  | mem_id, mem->canary_count); | 
|  | else if (mem_id == IPA_MEM_END_MARKER && mem->size) | 
|  | dev_err(dev, "non-zero end marker region size\n"); | 
|  | else | 
|  | return true; | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Verify each defined memory region is valid. */ | 
|  | static bool ipa_mem_valid(struct ipa *ipa, const struct ipa_mem_data *mem_data) | 
|  | { | 
|  | DECLARE_BITMAP(regions, IPA_MEM_COUNT) = { }; | 
|  | struct device *dev = &ipa->pdev->dev; | 
|  | enum ipa_mem_id mem_id; | 
|  | u32 i; | 
|  |  | 
|  | if (mem_data->local_count > IPA_MEM_COUNT) { | 
|  | dev_err(dev, "too many memory regions (%u > %u)\n", | 
|  | mem_data->local_count, IPA_MEM_COUNT); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < mem_data->local_count; i++) { | 
|  | const struct ipa_mem *mem = &mem_data->local[i]; | 
|  |  | 
|  | if (__test_and_set_bit(mem->id, regions)) { | 
|  | dev_err(dev, "duplicate memory region %u\n", mem->id); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Defined regions have non-zero size and/or canary count */ | 
|  | if (!ipa_mem_valid_one(ipa, mem)) | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Now see if any required regions are not defined */ | 
|  | for_each_clear_bit(mem_id, regions, IPA_MEM_COUNT) { | 
|  | if (ipa_mem_id_required(ipa, mem_id)) | 
|  | dev_err(dev, "required memory region %u missing\n", | 
|  | mem_id); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Do all memory regions fit within the IPA local memory? */ | 
|  | static bool ipa_mem_size_valid(struct ipa *ipa) | 
|  | { | 
|  | struct device *dev = &ipa->pdev->dev; | 
|  | u32 limit = ipa->mem_size; | 
|  | u32 i; | 
|  |  | 
|  | for (i = 0; i < ipa->mem_count; i++) { | 
|  | const struct ipa_mem *mem = &ipa->mem[i]; | 
|  |  | 
|  | if (mem->offset + mem->size <= limit) | 
|  | continue; | 
|  |  | 
|  | dev_err(dev, "region %u ends beyond memory limit (0x%08x)\n", | 
|  | mem->id, limit); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ipa_mem_config() - Configure IPA shared memory | 
|  | * @ipa:	IPA pointer | 
|  | * | 
|  | * Return:	0 if successful, or a negative error code | 
|  | */ | 
|  | int ipa_mem_config(struct ipa *ipa) | 
|  | { | 
|  | struct device *dev = &ipa->pdev->dev; | 
|  | const struct ipa_mem *mem; | 
|  | dma_addr_t addr; | 
|  | u32 mem_size; | 
|  | void *virt; | 
|  | u32 val; | 
|  | u32 i; | 
|  |  | 
|  | /* Check the advertised location and size of the shared memory area */ | 
|  | val = ioread32(ipa->reg_virt + IPA_REG_SHARED_MEM_SIZE_OFFSET); | 
|  |  | 
|  | /* The fields in the register are in 8 byte units */ | 
|  | ipa->mem_offset = 8 * u32_get_bits(val, SHARED_MEM_BADDR_FMASK); | 
|  | /* Make sure the end is within the region's mapped space */ | 
|  | mem_size = 8 * u32_get_bits(val, SHARED_MEM_SIZE_FMASK); | 
|  |  | 
|  | /* If the sizes don't match, issue a warning */ | 
|  | if (ipa->mem_offset + mem_size < ipa->mem_size) { | 
|  | dev_warn(dev, "limiting IPA memory size to 0x%08x\n", | 
|  | mem_size); | 
|  | ipa->mem_size = mem_size; | 
|  | } else if (ipa->mem_offset + mem_size > ipa->mem_size) { | 
|  | dev_dbg(dev, "ignoring larger reported memory size: 0x%08x\n", | 
|  | mem_size); | 
|  | } | 
|  |  | 
|  | /* We know our memory size; make sure regions are all in range */ | 
|  | if (!ipa_mem_size_valid(ipa)) | 
|  | return -EINVAL; | 
|  |  | 
|  | /* Prealloc DMA memory for zeroing regions */ | 
|  | virt = dma_alloc_coherent(dev, IPA_MEM_MAX, &addr, GFP_KERNEL); | 
|  | if (!virt) | 
|  | return -ENOMEM; | 
|  | ipa->zero_addr = addr; | 
|  | ipa->zero_virt = virt; | 
|  | ipa->zero_size = IPA_MEM_MAX; | 
|  |  | 
|  | /* For each defined region, write "canary" values in the | 
|  | * space prior to the region's base address if indicated. | 
|  | */ | 
|  | for (i = 0; i < ipa->mem_count; i++) { | 
|  | u16 canary_count = ipa->mem[i].canary_count; | 
|  | __le32 *canary; | 
|  |  | 
|  | if (!canary_count) | 
|  | continue; | 
|  |  | 
|  | /* Write canary values in the space before the region */ | 
|  | canary = ipa->mem_virt + ipa->mem_offset + ipa->mem[i].offset; | 
|  | do | 
|  | *--canary = IPA_MEM_CANARY_VAL; | 
|  | while (--canary_count); | 
|  | } | 
|  |  | 
|  | /* Make sure filter and route table memory regions are valid */ | 
|  | if (!ipa_table_valid(ipa)) | 
|  | goto err_dma_free; | 
|  |  | 
|  | /* Validate memory-related properties relevant to immediate commands */ | 
|  | if (!ipa_cmd_data_valid(ipa)) | 
|  | goto err_dma_free; | 
|  |  | 
|  | /* Verify the microcontroller ring alignment (if defined) */ | 
|  | mem = ipa_mem_find(ipa, IPA_MEM_UC_EVENT_RING); | 
|  | if (mem && mem->offset % 1024) { | 
|  | dev_err(dev, "microcontroller ring not 1024-byte aligned\n"); | 
|  | goto err_dma_free; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | err_dma_free: | 
|  | dma_free_coherent(dev, IPA_MEM_MAX, ipa->zero_virt, ipa->zero_addr); | 
|  |  | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* Inverse of ipa_mem_config() */ | 
|  | void ipa_mem_deconfig(struct ipa *ipa) | 
|  | { | 
|  | struct device *dev = &ipa->pdev->dev; | 
|  |  | 
|  | dma_free_coherent(dev, ipa->zero_size, ipa->zero_virt, ipa->zero_addr); | 
|  | ipa->zero_size = 0; | 
|  | ipa->zero_virt = NULL; | 
|  | ipa->zero_addr = 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ipa_mem_zero_modem() - Zero IPA-local memory regions owned by the modem | 
|  | * @ipa:	IPA pointer | 
|  | * | 
|  | * Zero regions of IPA-local memory used by the modem.  These are configured | 
|  | * (and initially zeroed) by ipa_mem_setup(), but if the modem crashes and | 
|  | * restarts via SSR we need to re-initialize them.  A QMI message tells the | 
|  | * modem where to find regions of IPA local memory it needs to know about | 
|  | * (these included). | 
|  | */ | 
|  | int ipa_mem_zero_modem(struct ipa *ipa) | 
|  | { | 
|  | struct gsi_trans *trans; | 
|  |  | 
|  | /* Get a transaction to zero the modem memory, modem header, | 
|  | * and modem processing context regions. | 
|  | */ | 
|  | trans = ipa_cmd_trans_alloc(ipa, 3); | 
|  | if (!trans) { | 
|  | dev_err(&ipa->pdev->dev, | 
|  | "no transaction to zero modem memory\n"); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | ipa_mem_zero_region_add(trans, IPA_MEM_MODEM_HEADER); | 
|  | ipa_mem_zero_region_add(trans, IPA_MEM_MODEM_PROC_CTX); | 
|  | ipa_mem_zero_region_add(trans, IPA_MEM_MODEM); | 
|  |  | 
|  | gsi_trans_commit_wait(trans); | 
|  |  | 
|  | 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 %zu\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; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * ipa_smem_init() - Initialize SMEM memory used by the IPA | 
|  | * @ipa:	IPA pointer | 
|  | * @item:	Item ID of SMEM memory | 
|  | * @size:	Size (bytes) of SMEM memory region | 
|  | * | 
|  | * SMEM is a managed block of shared DRAM, from which numbered "items" | 
|  | * can be allocated.  One item is designated for use by the IPA. | 
|  | * | 
|  | * The modem accesses SMEM memory directly, but the IPA accesses it | 
|  | * via the IOMMU, using the AP's credentials. | 
|  | * | 
|  | * If size provided is non-zero, we allocate it and map it for | 
|  | * access through the IOMMU. | 
|  | * | 
|  | * Note: @size and the item address are is not guaranteed to be page-aligned. | 
|  | */ | 
|  | static int ipa_smem_init(struct ipa *ipa, u32 item, size_t size) | 
|  | { | 
|  | struct device *dev = &ipa->pdev->dev; | 
|  | struct iommu_domain *domain; | 
|  | unsigned long iova; | 
|  | phys_addr_t phys; | 
|  | phys_addr_t addr; | 
|  | size_t actual; | 
|  | void *virt; | 
|  | int ret; | 
|  |  | 
|  | if (!size) | 
|  | return 0;	/* SMEM memory not used */ | 
|  |  | 
|  | /* SMEM is memory shared between the AP and another system entity | 
|  | * (in this case, the modem).  An allocation from SMEM is persistent | 
|  | * until the AP reboots; there is no way to free an allocated SMEM | 
|  | * region.  Allocation only reserves the space; to use it you need | 
|  | * to "get" a pointer it (this does not imply reference counting). | 
|  | * The item might have already been allocated, in which case we | 
|  | * use it unless the size isn't what we expect. | 
|  | */ | 
|  | ret = qcom_smem_alloc(QCOM_SMEM_HOST_MODEM, item, size); | 
|  | if (ret && ret != -EEXIST) { | 
|  | dev_err(dev, "error %d allocating size %zu SMEM item %u\n", | 
|  | ret, size, item); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Now get the address of the SMEM memory region */ | 
|  | virt = qcom_smem_get(QCOM_SMEM_HOST_MODEM, item, &actual); | 
|  | if (IS_ERR(virt)) { | 
|  | ret = PTR_ERR(virt); | 
|  | dev_err(dev, "error %d getting SMEM item %u\n", ret, item); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* In case the region was already allocated, verify the size */ | 
|  | if (ret && actual != size) { | 
|  | dev_err(dev, "SMEM item %u has size %zu, expected %zu\n", | 
|  | item, actual, size); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | domain = iommu_get_domain_for_dev(dev); | 
|  | if (!domain) { | 
|  | dev_err(dev, "no IOMMU domain found for SMEM\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | /* Align the address down and the size up to a page boundary */ | 
|  | addr = qcom_smem_virt_to_phys(virt) & PAGE_MASK; | 
|  | 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->smem_iova = iova; | 
|  | ipa->smem_size = size; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void ipa_smem_exit(struct ipa *ipa) | 
|  | { | 
|  | struct device *dev = &ipa->pdev->dev; | 
|  | struct iommu_domain *domain; | 
|  |  | 
|  | domain = iommu_get_domain_for_dev(dev); | 
|  | if (domain) { | 
|  | size_t size; | 
|  |  | 
|  | size = iommu_unmap(domain, ipa->smem_iova, ipa->smem_size); | 
|  | if (size != ipa->smem_size) | 
|  | dev_warn(dev, "unmapped %zu SMEM bytes, expected %zu\n", | 
|  | size, ipa->smem_size); | 
|  |  | 
|  | } else { | 
|  | dev_err(dev, "couldn't get IPA IOMMU domain for SMEM\n"); | 
|  | } | 
|  |  | 
|  | ipa->smem_size = 0; | 
|  | ipa->smem_iova = 0; | 
|  | } | 
|  |  | 
|  | /* Perform memory region-related initialization */ | 
|  | int ipa_mem_init(struct ipa *ipa, const struct ipa_mem_data *mem_data) | 
|  | { | 
|  | struct device *dev = &ipa->pdev->dev; | 
|  | struct resource *res; | 
|  | int ret; | 
|  |  | 
|  | /* Make sure the set of defined memory regions is valid */ | 
|  | if (!ipa_mem_valid(ipa, mem_data)) | 
|  | return -EINVAL; | 
|  |  | 
|  | ipa->mem_count = mem_data->local_count; | 
|  | ipa->mem = mem_data->local; | 
|  |  | 
|  | ret = dma_set_mask_and_coherent(&ipa->pdev->dev, DMA_BIT_MASK(64)); | 
|  | if (ret) { | 
|  | dev_err(dev, "error %d setting DMA mask\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | res = platform_get_resource_byname(ipa->pdev, IORESOURCE_MEM, | 
|  | "ipa-shared"); | 
|  | if (!res) { | 
|  | dev_err(dev, | 
|  | "DT error getting \"ipa-shared\" memory property\n"); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | ipa->mem_virt = memremap(res->start, resource_size(res), MEMREMAP_WC); | 
|  | if (!ipa->mem_virt) { | 
|  | dev_err(dev, "unable to remap \"ipa-shared\" memory\n"); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | ipa->mem_addr = res->start; | 
|  | ipa->mem_size = resource_size(res); | 
|  |  | 
|  | ret = ipa_imem_init(ipa, mem_data->imem_addr, mem_data->imem_size); | 
|  | if (ret) | 
|  | goto err_unmap; | 
|  |  | 
|  | ret = ipa_smem_init(ipa, mem_data->smem_id, mem_data->smem_size); | 
|  | if (ret) | 
|  | goto err_imem_exit; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | err_imem_exit: | 
|  | ipa_imem_exit(ipa); | 
|  | err_unmap: | 
|  | memunmap(ipa->mem_virt); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Inverse of ipa_mem_init() */ | 
|  | void ipa_mem_exit(struct ipa *ipa) | 
|  | { | 
|  | ipa_smem_exit(ipa); | 
|  | ipa_imem_exit(ipa); | 
|  | memunmap(ipa->mem_virt); | 
|  | } |