OSDN Git Service

icnss: Add support of suspend_noirq/resume_noirq
authorYuanyuan Liu <yuanliu@codeaurora.org>
Mon, 29 Aug 2016 18:50:49 +0000 (11:50 -0700)
committerYuanyuan Liu <yuanliu@codeaurora.org>
Mon, 12 Sep 2016 16:51:38 +0000 (09:51 -0700)
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>
drivers/soc/qcom/icnss.c
include/soc/qcom/icnss.h

index aaca82f..eb55579 100644 (file)
@@ -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,
        },
index 8b64bf3..778ad8f 100644 (file)
@@ -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);
 };