diff options
-rw-r--r-- | drivers/pinctrl/sh-pfc/core.c | 97 | ||||
-rw-r--r-- | drivers/pinctrl/sh-pfc/sh_pfc.h | 1 |
2 files changed, 98 insertions, 0 deletions
diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index 2fe5fd6c5d17..cf4ae4bc9115 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -24,6 +24,7 @@ #include <linux/of_device.h> #include <linux/pinctrl/machine.h> #include <linux/platform_device.h> +#include <linux/psci.h> #include <linux/slab.h> #include "core.h" @@ -572,6 +573,97 @@ static const struct of_device_id sh_pfc_of_table[] = { }; #endif +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM_PSCI_FW) +static void sh_pfc_nop_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) +{ +} + +static void sh_pfc_save_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) +{ + pfc->saved_regs[idx] = sh_pfc_read(pfc, reg); +} + +static void sh_pfc_restore_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) +{ + sh_pfc_write(pfc, reg, pfc->saved_regs[idx]); +} + +static unsigned int sh_pfc_walk_regs(struct sh_pfc *pfc, + void (*do_reg)(struct sh_pfc *pfc, u32 reg, unsigned int idx)) +{ + unsigned int i, n = 0; + + if (pfc->info->cfg_regs) + for (i = 0; pfc->info->cfg_regs[i].reg; i++) + do_reg(pfc, pfc->info->cfg_regs[i].reg, n++); + + if (pfc->info->drive_regs) + for (i = 0; pfc->info->drive_regs[i].reg; i++) + do_reg(pfc, pfc->info->drive_regs[i].reg, n++); + + if (pfc->info->bias_regs) + for (i = 0; pfc->info->bias_regs[i].puen; i++) { + do_reg(pfc, pfc->info->bias_regs[i].puen, n++); + if (pfc->info->bias_regs[i].pud) + do_reg(pfc, pfc->info->bias_regs[i].pud, n++); + } + + if (pfc->info->ioctrl_regs) + for (i = 0; pfc->info->ioctrl_regs[i].reg; i++) + do_reg(pfc, pfc->info->ioctrl_regs[i].reg, n++); + + return n; +} + +static int sh_pfc_suspend_init(struct sh_pfc *pfc) +{ + unsigned int n; + + /* This is the best we can do to check for the presence of PSCI */ + if (!psci_ops.cpu_suspend) + return 0; + + n = sh_pfc_walk_regs(pfc, sh_pfc_nop_reg); + if (!n) + return 0; + + pfc->saved_regs = devm_kmalloc_array(pfc->dev, n, + sizeof(*pfc->saved_regs), + GFP_KERNEL); + if (!pfc->saved_regs) + return -ENOMEM; + + dev_dbg(pfc->dev, "Allocated space to save %u regs\n", n); + return 0; +} + +static int sh_pfc_suspend_noirq(struct device *dev) +{ + struct sh_pfc *pfc = dev_get_drvdata(dev); + + if (pfc->saved_regs) + sh_pfc_walk_regs(pfc, sh_pfc_save_reg); + return 0; +} + +static int sh_pfc_resume_noirq(struct device *dev) +{ + struct sh_pfc *pfc = dev_get_drvdata(dev); + + if (pfc->saved_regs) + sh_pfc_walk_regs(pfc, sh_pfc_restore_reg); + return 0; +} + +static const struct dev_pm_ops sh_pfc_pm = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sh_pfc_suspend_noirq, sh_pfc_resume_noirq) +}; +#define DEV_PM_OPS &sh_pfc_pm +#else +static int sh_pfc_suspend_init(struct sh_pfc *pfc) { return 0; } +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP && CONFIG_ARM_PSCI_FW */ + static int sh_pfc_probe(struct platform_device *pdev) { #ifdef CONFIG_OF @@ -610,6 +702,10 @@ static int sh_pfc_probe(struct platform_device *pdev) info = pfc->info; } + ret = sh_pfc_suspend_init(pfc); + if (ret) + return ret; + /* Enable dummy states for those platforms without pinctrl support */ if (!of_have_populated_dt()) pinctrl_provide_dummies(); @@ -693,6 +789,7 @@ static struct platform_driver sh_pfc_driver = { .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(sh_pfc_of_table), + .pm = DEV_PM_OPS, }, }; diff --git a/drivers/pinctrl/sh-pfc/sh_pfc.h b/drivers/pinctrl/sh-pfc/sh_pfc.h index b9bb56c91b6f..213108a058fe 100644 --- a/drivers/pinctrl/sh-pfc/sh_pfc.h +++ b/drivers/pinctrl/sh-pfc/sh_pfc.h @@ -222,6 +222,7 @@ struct sh_pfc { unsigned int nr_gpio_pins; struct sh_pfc_chip *gpio; + u32 *saved_regs; }; struct sh_pfc_soc_operations { |