diff options
-rw-r--r-- | Documentation/devicetree/bindings/cnss/icnss.txt | 10 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/msmcobalt.dtsi | 5 | ||||
-rw-r--r-- | drivers/soc/qcom/icnss.c | 146 |
3 files changed, 161 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/cnss/icnss.txt b/Documentation/devicetree/bindings/cnss/icnss.txt index 08fab81f3393..7857eeb963a6 100644 --- a/Documentation/devicetree/bindings/cnss/icnss.txt +++ b/Documentation/devicetree/bindings/cnss/icnss.txt @@ -12,6 +12,11 @@ Required properties: - reg-names: Names of the memory regions defined in reg entry - interrupts: Copy engine interrupt table - qcom,wlan-msa-memory: MSA memory size + - clocks: List of clock phandles + - clock-names: List of clock names corresponding to the "clocks" property + - iommus: SMMUs and corresponding Stream IDs needed by WLAN + - qcom,wlan-smmu-iova-address: I/O virtual address range as <start length> + format to be used for allocations associated between WLAN and SMMU Optional properties: - qcom,skip-qmi: Boolean property to decide whether to use QMI or not @@ -22,6 +27,11 @@ Example: compatible = "qcom,icnss"; reg = <0x0a000000 0x1000000>; reg-names = "membase"; + clocks = <&clock_gcc clk_aggre2_noc_clk>; + clock-names = "smmu_aggre2_noc_clk"; + iommus = <&anoc2_smmu 0x1900>, + <&anoc2_smmu 0x1901>; + qcom,wlan-smmu-iova-address = <0 0x10000000>; interrupts = <0 130 0 /* CE0 */ >, <0 131 0 /* CE1 */ >, diff --git a/arch/arm/boot/dts/qcom/msmcobalt.dtsi b/arch/arm/boot/dts/qcom/msmcobalt.dtsi index 5da58c8b0554..ec90619127a8 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt.dtsi @@ -2145,6 +2145,11 @@ reg = <0x18800000 0x800000>, <0x10AC000 0x20>; reg-names = "membase", "mpm_config"; + clocks = <&clock_gcc clk_aggre2_noc_clk>; + clock-names = "smmu_aggre2_noc_clk"; + iommus = <&anoc2_smmu 0x1900>, + <&anoc2_smmu 0x1901>; + qcom,wlan-smmu-iova-address = <0xa0000000 0x10000000>; interrupts = <0 413 0 /* CE0 */ >, <0 414 0 /* CE1 */ >, <0 415 0 /* CE2 */ >, diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 887e49e3beb6..d34fdcd3b1fe 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -9,6 +9,9 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ +#include <asm/dma-iommu.h> +#include <linux/clk.h> +#include <linux/iommu.h> #include <linux/export.h> #include <linux/err.h> #include <linux/of.h> @@ -45,6 +48,7 @@ struct icnss_qmi_event { #define WLFW_SERVICE_INS_ID_V01 0 #define ICNSS_WLFW_QMI_CONNECTED BIT(0) #define ICNSS_FW_READY BIT(1) +#define SMMU_CLOCK_NAME "smmu_aggre2_noc_clk" #define ICNSS_IS_WLFW_QMI_CONNECTED(_state) \ ((_state) & ICNSS_WLFW_QMI_CONNECTED) @@ -84,6 +88,10 @@ static struct { void __iomem *mem_base_va; phys_addr_t mpm_config_pa; void __iomem *mpm_config_va; + struct dma_iommu_mapping *smmu_mapping; + dma_addr_t smmu_iova_start; + size_t smmu_iova_len; + struct clk *smmu_clk; struct qmi_handle *wlfw_clnt; struct list_head qmi_event_list; spinlock_t qmi_event_lock; @@ -1170,9 +1178,110 @@ static ssize_t icnss_wlan_mode_store(struct device *dev, static DEVICE_ATTR(icnss_wlan_mode, S_IWUSR, NULL, icnss_wlan_mode_store); +static struct clk *icnss_clock_init(struct device *dev, const char *cname) +{ + struct clk *c; + long rate; + + if (of_property_match_string(dev->of_node, "clock-names", cname) < 0) { + pr_err("%s: clock %s is not found!", __func__, cname); + return NULL; + } + + c = devm_clk_get(dev, cname); + if (IS_ERR(c)) { + pr_err("%s: couldn't get clock %s!", __func__, cname); + return NULL; + } + + if (clk_get_rate(c) == 0) { + rate = clk_round_rate(c, 1000); + clk_set_rate(c, rate); + } + + return c; +} + +static int icnss_clock_enable(struct clk *c) +{ + int ret = 0; + + ret = clk_prepare_enable(c); + + if (ret < 0) + pr_err("%s: couldn't enable clock!\n", __func__); + + return ret; +} + +static void icnss_clock_disable(struct clk *c) +{ + clk_disable_unprepare(c); +} + +static int icnss_smmu_init(struct device *dev) +{ + struct dma_iommu_mapping *mapping; + int disable_htw = 1; + int atomic_ctx = 1; + int ret = 0; + + mapping = arm_iommu_create_mapping(&platform_bus_type, + penv->smmu_iova_start, + penv->smmu_iova_len); + if (IS_ERR(mapping)) { + pr_err("%s: create mapping failed, err = %d\n", __func__, ret); + ret = PTR_ERR(mapping); + goto map_fail; + } + + ret = iommu_domain_set_attr(mapping->domain, + DOMAIN_ATTR_COHERENT_HTW_DISABLE, + &disable_htw); + if (ret < 0) { + pr_err("%s: set disable_htw attribute failed, err = %d\n", + __func__, ret); + goto set_attr_fail; + } + + ret = iommu_domain_set_attr(mapping->domain, + DOMAIN_ATTR_ATOMIC, + &atomic_ctx); + if (ret < 0) { + pr_err("%s: set atomic_ctx attribute failed, err = %d\n", + __func__, ret); + goto set_attr_fail; + } + + ret = arm_iommu_attach_device(dev, mapping); + if (ret < 0) { + pr_err("%s: attach device failed, err = %d\n", __func__, ret); + goto attach_fail; + } + + penv->smmu_mapping = mapping; + + return ret; + +attach_fail: +set_attr_fail: + arm_iommu_release_mapping(mapping); +map_fail: + return ret; +} + +static void icnss_smmu_remove(struct device *dev) +{ + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(penv->smmu_mapping); + + penv->smmu_mapping = NULL; +} + static int icnss_probe(struct platform_device *pdev) { int ret = 0; + u32 smmu_iova_address[2]; struct resource *res; int i; struct device *dev = &pdev->dev; @@ -1247,6 +1356,30 @@ static int icnss_probe(struct platform_device *pdev) goto unmap_mpm_config; } + if (of_property_read_u32_array(pdev->dev.of_node, + "qcom,wlan-smmu-iova-address", + smmu_iova_address, 2) == 0) { + penv->smmu_iova_start = smmu_iova_address[0]; + penv->smmu_iova_len = smmu_iova_address[1]; + + ret = icnss_smmu_init(&pdev->dev); + if (ret < 0) { + pr_err("%s: SMMU init failed, err = %d\n", + __func__, ret); + goto err_smmu_init; + } + + penv->smmu_clk = icnss_clock_init(&pdev->dev, SMMU_CLOCK_NAME); + if (penv->smmu_clk) { + ret = icnss_clock_enable(penv->smmu_clk); + if (ret < 0) { + pr_err("%s: SMMU clock enable failed!\n", + __func__); + goto err_smmu_clock_enable; + } + } + } + penv->skip_qmi = of_property_read_bool(dev->of_node, "qcom,skip-qmi"); @@ -1289,6 +1422,12 @@ err_qmi: err_workqueue: device_remove_file(&pdev->dev, &dev_attr_icnss_wlan_mode); err_wlan_mode: + if (penv->smmu_clk) + icnss_clock_disable(penv->smmu_clk); +err_smmu_clock_enable: + if (penv->smmu_mapping) + icnss_smmu_remove(&pdev->dev); +err_smmu_init: if (penv->msa_va) dma_free_coherent(&pdev->dev, penv->msa_mem_size, penv->msa_va, penv->msa_pa); @@ -1311,6 +1450,13 @@ static int icnss_remove(struct platform_device *pdev) if (penv->qmi_event_wq) destroy_workqueue(penv->qmi_event_wq); device_remove_file(&pdev->dev, &dev_attr_icnss_wlan_mode); + + if (penv->smmu_mapping) { + if (penv->smmu_clk) + icnss_clock_disable(penv->smmu_clk); + icnss_smmu_remove(&pdev->dev); + } + if (penv->msa_va) dma_free_coherent(&pdev->dev, penv->msa_mem_size, penv->msa_va, penv->msa_pa); |