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:
5
Kbuild
5
Kbuild
@@ -86,6 +86,11 @@ ifeq ($(CONFIG_FEATURE_SMEM_MAILBOX), y)
|
||||
KBUILD_CPPFLAGS += -DCONFIG_FEATURE_SMEM_MAILBOX
|
||||
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_ICNSS2) += icnss2/
|
||||
obj-$(CONFIG_CNSS_GENL) += cnss_genl/
|
||||
|
63
cnss2/bus.c
63
cnss2/bus.c
@@ -7,6 +7,7 @@
|
||||
#include "bus.h"
|
||||
#include "debug.h"
|
||||
#include "pci.h"
|
||||
#include "pci_platform.h"
|
||||
|
||||
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,
|
||||
unsigned int time_sync_period)
|
||||
{
|
||||
|
@@ -77,4 +77,10 @@ int cnss_bus_get_msi_assignment(struct cnss_plat_data *plat_priv,
|
||||
int *num_vectors,
|
||||
u32 *user_base_data,
|
||||
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 */
|
||||
|
@@ -767,4 +767,6 @@ size_t cnss_get_platform_name(struct cnss_plat_data *plat_priv,
|
||||
char *buf, const size_t buf_len);
|
||||
int cnss_iommu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
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 */
|
||||
|
11
cnss2/pci.h
11
cnss2/pci.h
@@ -44,6 +44,14 @@
|
||||
ee == MHI_EE_WFW || \
|
||||
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 {
|
||||
CNSS_MHI_INIT,
|
||||
CNSS_MHI_DEINIT,
|
||||
@@ -185,6 +193,9 @@ struct cnss_pci_data {
|
||||
bool drv_supported;
|
||||
bool is_smmu_fault;
|
||||
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)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/* 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
|
||||
#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);
|
||||
int cnss_pci_init_smmu(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
|
||||
* @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,
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
static inline bool cnss_pci_get_drv_supported(struct cnss_pci_data *pci_priv)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
// 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 "debug.h"
|
||||
@@ -403,6 +403,93 @@ retry:
|
||||
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)
|
||||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include "debug.h"
|
||||
#include "bus.h"
|
||||
#include <linux/soc/qcom/qcom_aoss.h>
|
||||
#include "pci_platform.h"
|
||||
|
||||
#if IS_ENABLED(CONFIG_ARCH_QCOM)
|
||||
static struct cnss_vreg_cfg cnss_vreg_list[] = {
|
||||
@@ -1008,8 +1009,8 @@ retry_gpio_req:
|
||||
gpio_free(xo_clk_gpio);
|
||||
}
|
||||
|
||||
static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv,
|
||||
bool state)
|
||||
int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv,
|
||||
bool state)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cnss_pinctrl_info *pinctrl_info;
|
||||
@@ -1105,7 +1106,7 @@ out:
|
||||
*
|
||||
* 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;
|
||||
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 ret = 0;
|
||||
int dsp_link_status = -1;
|
||||
|
||||
if (plat_priv->powered_on) {
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
clear_bit(CNSS_POWER_OFF, &plat_priv->driver_state);
|
||||
cnss_enable_dev_sol_irq(plat_priv);
|
||||
@@ -1229,6 +1248,7 @@ void cnss_power_off_device(struct cnss_plat_data *plat_priv)
|
||||
return;
|
||||
}
|
||||
|
||||
cnss_bus_dsp_link_control(plat_priv, false);
|
||||
set_bit(CNSS_POWER_OFF, &plat_priv->driver_state);
|
||||
cnss_bus_shutdown_cleanup(plat_priv);
|
||||
cnss_disable_dev_sol_irq(plat_priv);
|
||||
|
Reference in New Issue
Block a user