diff --git a/Kbuild b/Kbuild index 0e05bdd632..2e4f8194df 100644 --- a/Kbuild +++ b/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/ diff --git a/cnss2/bus.c b/cnss2/bus.c index b263bedbe2..a774b1ba24 100644 --- a/cnss2/bus.c +++ b/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) { diff --git a/cnss2/bus.h b/cnss2/bus.h index 7834e79760..4b1e23333c 100644 --- a/cnss2/bus.h +++ b/cnss2/bus.h @@ -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 */ diff --git a/cnss2/main.h b/cnss2/main.h index 8000a4c171..9908ae4f9a 100644 --- a/cnss2/main.h +++ b/cnss2/main.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 */ diff --git a/cnss2/pci.h b/cnss2/pci.h index 833d16cc8f..e203361a5f 100644 --- a/cnss2/pci.h +++ b/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) diff --git a/cnss2/pci_platform.h b/cnss2/pci_platform.h index eaa50ea9cb..05fa9ce54a 100644 --- a/cnss2/pci_platform.h +++ b/cnss2/pci_platform.h @@ -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) diff --git a/cnss2/pci_qcom.c b/cnss2/pci_qcom.c index aa867d3015..2b28784363 100644 --- a/cnss2/pci_qcom.c +++ b/cnss2/pci_qcom.c @@ -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); diff --git a/cnss2/power.c b/cnss2/power.c index 37cc6296f8..9621d61da5 100644 --- a/cnss2/power.c +++ b/cnss2/power.c @@ -19,6 +19,7 @@ #include "debug.h" #include "bus.h" #include +#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);