summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChaoli Zhou <quic_zchaoli@quicinc.com>2023-01-18 11:31:32 +0800
committerChaoli Zhou <quic_zchaoli@quicinc.com>2023-01-31 16:53:31 +0800
commitf2346285bc0009795abbe0d0c58d5861216eee55 (patch)
tree94a87e5c36db40b3a500e4ae52a1b1c361fb3865
parent436eadfdaa2e740bfdbd1f2254df1412d75a6ebf (diff)
downloadwlan-platform-f2346285bc0009795abbe0d0c58d5861216eee55.tar.gz
cnss2: Support dual wlan cards managed by cnss2 platform driver
Currently, the cnss2 platform driver just can support one wlan device attached, so do the following changes to cover dual wlan card attach case. Firstly, delay the second device go to pcie link suspend and power off state to make sure it really finished the pcie enumeration. Then pcie enumeration succeeds with multi-devices. Secondly, supporting to write the qrtr node instance id to PCIE register for wlan fw reading, which can fix qmi message exchange failure if active two Hastings devices. Thirdly, change the usage of plat_env to support dual Hastings. Change-Id: Ica41a23d4e983b91c0ff1b4e76b380803fb877ab
-rw-r--r--Kbuild4
-rw-r--r--cnss2/Kconfig8
-rw-r--r--cnss2/debug.c11
-rw-r--r--cnss2/main.c241
-rw-r--r--cnss2/main.h21
-rw-r--r--cnss2/pci.c280
-rw-r--r--cnss2/pci.h6
-rw-r--r--cnss2/qmi.c19
-rw-r--r--cnss2/reg.h1
9 files changed, 549 insertions, 42 deletions
diff --git a/Kbuild b/Kbuild
index 0643c05..e273439 100644
--- a/Kbuild
+++ b/Kbuild
@@ -36,6 +36,10 @@ ifeq ($(CONFIG_CNSS2_CONDITIONAL_POWEROFF),y)
KBUILD_CPPFLAGS += -DCONFIG_CNSS2_CONDITIONAL_POWEROFF
endif
+ifeq ($(CONFIG_CNSS_SUPPORT_DUAL_DEV),y)
+KBUILD_CPPFLAGS += -DCONFIG_CNSS_SUPPORT_DUAL_DEV
+endif
+
ifeq ($(CONFIG_AUTO_PROJECT),y)
KBUILD_CPPFLAGS += -DCONFIG_PULLDOWN_WLANEN
endif
diff --git a/cnss2/Kconfig b/cnss2/Kconfig
index a71b48f..09747fc 100644
--- a/cnss2/Kconfig
+++ b/cnss2/Kconfig
@@ -97,6 +97,14 @@ config CNSS_REQ_FW_DIRECT
configuration file to avoid 60s timeout while search file under user
space failure.
+config CNSS_SUPPORT_DUAL_DEV
+ bool "Enable cnss2 support dual wlan card"
+ depends on CNSS2 && !CNSS_ASYNC
+ help
+ This enables the changes from cnss2 platform driver to support dual
+ wlan card attach. Now just supports QCA6390 chip and does not support
+ asynchronous probe.
+
config CNSS2_CONDITIONAL_POWEROFF
bool "Enable/Disable conditional bus suspend and device power off"
depends on CNSS2
diff --git a/cnss2/debug.c b/cnss2/debug.c
index 59054ca..0ab9f57 100644
--- a/cnss2/debug.c
+++ b/cnss2/debug.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
#include <linux/err.h>
@@ -944,8 +944,15 @@ int cnss_debugfs_create(struct cnss_plat_data *plat_priv)
{
int ret = 0;
struct dentry *root_dentry;
+ char name[CNSS_FS_NAME_SIZE];
- root_dentry = debugfs_create_dir("cnss", 0);
+ if (cnss_is_dual_wlan_enabled())
+ snprintf(name, CNSS_FS_NAME_SIZE, CNSS_FS_NAME "_%d",
+ plat_priv->plat_idx);
+ else
+ snprintf(name, CNSS_FS_NAME_SIZE, CNSS_FS_NAME);
+
+ root_dentry = debugfs_create_dir(name, 0);
if (IS_ERR(root_dentry)) {
ret = PTR_ERR(root_dentry);
cnss_pr_err("Unable to create debugfs %d\n", ret);
diff --git a/cnss2/main.c b/cnss2/main.c
index 5516746..81bc2c1 100644
--- a/cnss2/main.c
+++ b/cnss2/main.c
@@ -88,7 +88,13 @@ enum cnss_recovery_type {
CNSS_PCSS_RECOVERY = 0x2,
};
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+#define CNSS_MAX_DEV_NUM 2
+static struct cnss_plat_data *plat_env[CNSS_MAX_DEV_NUM];
+static int plat_env_count;
+#else
static struct cnss_plat_data *plat_env;
+#endif
static bool cnss_allow_driver_loading;
@@ -111,15 +117,122 @@ struct cnss_driver_event {
void *data;
};
+bool cnss_check_driver_loading_allowed(void)
+{
+ return cnss_allow_driver_loading;
+}
+
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
static void cnss_set_plat_priv(struct platform_device *plat_dev,
struct cnss_plat_data *plat_priv)
{
- plat_env = plat_priv;
+ cnss_pr_dbg("Set plat_priv at %d", plat_env_count);
+ if (plat_priv) {
+ plat_priv->plat_idx = plat_env_count;
+ plat_env[plat_priv->plat_idx] = plat_priv;
+ plat_env_count++;
+ }
}
-bool cnss_check_driver_loading_allowed(void)
+struct cnss_plat_data *cnss_get_plat_priv(struct platform_device
+ *plat_dev)
{
- return cnss_allow_driver_loading;
+ int i;
+
+ if (!plat_dev)
+ return NULL;
+
+ for (i = 0; i < plat_env_count; i++) {
+ if (plat_env[i]->plat_dev == plat_dev)
+ return plat_env[i];
+ }
+ return NULL;
+}
+
+static void cnss_clear_plat_priv(struct cnss_plat_data *plat_priv)
+{
+ cnss_pr_dbg("Clear plat_priv at %d", plat_priv->plat_idx);
+ plat_env[plat_priv->plat_idx] = NULL;
+ plat_env_count--;
+}
+
+static int cnss_set_device_name(struct cnss_plat_data *plat_priv)
+{
+ snprintf(plat_priv->device_name, sizeof(plat_priv->device_name),
+ "wlan_%d", plat_priv->plat_idx);
+
+ return 0;
+}
+
+static int cnss_plat_env_available(void)
+{
+ int ret = 0;
+
+ if (plat_env_count >= CNSS_MAX_DEV_NUM) {
+ cnss_pr_err("ERROR: No space to store plat_priv\n");
+ ret = -ENOMEM;
+ }
+ return ret;
+}
+
+int cnss_get_plat_env_count(void)
+{
+ return plat_env_count;
+}
+
+struct cnss_plat_data *cnss_get_plat_env(int index)
+{
+ return plat_env[index];
+}
+
+struct cnss_plat_data *cnss_get_plat_priv_by_rc_num(int rc_num)
+{
+ int i;
+
+ for (i = 0; i < plat_env_count; i++) {
+ if (plat_env[i]->rc_num == rc_num)
+ return plat_env[i];
+ }
+ return NULL;
+}
+
+static inline int
+cnss_get_qrtr_node_id(struct cnss_plat_data *plat_priv)
+{
+ return of_property_read_u32(plat_priv->dev_node,
+ "qcom,qrtr_node_id", &plat_priv->qrtr_node_id);
+}
+
+void cnss_get_qrtr_info(struct cnss_plat_data *plat_priv)
+{
+ int ret = 0;
+
+ ret = cnss_get_qrtr_node_id(plat_priv);
+ if (ret) {
+ cnss_pr_warn("Failed to find qrtr_node_id err=%d\n", ret);
+ plat_priv->qrtr_node_id = 0;
+ plat_priv->wlfw_service_instance_id = 0;
+ } else {
+ plat_priv->wlfw_service_instance_id = plat_priv->qrtr_node_id +
+ QRTR_NODE_FW_ID_BASE;
+ cnss_pr_dbg("service_instance_id=0x%x\n",
+ plat_priv->wlfw_service_instance_id);
+ }
+}
+
+static inline int
+cnss_get_pld_bus_ops_name(struct cnss_plat_data *plat_priv)
+{
+ return of_property_read_string(plat_priv->plat_dev->dev.of_node,
+ "qcom,pld_bus_ops_name",
+ &plat_priv->pld_bus_ops_name);
+}
+
+#else
+static void cnss_set_plat_priv(struct platform_device *plat_dev,
+ struct cnss_plat_data *plat_priv)
+{
+ plat_env = plat_priv;
}
struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev)
@@ -127,6 +240,51 @@ struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev)
return plat_env;
}
+static void cnss_clear_plat_priv(struct cnss_plat_data *plat_priv)
+{
+ plat_env = NULL;
+}
+
+static int cnss_set_device_name(struct cnss_plat_data *plat_priv)
+{
+ snprintf(plat_priv->device_name, sizeof(plat_priv->device_name),
+ "wlan");
+ return 0;
+}
+
+static int cnss_plat_env_available(void)
+{
+ return 0;
+}
+
+struct cnss_plat_data *cnss_get_plat_priv_by_rc_num(int rc_num)
+{
+ return cnss_bus_dev_to_plat_priv(NULL);
+}
+
+void cnss_get_qrtr_info(struct cnss_plat_data *plat_priv)
+{
+}
+
+static int
+cnss_get_pld_bus_ops_name(struct cnss_plat_data *plat_priv)
+{
+ return 0;
+}
+#endif
+
+static inline int
+cnss_get_rc_num(struct cnss_plat_data *plat_priv)
+{
+ return of_property_read_u32(plat_priv->plat_dev->dev.of_node,
+ "qcom,wlan-rc-num", &plat_priv->rc_num);
+}
+
+bool cnss_is_dual_wlan_enabled(void)
+{
+ return IS_ENABLED(CONFIG_CNSS_SUPPORT_DUAL_DEV);
+}
+
/**
* cnss_get_mem_seg_count - Get segment count of memory
* @type: memory type
@@ -2482,7 +2640,7 @@ int cnss_register_subsys(struct cnss_plat_data *plat_priv)
subsys_info = &plat_priv->subsys_info;
- subsys_info->subsys_desc.name = "wlan";
+ subsys_info->subsys_desc.name = plat_priv->device_name;
subsys_info->subsys_desc.owner = THIS_MODULE;
subsys_info->subsys_desc.powerup = cnss_subsys_powerup;
subsys_info->subsys_desc.shutdown = cnss_subsys_shutdown;
@@ -3861,8 +4019,21 @@ static int cnss_create_sysfs_link(struct cnss_plat_data *plat_priv)
{
struct device *dev = &plat_priv->plat_dev->dev;
int ret;
+ char cnss_name[CNSS_FS_NAME_SIZE];
+ char shutdown_name[32];
+
+ if (cnss_is_dual_wlan_enabled()) {
+ snprintf(cnss_name, CNSS_FS_NAME_SIZE,
+ CNSS_FS_NAME "_%d", plat_priv->plat_idx);
+ snprintf(shutdown_name, sizeof(shutdown_name),
+ "shutdown_wlan_%d", plat_priv->plat_idx);
+ } else {
+ snprintf(cnss_name, CNSS_FS_NAME_SIZE, CNSS_FS_NAME);
+ snprintf(shutdown_name, sizeof(shutdown_name),
+ "shutdown_wlan");
+ }
- ret = sysfs_create_link(kernel_kobj, &dev->kobj, "cnss");
+ ret = sysfs_create_link(kernel_kobj, &dev->kobj, cnss_name);
if (ret) {
cnss_pr_err("Failed to create cnss link, err = %d\n",
ret);
@@ -3870,7 +4041,7 @@ static int cnss_create_sysfs_link(struct cnss_plat_data *plat_priv)
}
/* This is only for backward compatibility. */
- ret = sysfs_create_link(kernel_kobj, &dev->kobj, "shutdown_wlan");
+ ret = sysfs_create_link(kernel_kobj, &dev->kobj, shutdown_name);
if (ret) {
cnss_pr_err("Failed to create shutdown_wlan link, err = %d\n",
ret);
@@ -3880,15 +4051,29 @@ static int cnss_create_sysfs_link(struct cnss_plat_data *plat_priv)
return 0;
rm_cnss_link:
- sysfs_remove_link(kernel_kobj, "cnss");
+ sysfs_remove_link(kernel_kobj, cnss_name);
out:
return ret;
}
static void cnss_remove_sysfs_link(struct cnss_plat_data *plat_priv)
{
- sysfs_remove_link(kernel_kobj, "shutdown_wlan");
- sysfs_remove_link(kernel_kobj, "cnss");
+ char cnss_name[CNSS_FS_NAME_SIZE];
+ char shutdown_name[32];
+
+ if (cnss_is_dual_wlan_enabled()) {
+ snprintf(cnss_name, CNSS_FS_NAME_SIZE,
+ CNSS_FS_NAME "_%d", plat_priv->plat_idx);
+ snprintf(shutdown_name, sizeof(shutdown_name),
+ "shutdown_wlan_%d", plat_priv->plat_idx);
+ } else {
+ snprintf(cnss_name, CNSS_FS_NAME_SIZE, CNSS_FS_NAME);
+ snprintf(shutdown_name, sizeof(shutdown_name),
+ "shutdown_wlan");
+ }
+
+ sysfs_remove_link(kernel_kobj, shutdown_name);
+ sysfs_remove_link(kernel_kobj, cnss_name);
}
static int cnss_create_sysfs(struct cnss_plat_data *plat_priv)
@@ -4371,6 +4556,10 @@ static int cnss_probe(struct platform_device *plat_dev)
goto out;
}
+ ret = cnss_plat_env_available();
+ if (ret)
+ goto out;
+
of_id = of_match_device(cnss_of_match_table, &plat_dev->dev);
if (!of_id || !of_id->data) {
cnss_pr_err("Failed to find of match device!\n");
@@ -4403,10 +4592,23 @@ static int cnss_probe(struct platform_device *plat_dev)
goto reset_plat_dev;
}
+ ret = cnss_get_pld_bus_ops_name(plat_priv);
+ if (ret)
+ cnss_pr_err("Failed to find bus ops name, err = %d\n",
+ ret);
+
+ ret = cnss_get_rc_num(plat_priv);
+
+ if (ret)
+ cnss_pr_err("Failed to find PCIe RC number, err = %d\n", ret);
+
+ cnss_pr_dbg("rc_num=%d\n", plat_priv->rc_num);
+
plat_priv->bus_type = cnss_get_bus_type(plat_priv);
plat_priv->use_nv_mac = cnss_use_nv_mac(plat_priv);
plat_priv->driver_mode = CNSS_DRIVER_MODE_MAX;
cnss_set_plat_priv(plat_dev, plat_priv);
+ cnss_set_device_name(plat_priv);
platform_set_drvdata(plat_dev, plat_priv);
INIT_LIST_HEAD(&plat_priv->vreg_list);
INIT_LIST_HEAD(&plat_priv->clk_list);
@@ -4439,13 +4641,9 @@ static int cnss_probe(struct platform_device *plat_dev)
if (ret)
goto remove_sysfs;
- ret = cnss_qmi_init(plat_priv);
- if (ret)
- goto deinit_event_work;
-
ret = cnss_dms_init(plat_priv);
if (ret)
- goto deinit_qmi;
+ goto deinit_event_work;
ret = cnss_debugfs_create(plat_priv);
if (ret)
@@ -4472,10 +4670,6 @@ static int cnss_probe(struct platform_device *plat_dev)
cnss_register_coex_service(plat_priv);
cnss_register_ims_service(plat_priv);
- ret = cnss_genl_init();
- if (ret < 0)
- cnss_pr_err("CNSS genl init failed %d\n", ret);
-
cnss_pr_info("Platform driver probed successfully.\n");
return 0;
@@ -4486,8 +4680,6 @@ destroy_debugfs:
cnss_debugfs_destroy(plat_priv);
deinit_dms:
cnss_dms_deinit(plat_priv);
-deinit_qmi:
- cnss_qmi_deinit(plat_priv);
deinit_event_work:
cnss_event_work_deinit(plat_priv);
remove_sysfs:
@@ -4501,7 +4693,7 @@ free_res:
reset_ctx:
platform_set_drvdata(plat_dev, NULL);
reset_plat_dev:
- cnss_set_plat_priv(plat_dev, NULL);
+ cnss_clear_plat_priv(plat_priv);
out:
return ret;
}
@@ -4530,7 +4722,7 @@ static int cnss_remove(struct platform_device *plat_dev)
mbox_free_channel(plat_priv->mbox_chan);
platform_set_drvdata(plat_dev, NULL);
- plat_env = NULL;
+ cnss_clear_plat_priv(plat_priv);
return 0;
}
@@ -4599,11 +4791,16 @@ static int __init cnss_initialize(void)
if (ret)
cnss_debug_deinit();
+ ret = cnss_genl_init();
+ if (ret < 0)
+ cnss_pr_err("CNSS genl init failed %d\n", ret);
+
return ret;
}
static void __exit cnss_exit(void)
{
+ cnss_genl_exit();
platform_driver_unregister(&cnss_platform_driver);
cnss_debug_deinit();
}
diff --git a/cnss2/main.h b/cnss2/main.h
index 460b73d..1b4bf3e 100644
--- a/cnss2/main.h
+++ b/cnss2/main.h
@@ -55,7 +55,17 @@
#define CNSS_RAMDUMP_VERSION 0
#define MAX_FIRMWARE_NAME_LEN 40
#define FW_V2_NUMBER 2
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+#define POWER_ON_RETRY_MAX_TIMES 2
+#else
#define POWER_ON_RETRY_MAX_TIMES 4
+#endif
+#define POWER_ON_RETRY_DELAY_MS 500
+#define CNSS_FS_NAME "cnss"
+#define CNSS_FS_NAME_SIZE 15
+#define CNSS_DEVICE_NAME_SIZE 16
+#define QRTR_NODE_FW_ID_BASE 7
+
#define POWER_ON_RETRY_DELAY_MS 500
#define WLFW_MAX_HANG_EVENT_DATA_SIZE 384
@@ -578,6 +588,12 @@ struct cnss_plat_data {
uint32_t num_shadow_regs_v3;
bool sec_peri_feature_disable;
struct device_node *dev_node;
+ char device_name[CNSS_DEVICE_NAME_SIZE];
+ u32 plat_idx;
+ bool enumerate_done;
+ int qrtr_node_id;
+ unsigned int wlfw_service_instance_id;
+ const char *pld_bus_ops_name;
};
#if IS_ENABLED(CONFIG_ARCH_QCOM)
@@ -605,6 +621,11 @@ int cnss_wlan_hw_enable(void);
struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev);
void cnss_pm_stay_awake(struct cnss_plat_data *plat_priv);
void cnss_pm_relax(struct cnss_plat_data *plat_priv);
+struct cnss_plat_data *cnss_get_plat_priv_by_rc_num(int rc_num);
+int cnss_get_plat_env_count(void);
+struct cnss_plat_data *cnss_get_plat_env(int index);
+void cnss_get_qrtr_info(struct cnss_plat_data *plat_priv);
+bool cnss_is_dual_wlan_enabled(void);
int cnss_driver_event_post(struct cnss_plat_data *plat_priv,
enum cnss_driver_event_type type,
u32 flags, void *data);
diff --git a/cnss2/pci.c b/cnss2/pci.c
index 2825a44..4084f05 100644
--- a/cnss2/pci.c
+++ b/cnss2/pci.c
@@ -68,6 +68,8 @@
#define CNSS_256KB_SIZE 0x40000
#define DEVICE_RDDM_COOKIE 0xCAFECACE
+static bool cnss_driver_registered;
+
static DEFINE_SPINLOCK(pci_link_down_lock);
static DEFINE_SPINLOCK(pci_reg_window_lock);
static DEFINE_SPINLOCK(time_sync_lock);
@@ -82,6 +84,8 @@ static DEFINE_SPINLOCK(time_sync_lock);
#define FORCE_WAKE_DELAY_MAX_US 6000
#define FORCE_WAKE_DELAY_TIMEOUT_US 60000
+#define REG_RETRY_MAX_TIMES 3
+
#define MHI_SUSPEND_RETRY_MAX_TIMES 3
#define MHI_SUSPEND_RETRY_DELAY_US 5000
@@ -667,6 +671,8 @@ static struct cnss_print_optimize print_optimize;
#define SYSPM_REG_SIZE ARRAY_SIZE(syspm_reg_access_seq)
static int cnss_pci_update_fw_name(struct cnss_pci_data *pci_priv);
+static void cnss_pci_suspend_pwroff(struct pci_dev *pci_dev);
+
#if IS_ENABLED(CONFIG_MHI_BUS_MISC)
static void cnss_mhi_debug_reg_dump(struct cnss_pci_data *pci_priv)
@@ -1933,11 +1939,158 @@ static int cnss_pci_config_msi_data(struct cnss_pci_data *pci_priv)
return 0;
}
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+#define PLC_PCIE_NAME_LEN 14
+
+static struct cnss_plat_data *
+cnss_get_plat_priv_by_driver_ops(struct cnss_wlan_driver *driver_ops)
+{
+ int plat_env_count = cnss_get_plat_env_count();
+ struct cnss_plat_data *plat_env;
+ struct cnss_pci_data *pci_priv;
+ int i = 0;
+
+ if (!driver_ops) {
+ cnss_pr_err("No cnss driver\n");
+ return NULL;
+ }
+
+ for (i = 0; i < plat_env_count; i++) {
+ plat_env = cnss_get_plat_env(i);
+ if (!plat_env)
+ continue;
+ if (driver_ops->name && plat_env->pld_bus_ops_name) {
+ /* driver_ops->name = PLD_PCIE_OPS_NAME
+ * #ifdef MULTI_IF_NAME
+ * #define PLD_PCIE_OPS_NAME "pld_pcie_" MULTI_IF_NAME
+ * #else
+ * #define PLD_PCIE_OPS_NAME "pld_pcie"
+ * #endif
+ */
+ if (memcmp(driver_ops->name,
+ plat_env->pld_bus_ops_name,
+ PLC_PCIE_NAME_LEN) == 0)
+ return plat_env;
+ }
+ }
+
+ cnss_pr_err("Invalid cnss driver name from ko %s\n", driver_ops->name);
+ /* in the dual wlan card case, the pld_bus_ops_name from dts
+ * and driver_ops-> name from ko should match, otherwise
+ * wlanhost driver don't know which plat_env it can use;
+ * if doesn't find the match one, then get first available
+ * instance insteadly.
+ */
+
+ for (i = 0; i < plat_env_count; i++) {
+ plat_env = cnss_get_plat_env(i);
+
+ if (!plat_env)
+ continue;
+
+ pci_priv = plat_env->bus_priv;
+ if (!pci_priv) {
+ cnss_pr_err("pci_priv is NULL\n");
+ continue;
+ }
+
+ if (driver_ops == pci_priv->driver_ops)
+ return plat_env;
+ }
+ /* Doesn't find the existing instance,
+ * so return the fist empty instance
+ */
+ for (i = 0; i < plat_env_count; i++) {
+ plat_env = cnss_get_plat_env(i);
+
+ if (!plat_env)
+ continue;
+ pci_priv = plat_env->bus_priv;
+ if (!pci_priv) {
+ cnss_pr_err("pci_priv is NULL\n");
+ continue;
+ }
+
+ if (!pci_priv->driver_ops)
+ return plat_env;
+ }
+
+ return NULL;
+}
+
+static int cnss_pci_store_qrtr_node_id(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+ u32 scratch = QCA6390_PCIE_SOC_PCIE_REG_PCIE_SCRATCH_2_SOC_PCIE_REG;
+ struct cnss_plat_data *plat_priv;
+
+ if (!pci_priv) {
+ cnss_pr_err("pci_priv is NULL\n");
+ return -ENODEV;
+ }
+
+ plat_priv = pci_priv->plat_priv;
+ /**
+ * in the single wlan chipset case, plat_priv->qrtr_node_id always is 0,
+ * wlan fw will use the hardcode 7 as the qrtr node id.
+ * in the dual Hastings case, we will read qrtr node id
+ * from device tree and pass to get plat_priv->qrtr_node_id,
+ * which always is not zero. And then store this new value
+ * to pcie register, wlan fw will read out this qrtr node id
+ * from this register and overwrite to the hardcode one
+ * while do initialization for ipc router.
+ * without this change, two Hastings will use the same
+ * qrtr node instance id, which will mess up qmi message
+ * exchange. According to qrtr spec, every node should
+ * have unique qrtr node id
+ */
+ if (plat_priv->device_id == QCA6390_DEVICE_ID &&
+ plat_priv->qrtr_node_id) {
+ u32 val;
+
+ cnss_pr_dbg("write 0x%x to SCRATCH REG\n",
+ plat_priv->qrtr_node_id);
+ ret = cnss_pci_reg_write(pci_priv, scratch,
+ plat_priv->qrtr_node_id);
+ if (ret) {
+ cnss_pr_err("Failed to write register offset 0x%x, err = %d\n",
+ scratch, ret);
+ goto out;
+ }
+
+ ret = cnss_pci_reg_read(pci_priv, scratch, &val);
+ if (ret) {
+ cnss_pr_err("Failed to read SCRATCH REG");
+ goto out;
+ }
+
+ if (val != plat_priv->qrtr_node_id) {
+ cnss_pr_err("qrtr node id write to register doesn't match with readout value");
+ return -ERANGE;
+ }
+ }
+out:
+ return ret;
+}
+#else
+static struct cnss_plat_data *
+cnss_get_plat_priv_by_driver_ops(struct cnss_wlan_driver *driver_ops)
+{
+ return cnss_bus_dev_to_plat_priv(NULL);
+}
+
+static int cnss_pci_store_qrtr_node_id(struct cnss_pci_data *pci_priv)
+{
+ return 0;
+}
+#endif
+
int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv)
{
int ret = 0;
struct cnss_plat_data *plat_priv;
unsigned int timeout = 0;
+ int retry = 0;
if (!pci_priv) {
cnss_pr_err("pci_priv is NULL\n");
@@ -1963,6 +2116,15 @@ int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv)
else /* For perf builds the timeout is 10 (default) * 3 seconds */
pci_priv->mhi_ctrl->timeout_ms *= 3;
+retry:
+ ret = cnss_pci_store_qrtr_node_id(pci_priv);
+ if (ret) {
+ if (retry++ < REG_RETRY_MAX_TIMES)
+ goto retry;
+ else
+ return ret;
+ }
+
/* Start the timer to dump MHI/PBL/SBL debug data periodically */
mod_timer(&pci_priv->boot_debug_timer,
jiffies + msecs_to_jiffies(BOOT_DEBUG_TIMEOUT_MS));
@@ -3111,7 +3273,7 @@ reg_driver:
int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops)
{
int ret = 0;
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
+ struct cnss_plat_data *plat_priv;
struct cnss_pci_data *pci_priv;
const struct pci_device_id *id_table = driver_ops->id_table;
unsigned int timeout;
@@ -3121,6 +3283,8 @@ int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops)
return -ENODEV;
}
+ plat_priv = cnss_get_plat_priv_by_driver_ops(driver_ops);
+
if (!plat_priv) {
cnss_pr_buf("plat_priv is not ready for register driver\n");
return -EAGAIN;
@@ -3216,10 +3380,11 @@ EXPORT_SYMBOL(cnss_wlan_register_driver);
void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver_ops)
{
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
+ struct cnss_plat_data *plat_priv;
int ret = 0;
unsigned int timeout;
+ plat_priv = cnss_get_plat_priv_by_driver_ops(driver_ops);
if (!plat_priv) {
cnss_pr_err("plat_priv is NULL\n");
return;
@@ -5907,6 +6072,10 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv)
const struct mhi_controller_config *cnss_mhi_config =
&cnss_mhi_config_default;
+ ret = cnss_qmi_init(plat_priv);
+ if (ret)
+ return -EINVAL;
+
if (pci_priv->device_id == QCA6174_DEVICE_ID)
return 0;
@@ -6150,6 +6319,57 @@ static void cnss_pci_wake_gpio_deinit(struct cnss_pci_data *pci_priv)
}
#endif
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+static int cnss_try_suspend(struct cnss_plat_data *plat_priv)
+{
+ int ret = 0;
+
+ /* in the dual wlan card case, if call pci_register_driver after
+ * finishing the first pcie device enumeration, it will cause
+ * the cnss_pci_probe called in advance with the second wlan card,
+ * and the sequence like this:
+ * enter msm_pcie_enumerate -> pci_bus_add_devices -> cnss_pci_probe
+ * -> exit msm_pcie_enumerate.
+ * But the correct sequence we expected is like this:
+ * enter msm_pcie_enumerate -> pci_bus_add_devices ->
+ * exit msm_pcie_enumerate -> cnss_pci_probe.
+ * And this unexpected sequence will make the second wlan card do
+ * pcie link suspend while the pcie enumeration not finished.
+ * So need to add below logical to avoid doing pcie link suspend
+ * if the enumeration has not finish.
+ */
+ plat_priv->enumerate_done = true;
+
+ /* Now enumeration is finished, try to suspend PCIe link */
+ if (plat_priv->bus_priv) {
+ struct cnss_pci_data *pci_priv = plat_priv->bus_priv;
+ struct pci_dev *pci_dev = pci_priv->pci_dev;
+
+ switch (pci_dev->device) {
+ case QCA6390_DEVICE_ID:
+ cnss_pci_set_wlaon_pwr_ctrl(pci_priv,
+ false,
+ true,
+ false);
+
+ cnss_pci_suspend_pwroff(pci_dev);
+ break;
+ default:
+ cnss_pr_err("Unknown PCI device found: 0x%x\n",
+ pci_dev->device);
+ ret = -ENODEV;
+ }
+ }
+
+ return ret;
+}
+#else
+static int cnss_try_suspend(struct cnss_plat_data *plat_priv)
+{
+ return 0;
+}
+#endif
+
/* Setting to use this cnss_pm_domain ops will let PM framework override the
* ops from dev->bus->pm which is pci_dev_pm_ops from pci-driver.c. This ops
* has to take care everything device driver needed which is currently done
@@ -6238,10 +6458,13 @@ static bool cnss_should_suspend_pwroff(struct pci_dev *pci_dev)
static void cnss_pci_suspend_pwroff(struct pci_dev *pci_dev)
{
struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev);
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
+ int rc_num = pci_dev->bus->domain_nr;
+ struct cnss_plat_data *plat_priv;
int ret = 0;
bool suspend_pwroff = cnss_should_suspend_pwroff(pci_dev);
+ plat_priv = cnss_get_plat_priv_by_rc_num(rc_num);
+
if (suspend_pwroff) {
ret = cnss_suspend_pci_link(pci_priv);
if (ret)
@@ -6259,11 +6482,17 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
{
int ret = 0;
struct cnss_pci_data *pci_priv;
- struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL);
struct device *dev = &pci_dev->dev;
+ int rc_num = pci_dev->bus->domain_nr;
+ struct cnss_plat_data *plat_priv = cnss_get_plat_priv_by_rc_num(rc_num);
- cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n",
- id->vendor, pci_dev->device);
+ cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x rc_num %d\n",
+ id->vendor, pci_dev->device, rc_num);
+ if (!plat_priv) {
+ cnss_pr_err("Find match plat_priv with rc number failure\n");
+ ret = -ENODEV;
+ goto out;
+ }
pci_priv = devm_kzalloc(dev, sizeof(*pci_priv), GFP_KERNEL);
if (!pci_priv) {
@@ -6336,7 +6565,11 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
case KIWI_DEVICE_ID:
case MANGO_DEVICE_ID:
case PEACH_DEVICE_ID:
- cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false);
+ if ((cnss_is_dual_wlan_enabled() &&
+ plat_priv->enumerate_done) || !cnss_is_dual_wlan_enabled())
+ cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false,
+ false);
+
timer_setup(&pci_priv->dev_rddm_timer,
cnss_dev_rddm_timeout_hdlr, 0);
timer_setup(&pci_priv->boot_debug_timer,
@@ -6357,7 +6590,11 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
cnss_pci_config_regs(pci_priv);
if (EMULATION_HW)
goto out;
+ if (cnss_is_dual_wlan_enabled() && !plat_priv->enumerate_done)
+ goto probe_done;
cnss_pci_suspend_pwroff(pci_dev);
+
+probe_done:
set_bit(CNSS_PCI_PROBE_DONE, &plat_priv->driver_state);
return 0;
@@ -6443,7 +6680,7 @@ static const struct dev_pm_ops cnss_pm_ops = {
cnss_pci_runtime_idle)
};
-struct pci_driver cnss_pci_driver = {
+static struct pci_driver cnss_pci_driver = {
.name = "cnss_pci",
.id_table = cnss_pci_id_table,
.probe = cnss_pci_probe,
@@ -6515,17 +6752,25 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv)
goto out;
}
- ret = pci_register_driver(&cnss_pci_driver);
+ ret = cnss_try_suspend(plat_priv);
if (ret) {
- cnss_pr_err("Failed to register to PCI framework, err = %d\n",
- ret);
+ cnss_pr_err("Failed to suspend, ret: %d\n", ret);
goto out;
}
- if (!plat_priv->bus_priv) {
- cnss_pr_err("Failed to probe PCI driver\n");
- ret = -ENODEV;
- goto unreg_pci;
+ if (!cnss_driver_registered) {
+ ret = pci_register_driver(&cnss_pci_driver);
+ if (ret) {
+ cnss_pr_err("Failed to register to PCI framework, err = %d\n",
+ ret);
+ goto out;
+ }
+ if (!plat_priv->bus_priv) {
+ cnss_pr_err("Failed to probe PCI driver\n");
+ ret = -ENODEV;
+ goto unreg_pci;
+ }
+ cnss_driver_registered = true;
}
return 0;
@@ -6538,5 +6783,8 @@ out:
void cnss_pci_deinit(struct cnss_plat_data *plat_priv)
{
- pci_unregister_driver(&cnss_pci_driver);
+ if (cnss_driver_registered) {
+ pci_unregister_driver(&cnss_pci_driver);
+ cnss_driver_registered = false;
+ }
}
diff --git a/cnss2/pci.h b/cnss2/pci.h
index 1b35cc6..40de540 100644
--- a/cnss2/pci.h
+++ b/cnss2/pci.h
@@ -23,7 +23,13 @@
#define PM_OPTIONS_DEFAULT 0
#define PCI_LINK_DOWN 0
+
+#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
+#define LINK_TRAINING_RETRY_MAX_TIMES 2
+#else
#define LINK_TRAINING_RETRY_MAX_TIMES 3
+#endif
+
#define LINK_TRAINING_RETRY_DELAY_MS 500
#define MSI_USERS 4
diff --git a/cnss2/qmi.c b/cnss2/qmi.c
index b7c88a5..bfc9b1c 100644
--- a/cnss2/qmi.c
+++ b/cnss2/qmi.c
@@ -3211,9 +3211,25 @@ static struct qmi_ops qmi_wlfw_ops = {
.del_server = wlfw_del_server,
};
+static int cnss_qmi_add_lookup(struct cnss_plat_data *plat_priv)
+{
+ unsigned int id = WLFW_SERVICE_INS_ID_V01;
+
+ /* In order to support dual wlan card attach case,
+ * need separate qmi service instance id for each dev
+ */
+ if (cnss_is_dual_wlan_enabled() && plat_priv->qrtr_node_id != 0 &&
+ plat_priv->wlfw_service_instance_id != 0)
+ id = plat_priv->wlfw_service_instance_id;
+
+ return qmi_add_lookup(&plat_priv->qmi_wlfw, WLFW_SERVICE_ID_V01,
+ WLFW_SERVICE_VERS_V01, id);
+}
+
int cnss_qmi_init(struct cnss_plat_data *plat_priv)
{
int ret = 0;
+ cnss_get_qrtr_info(plat_priv);
ret = qmi_handle_init(&plat_priv->qmi_wlfw,
QMI_WLFW_MAX_RECV_BUF_SIZE,
@@ -3224,8 +3240,7 @@ int cnss_qmi_init(struct cnss_plat_data *plat_priv)
goto out;
}
- ret = qmi_add_lookup(&plat_priv->qmi_wlfw, WLFW_SERVICE_ID_V01,
- WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01);
+ ret = cnss_qmi_add_lookup(plat_priv);
if (ret < 0)
cnss_pr_err("Failed to add WLFW QMI lookup, err: %d\n", ret);
diff --git a/cnss2/reg.h b/cnss2/reg.h
index fa55bf4..e670979 100644
--- a/cnss2/reg.h
+++ b/cnss2/reg.h
@@ -124,6 +124,7 @@
#define QCA6390_PCIE_SOC_PCIE_WRAP_INTR_STATUS_SOC_PCIE_REG 0x1E04058
#define QCA6390_PCIE_SOC_COMMIT_REPLAY_SOC_PCIE_REG 0x01E05090
#define PEACH_PCIE_SOC_COMMIT_REPLAY_SOC_PCIE_REG 0x01E01100
+#define QCA6390_PCIE_SOC_PCIE_REG_PCIE_SCRATCH_2_SOC_PCIE_REG 0x01E0405C
#define QCA6390_PCIE_PCIE_PARF_LTSSM 0x01E081B0
#define QCA6390_PCIE_PCIE_PARF_PM_STTS 0x01E08024
#define QCA6390_PCIE_PCIE_PARF_PM_STTS_1 0x01E08028