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
This commit is contained in:
Chaoli Zhou
2023-01-18 11:31:32 +08:00
parent 436eadfdaa
commit f2346285bc
9 changed files with 551 additions and 44 deletions

View File

@@ -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;
}
}