Skip to content

Commit

Permalink
gpio: pca953x: Restore registers after suspend/resume cycle
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
marex authored and linusw committed Dec 14, 2018
1 parent 87813cf commit b765743
Showing 1 changed file with 88 additions and 0 deletions.
88 changes: 88 additions & 0 deletions drivers/gpio/gpio-pca953x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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),
},
Expand Down

0 comments on commit b765743

Please sign in to comment.