diff options
Diffstat (limited to 'drivers/pinctrl/pinctrl-samsung.c')
| -rw-r--r-- | drivers/pinctrl/pinctrl-samsung.c | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 055d0162098b..63ac22e89678 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) */ | ||
| 53 | LIST_HEAD(drvdata_list); | ||
| 54 | |||
| 51 | static unsigned int pin_base; | 55 | static unsigned int pin_base; |
| 52 | 56 | ||
| 53 | static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) | 57 | static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) |
| @@ -956,9 +960,151 @@ 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 | */ | ||
| 977 | static 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 | if (ctrl->suspend) | ||
| 1015 | ctrl->suspend(drvdata); | ||
| 1016 | } | ||
| 1017 | |||
| 1018 | /** | ||
| 1019 | * samsung_pinctrl_resume_dev - restore pinctrl state from suspend for a device | ||
| 1020 | * | ||
| 1021 | * Restore one of the banks that was saved during suspend. | ||
| 1022 | * | ||
| 1023 | * We don't bother doing anything complicated to avoid glitching lines since | ||
| 1024 | * we're called before pad retention is turned off. | ||
| 1025 | */ | ||
| 1026 | static void samsung_pinctrl_resume_dev(struct samsung_pinctrl_drv_data *drvdata) | ||
| 1027 | { | ||
| 1028 | struct samsung_pin_ctrl *ctrl = drvdata->ctrl; | ||
| 1029 | void __iomem *virt_base = drvdata->virt_base; | ||
| 1030 | int i; | ||
| 1031 | |||
| 1032 | if (ctrl->resume) | ||
| 1033 | ctrl->resume(drvdata); | ||
| 1034 | |||
| 1035 | for (i = 0; i < ctrl->nr_banks; i++) { | ||
| 1036 | struct samsung_pin_bank *bank = &ctrl->pin_banks[i]; | ||
| 1037 | void __iomem *reg = virt_base + bank->pctl_offset; | ||
| 1038 | |||
| 1039 | u8 *offs = bank->type->reg_offset; | ||
| 1040 | u8 *widths = bank->type->fld_width; | ||
| 1041 | enum pincfg_type type; | ||
| 1042 | |||
| 1043 | /* Registers without a powerdown config aren't lost */ | ||
| 1044 | if (!widths[PINCFG_TYPE_CON_PDN]) | ||
| 1045 | continue; | ||
| 1046 | |||
| 1047 | if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) { | ||
| 1048 | /* Some banks have two config registers */ | ||
| 1049 | pr_debug("%s @ %p (con %#010x %08x => %#010x %08x)\n", | ||
| 1050 | bank->name, reg, | ||
| 1051 | readl(reg + offs[PINCFG_TYPE_FUNC]), | ||
| 1052 | readl(reg + offs[PINCFG_TYPE_FUNC] + 4), | ||
| 1053 | bank->pm_save[PINCFG_TYPE_FUNC], | ||
| 1054 | bank->pm_save[PINCFG_TYPE_NUM]); | ||
| 1055 | writel(bank->pm_save[PINCFG_TYPE_NUM], | ||
| 1056 | reg + offs[PINCFG_TYPE_FUNC] + 4); | ||
| 1057 | } else { | ||
| 1058 | pr_debug("%s @ %p (con %#010x => %#010x)\n", bank->name, | ||
| 1059 | reg, readl(reg + offs[PINCFG_TYPE_FUNC]), | ||
| 1060 | bank->pm_save[PINCFG_TYPE_FUNC]); | ||
| 1061 | } | ||
| 1062 | for (type = 0; type < PINCFG_TYPE_NUM; type++) | ||
| 1063 | if (widths[type]) | ||
| 1064 | writel(bank->pm_save[type], reg + offs[type]); | ||
| 1065 | } | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | /** | ||
| 1069 | * samsung_pinctrl_suspend - save pinctrl state for suspend | ||
| 1070 | * | ||
| 1071 | * Save data for all banks across all devices. | ||
| 1072 | */ | ||
| 1073 | static int samsung_pinctrl_suspend(void) | ||
| 1074 | { | ||
| 1075 | struct samsung_pinctrl_drv_data *drvdata; | ||
| 1076 | |||
| 1077 | list_for_each_entry(drvdata, &drvdata_list, node) { | ||
| 1078 | samsung_pinctrl_suspend_dev(drvdata); | ||
| 1079 | } | ||
| 1080 | |||
| 1081 | return 0; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | /** | ||
| 1085 | * samsung_pinctrl_resume - restore pinctrl state for suspend | ||
| 1086 | * | ||
| 1087 | * Restore data for all banks across all devices. | ||
| 1088 | */ | ||
| 1089 | static void samsung_pinctrl_resume(void) | ||
| 1090 | { | ||
| 1091 | struct samsung_pinctrl_drv_data *drvdata; | ||
| 1092 | |||
| 1093 | list_for_each_entry_reverse(drvdata, &drvdata_list, node) { | ||
| 1094 | samsung_pinctrl_resume_dev(drvdata); | ||
| 1095 | } | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | #else | ||
| 1099 | #define samsung_pinctrl_suspend NULL | ||
| 1100 | #define samsung_pinctrl_resume NULL | ||
| 1101 | #endif | ||
| 1102 | |||
| 1103 | static struct syscore_ops samsung_pinctrl_syscore_ops = { | ||
| 1104 | .suspend = samsung_pinctrl_suspend, | ||
| 1105 | .resume = samsung_pinctrl_resume, | ||
| 1106 | }; | ||
| 1107 | |||
| 962 | static const struct of_device_id samsung_pinctrl_dt_match[] = { | 1108 | static const struct of_device_id samsung_pinctrl_dt_match[] = { |
| 963 | #ifdef CONFIG_PINCTRL_EXYNOS | 1109 | #ifdef CONFIG_PINCTRL_EXYNOS |
| 964 | { .compatible = "samsung,exynos4210-pinctrl", | 1110 | { .compatible = "samsung,exynos4210-pinctrl", |
| @@ -987,6 +1133,14 @@ static struct platform_driver samsung_pinctrl_driver = { | |||
| 987 | 1133 | ||
| 988 | static int __init samsung_pinctrl_drv_register(void) | 1134 | static int __init samsung_pinctrl_drv_register(void) |
| 989 | { | 1135 | { |
| 1136 | /* | ||
| 1137 | * Register syscore ops for save/restore of registers across suspend. | ||
| 1138 | * It's important to ensure that this driver is running at an earlier | ||
| 1139 | * initcall level than any arch-specific init calls that install syscore | ||
| 1140 | * ops that turn off pad retention (like exynos_pm_resume). | ||
| 1141 | */ | ||
| 1142 | register_syscore_ops(&samsung_pinctrl_syscore_ops); | ||
| 1143 | |||
| 990 | return platform_driver_register(&samsung_pinctrl_driver); | 1144 | return platform_driver_register(&samsung_pinctrl_driver); |
| 991 | } | 1145 | } |
| 992 | postcore_initcall(samsung_pinctrl_drv_register); | 1146 | postcore_initcall(samsung_pinctrl_drv_register); |
