aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;