From 26eff67153cf547158db519817fb61dfd6e9da30 Mon Sep 17 00:00:00 2001 From: Frank Liu Date: Fri, 27 Jul 2018 14:32:53 +0800 Subject: [PATCH] cnss2: Add support for PCIe WLAN IPA uc SMMU feature To add support for PCIe WLAN IPA uc SMMU feature, prvoide related platform api for wlan driver to get the smmu map handle and do the mapping. Change-Id: I672b1a48879ada65b3ddb3f16c4bd787dc1b70a6 Signed-off-by: Frank Liu --- drivers/net/wireless/cnss2/pci.c | 66 ++++++++++++++++++++++++++++++++ drivers/net/wireless/cnss2/pci.h | 2 + include/net/cnss2.h | 3 ++ 3 files changed, 71 insertions(+) diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index ff053b098c22..0f4ef3712dd7 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -1414,6 +1414,61 @@ void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv) CNSS_REASON_TIMEOUT); } +struct dma_iommu_mapping *cnss_smmu_get_mapping(struct device *dev) +{ + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev)); + + if (!pci_priv) + return NULL; + + return pci_priv->smmu_mapping; +} +EXPORT_SYMBOL(cnss_smmu_get_mapping); + +int cnss_smmu_map(struct device *dev, + phys_addr_t paddr, 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 len; + int ret = 0; + + if (!pci_priv) + return -ENODEV; + + if (!iova_addr) { + cnss_pr_err("iova_addr is NULL, paddr %pa, size %zu\n", + &paddr, size); + return -EINVAL; + } + + len = roundup(size + paddr - rounddown(paddr, PAGE_SIZE), PAGE_SIZE); + iova = roundup(pci_priv->smmu_iova_ipa_start, PAGE_SIZE); + + if (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, + pci_priv->smmu_iova_ipa_len); + return -ENOMEM; + } + + ret = iommu_map(pci_priv->smmu_mapping->domain, iova, + rounddown(paddr, PAGE_SIZE), len, + IOMMU_READ | IOMMU_WRITE); + if (ret) { + cnss_pr_err("PA to IOVA mapping failed, ret %d\n", ret); + return ret; + } + + pci_priv->smmu_iova_ipa_start = iova + len; + *iova_addr = (uint32_t)(iova + paddr - rounddown(paddr, PAGE_SIZE)); + + return 0; +} +EXPORT_SYMBOL(cnss_smmu_map); + int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) { int ret = 0; @@ -2129,6 +2184,17 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, &pci_priv->smmu_iova_start, pci_priv->smmu_iova_len); + res = platform_get_resource_byname(plat_priv->plat_dev, + IORESOURCE_MEM, + "smmu_iova_ipa"); + if (res) { + pci_priv->smmu_iova_ipa_start = res->start; + pci_priv->smmu_iova_ipa_len = resource_size(res); + cnss_pr_dbg("smmu_iova_ipa_start: %pa, smmu_iova_ipa_len: %zu\n", + &pci_priv->smmu_iova_ipa_start, + pci_priv->smmu_iova_ipa_len); + } + ret = cnss_pci_init_smmu(pci_priv); if (ret) { cnss_pr_err("Failed to init SMMU, err = %d\n", ret); diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index 182355ae7577..e47f14e8a325 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -73,6 +73,8 @@ struct cnss_pci_data { struct dma_iommu_mapping *smmu_mapping; dma_addr_t smmu_iova_start; size_t smmu_iova_len; + dma_addr_t smmu_iova_ipa_start; + size_t smmu_iova_ipa_len; void __iomem *bar; struct cnss_msi_config *msi_config; u32 msi_ep_base_data; diff --git a/include/net/cnss2.h b/include/net/cnss2.h index f80d87533a99..53f73d436752 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -171,6 +171,9 @@ extern int cnss_get_fw_files_for_target(struct device *dev, u32 target_type, u32 target_version); extern int cnss_get_platform_cap(struct device *dev, struct cnss_platform_cap *cap); +extern struct dma_iommu_mapping *cnss_smmu_get_mapping(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_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);