diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c index d48c4d0ac40c..db6ed2495d0e 100644 --- a/drivers/net/wireless/cnss2/bus.c +++ b/drivers/net/wireless/cnss2/bus.c @@ -621,3 +621,20 @@ bool cnss_bus_is_smmu_s1_enabled(struct cnss_plat_data *plat_priv) return false; } } + +int cnss_bus_dsp_link_control(struct cnss_plat_data *plat_priv, + bool link_enable) +{ + if (!plat_priv) + 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; + } +} diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h index 716e242cb70b..6ae2ffe0498b 100644 --- a/drivers/net/wireless/cnss2/bus.h +++ b/drivers/net/wireless/cnss2/bus.h @@ -72,4 +72,6 @@ int cnss_bus_get_msi_assignment(struct cnss_plat_data *plat_priv, u32 *user_base_data, u32 *base_vector); 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); #endif /* _CNSS_BUS_H */ diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index ccea088d6a95..a9ab9735561d 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -612,6 +612,12 @@ static int _cnss_pci_get_reg_dump(struct cnss_pci_data *pci_priv, { return msm_pcie_reg_dump(pci_priv->pci_dev, buf, len); } + +int cnss_pci_dsp_link_control(struct cnss_pci_data *pci_priv, + bool link_enable) +{ + return msm_pcie_dsp_link_control(pci_priv->pci_dev, link_enable); +} #else static int _cnss_pci_enumerate(struct cnss_plat_data *plat_priv, u32 rc_num) { diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index 8af91a41985b..3fc605f35227 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -262,4 +262,6 @@ int cnss_pci_get_user_msi_assignment(struct cnss_pci_data *pci_priv, u32 *user_base_data, u32 *base_vector); bool cnss_pci_is_smmu_s1_enabled(struct cnss_pci_data *pci_priv); +int cnss_pci_dsp_link_control(struct cnss_pci_data *pci_priv, + bool link_enable); #endif /* _CNSS_PCI_H */ diff --git a/drivers/net/wireless/cnss2/power.c b/drivers/net/wireless/cnss2/power.c index 55e2d253d7b7..a50b862f24ee 100644 --- a/drivers/net/wireless/cnss2/power.c +++ b/drivers/net/wireless/cnss2/power.c @@ -69,6 +69,10 @@ static struct cnss_clk_cfg cnss_clk_list[] = { #define MAX_TCS_CMD_NUM 5 #define BT_CXMX_VOLTAGE_MV 950 +#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) + static int cnss_get_vreg_single(struct cnss_plat_data *plat_priv, struct cnss_vreg_info *vreg) { @@ -975,12 +979,20 @@ int cnss_gpio_get_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; + bool dsp_link_disabled = false; + int retry_count = 0; if (plat_priv->powered_on) { cnss_pr_dbg("Already powered up"); return 0; } + if (plat_priv->bus_priv && + (plat_priv->bus_type == CNSS_BUS_PCI)) { + cnss_bus_dsp_link_control(plat_priv, false); + dsp_link_disabled = true; + } + ret = cnss_vreg_on_type(plat_priv, CNSS_VREG_PRIM); if (ret) { cnss_pr_err("Failed to turn on vreg, err = %d\n", ret); @@ -1016,6 +1028,26 @@ int cnss_power_on_device(struct cnss_plat_data *plat_priv, bool reset) goto clk_off; } + while (dsp_link_disabled && + (retry_count++ < DSP_LINK_ENABLE_RETRY_COUNT_MAX)) { + ret = cnss_bus_dsp_link_control(plat_priv, true); + if (!ret) + break; + + cnss_bus_dsp_link_control(plat_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); + goto clk_off; + } + usleep_range(DSP_LINK_ENABLE_DELAY_TIME_US_MIN, + DSP_LINK_ENABLE_DELAY_TIME_US_MAX); + } + plat_priv->powered_on = true; return 0; @@ -1035,6 +1067,11 @@ void cnss_power_off_device(struct cnss_plat_data *plat_priv) return; } + if (plat_priv->bus_priv && + (plat_priv->bus_type == CNSS_BUS_PCI)) { + cnss_bus_dsp_link_control(plat_priv, false); + } + cnss_select_pinctrl_state(plat_priv, false); cnss_clk_off(plat_priv, &plat_priv->clk_list); cnss_vreg_off_type(plat_priv, CNSS_VREG_PRIM);