icnss: Prevent suspend while processing events
Power collapsing while processing events posted to work queue causes stability issues because of suspending while in the process of bring-up, bring-down or recovery. Prevent suspend while processing events by calling pm_stay_awake(). CRs-fixed: 1073854 Change-Id: I6c7ae3b6e52529b14075fb2212bc2c5f76ae5730 Signed-off-by: Prashanth Bhatta <bhattap@codeaurora.org>
This commit is contained in:
parent
71adcb3196
commit
5972a06743
1 changed files with 44 additions and 20 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -2378,8 +2413,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);
|
||||
|
@ -2397,10 +2430,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;
|
||||
}
|
||||
|
||||
|
@ -2411,8 +2441,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))
|
||||
|
@ -2438,21 +2466,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;
|
||||
|
@ -2468,7 +2491,6 @@ static int icnss_driver_event_unregister_driver(void *data)
|
|||
icnss_hw_power_off(penv);
|
||||
|
||||
out:
|
||||
__pm_relax(&penv->ws);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2546,6 +2568,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)) {
|
||||
|
@ -2605,6 +2629,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,
|
||||
|
@ -3943,6 +3969,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);
|
||||
|
||||
|
@ -4394,8 +4422,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");
|
||||
|
@ -4457,8 +4483,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);
|
||||
|
|
Loading…
Add table
Reference in a new issue