summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKomal Seelam <kseelam@qti.qualcomm.com>2015-04-29 17:50:21 +0530
committerAnjaneeDevi Kapparapu <c_akappa@qti.qualcomm.com>2015-05-22 21:06:43 +0530
commitacf8184ec1bb7d813dbe467bb00f402087f5e91b (patch)
tree06f1a73f76deb728181c57d898808540d67f887f
parent8ce8419d0bf78b8d53712d46fb387f4775332888 (diff)
qcacld: HIF: Enable Runtime PM Support.
Runtime PM Feature is to put wlan subsystem to low power device states based on the inactivity between HOST and FW/MAC. Change-Id: I72a6d95f5404e00852146e437266684ad1036f42 CRs-Fixed: 834747
-rw-r--r--CORE/SERVICES/COMMON/hif.h21
-rw-r--r--CORE/SERVICES/COMMON/hif_msg_based.h3
-rw-r--r--CORE/SERVICES/HIF/PCIe/hif_pci.c95
-rw-r--r--CORE/SERVICES/HIF/PCIe/hif_pci.h57
-rw-r--r--CORE/SERVICES/HIF/PCIe/if_pci.c352
-rw-r--r--CORE/SERVICES/HIF/PCIe/if_pci.h30
-rw-r--r--CORE/SERVICES/HIF/USB/if_usb.c6
-rw-r--r--CORE/SERVICES/HIF/sdio/linux/if_ath_sdio.c6
8 files changed, 537 insertions, 33 deletions
diff --git a/CORE/SERVICES/COMMON/hif.h b/CORE/SERVICES/COMMON/hif.h
index 15552a7261dd..5013764d75be 100644
--- a/CORE/SERVICES/COMMON/hif.h
+++ b/CORE/SERVICES/COMMON/hif.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -827,9 +827,22 @@ void HIFIpaGetCEResource(HIF_DEVICE *hif_device,
void HIFSetMailboxSwap(HIF_DEVICE *device);
-/* runtime power management API of HIF to prevent suspending */
-int hif_pm_runtime_get(void);
-int hif_pm_runtime_put(void);
+#ifdef FEATURE_RUNTIME_PM
+/* Runtime power management API of HIF to control
+ * runtime pm. During Runtime Suspend the get API
+ * return -EAGAIN. The caller can queue the cmd or return.
+ * The put API decrements the usage count.
+ * The get API increments the usage count.
+ * The API's are exposed to HTT and WMI Services only.
+ */
+int hif_pm_runtime_get(HIF_DEVICE *);
+int hif_pm_runtime_put(HIF_DEVICE *);
+#else
+static inline int hif_pm_runtime_get(HIF_DEVICE *device) { return 0; }
+static inline int hif_pm_runtime_put(HIF_DEVICE *device) { return 0; }
+#endif
+int hif_pm_runtime_prevent_suspend(void *ol_sc);
+int hif_pm_runtime_allow_suspend(void *ol_sc);
#ifdef __cplusplus
}
#endif
diff --git a/CORE/SERVICES/COMMON/hif_msg_based.h b/CORE/SERVICES/COMMON/hif_msg_based.h
index 3027c8f3b08f..f7143aab3d48 100644
--- a/CORE/SERVICES/COMMON/hif_msg_based.h
+++ b/CORE/SERVICES/COMMON/hif_msg_based.h
@@ -1,5 +1,5 @@
/*
- *Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
+ *Copyright (c) 2013-2015 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -49,6 +49,7 @@ typedef struct {
u_int8_t pipeID);
void (*txResourceAvailHandler)(void *context, u_int8_t pipe);
void (*fwEventHandler)(void *context, A_STATUS status);
+ void (*txResumeAllHandler)(void *context);
} MSG_BASED_HIF_CALLBACKS;
int HIF_deregister(void);
diff --git a/CORE/SERVICES/HIF/PCIe/hif_pci.c b/CORE/SERVICES/HIF/PCIe/hif_pci.c
index 21d3fd3f4083..2f7ebb4be3f7 100644
--- a/CORE/SERVICES/HIF/PCIe/hif_pci.c
+++ b/CORE/SERVICES/HIF/PCIe/hif_pci.c
@@ -532,6 +532,7 @@ HIF_PCI_CE_recv_data(struct CE_handle *copyeng, void *ce_context, void *transfer
compl_queue_head = compl_queue_tail = NULL;
do {
+ hif_pm_runtime_mark_last_busy(sc->dev);
adf_os_spin_lock(&pipe_info->completion_freeq_lock);
compl_state = pipe_info->completion_freeq_head;
ASSERT(compl_state != NULL);
@@ -3073,3 +3074,97 @@ void HIFIpaGetCEResource(HIF_DEVICE *hif_device,
}
#endif /* IPA_UC_OFFLOAD */
+
+#ifdef FEATURE_RUNTIME_PM
+int hif_pm_runtime_get(HIF_DEVICE *hif_device)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)hif_device;
+ struct hif_pci_softc *sc = hif_state->sc;
+ int ret = 0;
+
+ if (adf_os_atomic_read(&sc->pm_state) == HIF_PM_RUNTIME_STATE_ON) {
+ sc->pm_stats.runtime_get++;
+ ret = __hif_pm_runtime_get(sc->dev);
+
+ /* Get can return 1 if the device is already active, just return
+ * success in that case
+ */
+ if (ret > 0)
+ ret = 0;
+
+ if (ret < 0)
+ hif_pm_runtime_put(hif_device);
+
+ return ret;
+ }
+
+ sc->pm_stats.request_resume++;
+ sc->pm_stats.last_resume_caller = (void *)_RET_IP_;
+ ret = hif_pm_request_resume(sc->dev);
+
+ pr_debug("%s: request resume:%pS in pm_state:%d ret: %d\n",
+ __func__, (void *)_RET_IP_,
+ adf_os_atomic_read(&sc->pm_state), ret);
+ return -EAGAIN;
+}
+
+int hif_pm_runtime_put(HIF_DEVICE *hif_device)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)hif_device;
+ struct hif_pci_softc *sc = hif_state->sc;
+ int ret = 0;
+
+ sc->pm_stats.runtime_put++;
+
+ hif_pm_runtime_mark_last_busy(sc->dev);
+ ret = hif_pm_runtime_put_auto(sc->dev);
+
+ pr_debug("%s: %pS in pm_state:%d ret: %d\n", __func__, (void *)_RET_IP_,
+ adf_os_atomic_read(&sc->pm_state), ret);
+
+ return 0;
+}
+
+int hif_pm_runtime_prevent_suspend(void *ol_sc)
+{
+ struct ol_softc *sc = (struct ol_softc *)ol_sc;
+ struct hif_pci_softc *hif_sc = sc->hif_sc;
+ int ret = 0;
+
+ hif_sc->pm_stats.prevent_suspend++;
+
+ ret = __hif_pm_runtime_get(hif_sc->dev);
+
+ pr_debug("%s: request resume:%pS in pm_state:%d ret: %d\n",
+ __func__, (void *)_RET_IP_,
+ adf_os_atomic_read(&hif_sc->pm_state), ret);
+
+ return 0;
+
+}
+int hif_pm_runtime_allow_suspend(void *ol_sc)
+{
+ struct ol_softc *sc = (struct ol_softc *)ol_sc;
+ struct hif_pci_softc *hif_sc = sc->hif_sc;
+ int ret = 0;
+
+ hif_sc->pm_stats.allow_suspend++;
+
+ hif_pm_runtime_mark_last_busy(hif_sc->dev);
+ ret = hif_pm_runtime_put_auto(hif_sc->dev);
+
+ pr_debug("%s: %pS in pm_state:%d ret: %d\n", __func__, (void *)_RET_IP_,
+ adf_os_atomic_read(&hif_sc->pm_state), ret);
+
+ return 0;
+}
+#else
+int hif_pm_runtime_prevent_suspend(void *ol_sc)
+{
+ return 0;
+}
+int hif_pm_runtime_allow_suspend(void *ol_sc)
+{
+ return 0;
+}
+#endif
diff --git a/CORE/SERVICES/HIF/PCIe/hif_pci.h b/CORE/SERVICES/HIF/PCIe/hif_pci.h
index 2ab94a80e76a..d5b3da8bcd8c 100644
--- a/CORE/SERVICES/HIF/PCIe/hif_pci.h
+++ b/CORE/SERVICES/HIF/PCIe/hif_pci.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -152,5 +152,60 @@ void priv_dump_chaninfo(struct hif_pci_softc *sc);
void priv_dump_bbwatchdog(struct hif_pci_softc *sc);
void hif_dump_pipe_debug_count(HIF_DEVICE *hif_device);
+#ifdef FEATURE_RUNTIME_PM
+#include <linux/pm_runtime.h>
+#ifdef WLAN_OPEN_SOURCE
+static inline int hif_pm_request_resume(struct device *dev)
+{
+ return pm_request_resume(dev);
+}
+static inline int __hif_pm_runtime_get(struct device *dev)
+{
+ return pm_runtime_get(dev);
+}
+
+static inline int hif_pm_runtime_put_auto(struct device *dev)
+{
+ return pm_runtime_put_autosuspend(dev);
+}
+
+static inline void hif_pm_runtime_mark_last_busy(struct device *dev)
+{
+ pm_runtime_mark_last_busy(dev);
+}
+
+static inline int hif_pm_runtime_resume(struct device *dev)
+{
+ return pm_runtime_resume(dev);
+}
+#else
+static inline int hif_pm_request_resume(struct device *dev)
+{
+ return cnss_pm_runtime_request(dev, CNSS_PM_REQUEST_RESUME);
+}
+
+static inline int __hif_pm_runtime_get(struct device *dev)
+{
+ return cnss_pm_runtime_request(dev, CNSS_PM_RUNTIME_GET);
+}
+
+static inline int hif_pm_runtime_put_auto(struct device *dev)
+{
+ return cnss_pm_runtime_request(dev, CNSS_PM_RUNTIME_PUT_AUTO);
+}
+
+static inline void hif_pm_runtime_mark_last_busy(struct device *dev)
+{
+ cnss_pm_runtime_request(dev, CNSS_PM_RUNTIME_MARK_LAST_BUSY);
+}
+static inline int hif_pm_runtime_resume(struct device *dev)
+{
+ return cnss_pm_runtime_request(dev, CNSS_PM_RUNTIME_RESUME);
+}
+#endif /* WLAN_OPEN_SOURCE */
+#else /* FEATURE_RUNTIME_PM */
+static inline void hif_pm_runtime_mark_last_busy(struct device *dev) { }
+#endif
+
#define CE_HTT_T2H_MSG 1
#define CE_HTT_H2T_MSG 4
diff --git a/CORE/SERVICES/HIF/PCIe/if_pci.c b/CORE/SERVICES/HIF/PCIe/if_pci.c
index 0f6346322eba..9bd0e4d0b3a9 100644
--- a/CORE/SERVICES/HIF/PCIe/if_pci.c
+++ b/CORE/SERVICES/HIF/PCIe/if_pci.c
@@ -40,6 +40,8 @@
#include "bmi_msg.h" /* TARGET_TYPE_ */
#include "regtable.h"
#include "ol_fw.h"
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <osapi_linux.h>
#include "vos_api.h"
#include "vos_sched.h"
@@ -82,6 +84,8 @@ module_param(msienable, int, 0644);
int hif_pci_configure(struct hif_pci_softc *sc, hif_handle_t *hif_hdl);
void hif_nointrs(struct hif_pci_softc *sc);
+static int __hif_pci_suspend(struct pci_dev *, pm_message_t, bool);
+static int __hif_pci_resume(struct pci_dev *, bool);
static struct pci_device_id hif_pci_id_table[] = {
{ 0x168c, 0x003c, PCI_ANY_ID, PCI_ANY_ID },
@@ -712,6 +716,313 @@ irq_handled:
#define ATH_PCI_PROBE_RETRY_MAX 3
+#ifdef FEATURE_RUNTIME_PM
+static void hif_pci_pm_work(struct work_struct *work)
+{
+ struct hif_pci_softc *sc = container_of(work, struct hif_pci_softc,
+ pm_work);
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)sc->hif_device;
+ MSG_BASED_HIF_CALLBACKS *msg_callbacks;
+
+ pr_debug("%s: Resume HTT & WMI Service in runtime_pm state %d\n",
+ __func__, atomic_read(&sc->pm_state));
+
+ msg_callbacks = &hif_state->msg_callbacks_current;
+
+ msg_callbacks->txResumeAllHandler(msg_callbacks->Context);
+}
+
+static int hif_pci_autopm_debugfs_show(struct seq_file *s, void *data)
+{
+#define HIF_PCI_AUTOPM_STATS(_s, _sc, _name) \
+ seq_printf(_s, "%20s: %u\n", #_name, _sc->pm_stats._name)
+ struct hif_pci_softc *sc = s->private;
+ char *autopm_state[] = {"ON", "INPROGRESS", "SUSPENDED"};
+ unsigned int msecs_age;
+ int pm_state = atomic_read(&sc->pm_state);
+
+ seq_printf(s, "%20s: %s\n", "Runtime PM state",
+ autopm_state[pm_state]);
+ seq_printf(s, "%20s: %pf\n", "Last Resume Caller",
+ sc->pm_stats.last_resume_caller);
+
+ if (pm_state == HIF_PM_RUNTIME_STATE_SUSPENDED) {
+ msecs_age = jiffies_to_msecs(jiffies - sc->pm_stats.suspend_jiffies);
+ seq_printf(s, "%20s: %d.%03ds\n", "Suspended Since",
+ msecs_age / 1000, msecs_age % 1000);
+ }
+
+ seq_printf(s, "%20s: %d\n", "PM Usage count",
+ atomic_read(&sc->dev->power.usage_count));
+
+ HIF_PCI_AUTOPM_STATS(s, sc, suspended);
+ HIF_PCI_AUTOPM_STATS(s, sc, suspend_err);
+ HIF_PCI_AUTOPM_STATS(s, sc, resumed);
+ HIF_PCI_AUTOPM_STATS(s, sc, runtime_get);
+ HIF_PCI_AUTOPM_STATS(s, sc, runtime_put);
+ HIF_PCI_AUTOPM_STATS(s, sc, request_resume);
+ HIF_PCI_AUTOPM_STATS(s, sc, prevent_suspend);
+ HIF_PCI_AUTOPM_STATS(s, sc, allow_suspend);
+ return 0;
+#undef HIF_PCI_AUTOPM_STATS
+}
+
+static int hif_pci_autopm_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hif_pci_autopm_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations hif_pci_autopm_fops = {
+ .owner = THIS_MODULE,
+ .open = hif_pci_autopm_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+static int __hif_pci_runtime_suspend(struct pci_dev *pdev)
+{
+ struct hif_pci_softc *sc = pci_get_drvdata(pdev);
+ void *vos_context = vos_get_global_context(VOS_MODULE_ID_HIF, NULL);
+ pm_message_t state = { .event = PM_EVENT_SUSPEND };
+ v_VOID_t *temp_module;
+ ol_txrx_pdev_handle txrx_pdev;
+ int ret = -EBUSY, test = 0;
+
+ adf_os_atomic_set(&sc->pm_state, HIF_PM_RUNTIME_STATE_INPROGRESS);
+
+ if (vos_is_load_unload_in_progress(VOS_MODULE_ID_HIF, NULL)) {
+ pr_err("%s: Load/Unload in Progress\n", __func__);
+ goto out;
+ }
+
+ if (vos_is_logp_in_progress(VOS_MODULE_ID_HIF, NULL)) {
+ pr_err("%s: LOGP in progress\n", __func__);
+ goto out;
+ }
+
+ txrx_pdev = vos_get_context(VOS_MODULE_ID_TXRX, vos_context);
+ if (!txrx_pdev) {
+ pr_err("%s: txrx_pdev is NULL\n", __func__);
+ goto out;
+ }
+
+ if ((test = ol_txrx_get_tx_pending(txrx_pdev))) {
+ pr_err("%s: txrx pending(%d), get: %u, put: %u\n", __func__,
+ test,
+ sc->pm_stats.runtime_get,
+ sc->pm_stats.runtime_put);
+ goto out;
+ }
+
+ temp_module = vos_get_context(VOS_MODULE_ID_WDA, vos_context);
+ if (!temp_module) {
+ pr_err("%s: WDA module is NULL\n", __func__);
+ goto out;
+ }
+
+ if (wma_check_scan_in_progress(temp_module)) {
+ pr_err("%s: Scan in Progress. Aborting runtime suspend\n",
+ __func__);
+ goto out;
+ }
+
+ ret = wma_runtime_suspend_req(temp_module);
+ if (ret) {
+ pr_err("%s: Runtime Offloads configuration failed: %d\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = __hif_pci_suspend(pdev, state, true);
+ if (ret) {
+ pr_err("%s: pci_suspend failed: %d\n", __func__, ret);
+ goto suspend_fail;
+ }
+
+#ifdef FEATURE_WLAN_D0WOW
+ if (wma_get_client_count(temp_module)) {
+ pr_err("%s: Runtime PM not supported when clients are connected\n",
+ __func__);
+ ret = -EINVAL;
+ goto suspend_fail;
+ }
+#endif
+
+ ret = cnss_auto_suspend();
+
+ if (ret) {
+ ret = -EAGAIN;
+ goto suspend_fail;
+ }
+
+ adf_os_atomic_set(&sc->pm_state, HIF_PM_RUNTIME_STATE_SUSPENDED);
+ sc->pm_stats.suspended++;
+ sc->pm_stats.suspend_jiffies = jiffies;
+
+ return 0;
+
+suspend_fail:
+ wma_runtime_resume_req(temp_module);
+out:
+ adf_os_atomic_set(&sc->pm_state, HIF_PM_RUNTIME_STATE_ON);
+ sc->pm_stats.suspend_err++;
+ hif_pm_runtime_mark_last_busy(sc->dev);
+
+ ASSERT(ret == -EAGAIN || ret == -EBUSY);
+
+ return ret;
+}
+
+static int hif_pci_runtime_suspend(struct pci_dev *pdev)
+{
+ int ret = 0;
+ vos_ssr_protect(__func__);
+ ret = __hif_pci_runtime_suspend(pdev);
+ vos_ssr_unprotect(__func__);
+
+ return ret;
+}
+
+static int __hif_pci_runtime_resume(struct pci_dev *pdev)
+{
+ struct hif_pci_softc *sc = pci_get_drvdata(pdev);
+ void *vos_context = vos_get_global_context(VOS_MODULE_ID_HIF, NULL);
+ int ret = 0;
+ v_VOID_t * temp_module;
+
+ adf_os_atomic_set(&sc->pm_state, HIF_PM_RUNTIME_STATE_INPROGRESS);
+
+ temp_module = vos_get_context(VOS_MODULE_ID_WDA, vos_context);
+ if (!temp_module) {
+ pr_err("%s: WDA module is NULL\n", __func__);
+ goto out;
+ }
+
+#ifdef FEATURE_WLAN_D0WOW
+ if (wma_get_client_count(temp_module)) {
+ pr_err("%s: Runtime PM not supported when clients are connected\n",
+ __func__);
+ ASSERT(0);
+
+ ret = -EINVAL;
+ goto out;
+ }
+#endif
+ ret = cnss_auto_resume();
+
+ if (ret) {
+ pr_err("%s: Failed to resume PCIe link: %d\n", __func__, ret);
+ goto out;
+ }
+
+ ret = __hif_pci_resume(pdev, true);
+
+ if (ret)
+ goto out;
+
+ ret = wma_runtime_resume_req(temp_module);
+ if (ret)
+ goto out;
+
+ adf_os_atomic_set(&sc->pm_state, HIF_PM_RUNTIME_STATE_ON);
+ hif_pm_runtime_mark_last_busy(sc->dev);
+ sc->pm_stats.resumed++;
+
+ schedule_work(&sc->pm_work);
+
+ return 0;
+out:
+ /* In Resume we should never fail */
+ ASSERT(0);
+ return ret;
+}
+
+static int hif_pci_runtime_resume(struct pci_dev *pdev)
+{
+ int ret = 0;
+
+ vos_ssr_protect(__func__);
+ ret = __hif_pci_runtime_resume(pdev);
+ vos_ssr_unprotect(__func__);
+
+ return ret;
+}
+
+/* TODO: Change this to dev_pm_ops */
+struct cnss_wlan_runtime_ops runtime_pm_ops = {
+ .runtime_suspend = hif_pci_runtime_suspend,
+ .runtime_resume = hif_pci_runtime_resume,
+};
+
+#ifdef WLAN_OPEN_SOURCE
+static inline void hif_pci_pm_debugfs(struct hif_pci_softc *sc, bool init)
+{
+ if (init)
+ sc->pm_dentry = debugfs_create_file("cnss_runtime_pm",
+ S_IRUSR, NULL, sc,
+ &hif_pci_autopm_fops);
+ else
+ debugfs_remove(sc->pm_dentry);
+}
+#else
+static inline void hif_pci_pm_debugfs(struct hif_pci_softc *sc, bool init)
+{
+
+}
+#endif
+
+static void hif_pci_pm_runtime_init(struct hif_pci_softc *sc)
+{
+ struct ol_softc *ol_sc;
+
+ ol_sc = sc->ol_sc;
+
+ if (!ol_sc->enable_runtime_pm) {
+ pr_info("%s: RUNTIME PM is disabled in ini\n", __func__);
+ return;
+ }
+
+ if (vos_get_conparam() == VOS_FTM_MODE ||
+ WLAN_IS_EPPING_ENABLED(vos_get_conparam())) {
+ pr_info("%s: RUNTIME PM is disabled for FTM/EPPING mode\n",
+ __func__);
+ return;
+ }
+
+ pr_info("%s: Enabling RUNTIME PM, Delay: %d ms\n", __func__,
+ ol_sc->runtime_pm_delay);
+
+ adf_os_atomic_init(&sc->pm_state);
+ adf_os_atomic_set(&sc->pm_state, HIF_PM_RUNTIME_STATE_ON);
+ cnss_init_work(&sc->pm_work, hif_pci_pm_work);
+ cnss_runtime_init(sc->dev, ol_sc->runtime_pm_delay);
+ hif_pci_pm_debugfs(sc, true);
+}
+
+static void hif_pci_pm_runtime_exit(struct hif_pci_softc *sc)
+{
+ struct ol_softc *ol_sc;
+
+ ol_sc = sc->ol_sc;
+
+ if (!ol_sc->enable_runtime_pm)
+ return;
+
+ if (vos_get_conparam() == VOS_FTM_MODE ||
+ WLAN_IS_EPPING_ENABLED(vos_get_conparam()))
+ return;
+
+ hif_pm_runtime_resume(sc->dev);
+ cnss_runtime_exit(sc->dev);
+ hif_pci_pm_debugfs(sc, false);
+}
+#else
+static inline void hif_pci_pm_runtime_init(struct hif_pci_softc *sc) { }
+
+static inline void hif_pci_pm_runtime_exit(struct hif_pci_softc *sc) { }
+#endif
+
int
hif_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
@@ -991,6 +1302,7 @@ again:
/* Re-enable ASPM after firmware/OTP download is complete */
pci_write_config_dword(pdev, 0x80, lcr_val);
+ hif_pci_pm_runtime_init(sc);
#ifndef REMOVE_PKT_LOG
if (vos_get_conparam() != VOS_FTM_MODE &&
!WLAN_IS_EPPING_ENABLED(vos_get_conparam())) {
@@ -1334,6 +1646,7 @@ again:
goto err_config;
}
+ hif_pci_pm_runtime_init(sc);
#ifndef REMOVE_PKT_LOG
if (vos_get_conparam() != VOS_FTM_MODE &&
!WLAN_IS_EPPING_ENABLED(vos_get_conparam())) {
@@ -1669,6 +1982,8 @@ hif_pci_remove(struct pci_dev *pdev)
pktlogmod_exit(scn);
#endif
+ hif_pci_pm_runtime_exit(sc);
+
__hdd_wlan_exit();
mem = (void __iomem *)sc->mem;
@@ -1722,6 +2037,8 @@ void hif_pci_shutdown(struct pci_dev *pdev)
pktlogmod_exit(scn);
#endif
+ hif_pci_pm_runtime_exit(sc);
+
if (!vos_is_ssr_ready(__func__))
printk("Host driver is not ready for SSR, attempting anyway\n");
@@ -1802,7 +2119,7 @@ out:
#define OL_ATH_PCI_PM_CONTROL 0x44
static int
-__hif_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+__hif_pci_suspend(struct pci_dev *pdev, pm_message_t state, bool runtime_pm)
{
struct hif_pci_softc *sc = pci_get_drvdata(pdev);
void *vos = vos_get_global_context(VOS_MODULE_ID_HIF, NULL);
@@ -1814,7 +2131,7 @@ __hif_pci_suspend(struct pci_dev *pdev, pm_message_t state)
u32 ce_drain_wait_cnt = 0;
v_VOID_t * temp_module;
u32 tmp;
- int ret = -1;
+ int ret = -EBUSY;
if (vos_is_logp_in_progress(VOS_MODULE_ID_HIF, NULL))
return ret;
@@ -1856,7 +2173,7 @@ __hif_pci_suspend(struct pci_dev *pdev, pm_message_t state)
wma_is_wow_mode_selected(temp_module), state.event);
if (wma_is_wow_mode_selected(temp_module)) {
- if(wma_enable_wow_in_fw(temp_module))
+ if(wma_enable_wow_in_fw(temp_module, runtime_pm))
goto out;
} else if (state.event == PM_EVENT_FREEZE || state.event == PM_EVENT_SUSPEND) {
if (wma_suspend_target(temp_module, 0))
@@ -1876,11 +2193,11 @@ __hif_pci_suspend(struct pci_dev *pdev, pm_message_t state)
if (!wma_is_wow_mode_selected(temp_module) &&
(val == PM_EVENT_HIBERNATE || val == PM_EVENT_SUSPEND)) {
- wma_resume_target(temp_module);
+ wma_resume_target(temp_module, runtime_pm);
goto out;
}
else {
- wma_disable_wow_in_fw(temp_module);
+ wma_disable_wow_in_fw(temp_module, runtime_pm);
goto out;
}
}
@@ -1892,7 +2209,7 @@ __hif_pci_suspend(struct pci_dev *pdev, pm_message_t state)
if (wma_get_client_count(temp_module)) {
if (enable_irq_wake(pdev->irq)) {
pr_err("%s: Fail to enable wake IRQ!\n", __func__);
- ret = -1;
+ ret = -EAGAIN;
goto out;
}
@@ -1956,7 +2273,7 @@ static int hif_pci_suspend(struct pci_dev *pdev, pm_message_t state)
vos_ssr_protect(__func__);
- ret = __hif_pci_suspend(pdev, state);
+ ret = __hif_pci_suspend(pdev, state, false);
vos_ssr_unprotect(__func__);
@@ -1964,7 +2281,7 @@ static int hif_pci_suspend(struct pci_dev *pdev, pm_message_t state)
}
static int
-__hif_pci_resume(struct pci_dev *pdev)
+__hif_pci_resume(struct pci_dev *pdev, bool runtime_pm)
{
struct hif_pci_softc *sc = pci_get_drvdata(pdev);
void *vos_context = vos_get_global_context(VOS_MODULE_ID_HIF, NULL);
@@ -2052,9 +2369,9 @@ __hif_pci_resume(struct pci_dev *pdev)
if (!wma_is_wow_mode_selected(temp_module) &&
(val == PM_EVENT_HIBERNATE || val == PM_EVENT_SUSPEND))
- err = wma_resume_target(temp_module);
+ err = wma_resume_target(temp_module, runtime_pm);
else
- err = wma_disable_wow_in_fw(temp_module);
+ err = wma_disable_wow_in_fw(temp_module, runtime_pm);
#ifdef FEATURE_WLAN_D0WOW
if (wma_get_client_count(temp_module)) {
@@ -2082,7 +2399,7 @@ hif_pci_resume(struct pci_dev *pdev)
vos_ssr_protect(__func__);
- ret = __hif_pci_resume(pdev);
+ ret = __hif_pci_resume(pdev, false);
vos_ssr_unprotect(__func__);
@@ -2110,6 +2427,9 @@ struct cnss_wlan_driver cnss_wlan_drv_id = {
#ifdef ATH_BUS_PM
.suspend = hif_pci_suspend,
.resume = hif_pci_resume,
+#ifdef FEATURE_RUNTIME_PM
+ .runtime_ops = &runtime_pm_ops,
+#endif
#endif
};
#else
@@ -2239,16 +2559,6 @@ void hif_set_fw_info(void *ol_sc, u32 target_fw_version)
((struct ol_softc *)ol_sc)->target_fw_version = target_fw_version;
}
-int hif_pm_runtime_get(void)
-{
- return 0;
-}
-
-int hif_pm_runtime_put(void)
-{
- return 0;
-}
-
#ifdef IPA_UC_OFFLOAD
/* Micro controller needs PCI BAR address to access CE register */
void hif_read_bar(struct hif_pci_softc *sc, u32 *bar_value)
diff --git a/CORE/SERVICES/HIF/PCIe/if_pci.h b/CORE/SERVICES/HIF/PCIe/if_pci.h
index a4d0e3416f26..0cb21f030ef2 100644
--- a/CORE/SERVICES/HIF/PCIe/if_pci.h
+++ b/CORE/SERVICES/HIF/PCIe/if_pci.h
@@ -53,6 +53,28 @@ struct ol_softc;
/* An address (e.g. of a buffer) in Copy Engine space. */
typedef ath_dma_addr_t CE_addr_t;
+#ifdef FEATURE_RUNTIME_PM
+/* Driver States for Runtime Power Management */
+enum hif_pm_runtime_state {
+ HIF_PM_RUNTIME_STATE_ON,
+ HIF_PM_RUNTIME_STATE_INPROGRESS,
+ HIF_PM_RUNTIME_STATE_SUSPENDED,
+};
+
+/* Debugging stats for Runtime PM */
+struct hif_pci_pm_stats {
+ u32 suspended;
+ u32 suspend_err;
+ u32 resumed;
+ u32 runtime_get;
+ u32 runtime_put;
+ u32 request_resume;
+ u32 allow_suspend;
+ u32 prevent_suspend;
+ void *last_resume_caller;
+ unsigned long suspend_jiffies;
+};
+#endif
struct hif_pci_softc {
void __iomem *mem; /* PCI address. */
/* For efficiency, should be first in struct */
@@ -91,6 +113,14 @@ struct hif_pci_softc {
bool recovery;
bool hdd_startup_reinit_flag;
int htc_endpoint;
+#ifdef FEATURE_RUNTIME_PM
+ atomic_t pm_state;
+ struct hif_pci_pm_stats pm_stats;
+ struct work_struct pm_work;
+#ifdef WLAN_OPEN_SOURCE
+ struct dentry *pm_dentry;
+#endif
+#endif
};
#define TARGID(sc) ((A_target_id_t)(&(sc)->mem))
#define TARGID_TO_HIF(targid) (((struct hif_pci_softc *)((char *)(targid) - (char *)&(((struct hif_pci_softc *)0)->mem)))->hif_device)
diff --git a/CORE/SERVICES/HIF/USB/if_usb.c b/CORE/SERVICES/HIF/USB/if_usb.c
index f6259e1a75a8..807521ad53c9 100644
--- a/CORE/SERVICES/HIF/USB/if_usb.c
+++ b/CORE/SERVICES/HIF/USB/if_usb.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -688,7 +688,7 @@ void hif_set_fw_info(void *ol_sc, u32 target_fw_version)
((struct ol_softc *)ol_sc)->target_fw_version = target_fw_version;
}
-int hif_pm_runtime_get(void)
+int hif_pm_runtime_prevent_suspend(void *ol_sc)
{
if (usb_sc && usb_sc->interface)
return usb_autopm_get_interface_async(usb_sc->interface);
@@ -698,7 +698,7 @@ int hif_pm_runtime_get(void)
}
}
-int hif_pm_runtime_put(void)
+int hif_pm_runtime_allow_suspend(void *ol_sc)
{
if (usb_sc && usb_sc->interface)
usb_autopm_put_interface_async(usb_sc->interface);
diff --git a/CORE/SERVICES/HIF/sdio/linux/if_ath_sdio.c b/CORE/SERVICES/HIF/sdio/linux/if_ath_sdio.c
index e1111d892855..de40dc2e0af2 100644
--- a/CORE/SERVICES/HIF/sdio/linux/if_ath_sdio.c
+++ b/CORE/SERVICES/HIF/sdio/linux/if_ath_sdio.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
@@ -447,12 +447,12 @@ void hif_set_fw_info(void *ol_sc, u32 target_fw_version)
((struct ol_softc *)ol_sc)->target_fw_version = target_fw_version;
}
-int hif_pm_runtime_get(void)
+int hif_pm_runtime_prevent_suspend(void *ol_sc)
{
return 0;
}
-int hif_pm_runtime_put(void)
+int hif_pm_runtime_allow_suspend(void *ol_sc)
{
return 0;
}