cnss2: Control the power on/off wlan device for PCIe switch platform

CNSS driver controls the power on/off wlan device. because on CPE
platform the link/power status of RC<->USP may be still on when wlan
device is turning on/off, this may cause uncorrectable AER error on
DSP side, to avoid this issue DSP's downstream link should be
disabled before power on/off wlan device, and re-enabled after wlan
power on, and also wait for link training of DSP<->WLAN to complete.

To be compitable with non PCIE switch platform, add a new config
CONFIG_PCIE_SWITCH_SUPPORT to enable/disable this function

Change-Id: Iaca103850d5ffc717a2a8bc40d16358e03c0c9db
CRs-Fixed: 3848272
This commit is contained in:
Jiani Liu
2024-07-22 13:20:07 +08:00
committed by Ravindra Konda
parent 867a9e6cd5
commit d792a01299
8 changed files with 226 additions and 6 deletions

5
Kbuild
View File

@@ -86,6 +86,11 @@ ifeq ($(CONFIG_FEATURE_SMEM_MAILBOX), y)
KBUILD_CPPFLAGS += -DCONFIG_FEATURE_SMEM_MAILBOX KBUILD_CPPFLAGS += -DCONFIG_FEATURE_SMEM_MAILBOX
endif endif
found = $(shell if grep -qF "int msm_pcie_dsp_link_control" $(srctree)/include/linux/msm_pcie.h; then echo "yes" ;else echo "no" ;fi;)
ifeq ($(findstring yes, $(found)), yes)
KBUILD_CPPFLAGS += -DCONFIG_PCIE_SWITCH_SUPPORT
endif
obj-$(CONFIG_CNSS2) += cnss2/ obj-$(CONFIG_CNSS2) += cnss2/
obj-$(CONFIG_ICNSS2) += icnss2/ obj-$(CONFIG_ICNSS2) += icnss2/
obj-$(CONFIG_CNSS_GENL) += cnss_genl/ obj-$(CONFIG_CNSS_GENL) += cnss_genl/

View File

