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;