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>
This commit is contained in:
Prashanth Bhatta 2016-08-30 15:14:31 -07:00
parent 5fc63ed649
commit a3a3c169dd

View file

@ -372,6 +372,7 @@ static struct icnss_priv {
struct notifier_block get_service_nb; struct notifier_block get_service_nb;
void *modem_notify_handler; void *modem_notify_handler;
struct notifier_block modem_ssr_nb; struct notifier_block modem_ssr_nb;
struct wakeup_source ws;
} *penv; } *penv;
static void icnss_hw_write_reg(void *base, u32 offset, u32 val) 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) if (!penv)
return -ENODEV; return -ENODEV;
__pm_stay_awake(&penv->ws);
set_bit(ICNSS_FW_READY, &penv->state); set_bit(ICNSS_FW_READY, &penv->state);
icnss_pr_info("WLAN FW is ready: 0x%lx\n", 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 else
ret = icnss_call_driver_probe(penv); ret = icnss_call_driver_probe(penv);
__pm_relax(&penv->ws);
out: out:
__pm_relax(&penv->ws);
return ret; return ret;
} }
@ -1965,10 +1971,10 @@ static int icnss_driver_event_register_driver(void *data)
{ {
int ret = 0; int ret = 0;
if (penv->ops) { if (penv->ops)
ret = -EEXIST; return -EEXIST;
goto out;
} __pm_stay_awake(&penv->ws);
penv->ops = data; penv->ops = data;
@ -1995,16 +2001,21 @@ static int icnss_driver_event_register_driver(void *data)
set_bit(ICNSS_DRIVER_PROBED, &penv->state); set_bit(ICNSS_DRIVER_PROBED, &penv->state);
__pm_relax(&penv->ws);
return 0; return 0;
power_off: power_off:
icnss_hw_power_off(penv); icnss_hw_power_off(penv);
out: out:
__pm_relax(&penv->ws);
return ret; return ret;
} }
static int icnss_driver_event_unregister_driver(void *data) static int icnss_driver_event_unregister_driver(void *data)
{ {
__pm_stay_awake(&penv->ws);
if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) { if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) {
penv->ops = NULL; penv->ops = NULL;
goto out; goto out;
@ -2020,6 +2031,7 @@ static int icnss_driver_event_unregister_driver(void *data)
icnss_hw_power_off(penv); icnss_hw_power_off(penv);
out: out:
__pm_relax(&penv->ws);
return 0; return 0;
} }
@ -3532,6 +3544,8 @@ static int icnss_probe(struct platform_device *pdev)
spin_lock_init(&priv->event_lock); spin_lock_init(&priv->event_lock);
spin_lock_init(&priv->on_off_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); priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1);
if (!priv->event_wq) { if (!priv->event_wq) {
icnss_pr_err("Workqueue creation failed\n"); icnss_pr_err("Workqueue creation failed\n");
@ -3593,6 +3607,8 @@ static int icnss_remove(struct platform_device *pdev)
icnss_bw_deinit(penv); icnss_bw_deinit(penv);
wakeup_source_trash(&penv->ws);
icnss_hw_power_off(penv); icnss_hw_power_off(penv);
dev_set_drvdata(&pdev->dev, NULL); dev_set_drvdata(&pdev->dev, NULL);