Merge "qcacld-3.0: Fix race condition between PM suspend and fw irq" into wlan-cld3.driver.lnx.1.1-dev

This commit is contained in:
Service qcabuildsw 2016-09-19 12:38:58 -07:00 • committed by Gerrit - the friendly Code Review server
commit 602b0bf83f
3 changed files with 202 additions and 14 deletions

View file

@ -530,17 +530,17 @@ static int __wlan_hdd_bus_suspend(pm_message_t state)
if (err)
goto done;
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) {
err = -EINVAL;
goto done;
}
if (hdd_ctx->driver_status != DRIVER_MODULES_ENABLED) {
hdd_info("Driver Module closed return success");
return 0;
}
err = qdf_status_to_os_return(
ol_txrx_bus_suspend());
if (err)
@ -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");
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,79 @@ 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) {
hdd_err("Invalid HDD context: %d", err);
return err;
}
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) {
err = -EINVAL;
goto done;
}
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_info("suspend_noirq done");
return err;
resume_hif_noirq:
status = hif_bus_resume_noirq(hif_ctx);
QDF_BUG(!status);
done:
hdd_err("suspend_noirq failed, 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 +725,65 @@ 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) {
hdd_err("Invalid HDD context: %d", 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_info("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 +1046,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 +1149,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

View file

@ -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);

View file

@ -6611,9 +6611,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;
}
/**