OSDN Git Service

icnss: Prevent power collapse during driver probe/remove
authorPrashanth Bhatta <bhattap@codeaurora.org>
Tue, 30 Aug 2016 22:14:31 +0000 (15:14 -0700)
committerPrashanth Bhatta <bhattap@codeaurora.org>
Wed, 31 Aug 2016 00:18:27 +0000 (17:18 -0700)
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>
drivers/soc/qcom/icnss.c

index c5473ca..96d51bc 100644 (file)
@@ -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);