Procházet zdrojové kódy

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
Jiani Liu před 7 měsíci
rodič
revize
d792a01299
8 změnil soubory, kde provedl 226 přidání a 6 odebrání
  1. 5 0
      Kbuild
  2. 63 0
      cnss2/bus.c
  3. 6 0
      cnss2/bus.h
  4. 2 0
      cnss2/main.h
  5. 11 0
      cnss2/pci.h
  6. 28 2
      cnss2/pci_platform.h
  7. 88 1
      cnss2/pci_qcom.c
  8. 23 3
      cnss2/power.c

+ 5 - 0
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 - 0
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)
 {

+ 6 - 0
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 */

+ 2 - 0
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 */

+ 11 - 0
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)

+ 28 - 2
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)

+ 88 - 1
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);

+ 23 - 3
cnss2/power.c

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