icnss: Add SMMU support
Add SMMU support for WLAN. Config it as stage-1 enable by default. Change-Id: I70db6555d236857c5a8d62a337afdc9fec22c97f CRs-fixed: 1009865 Signed-off-by: Yue Ma <yuem@codeaurora.org>
This commit is contained in:
parent
9f53b1a0e7
commit
ec4d9af2e7
3 changed files with 161 additions and 0 deletions
|
@ -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 */ >,
|
||||
|
|
|
@ -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 */ >,
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue