Added suspend/resume pm ops. We need to store current regs vals on suspend and
restore them on resume.

Signed-off-by: Dmitry Osipenko <dig...@gmail.com>
---
Tested on my tablet.

 drivers/pinctrl/pinctrl-tegra.c | 94 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 91 insertions(+), 3 deletions(-)

diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c
index 7da0b37..03a3afb 100644
--- a/drivers/pinctrl/pinctrl-tegra.c
+++ b/drivers/pinctrl/pinctrl-tegra.c
@@ -41,6 +41,11 @@ struct tegra_pmx {
 
        int nbanks;
        void __iomem **regs;
+
+       int *bank_size;
+#ifdef CONFIG_PM_SLEEP
+       u32 *regs_store;
+#endif
 };
 
 static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg)
@@ -685,11 +690,72 @@ static struct pinctrl_desc tegra_pinctrl_desc = {
        .owner = THIS_MODULE,
 };
 
+#ifdef CONFIG_PM_SLEEP
+static int tegra_pinctrl_suspend_noirq(struct device *dev)
+{
+       struct tegra_pmx *pmx = dev_get_drvdata(dev);
+       int store_offset = 0;
+       int i, reg;
+       u32 val;
+
+       if (!pmx)
+               return -ENOMEM;
+
+       for (i = 0; i < pmx->nbanks; i++) {
+
+               for (reg = 0; reg < pmx->bank_size[i]; reg += 4) {
+                       val = pmx_readl(pmx, i, reg);
+                       pmx->regs_store[store_offset] = val;
+                       store_offset++;
+
+                       dev_dbg(dev, "stored val: 0x%x bank: %d reg: 0x%x\n",
+                               val, i, reg);
+               }
+       }
+
+       return 0;
+}
+
+static int tegra_pinctrl_resume_noirq(struct device *dev)
+{
+       struct tegra_pmx *pmx = dev_get_drvdata(dev);
+       int store_offset = 0;
+       int i, reg;
+       u32 val;
+
+       if (!pmx)
+               return -ENOMEM;
+
+       for (i = 0; i < pmx->nbanks; i++) {
+
+               for (reg = 0; reg < pmx->bank_size[i]; reg += 4) {
+                       val = pmx->regs_store[store_offset];
+                       pmx_writel(pmx, val, i, reg);
+                       store_offset++;
+
+                       dev_dbg(dev, "restored val: 0x%x bank: %d reg: 0x%x\n",
+                               val, i, reg);
+               }
+       }
+
+       return 0;
+}
+
+static const struct dev_pm_ops tegra_pinctrl_pm_ops = {
+       .suspend_noirq = tegra_pinctrl_suspend_noirq,
+       .resume_noirq = tegra_pinctrl_resume_noirq,
+};
+#define TEGRA_PINCTRL_PM       (&tegra_pinctrl_pm_ops)
+#else
+#define TEGRA_PINCTRL_PM       NULL
+#endif
+
 int __devinit tegra_pinctrl_probe(struct platform_device *pdev,
                        const struct tegra_pinctrl_soc_data *soc_data)
 {
        struct tegra_pmx *pmx;
        struct resource *res;
+       int nregs = 0;
        int i;
 
        pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
@@ -712,6 +778,13 @@ int __devinit tegra_pinctrl_probe(struct platform_device 
*pdev,
        }
        pmx->nbanks = i;
 
+       pmx->bank_size = devm_kzalloc(&pdev->dev, pmx->nbanks * sizeof(int),
+                                     GFP_KERNEL);
+       if (!pmx->bank_size) {
+               dev_err(&pdev->dev, "Can't alloc banks sizes pointer\n");
+               return -ENODEV;
+       }
+
        pmx->regs = devm_kzalloc(&pdev->dev, pmx->nbanks * sizeof(*pmx->regs),
                                 GFP_KERNEL);
        if (!pmx->regs) {
@@ -726,22 +799,35 @@ int __devinit tegra_pinctrl_probe(struct platform_device 
*pdev,
                        return -ENODEV;
                }
 
+               pmx->bank_size[i] = resource_size(res);
+
                if (!devm_request_mem_region(&pdev->dev, res->start,
-                                           resource_size(res),
-                                           dev_name(&pdev->dev))) {
+                                            pmx->bank_size[i],
+                                            dev_name(&pdev->dev))) {
                        dev_err(&pdev->dev,
                                "Couldn't request MEM resource %d\n", i);
                        return -ENODEV;
                }
 
                pmx->regs[i] = devm_ioremap(&pdev->dev, res->start,
-                                           resource_size(res));
+                                           pmx->bank_size[i]);
                if (!pmx->regs[i]) {
                        dev_err(&pdev->dev, "Couldn't ioremap regs %d\n", i);
                        return -ENODEV;
                }
+
+               nregs += pmx->bank_size[i] / 4;
        }
 
+#ifdef CONFIG_PM_SLEEP
+       pmx->regs_store = devm_kzalloc(&pdev->dev, nregs * sizeof(u32),
+                                      GFP_KERNEL);
+       if (!pmx->regs_store) {
+               dev_err(&pdev->dev, "Can't alloc regs store pointer\n");
+               return -ENODEV;
+       }
+#endif
+
        pmx->pctl = pinctrl_register(&tegra_pinctrl_desc, &pdev->dev, pmx);
        if (!pmx->pctl) {
                dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
@@ -752,6 +838,8 @@ int __devinit tegra_pinctrl_probe(struct platform_device 
*pdev,
 
        platform_set_drvdata(pdev, pmx);
 
+       pdev->dev.driver->pm = TEGRA_PINCTRL_PM;
+
        dev_dbg(&pdev->dev, "Probed Tegra pinctrl driver\n");
 
        return 0;
-- 
1.7.12

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to