Merge "cnss2: Support dual wlan cards managed by cnss2 platform driver"

This commit is contained in:
qctecmdr 2020-09-18 04:24:49 -07:00 committed by Gerrit - the friendly Code Review server
commit a1cfcac78f
7 changed files with 392 additions and 31 deletions

View File

@ -72,3 +72,9 @@ config CNSS_EMULATION
emulation hardware.
These changes are needed for WLAN drivers to support and meet the
requirement of emulation hardware.
config CNSS_SUPPORT_DUAL_DEV
bool "Enable cnss support dual wlan card"
---help---
This enables the changes from cnss platform driver to support dual
wlan card attach

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -681,8 +681,14 @@ int cnss_debugfs_create(struct cnss_plat_data *plat_priv)
{
int ret = 0;
struct dentry *root_dentry;
char name[15];
if (cnss_get_dual_wlan())
snprintf(name, sizeof(name), "cnss_%d", plat_priv->idx);
else
snprintf(name, sizeof(name), "cnss");
root_dentry = debugfs_create_dir(name, NULL);
root_dentry = debugfs_create_dir("cnss", 0);
if (IS_ERR(root_dentry)) {
ret = PTR_ERR(root_dentry);
cnss_pr_err("Unable to create debugfs %d\n", ret);

View File

@ -47,7 +47,20 @@
#define CNSS_QMI_TIMEOUT_DEFAULT 10000
#define CNSS_BDF_TYPE_DEFAULT CNSS_BDF_ELF
#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
#define CNSS_DUAL_WLAN 1
#else
#define CNSS_DUAL_WLAN 0
#endif
#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
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 pm_notify_registered;
static DECLARE_RWSEM(cnss_pm_sem);
@ -70,6 +83,80 @@ struct cnss_driver_event {
void *data;
};
#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
static void cnss_set_plat_priv(struct platform_device *plat_dev,
struct cnss_plat_data *plat_priv)
{
cnss_pr_dbg("Set plat_priv at %d", plat_env_count);
if (plat_priv) {
plat_priv->idx = plat_env_count;
plat_env[plat_priv->idx] = plat_priv;
plat_env_count++;
}
}
struct cnss_plat_data *cnss_get_plat_priv(struct platform_device
*plat_dev)
{
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->idx);
plat_env[plat_priv->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->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;
}
#else
static void cnss_set_plat_priv(struct platform_device *plat_dev,
struct cnss_plat_data *plat_priv)
{
@ -81,6 +168,35 @@ 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);
}
#endif
bool cnss_get_dual_wlan(void)
{
return CNSS_DUAL_WLAN;
}
static int cnss_pm_notify(struct notifier_block *b,
unsigned long event, void *p)
{
@ -1550,7 +1666,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;
@ -1926,7 +2042,10 @@ static int cnss_misc_init(struct cnss_plat_data *plat_priv)
setup_timer(&plat_priv->fw_boot_timer, cnss_bus_fw_boot_timeout_hdlr,
(unsigned long)plat_priv);
register_pm_notifier(&cnss_pm_notifier);
if (!pm_notify_registered) {
register_pm_notifier(&cnss_pm_notifier);
pm_notify_registered = true;
}
ret = device_init_wakeup(&plat_priv->plat_dev->dev, true);
if (ret)
@ -1949,7 +2068,10 @@ static void cnss_misc_deinit(struct cnss_plat_data *plat_priv)
complete_all(&plat_priv->cal_complete);
complete_all(&plat_priv->power_up_complete);
device_init_wakeup(&plat_priv->plat_dev->dev, false);
unregister_pm_notifier(&cnss_pm_notifier);
if (pm_notify_registered) {
unregister_pm_notifier(&cnss_pm_notifier);
pm_notify_registered = false;
}
del_timer(&plat_priv->fw_boot_timer);
}
@ -2042,6 +2164,20 @@ cnss_is_converged_dt(struct cnss_plat_data *plat_priv)
"qcom,converged-dt");
}
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);
}
static inline int
cnss_get_qrtr_node_id(struct cnss_plat_data *plat_priv)
{
return of_property_read_u32(plat_priv->plat_dev->dev.of_node,
"qcom,qrtr_node_id", &plat_priv->qrtr_node_id);
}
static int cnss_probe(struct platform_device *plat_dev)
{
int ret = 0;
@ -2054,6 +2190,9 @@ static int cnss_probe(struct platform_device *plat_dev)
ret = -EEXIST;
goto out;
}
ret = cnss_plat_env_available();
if (ret != 0)
goto out;
of_id = of_match_device(cnss_of_match_table, &plat_dev->dev);
if (!of_id || !of_id->data) {
@ -2074,12 +2213,31 @@ static int cnss_probe(struct platform_device *plat_dev)
plat_priv->plat_dev = plat_dev;
plat_priv->dev_node = NULL;
plat_priv->device_id = device_id->driver_data;
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("%s: rc_num=%d\n", __func__, plat_priv->rc_num);
ret = cnss_get_qrtr_node_id(plat_priv);
if (ret) {
cnss_pr_dbg("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 +
FW_ID_BASE;
cnss_pr_dbg("service_instance_id=0x%x\n",
plat_priv->wlfw_service_instance_id);
}
plat_priv->is_converged_dt = cnss_is_converged_dt(plat_priv);
cnss_pr_dbg("Probing platform driver from %s DT\n",
plat_priv->is_converged_dt ? "converged" : "single");
plat_priv->bus_type = cnss_get_bus_type(plat_priv);
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);
@ -2128,10 +2286,6 @@ static int cnss_probe(struct platform_device *plat_dev)
if (ret)
goto destroy_debugfs;
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;
@ -2158,7 +2312,7 @@ free_res:
cnss_put_resources(plat_priv);
reset_ctx:
platform_set_drvdata(plat_dev, NULL);
cnss_set_plat_priv(plat_dev, NULL);
cnss_clear_plat_priv(plat_priv);
out:
return ret;
}
@ -2167,7 +2321,6 @@ static int cnss_remove(struct platform_device *plat_dev)
{
struct cnss_plat_data *plat_priv = platform_get_drvdata(plat_dev);
cnss_genl_exit();
cnss_misc_deinit(plat_priv);
cnss_debugfs_destroy(plat_priv);
cnss_qmi_deinit(plat_priv);
@ -2178,7 +2331,7 @@ static int cnss_remove(struct platform_device *plat_dev)
cnss_bus_deinit(plat_priv);
cnss_put_resources(plat_priv);
platform_set_drvdata(plat_dev, NULL);
plat_env = NULL;
cnss_clear_plat_priv(plat_priv);
return 0;
}
@ -2204,6 +2357,9 @@ static int __init cnss_initialize(void)
ret = platform_driver_register(&cnss_platform_driver);
if (ret)
cnss_debug_deinit();
ret = cnss_genl_init();
if (ret < 0)
cnss_pr_err("CNSS genl init failed %d\n", ret);
return ret;
}
@ -2212,6 +2368,7 @@ static void __exit cnss_exit(void)
{
platform_driver_unregister(&cnss_platform_driver);
cnss_debug_deinit();
cnss_genl_exit();
}
module_init(cnss_initialize);

