summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuanyuan Liu <yuanliu@codeaurora.org>2016-08-29 11:50:49 -0700
committerYuanyuan Liu <yuanliu@codeaurora.org>2016-09-12 09:51:38 -0700
commitb56e6e2c964e0d7c8e2603b79aa988f8b5b3b96c (patch)
tree863c90c543549302410e199d3add0cfe1e356918
parent9c8924dbaadbb194f6d0bf30fa7fafde20ec096c (diff)
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 <yuanliu@codeaurora.org>
-rw-r--r--drivers/soc/qcom/icnss.c156
-rw-r--r--include/soc/qcom/icnss.h4
2 files changed, 160 insertions, 0 deletions
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);
};