gpio: pca953x: Restore registers after suspend/resume cycle
authorMarek Vasut <marek.vasut@gmail.com>
Wed, 12 Dec 2018 01:40:02 +0000 (02:40 +0100)
committerLinus Walleij <linus.walleij@linaro.org>
Fri, 14 Dec 2018 14:19:26 +0000 (15:19 +0100)
It is possible that the PCA953x is powered down during suspend.
Use regmap cache to assure the registers in the PCA953x are in
line with the driver state after resume.

Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
Cc: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/gpio/gpio-pca953x.c

index 48a16a3e8ce97d1a7d5f2610822b7566976922ba..905dc1916883cf6368aabfd6a2cc8151257127b0 100644 (file)
@@ -975,6 +975,91 @@ static int pca953x_remove(struct i2c_client *client)
        return ret;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int pca953x_regcache_sync(struct device *dev)
+{
+       struct pca953x_chip *chip = dev_get_drvdata(dev);
+       int ret;
+
+       /*
+        * The ordering between direction and output is important,
+        * sync these registers first and only then sync the rest.
+        */
+       ret = regcache_sync_region(chip->regmap, chip->regs->direction,
+                                  chip->regs->direction + NBANK(chip));
+       if (ret != 0) {
+               dev_err(dev, "Failed to sync GPIO dir registers: %d\n", ret);
+               return ret;
+       }
+
+       ret = regcache_sync_region(chip->regmap, chip->regs->output,
+                                  chip->regs->output + NBANK(chip));
+       if (ret != 0) {
+               dev_err(dev, "Failed to sync GPIO out registers: %d\n", ret);
+               return ret;
+       }
+
+#ifdef CONFIG_GPIO_PCA953X_IRQ
+       if (chip->driver_data & PCA_PCAL) {
+               ret = regcache_sync_region(chip->regmap, PCAL953X_IN_LATCH,
+                                          PCAL953X_IN_LATCH + NBANK(chip));
+               if (ret != 0) {
+                       dev_err(dev, "Failed to sync INT latch registers: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               ret = regcache_sync_region(chip->regmap, PCAL953X_INT_MASK,
+                                          PCAL953X_INT_MASK + NBANK(chip));
+               if (ret != 0) {
+                       dev_err(dev, "Failed to sync INT mask registers: %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+#endif
+
+       return 0;
+}
+
+static int pca953x_suspend(struct device *dev)
+{
+       struct pca953x_chip *chip = dev_get_drvdata(dev);
+
+       regcache_cache_only(chip->regmap, true);
+
+       regulator_disable(chip->regulator);
+
+       return 0;
+}
+
+static int pca953x_resume(struct device *dev)
+{
+       struct pca953x_chip *chip = dev_get_drvdata(dev);
+       int ret;
+
+       ret = regulator_enable(chip->regulator);
+       if (ret != 0) {
+               dev_err(dev, "Failed to enable regulator: %d\n", ret);
+               return 0;
+       }
+
+       regcache_cache_only(chip->regmap, false);
+       regcache_mark_dirty(chip->regmap);
+       ret = pca953x_regcache_sync(dev);
+       if (ret)
+               return ret;
+
+       ret = regcache_sync(chip->regmap);
+       if (ret != 0) {
+               dev_err(dev, "Failed to restore register map: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
+
 /* convenience to stop overlong match-table lines */
 #define OF_953X(__nrgpio, __int) (void *)(__nrgpio | PCA953X_TYPE | __int)
 #define OF_957X(__nrgpio, __int) (void *)(__nrgpio | PCA957X_TYPE | __int)
@@ -1018,9 +1103,12 @@ static const struct of_device_id pca953x_dt_ids[] = {
 
 MODULE_DEVICE_TABLE(of, pca953x_dt_ids);
 
+static SIMPLE_DEV_PM_OPS(pca953x_pm_ops, pca953x_suspend, pca953x_resume);
+
 static struct i2c_driver pca953x_driver = {
        .driver = {
                .name   = "pca953x",
+               .pm     = &pca953x_pm_ops,
                .of_match_table = pca953x_dt_ids,
                .acpi_match_table = ACPI_PTR(pca953x_acpi_ids),
        },