View File

@ -37,6 +37,8 @@
#define CNSS_FW_PATH_MAX_LEN 32
#define CNSS_MAX_DEV_NUM 2
enum cnss_dev_bus_type {
CNSS_BUS_NONE = -1,
CNSS_BUS_PCI,
@ -320,9 +322,20 @@ struct cnss_plat_data {
u8 set_wlaon_pwr_ctrl;
bool fw_pcie_gen_switch;
u8 pcie_gen_speed;
u32 rc_num;
char device_name[16];
u32 idx;
bool enumerate_done;
int qrtr_node_id;
unsigned int wlfw_service_instance_id;
};
struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev);
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);
bool cnss_get_dual_wlan(void);
int cnss_driver_event_post(struct cnss_plat_data *plat_priv,
enum cnss_driver_event_type type,
u32 flags, void *data);

View File

@ -60,6 +60,8 @@
#define EMULATION_HW 0
#endif
static bool cnss_driver_registered;
static DEFINE_SPINLOCK(pci_link_down_lock);
static DEFINE_SPINLOCK(pci_reg_window_lock);
@ -137,6 +139,7 @@ static DEFINE_SPINLOCK(pci_reg_window_lock);
#define FORCE_WAKE_DELAY_TIMEOUT_US 60000
#define QCA6390_WLAON_QFPROM_PWR_CTRL_REG 0x1F8031C
#define QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG 0x1E04040
#define POWER_ON_RETRY_MAX_TIMES 3
#define POWER_ON_RETRY_DELAY_MS 200
@ -1236,13 +1239,72 @@ int cnss_pci_is_drv_connected(struct device *dev)
}
EXPORT_SYMBOL(cnss_pci_is_drv_connected);
#ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
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;
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;
}
#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);
}
#endif
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;
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 -ENODEV;
@ -1286,9 +1348,10 @@ 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;
plat_priv = cnss_get_plat_priv_by_driver_ops(driver_ops);
if (!plat_priv) {
cnss_pr_err("plat_priv is NULL\n");
return;
@ -3264,6 +3327,44 @@ int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv)
if (ret)
goto out;
/**
* 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 QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG\n",
plat_priv->qrtr_node_id);
ret = cnss_pci_reg_write(pci_priv,
QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG,
plat_priv->qrtr_node_id);
if (ret) {
cnss_pr_err("Failed to write register offset 0x%x, err = %d\n",
QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG, ret);
goto out;
}
if (cnss_pci_reg_read(pci_priv,
QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG,
&val))
cnss_pr_err("Failed to read QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG");
if (val != plat_priv->qrtr_node_id) {
cnss_pr_err("qrtr node id write to register doesn't match with readout value");
goto out;
}
}
ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_POWER_ON);
if (ret)
goto out;
@ -3442,7 +3543,8 @@ 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);
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);
@ -3507,6 +3609,8 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
switch (pci_dev->device) {
case QCA6174_DEVICE_ID:
if (cnss_get_dual_wlan() && !plat_priv->enumerate_done)
break;
pci_read_config_word(pci_dev, QCA6174_REV_ID_OFFSET,
&pci_priv->revision_id);
ret = cnss_suspend_pci_link(pci_priv);
@ -3517,7 +3621,9 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
break;
case QCA6290_DEVICE_ID:
case QCA6390_DEVICE_ID:
cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false);
if (cnss_get_dual_wlan() && plat_priv->enumerate_done)
cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false,
false, false);
case QCN7605_DEVICE_ID:
setup_timer(&pci_priv->dev_rddm_timer,
cnss_dev_rddm_timeout_hdlr,
@ -3542,6 +3648,9 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
if (EMULATION_HW)
break;
if (cnss_get_dual_wlan() && !plat_priv->enumerate_done)
break;
if (pci_dev->device != QCN7605_DEVICE_ID)
cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false,
true, false);
@ -3626,7 +3735,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,
@ -3662,19 +3771,79 @@ retry:
}
}
ret = pci_register_driver(&cnss_pci_driver);
if (ret) {
cnss_pr_err("Failed to register to PCI framework, err = %d\n",
ret);
goto out;
}
/* 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.
*/
if (cnss_get_dual_wlan()) {
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;
if (!plat_priv->bus_priv) {
cnss_pr_err("Failed to probe pci driver\n");
ret = -ENODEV;
goto deinit;
}
switch (pci_dev->device) {
case QCA6174_DEVICE_ID:
pci_read_config_word(pci_dev,
QCA6174_REV_ID_OFFSET,
&pci_priv->revision_id);
ret = cnss_suspend_pci_link(pci_priv);
if (ret)
cnss_pr_err("Failed to suspend PCI link, err = %d\n",
ret);
cnss_power_off_device(plat_priv);
break;
case QCA6290_DEVICE_ID:
case QCA6390_DEVICE_ID:
case QCN7605_DEVICE_ID:
if (pci_dev->device != QCN7605_DEVICE_ID) {
cnss_pci_set_wlaon_pwr_ctrl(pci_priv,
false,
false,
false);
cnss_pci_set_wlaon_pwr_ctrl(pci_priv,
false,
true,
false);
}
ret = cnss_suspend_pci_link(pci_priv);
if (ret)
cnss_pr_err("Failed to suspend PCI link, err = %d\n",
ret);
cnss_power_off_device(plat_priv);
break;
default:
cnss_pr_err("Unknown PCI device found: 0x%x\n",
pci_dev->device);
ret = -ENODEV;
}
}
}
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 deinit;
}
cnss_driver_registered = true;
}
return 0;
deinit:

