When an IOMMU device is available on the platform bus, allocate an IOMMU domain and attach the wlan target to it. WCN3990 target can then attach an DMA I/O virtual address space to scan out of bound transactions. Signed-off-by: Govind Singh <govinds@xxxxxxxxxxxxxx> --- drivers/net/wireless/ath/ath10k/snoc.c | 100 ++++++++++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/snoc.h | 3 + 2 files changed, 101 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index cd21b25..502263d 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -26,6 +26,10 @@ #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/clk.h> +#include <asm/dma-iommu.h> +#include <linux/iommu.h> +#include <linux/dma-mapping.h> + #define WCN3990_CE_ATTR_FLAGS 0 #define ATH10K_SNOC_RX_POST_RETRY_MS 50 #define CE_POLL_PIPE 4 @@ -1292,6 +1296,88 @@ static int ath10k_hw_power_off(struct ath10k *ar) return ret; } +static int ath10k_smmu_attach(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + struct dma_iommu_mapping *mapping; + struct platform_device *pdev; + int ret = 0; + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "Initializing SMMU\n"); + + pdev = ar_snoc->dev; + mapping = arm_iommu_create_mapping(&platform_bus_type, + ar_snoc->smmu_iova_start, + ar_snoc->smmu_iova_len); + if (IS_ERR(mapping)) { + ath10k_err(ar, "create mapping failed, err = %d\n", ret); + ret = PTR_ERR(mapping); + goto map_fail; + } + + ret = arm_iommu_attach_device(&pdev->dev, mapping); + if (ret < 0 && ret != -EEXIST) { + ath10k_err(ar, "iommu attach device failed, err = %d\n", ret); + goto attach_fail; + } else if (ret == -EEXIST) { + ret = 0; + } + + ar_snoc->smmu_mapping = mapping; + + return ret; + +attach_fail: + arm_iommu_release_mapping(mapping); +map_fail: + return ret; +} + +static void ath10k_smmu_deinit(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + struct platform_device *pdev; + + pdev = ar_snoc->dev; + + if (!ar_snoc->smmu_mapping) + return; + + arm_iommu_detach_device(&pdev->dev); + arm_iommu_release_mapping(ar_snoc->smmu_mapping); + + ar_snoc->smmu_mapping = NULL; +} + +static int ath10k_smmu_init(struct ath10k *ar) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + struct platform_device *pdev; + struct resource *res; + int ret = 0; + + pdev = ar_snoc->dev; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "smmu_iova_base"); + if (!res) { + ath10k_err(ar, "SMMU iova base not found\n"); + } else { + ar_snoc->smmu_iova_start = res->start; + ar_snoc->smmu_iova_len = resource_size(res); + ath10k_dbg(ar, ATH10K_DBG_SNOC, "SMMU iova start: %pa, len: %zu\n", + &ar_snoc->smmu_iova_start, ar_snoc->smmu_iova_len); + + ret = ath10k_smmu_attach(ar); + if (ret < 0) { + ath10k_err(ar, "SMMU init failed, err = %d, start: %pad, len: %zx\n", + ret, &ar_snoc->smmu_iova_start, + ar_snoc->smmu_iova_len); + } + } + + return ret; +} + static const struct of_device_id ath10k_snoc_dt_match[] = { { .compatible = "qcom,wcn3990-wifi", .data = &drv_priv, @@ -1339,16 +1425,22 @@ static int ath10k_snoc_probe(struct platform_device *pdev) ar_snoc->ce.bus_ops = &ath10k_snoc_bus_ops; ar->ce_priv = &ar_snoc->ce; + ret = ath10k_smmu_init(ar); + if (ret) { + ath10k_warn(ar, "failed to int SMMU: %d\n", ret); + goto err_core_destroy; + } + ath10k_snoc_resource_init(ar); if (ret) { ath10k_warn(ar, "failed to initialize resource: %d\n", ret); - goto err_core_destroy; + goto err_smmu_deinit; } ath10k_snoc_setup_resource(ar); if (ret) { ath10k_warn(ar, "failed to setup resource: %d\n", ret); - goto err_core_destroy; + goto err_smmu_deinit; } ret = ath10k_snoc_request_irq(ar); if (ret) { @@ -1396,6 +1488,9 @@ static int ath10k_snoc_probe(struct platform_device *pdev) err_release_resource: ath10k_snoc_release_resource(ar); +err_smmu_deinit: + ath10k_smmu_deinit(ar); + err_core_destroy: ath10k_core_destroy(ar); @@ -1409,6 +1504,7 @@ static int ath10k_snoc_remove(struct platform_device *pdev) ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc remove\n"); ath10k_core_unregister(ar); ath10k_hw_power_off(ar); + ath10k_smmu_deinit(ar); ath10k_snoc_free_irq(ar); ath10k_snoc_release_resource(ar); ath10k_core_destroy(ar); diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h index 05dc98f..a9bca2e 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.h +++ b/drivers/net/wireless/ath/ath10k/snoc.h @@ -82,6 +82,9 @@ struct ath10k_snoc { struct timer_list rx_post_retry; struct ath10k_wcn3990_vreg_info *vreg; struct ath10k_wcn3990_clk_info *clk; + struct dma_iommu_mapping *smmu_mapping; + dma_addr_t smmu_iova_start; + size_t smmu_iova_len; }; static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar) -- 1.9.1