@@ -7,6 +7,7 @@
#include "bus.h" #include "bus.h"
#include "debug.h" #include "debug.h"
#include "pci.h" #include "pci.h"
#include "pci_platform.h"
enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev)
{ {
@@ -711,6 +712,68 @@ bool cnss_bus_is_smmu_s1_enabled(struct cnss_plat_data *plat_priv)
} }
} }
int cnss_bus_dsp_link_control(struct cnss_plat_data *plat_priv,
bool link_enable)
{
if (!plat_priv || plat_priv->pcie_switch_type != PCIE_SWITCH_NTN3)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_dsp_link_control(plat_priv->bus_priv, link_enable);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_set_dsp_link_status(struct cnss_plat_data *plat_priv,
bool link_enable)
{
if (!plat_priv || plat_priv->pcie_switch_type != PCIE_SWITCH_NTN3)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_set_dsp_link_status(plat_priv->bus_priv, link_enable);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_get_dsp_link_status(struct cnss_plat_data *plat_priv)
{
if (!plat_priv || plat_priv->pcie_switch_type != PCIE_SWITCH_NTN3)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_get_dsp_link_status(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_dsp_link_enable(struct cnss_plat_data *plat_priv)
{
if (!plat_priv || plat_priv->pcie_switch_type != PCIE_SWITCH_NTN3)
return -ENODEV;
switch (plat_priv->bus_type) {
case CNSS_BUS_PCI:
return cnss_pci_dsp_link_enable(plat_priv->bus_priv);
default:
cnss_pr_err("Unsupported bus type: %d\n",
plat_priv->bus_type);
return -EINVAL;
}
}
int cnss_bus_update_time_sync_period(struct cnss_plat_data *plat_priv, int cnss_bus_update_time_sync_period(struct cnss_plat_data *plat_priv,
unsigned int time_sync_period) unsigned int time_sync_period)
{ {

View File

@@ -77,4 +77,10 @@ int cnss_bus_get_msi_assignment(struct cnss_plat_data *plat_priv,
int *num_vectors, int *num_vectors,
u32 *user_base_data, u32 *user_base_data,
u32 *base_vector); u32 *base_vector);
int cnss_bus_dsp_link_control(struct cnss_plat_data *plat_priv,
bool link_enable);
int cnss_bus_set_dsp_link_status(struct cnss_plat_data *plat_priv,
bool link_enable);
int cnss_bus_get_dsp_link_status(struct cnss_plat_data *plat_priv);
int cnss_bus_dsp_link_enable(struct cnss_plat_data *plat_priv);
#endif /* _CNSS_BUS_H */ #endif /* _CNSS_BUS_H */

View File

@@ -767,4 +767,6 @@ size_t cnss_get_platform_name(struct cnss_plat_data *plat_priv,
char *buf, const size_t buf_len); char *buf, const size_t buf_len);
int cnss_iommu_map(struct iommu_domain *domain, unsigned long iova, int cnss_iommu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot); phys_addr_t paddr, size_t size, int prot);
int cnss_select_pinctrl_enable(struct cnss_plat_data *plat_priv);
int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, bool state);
#endif /* _CNSS_MAIN_H */ #endif /* _CNSS_MAIN_H */

View File

@@ -44,6 +44,14 @@
ee == MHI_EE_WFW || \ ee == MHI_EE_WFW || \
ee == MHI_EE_FP) ee == MHI_EE_FP)
#define PCI_DSP_LINK_ENABLE 1
#define PCI_DSP_LINK_DISABLE 0
#ifdef CONFIG_PCIE_SWITCH_SUPPORT
#define DSP_LINK_ENABLE_DELAY_TIME_US_MIN (25000)
#define DSP_LINK_ENABLE_DELAY_TIME_US_MAX (25100)
#define DSP_LINK_ENABLE_RETRY_COUNT_MAX (3)
#endif
enum cnss_mhi_state { enum cnss_mhi_state {
CNSS_MHI_INIT, CNSS_MHI_INIT,
CNSS_MHI_DEINIT, CNSS_MHI_DEINIT,
@@ -185,6 +193,9 @@ struct cnss_pci_data {
bool drv_supported; bool drv_supported;
bool is_smmu_fault; bool is_smmu_fault;
unsigned long long smmu_fault_timestamp[SMMU_CB_MAX]; unsigned long long smmu_fault_timestamp[SMMU_CB_MAX];
#ifdef CONFIG_PCIE_SWITCH_SUPPORT
bool pci_dsp_link_status;
#endif
}; };
static inline void cnss_set_pci_priv(struct pci_dev *pci_dev, void *data) static inline void cnss_set_pci_priv(struct pci_dev *pci_dev, void *data)

View File

@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ /* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
#ifndef _CNSS_PCI_PLATFORM_H #ifndef _CNSS_PCI_PLATFORM_H
#define _CNSS_PCI_PLATFORM_H #define _CNSS_PCI_PLATFORM_H
@@ -113,7 +113,8 @@ int cnss_pci_get_one_msi_mhi_irq_array_size(struct cnss_pci_data *pci_priv);
bool cnss_pci_is_force_one_msi(struct cnss_pci_data *pci_priv); bool cnss_pci_is_force_one_msi(struct cnss_pci_data *pci_priv);
int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv); int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv);
void cnss_pci_update_drv_supported(struct cnss_pci_data *pci_priv); void cnss_pci_update_drv_supported(struct cnss_pci_data *pci_priv);
int cnss_pci_dsp_link_control(struct cnss_pci_data *pci_priv,
bool link_enable);
/** /**
* _cnss_pci_get_reg_dump() - Dump PCIe RC registers for debug * _cnss_pci_get_reg_dump() - Dump PCIe RC registers for debug
* @pci_priv: driver PCI bus context pointer * @pci_priv: driver PCI bus context pointer
@@ -127,6 +128,10 @@ void cnss_pci_update_drv_supported(struct cnss_pci_data *pci_priv);
*/ */
int _cnss_pci_get_reg_dump(struct cnss_pci_data *pci_priv, int _cnss_pci_get_reg_dump(struct cnss_pci_data *pci_priv,
u8 *buf, u32 len); u8 *buf, u32 len);
int cnss_pci_set_dsp_link_status(struct cnss_pci_data *pci_priv,
bool link_enable);
int cnss_pci_get_dsp_link_status(struct cnss_pci_data *pci_priv);
int cnss_pci_dsp_link_enable(struct cnss_pci_data *pci_priv);
#else #else
int _cnss_pci_enumerate(struct cnss_plat_data *plat_priv, u32 rc_num) int _cnss_pci_enumerate(struct cnss_plat_data *plat_priv, u32 rc_num)
{ {
@@ -204,6 +209,27 @@ void cnss_pci_update_drv_supported(struct cnss_pci_data *pci_priv)
pci_priv->drv_supported = false; pci_priv->drv_supported = false;
} }
int cnss_pci_dsp_link_control(struct cnss_pci_data *pci_priv,
bool link_enable)
{
return -EOPNOTSUPP;
}
int cnss_pci_set_dsp_link_status(struct cnss_pci_data *pci_priv,
bool link_enable)
{
return -EOPNOTSUPP;
}
int cnss_pci_get_dsp_link_status(struct cnss_pci_data *pci_priv)
{
return -EOPNOTSUPP;
}
int cnss_pci_dsp_link_enable(struct cnss_pci_data *pci_priv)
{
return -EOPNOTSUPP;
}
#endif /* CONFIG_PCI_MSM */ #endif /* CONFIG_PCI_MSM */
static inline bool cnss_pci_get_drv_supported(struct cnss_pci_data *pci_priv) static inline bool cnss_pci_get_drv_supported(struct cnss_pci_data *pci_priv)

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ /* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
#include "pci_platform.h" #include "pci_platform.h"
#include "debug.h" #include "debug.h"
@@ -403,6 +403,93 @@ retry:
return ret; return ret;
} }
#ifdef CONFIG_PCIE_SWITCH_SUPPORT
int cnss_pci_dsp_link_control(struct cnss_pci_data *pci_priv,
bool link_enable)
{
if (!pci_priv)
return -ENODEV;
pci_priv->pci_dsp_link_status = link_enable;
return msm_pcie_dsp_link_control(pci_priv->pci_dev, link_enable);
}
int cnss_pci_set_dsp_link_status(struct cnss_pci_data *pci_priv,
bool link_enable)
{
if (!pci_priv)
return -ENODEV;
pci_priv->pci_dsp_link_status = link_enable;
return 0;
}
int cnss_pci_get_dsp_link_status(struct cnss_pci_data *pci_priv)
{
if (!pci_priv)
return -ENODEV;
return pci_priv->pci_dsp_link_status;
}
int cnss_pci_dsp_link_enable(struct cnss_pci_data *pci_priv)
{
int ret = 0;
int retry_count = 0;
struct cnss_plat_data *plat_priv;
if (!pci_priv)
return -ENODEV;
plat_priv = pci_priv->plat_priv;
/* For PCIe switch platform, wait for link train of DSP<->WLAN complete
*/
while (retry_count++ < DSP_LINK_ENABLE_RETRY_COUNT_MAX) {
ret = cnss_pci_dsp_link_control(pci_priv, true);
if (!ret)
break;
cnss_pci_dsp_link_control(pci_priv, false);
cnss_pr_err("DSP<->WLAN link train failed, retry...\n");
cnss_select_pinctrl_state(plat_priv, false);
usleep_range(DSP_LINK_ENABLE_DELAY_TIME_US_MIN,
DSP_LINK_ENABLE_DELAY_TIME_US_MAX);
ret = cnss_select_pinctrl_enable(plat_priv);
if (ret) {
cnss_pr_err("Failed to select pinctrl state, err = %d\n", ret);
return ret;
}
usleep_range(DSP_LINK_ENABLE_DELAY_TIME_US_MIN,
DSP_LINK_ENABLE_DELAY_TIME_US_MAX);
}
return ret;
}
#else
int cnss_pci_dsp_link_control(struct cnss_pci_data *pci_priv,
bool link_enable)
{
return -EOPNOTSUPP;
}
int cnss_pci_set_dsp_link_status(struct cnss_pci_data *pci_priv,
bool link_enable)
{
return -EOPNOTSUPP;
}
int cnss_pci_get_dsp_link_status(struct cnss_pci_data *pci_priv)
{
return -EOPNOTSUPP;
}
int cnss_pci_dsp_link_enable(struct cnss_pci_data *pci_priv)
{
return -EOPNOTSUPP;
}
#endif
int cnss_pci_prevent_l1(struct device *dev) int cnss_pci_prevent_l1(struct device *dev)
{ {
struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_dev *pci_dev = to_pci_dev(dev);

View File

@@ -19,6 +19,7 @@
#include "debug.h" #include "debug.h"
#include "bus.h" #include "bus.h"
#include <linux/soc/qcom/qcom_aoss.h> #include <linux/soc/qcom/qcom_aoss.h>
#include "pci_platform.h"
#if IS_ENABLED(CONFIG_ARCH_QCOM) #if IS_ENABLED(CONFIG_ARCH_QCOM)
static struct cnss_vreg_cfg cnss_vreg_list[] = { static struct cnss_vreg_cfg cnss_vreg_list[] = {
@@ -1008,8 +1009,8 @@ retry_gpio_req:
gpio_free(xo_clk_gpio); gpio_free(xo_clk_gpio);
} }
static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv,
bool state) bool state)
{ {
int ret = 0; int ret = 0;
struct cnss_pinctrl_info *pinctrl_info; struct cnss_pinctrl_info *pinctrl_info;
@@ -1105,7 +1106,7 @@ out:
* *
* Return: Status of pinctrl select operation. 0 - Success. * Return: Status of pinctrl select operation. 0 - Success.
*/ */
static int cnss_select_pinctrl_enable(struct cnss_plat_data *plat_priv) int cnss_select_pinctrl_enable(struct cnss_plat_data *plat_priv)
{ {
int ret = 0, bt_en_gpio = plat_priv->pinctrl_info.bt_en_gpio; int ret = 0, bt_en_gpio = plat_priv->pinctrl_info.bt_en_gpio;
u8 wlan_en_state = 0; u8 wlan_en_state = 0;
@@ -1157,6 +1158,7 @@ int cnss_get_input_gpio_value(struct cnss_plat_data *plat_priv, int gpio_num)
int cnss_power_on_device(struct cnss_plat_data *plat_priv, bool reset) int cnss_power_on_device(struct cnss_plat_data *plat_priv, bool reset)
{ {
int ret = 0; int ret = 0;
int dsp_link_status = -1;
if (plat_priv->powered_on) { if (plat_priv->powered_on) {
cnss_pr_dbg("Already powered up"); cnss_pr_dbg("Already powered up");
@@ -1169,6 +1171,12 @@ int cnss_power_on_device(struct cnss_plat_data *plat_priv, bool reset)
return -EINVAL; return -EINVAL;
} }
/* For PCIe switch platform, disable DSP downstream link before power
* on/off wlan device to avoid uncorrectable AER erro on DSP side.
*/
cnss_bus_set_dsp_link_status(plat_priv, true);
cnss_bus_dsp_link_control(plat_priv, false);
ret = cnss_vreg_on_type(plat_priv, CNSS_VREG_PRIM); ret = cnss_vreg_on_type(plat_priv, CNSS_VREG_PRIM);
if (ret) { if (ret) {
cnss_pr_err("Failed to turn on vreg, err = %d\n", ret); cnss_pr_err("Failed to turn on vreg, err = %d\n", ret);
@@ -1207,6 +1215,17 @@ int cnss_power_on_device(struct cnss_plat_data *plat_priv, bool reset)
goto clk_off; goto clk_off;
} }
/* For PCIe switch platform, wait for link train of DSP<->WLAN complete
*/
dsp_link_status = cnss_bus_get_dsp_link_status(plat_priv);
if (dsp_link_status == PCI_DSP_LINK_DISABLE) {
ret = cnss_bus_dsp_link_enable(plat_priv);
if (ret) {
cnss_pr_err("Failed to enable bus dsp link, err = %d\n", ret);
goto clk_off;
}
}
plat_priv->powered_on = true; plat_priv->powered_on = true;
clear_bit(CNSS_POWER_OFF, &plat_priv->driver_state); clear_bit(CNSS_POWER_OFF, &plat_priv->driver_state);
cnss_enable_dev_sol_irq(plat_priv); cnss_enable_dev_sol_irq(plat_priv);
@@ -1229,6 +1248,7 @@ void cnss_power_off_device(struct cnss_plat_data *plat_priv)
return; return;
} }
cnss_bus_dsp_link_control(plat_priv, false);
set_bit(CNSS_POWER_OFF, &plat_priv->driver_state); set_bit(CNSS_POWER_OFF, &plat_priv->driver_state);
cnss_bus_shutdown_cleanup(plat_priv); cnss_bus_shutdown_cleanup(plat_priv);
cnss_disable_dev_sol_irq(plat_priv); cnss_disable_dev_sol_irq(plat_priv);