diff options
| author | Komal Seelam <kseelam@qti.qualcomm.com> | 2015-04-29 17:50:21 +0530 |
|---|---|---|
| committer | AnjaneeDevi Kapparapu <c_akappa@qti.qualcomm.com> | 2015-05-22 21:06:43 +0530 |
| commit | acf8184ec1bb7d813dbe467bb00f402087f5e91b (patch) | |
| tree | 06f1a73f76deb728181c57d898808540d67f887f | |
| parent | 8ce8419d0bf78b8d53712d46fb387f4775332888 (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.h | 21 | ||||
| -rw-r--r-- | CORE/SERVICES/COMMON/hif_msg_based.h | 3 | ||||
| -rw-r--r-- | CORE/SERVICES/HIF/PCIe/hif_pci.c | 95 | ||||
| -rw-r--r-- | CORE/SERVICES/HIF/PCIe/hif_pci.h | 57 | ||||
| -rw-r--r-- | CORE/SERVICES/HIF/PCIe/if_pci.c | 352 | ||||
| -rw-r--r-- | CORE/SERVICES/HIF/PCIe/if_pci.h | 30 | ||||
| -rw-r--r-- | CORE/SERVICES/HIF/USB/if_usb.c | 6 | ||||
| -rw-r--r-- | CORE/SERVICES/HIF/sdio/linux/if_ath_sdio.c | 6 |
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; } |
