diff --git a/core/hdd/src/wlan_hdd_driver_ops.c b/core/hdd/src/wlan_hdd_driver_ops.c index e335d2b76b..acff948192 100644 --- a/core/hdd/src/wlan_hdd_driver_ops.c +++ b/core/hdd/src/wlan_hdd_driver_ops.c @@ -554,16 +554,9 @@ static int __wlan_hdd_bus_suspend(pm_message_t state) if (err) goto resume_wma; - err = wma_is_target_wake_up_received(); - if (err) - goto resume_hif; - hdd_err("suspend done, status = %d", err); return err; -resume_hif: - status = hif_bus_resume(hif_ctx); - QDF_BUG(!status); resume_wma: status = wma_bus_resume(); QDF_BUG(!status); @@ -596,6 +589,77 @@ int wlan_hdd_bus_suspend(pm_message_t state) return ret; } +/** + * __wlan_hdd_bus_suspend_noirq() - handle .suspend_noirq callback + * + * This function is called by the platform driver to complete the + * bus suspend callback when device interrupts are disabled by kernel. + * Call HIF and WMA suspend_noirq callbacks to make sure there is no + * wake up pending from FW before allowing suspend. + * + * Return: 0 for success and -EBUSY if FW is requesting wake up + */ +int __wlan_hdd_bus_suspend_noirq(void) +{ + hdd_context_t *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + void *hif_ctx; + int err = wlan_hdd_validate_context(hdd_ctx); + int status; + + if (err) + goto done; + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (NULL == hif_ctx) { + err = -EINVAL; + goto done; + } + + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_info("Driver Module closed return success"); + return 0; + } + + err = hif_bus_suspend_noirq(hif_ctx); + if (err) + goto done; + + err = wma_is_target_wake_up_received(); + if (err) + goto resume_hif_noirq; + + hdd_err("suspend_noirq done, status = %d", err); + return err; + +resume_hif_noirq: + status = hif_bus_resume_noirq(hif_ctx); + QDF_BUG(!status); +done: + hdd_err("suspend_noirq done, status = %d", err); + return err; +} + +/** + * wlan_hdd_bus_suspend_noirq() - handle .suspend_noirq callback + * + * This function is called by the platform driver to complete the + * bus suspend callback when device interrupts are disabled by kernel. + * Call HIF and WMA suspend_noirq callbacks to make sure there is no + * wake up pending from FW before allowing suspend. + * + * Return: 0 for success and -EBUSY if FW is requesting wake up + */ +int wlan_hdd_bus_suspend_noirq(void) +{ + int ret; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_bus_suspend_noirq(); + cds_ssr_unprotect(__func__); + + return ret; +} + /** * __wlan_hdd_bus_resume() - handles platform resume * @@ -659,6 +723,63 @@ int wlan_hdd_bus_resume(void) return ret; } +/** + * __wlan_hdd_bus_resume_noirq(): handle bus resume no irq + * + * This function is called by the platform driver to do bus + * resume no IRQ before calling resume callback. Call WMA and HIF + * layers to complete the resume_noirq. + * + * Return: 0 for success and negative error code for failure + */ +int __wlan_hdd_bus_resume_noirq(void) +{ + hdd_context_t *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + void *hif_ctx; + int status = wlan_hdd_validate_context(hdd_ctx); + + if (status) + return status; + + if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) { + hdd_info("Driver Module closed return success"); + return 0; + } + + hif_ctx = cds_get_context(QDF_MODULE_ID_HIF); + if (NULL == hif_ctx) + return -EINVAL; + + status = wma_clear_target_wake_up(); + QDF_BUG(!status); + + status = hif_bus_resume_noirq(hif_ctx); + QDF_BUG(!status); + + hdd_err("resume_noirq done"); + return status; +} + +/** + * wlan_hdd_bus_resume_noirq(): handle bus resume no irq + * + * This function is called by the platform driver to do bus + * resume no IRQ before calling resume callback. Call WMA and HIF + * layers to complete the resume_noirq. + * + * Return: 0 for success and negative error code for failure + */ +int wlan_hdd_bus_resume_noirq(void) +{ + int ret; + + cds_ssr_protect(__func__); + ret = __wlan_hdd_bus_resume_noirq(); + cds_ssr_unprotect(__func__); + + return ret; +} + /** * wlan_hdd_bus_reset_resume() - resume wlan bus after reset * @@ -921,6 +1042,45 @@ static int wlan_hdd_pld_resume(struct device *dev, return wlan_hdd_bus_resume(); } + +/** + * wlan_hdd_pld_suspend_noirq() - handle suspend no irq + * @dev: device + * @pld_bus_type: PLD bus type + * + * Complete the actions started by suspend(). Carry out any + * additional operations required for suspending the device that might be + * racing with its driver's interrupt handler, which is guaranteed not to + * run while suspend_noirq() is being executed. Make sure to resume device + * if FW has sent initial wake up message and expecting APPS to wake up. + * + * Return: 0 on success + */ +static int wlan_hdd_pld_suspend_noirq(struct device *dev, + enum pld_bus_type bus_type) +{ + return wlan_hdd_bus_suspend_noirq(); +} + +/** + * wlan_hdd_pld_resume_noirq() - handle resume no irq + * @dev: device + * @pld_bus_type: PLD bus type + * + * Prepare for the execution of resume() by carrying out any + * operations required for resuming the device that might be racing with + * its driver's interrupt handler, which is guaranteed not to run while + * resume_noirq() is being executed. Make sure to clear target initial + * wake up request such that next suspend can happen cleanly. + * + * Return: 0 on success + */ +static int wlan_hdd_pld_resume_noirq(struct device *dev, + enum pld_bus_type bus_type) +{ + return wlan_hdd_bus_resume_noirq(); +} + /** * wlan_hdd_pld_reset_resume() - reset resume function registered to PLD * @dev: device @@ -985,6 +1145,8 @@ struct pld_driver_ops wlan_drv_ops = { .crash_shutdown = wlan_hdd_pld_crash_shutdown, .suspend = wlan_hdd_pld_suspend, .resume = wlan_hdd_pld_resume, + .suspend_noirq = wlan_hdd_pld_suspend_noirq, + .resume_noirq = wlan_hdd_pld_resume_noirq, .reset_resume = wlan_hdd_pld_reset_resume, .modem_status = wlan_hdd_pld_notify_handler, #ifdef FEATURE_RUNTIME_PM diff --git a/core/wma/inc/wma_api.h b/core/wma/inc/wma_api.h index 1bc33afb73..a63db28d3d 100644 --- a/core/wma/inc/wma_api.h +++ b/core/wma/inc/wma_api.h @@ -146,6 +146,7 @@ int wma_runtime_suspend(void); int wma_runtime_resume(void); int wma_bus_suspend(void); int wma_is_target_wake_up_received(void); +int wma_clear_target_wake_up(void); QDF_STATUS wma_suspend_target(WMA_HANDLE handle, int disable_target_intr); void wma_target_suspend_acknowledge(void *context, bool wow_nack); void wma_handle_initial_wake_up(void); diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c index f6c73a2794..bdb436335d 100644 --- a/core/wma/src/wma_features.c +++ b/core/wma/src/wma_features.c @@ -6625,9 +6625,30 @@ int wma_is_target_wake_up_received(void) if (wma->wow_initial_wake_up) { WMA_LOGE("Target initial wake up received try again"); return -EAGAIN; - } else { - return 0; } + + return 0; +} + +/** + * wma_clear_target_wake_up() - clear initial wake up + * + * Clear target initial wake up reason + * + * Return: 0 for success and negative error code for failure + */ +int wma_clear_target_wake_up(void) +{ + tp_wma_handle wma = cds_get_context(QDF_MODULE_ID_WMA); + + if (NULL == wma) { + WMA_LOGE("%s: wma is NULL", __func__); + return -EFAULT; + } + + wma->wow_initial_wake_up = false; + + return 0; } /**