From b56e6e2c964e0d7c8e2603b79aa988f8b5b3b96c Mon Sep 17 00:00:00 2001 From: Yuanyuan Liu Date: Mon, 29 Aug 2016 11:50:49 -0700 Subject: [PATCH] icnss: Add support of suspend_noirq/resume_noirq Register suspend_noirq and resume_noirq for WLAN driver. This could make sure that no wake up is pending from FW and if initial wake up is received then failing suspend_noirq should trigger resume. CRs-Fixed: 1060667 Change-Id: Ib233ce66ad21819d2d62155072f0bf985a12837b Signed-off-by: Yuanyuan Liu --- drivers/soc/qcom/icnss.c | 156 +++++++++++++++++++++++++++++++++++++++ include/soc/qcom/icnss.h | 4 + 2 files changed, 160 insertions(+) diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index aaca82f87159..eb55579c19f4 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -49,6 +49,7 @@ #define MAX_PROP_SIZE 32 #define NUM_LOG_PAGES 10 #define NUM_REG_LOG_PAGES 4 +#define ICNSS_MAGIC 0x5abc5abc /* * Registers: MPM2_PSHOLD @@ -269,6 +270,8 @@ enum icnss_driver_state { ICNSS_DRIVER_PROBED, ICNSS_FW_TEST_MODE, ICNSS_SUSPEND, + ICNSS_PM_SUSPEND, + ICNSS_PM_SUSPEND_NOIRQ, ICNSS_SSR_ENABLED, ICNSS_PDR_ENABLED, ICNSS_PD_RESTART, @@ -324,6 +327,15 @@ struct icnss_stats { uint32_t disable; } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS]; + uint32_t pm_suspend; + uint32_t pm_suspend_err; + uint32_t pm_resume; + uint32_t pm_resume_err; + uint32_t pm_suspend_noirq; + uint32_t pm_suspend_noirq_err; + uint32_t pm_resume_noirq; + uint32_t pm_resume_noirq_err; + uint32_t ind_register_req; uint32_t ind_register_resp; uint32_t ind_register_err; @@ -350,6 +362,7 @@ struct icnss_stats { }; static struct icnss_priv { + uint32_t magic; struct platform_device *pdev; struct icnss_driver_ops *ops; struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS]; @@ -3302,6 +3315,12 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_FW_TEST_MODE: seq_puts(s, "FW TEST MODE"); continue; + case ICNSS_PM_SUSPEND: + seq_puts(s, "PM SUSPEND"); + continue; + case ICNSS_PM_SUSPEND_NOIRQ: + seq_puts(s, "PM SUSPEND_NOIRQ"); + continue; case ICNSS_SSR_ENABLED: seq_puts(s, "SSR ENABLED"); continue; @@ -3402,6 +3421,16 @@ static int icnss_stats_show(struct seq_file *s, void *data) ICNSS_STATS_DUMP(s, priv, ini_resp); ICNSS_STATS_DUMP(s, priv, ini_req_err); + seq_puts(s, "\n<------------------ PM stats ------------------->\n"); + ICNSS_STATS_DUMP(s, priv, pm_suspend); + ICNSS_STATS_DUMP(s, priv, pm_suspend_err); + ICNSS_STATS_DUMP(s, priv, pm_resume); + ICNSS_STATS_DUMP(s, priv, pm_resume_err); + ICNSS_STATS_DUMP(s, priv, pm_suspend_noirq); + 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_show_irqs(s, priv); icnss_stats_show_capability(s, priv); @@ -3477,6 +3506,7 @@ static int icnss_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->magic = ICNSS_MAGIC; dev_set_drvdata(dev, priv); priv->pdev = pdev; @@ -3725,6 +3755,131 @@ out: return ret; } +#ifdef CONFIG_PM_SLEEP +static int icnss_pm_suspend(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + int ret = 0; + + if (priv->magic != ICNSS_MAGIC) { + icnss_pr_err("Invalid drvdata for pm suspend: dev %p, data %p, magic 0x%x\n", + dev, priv, priv->magic); + return -EINVAL; + } + + icnss_pr_dbg("PM Suspend, state: 0x%lx\n", priv->state); + + if (!priv->ops || !priv->ops->pm_suspend || + !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) + goto out; + + ret = priv->ops->pm_suspend(dev); + +out: + if (ret == 0) { + priv->stats.pm_suspend++; + set_bit(ICNSS_PM_SUSPEND, &priv->state); + } else { + priv->stats.pm_suspend_err++; + } + return ret; +} + +static int icnss_pm_resume(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + int ret = 0; + + if (priv->magic != ICNSS_MAGIC) { + icnss_pr_err("Invalid drvdata for pm resume: dev %p, data %p, magic 0x%x\n", + dev, priv, priv->magic); + return -EINVAL; + } + + icnss_pr_dbg("PM resume, state: 0x%lx\n", priv->state); + + if (!priv->ops || !priv->ops->pm_resume || + !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) + goto out; + + ret = priv->ops->pm_resume(dev); + +out: + if (ret == 0) { + priv->stats.pm_resume++; + clear_bit(ICNSS_PM_SUSPEND, &priv->state); + } else { + priv->stats.pm_resume_err++; + } + return ret; +} + +static int icnss_pm_suspend_noirq(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + int ret = 0; + + if (priv->magic != ICNSS_MAGIC) { + icnss_pr_err("Invalid drvdata for pm suspend_noirq: dev %p, data %p, magic 0x%x\n", + dev, priv, priv->magic); + return -EINVAL; + } + + icnss_pr_dbg("PM suspend_noirq, state: 0x%lx\n", priv->state); + + if (!priv->ops || !priv->ops->suspend_noirq || + !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) + goto out; + + ret = priv->ops->suspend_noirq(dev); + +out: + if (ret == 0) { + priv->stats.pm_suspend_noirq++; + set_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state); + } else { + priv->stats.pm_suspend_noirq_err++; + } + return ret; +} + +static int icnss_pm_resume_noirq(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + int ret = 0; + + if (priv->magic != ICNSS_MAGIC) { + icnss_pr_err("Invalid drvdata for pm resume_noirq: dev %p, data %p, magic 0x%x\n", + dev, priv, priv->magic); + return -EINVAL; + } + + icnss_pr_dbg("PM resume_noirq, state: 0x%lx\n", priv->state); + + if (!priv->ops || !priv->ops->resume_noirq || + !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) + goto out; + + ret = priv->ops->resume_noirq(dev); + +out: + if (ret == 0) { + priv->stats.pm_resume_noirq++; + clear_bit(ICNSS_PM_SUSPEND_NOIRQ, &priv->state); + } else { + priv->stats.pm_resume_noirq_err++; + } + return ret; +} +#endif + +static const struct dev_pm_ops icnss_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend, + icnss_pm_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(icnss_pm_suspend_noirq, + icnss_pm_resume_noirq) +}; + static const struct of_device_id icnss_dt_match[] = { {.compatible = "qcom,icnss"}, {} @@ -3739,6 +3894,7 @@ static struct platform_driver icnss_driver = { .resume = icnss_resume, .driver = { .name = "icnss", + .pm = &icnss_pm_ops, .owner = THIS_MODULE, .of_match_table = icnss_dt_match, }, diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h index 8b64bf3b8de9..778ad8f702db 100644 --- a/include/soc/qcom/icnss.h +++ b/include/soc/qcom/icnss.h @@ -26,6 +26,10 @@ struct icnss_driver_ops { void (*crash_shutdown)(void *pdev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); + int (*pm_suspend)(struct device *dev); + int (*pm_resume)(struct device *dev); + int (*suspend_noirq)(struct device *dev); + int (*resume_noirq)(struct device *dev); };