From c8d3106ddc44379a788f80d0cc4f83acc606b887 Mon Sep 17 00:00:00 2001 From: Jia Ding Date: Fri, 28 Aug 2020 16:14:33 +0800 Subject: [PATCH] cnss2: Add cnss_smmu_unmap API Add cnss_smmu_unmap API so that wlan function driver could unmap IOVA addresses that are IOMMU mapped. Change-Id: I1e3aa287589f73a609cc51fe8c826d1edf2fefd1 Signed-off-by: Jia Ding --- drivers/net/wireless/cnss2/pci.c | 53 +++++++++++++++++++++++++++++--- drivers/net/wireless/cnss2/pci.h | 2 ++ include/net/cnss2.h | 1 + 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index c5a3dbdce390..1128a25621c6 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -3462,12 +3462,17 @@ static int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) "smmu_iova_ipa"); if (res) { pci_priv->smmu_iova_ipa_start = res->start; + pci_priv->smmu_iova_ipa_current = res->start; pci_priv->smmu_iova_ipa_len = resource_size(res); cnss_pr_dbg("smmu_iova_ipa_start: %pa, smmu_iova_ipa_len: 0x%zx\n", &pci_priv->smmu_iova_ipa_start, pci_priv->smmu_iova_ipa_len); } + pci_priv->iommu_geometry = of_property_read_bool(of_node, + "qcom,iommu-geometry"); + cnss_pr_dbg("iommu_geometry: %d\n", pci_priv->iommu_geometry); + of_node_put(of_node); return 0; @@ -3542,10 +3547,11 @@ int cnss_smmu_map(struct device *dev, plat_priv = pci_priv->plat_priv; len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE); - iova = roundup(pci_priv->smmu_iova_ipa_start, PAGE_SIZE); + iova = roundup(pci_priv->smmu_iova_ipa_current, PAGE_SIZE); - if (iova >= - (pci_priv->smmu_iova_ipa_start + pci_priv->smmu_iova_ipa_len)) { + if (pci_priv->iommu_geometry && + iova >= pci_priv->smmu_iova_ipa_start + + pci_priv->smmu_iova_ipa_len) { cnss_pr_err("No IOVA space to map, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n", iova, &pci_priv->smmu_iova_ipa_start, @@ -3572,6 +3578,8 @@ int cnss_smmu_map(struct device *dev, } } + cnss_pr_dbg("IOMMU map: iova %lx, len %zu\n", iova, len); + ret = iommu_map(pci_priv->iommu_domain, iova, rounddown(paddr, PAGE_SIZE), len, flag); if (ret) { @@ -3579,13 +3587,50 @@ int cnss_smmu_map(struct device *dev, return ret; } - pci_priv->smmu_iova_ipa_start = iova + len; + pci_priv->smmu_iova_ipa_current = iova + len; *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE)); + cnss_pr_dbg("IOMMU map: iova_addr %lx\n", *iova_addr); return 0; } EXPORT_SYMBOL(cnss_smmu_map); +int cnss_smmu_unmap(struct device *dev, uint32_t iova_addr, size_t size) +{ + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev)); + unsigned long iova; + size_t unmapped; + size_t len; + + if (!pci_priv) + return -ENODEV; + + iova = rounddown(iova_addr, PAGE_SIZE); + len = roundup(size + iova_addr - iova, PAGE_SIZE); + + if (iova >= pci_priv->smmu_iova_ipa_start + + pci_priv->smmu_iova_ipa_len) { + cnss_pr_err("Out of IOVA space to unmap, iova %lx, smmu_iova_ipa_start %pad, smmu_iova_ipa_len %zu\n", + iova, + &pci_priv->smmu_iova_ipa_start, + pci_priv->smmu_iova_ipa_len); + return -ENOMEM; + } + + cnss_pr_dbg("IOMMU unmap: iova %lx, len %zu\n", iova, len); + + unmapped = iommu_unmap(pci_priv->iommu_domain, iova, len); + if (unmapped != len) { + cnss_pr_err("IOMMU unmap failed, unmapped = %zu, requested = %zu\n", + unmapped, len); + return -EINVAL; + } + + pci_priv->smmu_iova_ipa_current = iova; + return 0; +} +EXPORT_SYMBOL(cnss_smmu_unmap); + int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) { struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev)); diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index 8bd024575528..ae0beda5eeb1 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -83,6 +83,7 @@ struct cnss_pci_data { dma_addr_t smmu_iova_start; size_t smmu_iova_len; dma_addr_t smmu_iova_ipa_start; + dma_addr_t smmu_iova_ipa_current; size_t smmu_iova_ipa_len; void __iomem *bar; struct cnss_msi_config *msi_config; @@ -101,6 +102,7 @@ struct cnss_pci_data { u32 pcie_reg_size; struct cnss_misc_reg *wlaon_reg; u32 wlaon_reg_size; + u8 iommu_geometry; }; static inline void cnss_set_pci_priv(struct pci_dev *pci_dev, void *data) diff --git a/include/net/cnss2.h b/include/net/cnss2.h index 6700fd18a554..8758febdfd78 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -196,6 +196,7 @@ extern int cnss_get_platform_cap(struct device *dev, extern struct iommu_domain *cnss_smmu_get_domain(struct device *dev); extern int cnss_smmu_map(struct device *dev, phys_addr_t paddr, uint32_t *iova_addr, size_t size); +extern int cnss_smmu_unmap(struct device *dev, uint32_t iova_addr, size_t size); extern int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info); extern int cnss_request_bus_bandwidth(struct device *dev, int bandwidth); extern int cnss_power_up(struct device *dev);