summaryrefslogtreecommitdiff
path: root/drivers/soc
diff options
context:
space:
mode:
authorPrashanth Bhatta <bhattap@codeaurora.org>2016-08-30 15:14:31 -0700
committerPrashanth Bhatta <bhattap@codeaurora.org>2016-08-30 17:18:27 -0700
commita3a3c169dd0d98dcb3a04279ad2d8c7bfaf7e894 (patch)
treec2ff276339bc0dd07e6f9440e35660d5325bdb8c /drivers/soc
parent5fc63ed649d82ed08a3768245cf6478062379a94 (diff)
icnss: Prevent power collapse during driver probe/remove
Race condition observed during driver probe/remove and suspend/resume because suspend/resume is not serialized to work queue. Fix this problem by holding a wake lock during wlan driver probe/remove to prevent system suspend/resume. CRs-Fixed: 1061279 Change-Id: Iad1c9abbdbaea21d4c55d64a6c120d3bcf0df1eb Signed-off-by: Prashanth Bhatta <bhattap@codeaurora.org>
Diffstat (limited to 'drivers/soc')
-rw-r--r--drivers/soc/qcom/icnss.c24
1 files changed, 20 insertions, 4 deletions
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index c5473ca9ecfd..96d51bcc1cc5 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -372,6 +372,7 @@ static struct icnss_priv {
struct notifier_block get_service_nb;
void *modem_notify_handler;
struct notifier_block modem_ssr_nb;
+ struct wakeup_source ws;
} *penv;
static void icnss_hw_write_reg(void *base, u32 offset, u32 val)
@@ -1940,6 +1941,8 @@ static int icnss_driver_event_fw_ready_ind(void *data)
if (!penv)
return -ENODEV;
+ __pm_stay_awake(&penv->ws);
+
set_bit(ICNSS_FW_READY, &penv->state);
icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
@@ -1957,7 +1960,10 @@ static int icnss_driver_event_fw_ready_ind(void *data)
else
ret = icnss_call_driver_probe(penv);
+ __pm_relax(&penv->ws);
+
out:
+ __pm_relax(&penv->ws);
return ret;
}
@@ -1965,10 +1971,10 @@ static int icnss_driver_event_register_driver(void *data)
{
int ret = 0;
- if (penv->ops) {
- ret = -EEXIST;
- goto out;
- }
+ if (penv->ops)
+ return -EEXIST;
+
+ __pm_stay_awake(&penv->ws);
penv->ops = data;
@@ -1995,16 +2001,21 @@ static int icnss_driver_event_register_driver(void *data)
set_bit(ICNSS_DRIVER_PROBED, &penv->state);
+ __pm_relax(&penv->ws);
+
return 0;
power_off:
icnss_hw_power_off(penv);
out:
+ __pm_relax(&penv->ws);
return ret;
}
static int icnss_driver_event_unregister_driver(void *data)
{
+ __pm_stay_awake(&penv->ws);
+
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
penv->ops = NULL;
goto out;
@@ -2020,6 +2031,7 @@ static int icnss_driver_event_unregister_driver(void *data)
icnss_hw_power_off(penv);
out:
+ __pm_relax(&penv->ws);
return 0;
}
@@ -3532,6 +3544,8 @@ static int icnss_probe(struct platform_device *pdev)
spin_lock_init(&priv->event_lock);
spin_lock_init(&priv->on_off_lock);
+ wakeup_source_init(&penv->ws, "icnss_ws");
+
priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
if (!priv->event_wq) {
icnss_pr_err("Workqueue creation failed\n");
@@ -3593,6 +3607,8 @@ static int icnss_remove(struct platform_device *pdev)
icnss_bw_deinit(penv);
+ wakeup_source_trash(&penv->ws);
+
icnss_hw_power_off(penv);
dev_set_drvdata(&pdev->dev, NULL);