aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl
diff options
context:
space:
mode:
authorDoug Anderson <dianders@chromium.org>2013-05-17 00:33:18 -0400
committerLinus Walleij <linus.walleij@linaro.org>2013-05-27 09:37:08 -0400
commitd9f998639f539613bb25cbbca380c81c892d586c (patch)
tree2529a1916b63869dd6b7d418c2e190094371fe49 /drivers/pinctrl
parente4aa937ec75df0eea0bee03bffa3303ad36c986b (diff)
pinctrl: samsung: fix suspend/resume functionality
The GPIO states need to be restored after s2r and this is not currently supported in the pinctrl driver. This patch saves the gpio states before suspend and restores them after resume. Saving and restoring is done very early using syscore_ops and must happen before pins are released from their powerdown state. Patch originally from Prathyush K <prathyush.k@samsung.com> but rewritten by Doug Anderson <dianders@chromium.org>. Signed-off-by: Prathyush K <prathyush.k@samsung.com> Signed-off-by: Doug Anderson <dianders@chromium.org> Tested-by: Tomasz Figa <t.figa@samsung.com> Acked-by: Kukjin Kim <kgene.kim@samsung.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r--drivers/pinctrl/pinctrl-samsung.c148
-rw-r--r--drivers/pinctrl/pinctrl-samsung.h5
2 files changed, 153 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c
index 055d0162098b..15db2580c145 100644
--- a/drivers/pinctrl/pinctrl-samsung.c
+++ b/drivers/pinctrl/pinctrl-samsung.c
@@ -28,6 +28,7 @@
28#include <linux/gpio.h> 28#include <linux/gpio.h>
29#include <linux/irqdomain.h> 29#include <linux/irqdomain.h>
30#include <linux/spinlock.h> 30#include <linux/spinlock.h>
31#include <linux/syscore_ops.h>
31 32
32#include "core.h" 33#include "core.h"
33#include "pinctrl-samsung.h" 34#include "pinctrl-samsung.h"
@@ -48,6 +49,9 @@ static struct pin_config {
48 { "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN }, 49 { "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN },
49}; 50};
50 51
52/* Global list of devices (struct samsung_pinctrl_drv_data) */
53LIST_HEAD(drvdata_list);
54
51static unsigned int pin_base; 55static unsigned int pin_base;
52 56
53static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) 57static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc)
@@ -956,9 +960,145 @@ static int samsung_pinctrl_probe(struct platform_device *pdev)
956 ctrl->eint_wkup_init(drvdata); 960 ctrl->eint_wkup_init(drvdata);
957 961
958 platform_set_drvdata(pdev, drvdata); 962 platform_set_drvdata(pdev, drvdata);
963
964 /* Add to the global list */
965 list_add_tail(&drvdata->node, &drvdata_list);
966
959 return 0; 967 return 0;
960} 968}
961 969
970#ifdef CONFIG_PM
971
972/**
973 * samsung_pinctrl_suspend_dev - save pinctrl state for suspend for a device
974 *
975 * Save data for all banks handled by this device.
976 */
977static void samsung_pinctrl_suspend_dev(
978 struct samsung_pinctrl_drv_data *drvdata)
979{
980 struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
981 void __iomem *virt_base = drvdata->virt_base;
982 int i;
983
984 for (i = 0; i < ctrl->nr_banks; i++) {
985 struct samsung_pin_bank *bank = &ctrl->pin_banks[i];
986 void __iomem *reg = virt_base + bank->pctl_offset;
987
988 u8 *offs = bank->type->reg_offset;
989 u8 *widths = bank->type->fld_width;
990 enum pincfg_type type;
991
992 /* Registers without a powerdown config aren't lost */
993 if (!widths[PINCFG_TYPE_CON_PDN])
994 continue;
995
996 for (type = 0; type < PINCFG_TYPE_NUM; type++)
997 if (widths[type])
998 bank->pm_save[type] = readl(reg + offs[type]);
999
1000 if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) {
1001 /* Some banks have two config registers */
1002 bank->pm_save[PINCFG_TYPE_NUM] =
1003 readl(reg + offs[PINCFG_TYPE_FUNC] + 4);
1004 pr_debug("Save %s @ %p (con %#010x %08x)\n",
1005 bank->name, reg,
1006 bank->pm_save[PINCFG_TYPE_FUNC],
1007 bank->pm_save[PINCFG_TYPE_NUM]);
1008 } else {
1009 pr_debug("Save %s @ %p (con %#010x)\n", bank->name,
1010 reg, bank->pm_save[PINCFG_TYPE_FUNC]);
1011 }
1012 }
1013}
1014
1015/**
1016 * samsung_pinctrl_resume_dev - restore pinctrl state from suspend for a device
1017 *
1018 * Restore one of the banks that was saved during suspend.
1019 *
1020 * We don't bother doing anything complicated to avoid glitching lines since
1021 * we're called before pad retention is turned off.
1022 */
1023static void samsung_pinctrl_resume_dev(struct samsung_pinctrl_drv_data *drvdata)
1024{
1025 struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
1026 void __iomem *virt_base = drvdata->virt_base;
1027 int i;
1028
1029 for (i = 0; i < ctrl->nr_banks; i++) {
1030 struct samsung_pin_bank *bank = &ctrl->pin_banks[i];
1031 void __iomem *reg = virt_base + bank->pctl_offset;
1032
1033 u8 *offs = bank->type->reg_offset;
1034 u8 *widths = bank->type->fld_width;
1035 enum pincfg_type type;
1036
1037 /* Registers without a powerdown config aren't lost */
1038 if (!widths[PINCFG_TYPE_CON_PDN])
1039 continue;
1040
1041 if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) {
1042 /* Some banks have two config registers */
1043 pr_debug("%s @ %p (con %#010x %08x => %#010x %08x)\n",
1044 bank->name, reg,
1045 readl(reg + offs[PINCFG_TYPE_FUNC]),
1046 readl(reg + offs[PINCFG_TYPE_FUNC] + 4),
1047 bank->pm_save[PINCFG_TYPE_FUNC],
1048 bank->pm_save[PINCFG_TYPE_NUM]);
1049 writel(bank->pm_save[PINCFG_TYPE_NUM],
1050 reg + offs[PINCFG_TYPE_FUNC] + 4);
1051 } else {
1052 pr_debug("%s @ %p (con %#010x => %#010x)\n", bank->name,
1053 reg, readl(reg + offs[PINCFG_TYPE_FUNC]),
1054 bank->pm_save[PINCFG_TYPE_FUNC]);
1055 }
1056 for (type = 0; type < PINCFG_TYPE_NUM; type++)
1057 if (widths[type])
1058 writel(bank->pm_save[type], reg + offs[type]);
1059 }
1060}
1061
1062/**
1063 * samsung_pinctrl_suspend - save pinctrl state for suspend
1064 *
1065 * Save data for all banks across all devices.
1066 */
1067static int samsung_pinctrl_suspend(void)
1068{
1069 struct samsung_pinctrl_drv_data *drvdata;
1070
1071 list_for_each_entry(drvdata, &drvdata_list, node) {
1072 samsung_pinctrl_suspend_dev(drvdata);
1073 }
1074
1075 return 0;
1076}
1077
1078/**
1079 * samsung_pinctrl_resume - restore pinctrl state for suspend
1080 *
1081 * Restore data for all banks across all devices.
1082 */
1083static void samsung_pinctrl_resume(void)
1084{
1085 struct samsung_pinctrl_drv_data *drvdata;
1086
1087 list_for_each_entry_reverse(drvdata, &drvdata_list, node) {
1088 samsung_pinctrl_resume_dev(drvdata);
1089 }
1090}
1091
1092#else
1093#define samsung_pinctrl_suspend NULL
1094#define samsung_pinctrl_resume NULL
1095#endif
1096
1097static struct syscore_ops samsung_pinctrl_syscore_ops = {
1098 .suspend = samsung_pinctrl_suspend,
1099 .resume = samsung_pinctrl_resume,
1100};
1101
962static const struct of_device_id samsung_pinctrl_dt_match[] = { 1102static const struct of_device_id samsung_pinctrl_dt_match[] = {
963#ifdef CONFIG_PINCTRL_EXYNOS 1103#ifdef CONFIG_PINCTRL_EXYNOS
964 { .compatible = "samsung,exynos4210-pinctrl", 1104 { .compatible = "samsung,exynos4210-pinctrl",
@@ -987,6 +1127,14 @@ static struct platform_driver samsung_pinctrl_driver = {
987 1127
988static int __init samsung_pinctrl_drv_register(void) 1128static int __init samsung_pinctrl_drv_register(void)
989{ 1129{
1130 /*
1131 * Register syscore ops for save/restore of registers across suspend.
1132 * It's important to ensure that this driver is running at an earlier
1133 * initcall level than any arch-specific init calls that install syscore
1134 * ops that turn off pad retention (like exynos_pm_resume).
1135 */
1136 register_syscore_ops(&samsung_pinctrl_syscore_ops);
1137
990 return platform_driver_register(&samsung_pinctrl_driver); 1138 return platform_driver_register(&samsung_pinctrl_driver);
991} 1139}
992postcore_initcall(samsung_pinctrl_drv_register); 1140postcore_initcall(samsung_pinctrl_drv_register);
diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h
index 7c7f9ebcd05b..9f5cc811b8cf 100644
--- a/drivers/pinctrl/pinctrl-samsung.h
+++ b/drivers/pinctrl/pinctrl-samsung.h
@@ -127,6 +127,7 @@ struct samsung_pin_bank_type {
127 * @gpio_chip: GPIO chip of the bank. 127 * @gpio_chip: GPIO chip of the bank.
128 * @grange: linux gpio pin range supported by this bank. 128 * @grange: linux gpio pin range supported by this bank.
129 * @slock: spinlock protecting bank registers 129 * @slock: spinlock protecting bank registers
130 * @pm_save: saved register values during suspend
130 */ 131 */
131struct samsung_pin_bank { 132struct samsung_pin_bank {
132 struct samsung_pin_bank_type *type; 133 struct samsung_pin_bank_type *type;
@@ -144,6 +145,8 @@ struct samsung_pin_bank {
144 struct gpio_chip gpio_chip; 145 struct gpio_chip gpio_chip;
145 struct pinctrl_gpio_range grange; 146 struct pinctrl_gpio_range grange;
146 spinlock_t slock; 147 spinlock_t slock;
148
149 u32 pm_save[PINCFG_TYPE_NUM + 1]; /* +1 to handle double CON registers*/
147}; 150};
148 151
149/** 152/**
@@ -189,6 +192,7 @@ struct samsung_pin_ctrl {
189 192
190/** 193/**
191 * struct samsung_pinctrl_drv_data: wrapper for holding driver data together. 194 * struct samsung_pinctrl_drv_data: wrapper for holding driver data together.
195 * @node: global list node
192 * @virt_base: register base address of the controller. 196 * @virt_base: register base address of the controller.
193 * @dev: device instance representing the controller. 197 * @dev: device instance representing the controller.
194 * @irq: interrpt number used by the controller to notify gpio interrupts. 198 * @irq: interrpt number used by the controller to notify gpio interrupts.
@@ -201,6 +205,7 @@ struct samsung_pin_ctrl {
201 * @nr_function: number of such pin functions. 205 * @nr_function: number of such pin functions.
202 */ 206 */
203struct samsung_pinctrl_drv_data { 207struct samsung_pinctrl_drv_data {
208 struct list_head node;
204 void __iomem *virt_base; 209 void __iomem *virt_base;
205 struct device *dev; 210 struct device *dev;
206 int irq; 211 int irq;