diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index a4127bb7b564..134ce317621a 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -90,6 +90,7 @@ struct cnss_pinctrl_info { struct pinctrl_state *wlan_en_active; struct pinctrl_state *wlan_en_sleep; int bt_en_gpio; + int xo_clk_gpio; /*qca6490 only */ }; #if IS_ENABLED(CONFIG_MSM_SUBSYSTEM_RESTART) diff --git a/drivers/net/wireless/cnss2/power.c b/drivers/net/wireless/cnss2/power.c index add15f9eead9..f57925d45588 100644 --- a/drivers/net/wireless/cnss2/power.c +++ b/drivers/net/wireless/cnss2/power.c @@ -43,6 +43,7 @@ static struct cnss_clk_cfg cnss_clk_list[] = { #define BOOTSTRAP_ACTIVE "bootstrap_active" #define WLAN_EN_GPIO "wlan-en-gpio" #define BT_EN_GPIO "qcom,bt-en-gpio" +#define XO_CLK_GPIO "qcom,xo-clk-gpio" #define WLAN_EN_ACTIVE "wlan_en_active" #define WLAN_EN_SLEEP "wlan_en_sleep" @@ -711,11 +712,57 @@ int cnss_get_pinctrl(struct cnss_plat_data *plat_priv) pinctrl_info->bt_en_gpio = -EINVAL; } + /* Added for QCA6490 to minimize XO CLK selection leakage prevention */ + if (of_find_property(dev->of_node, XO_CLK_GPIO, NULL)) { + pinctrl_info->xo_clk_gpio = of_get_named_gpio(dev->of_node, + XO_CLK_GPIO, 0); + cnss_pr_dbg("QCA6490 XO_CLK GPIO: %d\n", + pinctrl_info->xo_clk_gpio); + } else { + pinctrl_info->xo_clk_gpio = -EINVAL; + } return 0; out: return ret; } +#define CNSS_XO_CLK_RETRY_COUNT_MAX 5 +static void cnss_set_xo_clk_gpio_state(struct cnss_plat_data *plat_priv, + bool enable) +{ + int xo_clk_gpio = plat_priv->pinctrl_info.xo_clk_gpio, retry = 0, ret; + + if (xo_clk_gpio < 0 || plat_priv->device_id != QCA6490_DEVICE_ID) + return; + +retry_gpio_req: + ret = gpio_request(xo_clk_gpio, "XO_CLK_GPIO"); + if (ret) { + if (retry++ < CNSS_XO_CLK_RETRY_COUNT_MAX) { + /* wait for ~(10 - 20) ms */ + usleep_range(10000, 20000); + goto retry_gpio_req; + } + } + + if (ret) { + cnss_pr_err("QCA6490 XO CLK Gpio request failed\n"); + return; + } + + if (enable) { + gpio_direction_output(xo_clk_gpio, 1); + /*XO CLK must be asserted for some time before WLAN_EN */ + usleep_range(100, 200); + } else { + /* Assert XO CLK ~(2-5)ms before off for valid latch in HW */ + usleep_range(2000, 5000); + gpio_direction_output(xo_clk_gpio, 0); + } + + gpio_free(xo_clk_gpio); +} + static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, bool state) { @@ -742,7 +789,7 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, } udelay(BOOTSTRAP_DELAY); } - + cnss_set_xo_clk_gpio_state(plat_priv, true); if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_active)) { ret = pinctrl_select_state (pinctrl_info->pinctrl, @@ -754,6 +801,7 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, } udelay(WLAN_ENABLE_DELAY); } + cnss_set_xo_clk_gpio_state(plat_priv, false); } else { if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_sleep)) { ret = pinctrl_select_state(pinctrl_info->pinctrl,