View File

@ -1726,9 +1726,18 @@ int cnss_qmi_init(struct cnss_plat_data *plat_priv)
cnss_pr_err("Failed to initialize QMI handle, err: %d\n", ret);
goto out;
}
ret = qmi_add_lookup(&plat_priv->qmi_wlfw, WLFW_SERVICE_ID_V01,
WLFW_SERVICE_VERS_V01, WLFW_SERVICE_INS_ID_V01);
/* In order to support dual wlan card attach case,
* need separate qmi service instance id for each dev
*/
if (plat_priv->qrtr_node_id != 0 &&
plat_priv->wlfw_service_instance_id != 0)
ret = qmi_add_lookup(&plat_priv->qmi_wlfw, WLFW_SERVICE_ID_V01,
WLFW_SERVICE_VERS_V01,
plat_priv->wlfw_service_instance_id);
else
ret = qmi_add_lookup(&plat_priv->qmi_wlfw, WLFW_SERVICE_ID_V01,
WLFW_SERVICE_VERS_V01,
WLFW_SERVICE_INS_ID_V01);
if (ret < 0)
cnss_pr_err("Failed to add QMI lookup, err: %d\n", ret);

View File

@ -37,6 +37,7 @@ struct cnss_qmi_event_qdss_trace_save_data {
char file_name[QDSS_TRACE_FILE_NAME_MAX + 1];
};
#define FW_ID_BASE 7
#ifdef CONFIG_CNSS2_QMI
#include "wlan_firmware_service_v01.h"