diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2016-10-08 06:35:54 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-10-08 06:35:53 -0700 |
commit | 062b309db9be77da33176c8534fa06ff2a10e8c9 (patch) | |
tree | 4038587fb520650ef32974a83498d9bd441c9710 /drivers/soc | |
parent | 3ffe336b5e3673039568fabea7785ff066c32fca (diff) | |
parent | 5972a06743bd34ebd60cd7958d34c16a9ab56eb8 (diff) |
Merge "icnss: Prevent suspend while processing events"
Diffstat (limited to 'drivers/soc')
-rw-r--r-- | drivers/soc/qcom/icnss.c | 64 |
1 files changed, 44 insertions, 20 deletions
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 9cb14b1bb207..460c2d1f6824 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -358,6 +358,8 @@ struct icnss_stats { uint32_t pm_suspend_noirq_err; uint32_t pm_resume_noirq; uint32_t pm_resume_noirq_err; + uint32_t pm_stay_awake; + uint32_t pm_relax; uint32_t ind_register_req; uint32_t ind_register_resp; @@ -435,7 +437,6 @@ static struct icnss_priv { struct notifier_block get_service_nb; void *modem_notify_handler; struct notifier_block modem_ssr_nb; - struct wakeup_source ws; uint32_t diag_reg_read_addr; uint32_t diag_reg_read_mem_type; uint32_t diag_reg_read_len; @@ -444,6 +445,7 @@ static struct icnss_priv { struct qpnp_adc_tm_chip *adc_tm_dev; struct qpnp_vadc_chip *vadc_dev; uint64_t vph_pwr; + atomic_t pm_count; } *penv; static void icnss_hw_write_reg(void *base, u32 offset, u32 val) @@ -511,6 +513,35 @@ static int icnss_hw_poll_reg_field(void *base, u32 offset, u32 mask, u32 val, return 0; } +static void icnss_pm_stay_awake(struct icnss_priv *priv) +{ + if (atomic_inc_return(&priv->pm_count) != 1) + return; + + icnss_pr_dbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state, + atomic_read(&priv->pm_count)); + + pm_stay_awake(&priv->pdev->dev); + + priv->stats.pm_stay_awake++; +} + +static void icnss_pm_relax(struct icnss_priv *priv) +{ + int r = atomic_dec_return(&priv->pm_count); + + WARN_ON(r < 0); + + if (r != 0) + return; + + icnss_pr_dbg("PM relax, state: 0x%lx, count: %d\n", priv->state, + atomic_read(&priv->pm_count)); + + pm_relax(&priv->pdev->dev); + priv->stats.pm_relax++; +} + static char *icnss_driver_event_to_str(enum icnss_driver_event_type type) { switch (type) { @@ -557,6 +588,8 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type, if (event == NULL) return -ENOMEM; + icnss_pm_stay_awake(penv); + event->type = type; event->data = data; init_completion(&event->complete); @@ -571,7 +604,7 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type, queue_work(penv->event_wq, &penv->event_work); if (!sync) - return ret; + goto out; ret = wait_for_completion_interruptible(&event->complete); @@ -583,13 +616,15 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type, if (ret == -ERESTARTSYS && event->ret == ICNSS_EVENT_PENDING) { event->sync = false; spin_unlock_irqrestore(&penv->event_lock, flags); - return ret; + goto out; } spin_unlock_irqrestore(&penv->event_lock, flags); ret = event->ret; kfree(event); +out: + icnss_pm_relax(penv); return ret; } @@ -2381,8 +2416,6 @@ 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); @@ -2400,10 +2433,7 @@ 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; } @@ -2414,8 +2444,6 @@ static int icnss_driver_event_register_driver(void *data) if (penv->ops) return -EEXIST; - __pm_stay_awake(&penv->ws); - penv->ops = data; if (test_bit(SKIP_QMI, &quirks)) @@ -2441,21 +2469,16 @@ 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; @@ -2471,7 +2494,6 @@ static int icnss_driver_event_unregister_driver(void *data) icnss_hw_power_off(penv); out: - __pm_relax(&penv->ws); return 0; } @@ -2549,6 +2571,8 @@ static void icnss_driver_event_work(struct work_struct *work) unsigned long flags; int ret; + icnss_pm_stay_awake(penv); + spin_lock_irqsave(&penv->event_lock, flags); while (!list_empty(&penv->event_list)) { @@ -2608,6 +2632,8 @@ static void icnss_driver_event_work(struct work_struct *work) spin_lock_irqsave(&penv->event_lock, flags); } spin_unlock_irqrestore(&penv->event_lock, flags); + + icnss_pm_relax(penv); } static int icnss_qmi_wlfw_clnt_svc_event_notify(struct notifier_block *this, @@ -3949,6 +3975,8 @@ static int icnss_stats_show(struct seq_file *s, void *data) ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq_err); ICNSS_STATS_DUMP(s, priv, pm_resume_noirq); ICNSS_STATS_DUMP(s, priv, pm_resume_noirq_err); + ICNSS_STATS_DUMP(s, priv, pm_stay_awake); + ICNSS_STATS_DUMP(s, priv, pm_relax); icnss_stats_show_irqs(s, priv); @@ -4400,8 +4428,6 @@ static int icnss_probe(struct platform_device *pdev) spin_lock_init(&priv->event_lock); spin_lock_init(&priv->on_off_lock); - wakeup_source_init(&priv->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"); @@ -4463,8 +4489,6 @@ 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); |