diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index 78adb20b56f6dd8acb59e1bbe3535afc4471f613..e720b06d16ec351a13b086ee437e3969ef6268d9 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -36,6 +36,7 @@
#include <linux/mfd/pm8xxx/misc.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/pm_qos.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
@@ -51,6 +52,10 @@
#define WCNSS_PINCTRL_STATE_SLEEP "wcnss_sleep"
#define WCNSS_PINCTRL_GPIO_STATE_DEFAULT "wcnss_gpio_default"
+#define WCNSS_DISABLE_PC_LATENCY 100
+#define WCNSS_ENABLE_PC_LATENCY PM_QOS_DEFAULT_VALUE
+#define WCNSS_PM_QOS_TIMEOUT 15000
+
/* module params */
#define WCNSS_CONFIG_UNSPECIFIED (-1)
#define UINT32_MAX (0xFFFFFFFFU)
@@ -403,6 +408,9 @@ static struct {
int gpios[WCNSS_WLAN_MAX_GPIO];
int use_pinctrl;
u8 is_shutdown;
+ struct pm_qos_request wcnss_pm_qos_request;
+ int pc_disabled;
+ struct delayed_work wcnss_pm_qos_del_req;
} *penv = NULL;
static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
@@ -1070,6 +1078,46 @@ static void wcnss_remove_sysfs(struct device *dev)
device_remove_file(dev, &dev_attr_wcnss_mac_addr);
}
}
+
+static void wcnss_pm_qos_add_request(void)
+{
+ pr_info("%s: add request", __func__);
+ pm_qos_add_request(&penv->wcnss_pm_qos_request, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+}
+
+static void wcnss_pm_qos_remove_request(void)
+{
+ pr_info("%s: remove request", __func__);
+ pm_qos_remove_request(&penv->wcnss_pm_qos_request);
+}
+
+void wcnss_pm_qos_update_request(int val)
+{
+ pr_info("%s: update request %d", __func__, val);
+ pm_qos_update_request(&penv->wcnss_pm_qos_request, val);
+}
+
+void wcnss_disable_pc_remove_req(void)
+{
+ if (penv->pc_disabled) {
+ wcnss_pm_qos_update_request(WCNSS_ENABLE_PC_LATENCY);
+ wcnss_pm_qos_remove_request();
+ wcnss_allow_suspend();
+ penv->pc_disabled = 0;
+ }
+}
+
+void wcnss_disable_pc_add_req(void)
+{
+ if (!penv->pc_disabled) {
+ wcnss_pm_qos_add_request();
+ wcnss_prevent_suspend();
+ wcnss_pm_qos_update_request(WCNSS_DISABLE_PC_LATENCY);
+ penv->pc_disabled = 1;
+ }
+}
+
static void wcnss_smd_notify_event(void *data, unsigned int event)
{
int len = 0;
@@ -1093,6 +1141,8 @@ static void wcnss_smd_notify_event(void *data, unsigned int event)
WCNSS_CTRL_CHANNEL);
schedule_work(&penv->wcnssctrl_version_work);
schedule_work(&penv->wcnss_pm_config_work);
+ cancel_delayed_work(&penv->wcnss_pm_qos_del_req);
+ schedule_delayed_work(&penv->wcnss_pm_qos_del_req, 0);
break;
@@ -2071,6 +2121,11 @@ static void wcnss_send_pm_config(struct work_struct *worker)
return;
}
+static void wcnss_pm_qos_enable_pc(struct work_struct *worker)
+{
+ wcnss_disable_pc_remove_req();
+ return;
+}
static DECLARE_RWSEM(wcnss_pm_sem);
@@ -2501,8 +2556,11 @@ wcnss_trigger_config(struct platform_device *pdev)
INIT_WORK(&penv->wcnssctrl_version_work, wcnss_send_version_req);
INIT_WORK(&penv->wcnss_pm_config_work, wcnss_send_pm_config);
INIT_WORK(&penv->wcnssctrl_nvbin_dnld_work, wcnss_nvbin_dnld_main);
+ INIT_DELAYED_WORK(&penv->wcnss_pm_qos_del_req, wcnss_pm_qos_enable_pc);
wake_lock_init(&penv->wcnss_wake_lock, WAKE_LOCK_SUSPEND, "wcnss");
+ /* Add pm_qos request to disable power collapse for DDR */
+ wcnss_disable_pc_add_req();
if (wcnss_hardware_type() == WCNSS_PRONTO_HW) {
res = platform_get_resource_byname(pdev,
@@ -2752,6 +2810,7 @@ wcnss_trigger_config(struct platform_device *pdev)
if (IS_ERR(penv->pil)) {
dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
ret = PTR_ERR(penv->pil);
+ wcnss_disable_pc_add_req();
wcnss_pronto_log_debug_regs();
}
} while (pil_retry++ < WCNSS_MAX_PIL_RETRY && IS_ERR(penv->pil));
@@ -2764,6 +2823,8 @@ wcnss_trigger_config(struct platform_device *pdev)
penv->pil = NULL;
goto fail_ioremap2;
}
+ /* Remove pm_qos request */
+ wcnss_disable_pc_remove_req();
return 0;
@@ -2780,6 +2841,7 @@ fail_res:
else
wcnss_pronto_gpios_config(pdev, false);
fail_gpio_res:
+ wcnss_disable_pc_remove_req();
penv = NULL;
return ret;
}
@@ -2911,7 +2973,7 @@ static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
struct notif_data *data = (struct notif_data *)ss_handle;
int ret, xo_mode;
- pr_debug("%s: wcnss notification event: %lu\n", __func__, code);
+ pr_info("%s: wcnss notification event: %lu\n", __func__, code);
if (code == SUBSYS_PROXY_VOTE) {
if (pdev && pwlanconfig) {
@@ -2932,16 +2994,23 @@ static int wcnss_notif_cb(struct notifier_block *this, unsigned long code,
WCNSS_WLAN_SWITCH_OFF, NULL);
}
} else if ((code == SUBSYS_BEFORE_SHUTDOWN && data && data->crashed) ||
- code == SUBSYS_SOC_RESET)
+ code == SUBSYS_SOC_RESET) {
+ wcnss_disable_pc_add_req();
+ schedule_delayed_work(&penv->wcnss_pm_qos_del_req,
+ msecs_to_jiffies(WCNSS_PM_QOS_TIMEOUT));
wcnss_log_debug_regs_on_bite();
- else if (code == SUBSYS_POWERUP_FAILURE) {
+ } else if (code == SUBSYS_POWERUP_FAILURE) {
if (pdev && pwlanconfig)
wcnss_wlan_power(&pdev->dev, pwlanconfig,
WCNSS_WLAN_SWITCH_OFF, NULL);
wcnss_pronto_log_debug_regs();
- } else if (SUBSYS_BEFORE_SHUTDOWN == code)
+ wcnss_disable_pc_remove_req();
+ } else if (SUBSYS_BEFORE_SHUTDOWN == code) {
+ wcnss_disable_pc_add_req();
+ schedule_delayed_work(&penv->wcnss_pm_qos_del_req,
+ msecs_to_jiffies(WCNSS_PM_QOS_TIMEOUT));
penv->is_shutdown = 1;
- else if (SUBSYS_AFTER_POWERUP == code)
+ } else if (SUBSYS_AFTER_POWERUP == code)
penv->is_shutdown = 0;
return NOTIFY_DONE;