aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/plat-s3c/Makefile1
-rw-r--r--arch/arm/plat-s3c/gpio.c11
-rw-r--r--arch/arm/plat-s3c/include/plat/gpio-core.h30
-rw-r--r--arch/arm/plat-s3c/pm-gpio.c380
-rw-r--r--arch/arm/plat-s3c24xx/gpiolib.c8
-rw-r--r--arch/arm/plat-s3c24xx/pm.c213
-rw-r--r--arch/arm/plat-s3c64xx/gpiolib.c10
-rw-r--r--arch/arm/plat-s3c64xx/pm.c11
8 files changed, 438 insertions, 226 deletions
diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile
index aa995a49e567..bfc8f537db18 100644
--- a/arch/arm/plat-s3c/Makefile
+++ b/arch/arm/plat-s3c/Makefile
@@ -21,6 +21,7 @@ obj-y += gpio-config.o
21# PM support 21# PM support
22 22
23obj-$(CONFIG_PM) += pm.o 23obj-$(CONFIG_PM) += pm.o
24obj-$(CONFIG_PM) += pm-gpio.o
24obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o 25obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o
25 26
26# devices 27# devices
diff --git a/arch/arm/plat-s3c/gpio.c b/arch/arm/plat-s3c/gpio.c
index d71dd6d9ce5c..260fdc6ad685 100644
--- a/arch/arm/plat-s3c/gpio.c
+++ b/arch/arm/plat-s3c/gpio.c
@@ -16,7 +16,7 @@
16#include <linux/io.h> 16#include <linux/io.h>
17#include <linux/gpio.h> 17#include <linux/gpio.h>
18 18
19#include <plat/gpio-core.h> 19#include <mach/gpio-core.h>
20 20
21#ifdef CONFIG_S3C_GPIO_TRACK 21#ifdef CONFIG_S3C_GPIO_TRACK
22struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END]; 22struct s3c_gpio_chip *s3c_gpios[S3C_GPIO_END];
@@ -140,6 +140,15 @@ __init void s3c_gpiolib_add(struct s3c_gpio_chip *chip)
140 if (!gc->get) 140 if (!gc->get)
141 gc->get = s3c_gpiolib_get; 141 gc->get = s3c_gpiolib_get;
142 142
143#ifdef CONFIG_PM
144 if (chip->pm != NULL) {
145 if (!chip->pm->save || !chip->pm->resume)
146 printk(KERN_ERR "gpio: %s has missing PM functions\n",
147 gc->label);
148 } else
149 printk(KERN_ERR "gpio: %s has no PM function\n", gc->label);
150#endif
151
143 /* gpiochip_add() prints own failure message on error. */ 152 /* gpiochip_add() prints own failure message on error. */
144 ret = gpiochip_add(gc); 153 ret = gpiochip_add(gc);
145 if (ret >= 0) 154 if (ret >= 0)
diff --git a/arch/arm/plat-s3c/include/plat/gpio-core.h b/arch/arm/plat-s3c/include/plat/gpio-core.h
index 2fc60a580ac8..32af612767aa 100644
--- a/arch/arm/plat-s3c/include/plat/gpio-core.h
+++ b/arch/arm/plat-s3c/include/plat/gpio-core.h
@@ -20,6 +20,18 @@
20 * specific code. 20 * specific code.
21*/ 21*/
22 22
23struct s3c_gpio_chip;
24
25/**
26 * struct s3c_gpio_pm - power management (suspend/resume) information
27 * @save: Routine to save the state of the GPIO block
28 * @resume: Routine to resume the GPIO block.
29 */
30struct s3c_gpio_pm {
31 void (*save)(struct s3c_gpio_chip *chip);
32 void (*resume)(struct s3c_gpio_chip *chip);
33};
34
23struct s3c_gpio_cfg; 35struct s3c_gpio_cfg;
24 36
25/** 37/**
@@ -27,6 +39,7 @@ struct s3c_gpio_cfg;
27 * @chip: The chip structure to be exported via gpiolib. 39 * @chip: The chip structure to be exported via gpiolib.
28 * @base: The base pointer to the gpio configuration registers. 40 * @base: The base pointer to the gpio configuration registers.
29 * @config: special function and pull-resistor control information. 41 * @config: special function and pull-resistor control information.
42 * @pm_save: Save information for suspend/resume support.
30 * 43 *
31 * This wrapper provides the necessary information for the Samsung 44 * This wrapper provides the necessary information for the Samsung
32 * specific gpios being registered with gpiolib. 45 * specific gpios being registered with gpiolib.
@@ -34,7 +47,11 @@ struct s3c_gpio_cfg;
34struct s3c_gpio_chip { 47struct s3c_gpio_chip {
35 struct gpio_chip chip; 48 struct gpio_chip chip;
36 struct s3c_gpio_cfg *config; 49 struct s3c_gpio_cfg *config;
50 struct s3c_gpio_pm *pm;
37 void __iomem *base; 51 void __iomem *base;
52#ifdef CONFIG_PM
53 u32 pm_save[4];
54#endif
38}; 55};
39 56
40static inline struct s3c_gpio_chip *to_s3c_gpio(struct gpio_chip *gpc) 57static inline struct s3c_gpio_chip *to_s3c_gpio(struct gpio_chip *gpc)
@@ -75,3 +92,16 @@ static inline struct s3c_gpio_chip *s3c_gpiolib_getchip(unsigned int chip)
75 92
76static inline void s3c_gpiolib_track(struct s3c_gpio_chip *chip) { } 93static inline void s3c_gpiolib_track(struct s3c_gpio_chip *chip) { }
77#endif 94#endif
95
96#ifdef CONFIG_PM
97extern struct s3c_gpio_pm s3c_gpio_pm_1bit;
98extern struct s3c_gpio_pm s3c_gpio_pm_2bit;
99extern struct s3c_gpio_pm s3c_gpio_pm_4bit;
100#define __gpio_pm(x) x
101#else
102#define s3c_gpio_pm_1bit NULL
103#define s3c_gpio_pm_2bit NULL
104#define s3c_gpio_pm_4bit NULL
105#define __gpio_pm(x) NULL
106
107#endif /* CONFIG_PM */
diff --git a/arch/arm/plat-s3c/pm-gpio.c b/arch/arm/plat-s3c/pm-gpio.c
new file mode 100644
index 000000000000..cfd326a8b693
--- /dev/null
+++ b/arch/arm/plat-s3c/pm-gpio.c
@@ -0,0 +1,380 @@
1
2/* linux/arch/arm/plat-s3c/pm-gpio.c
3 *
4 * Copyright 2008 Openmoko, Inc.
5 * Copyright 2008 Simtec Electronics
6 * Ben Dooks <ben@simtec.co.uk>
7 * http://armlinux.simtec.co.uk/
8 *
9 * S3C series GPIO PM code
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14*/
15
16#include <linux/kernel.h>
17#include <linux/sysdev.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/gpio.h>
21
22#include <mach/gpio-core.h>
23#include <plat/pm.h>
24
25/* PM GPIO helpers */
26
27#define OFFS_CON (0x00)
28#define OFFS_DAT (0x04)
29#define OFFS_UP (0x08)
30
31static void s3c_gpio_pm_1bit_save(struct s3c_gpio_chip *chip)
32{
33 chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
34 chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
35}
36
37static void s3c_gpio_pm_1bit_resume(struct s3c_gpio_chip *chip)
38{
39 void __iomem *base = chip->base;
40 u32 old_gpcon = __raw_readl(base + OFFS_CON);
41 u32 old_gpdat = __raw_readl(base + OFFS_DAT);
42 u32 gps_gpcon = chip->pm_save[0];
43 u32 gps_gpdat = chip->pm_save[1];
44 u32 gpcon;
45
46 /* GPACON only has one bit per control / data and no PULLUPs.
47 * GPACON[x] = 0 => Output, 1 => SFN */
48
49 /* first set all SFN bits to SFN */
50
51 gpcon = old_gpcon | gps_gpcon;
52 __raw_writel(gpcon, base + OFFS_CON);
53
54 /* now set all the other bits */
55
56 __raw_writel(gps_gpdat, base + OFFS_DAT);
57 __raw_writel(gps_gpcon, base + OFFS_CON);
58
59 S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
60 chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
61}
62
63struct s3c_gpio_pm s3c_gpio_pm_1bit = {
64 .save = s3c_gpio_pm_1bit_save,
65 .resume = s3c_gpio_pm_1bit_resume,
66};
67
68static void s3c_gpio_pm_2bit_save(struct s3c_gpio_chip *chip)
69{
70 chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
71 chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
72 chip->pm_save[2] = __raw_readl(chip->base + OFFS_UP);
73}
74
75/* Test whether the given masked+shifted bits of an GPIO configuration
76 * are one of the SFN (special function) modes. */
77
78static inline int is_sfn(unsigned long con)
79{
80 return con >= 2;
81}
82
83/* Test if the given masked+shifted GPIO configuration is an input */
84
85static inline int is_in(unsigned long con)
86{
87 return con == 0;
88}
89
90/* Test if the given masked+shifted GPIO configuration is an output */
91
92static inline int is_out(unsigned long con)
93{
94 return con == 1;
95}
96
97/**
98 * s3c_gpio_pm_2bit_resume() - restore the given GPIO bank
99 * @chip: The chip information to resume.
100 *
101 * Restore one of the GPIO banks that was saved during suspend. This is
102 * not as simple as once thought, due to the possibility of glitches
103 * from the order that the CON and DAT registers are set in.
104 *
105 * The three states the pin can be are {IN,OUT,SFN} which gives us 9
106 * combinations of changes to check. Three of these, if the pin stays
107 * in the same configuration can be discounted. This leaves us with
108 * the following:
109 *
110 * { IN => OUT } Change DAT first
111 * { IN => SFN } Change CON first
112 * { OUT => SFN } Change CON first, so new data will not glitch
113 * { OUT => IN } Change CON first, so new data will not glitch
114 * { SFN => IN } Change CON first
115 * { SFN => OUT } Change DAT first, so new data will not glitch [1]
116 *
117 * We do not currently deal with the UP registers as these control
118 * weak resistors, so a small delay in change should not need to bring
119 * these into the calculations.
120 *
121 * [1] this assumes that writing to a pin DAT whilst in SFN will set the
122 * state for when it is next output.
123 */
124static void s3c_gpio_pm_2bit_resume(struct s3c_gpio_chip *chip)
125{
126 void __iomem *base = chip->base;
127 u32 old_gpcon = __raw_readl(base + OFFS_CON);
128 u32 old_gpdat = __raw_readl(base + OFFS_DAT);
129 u32 gps_gpcon = chip->pm_save[0];
130 u32 gps_gpdat = chip->pm_save[1];
131 u32 gpcon, old, new, mask;
132 u32 change_mask = 0x0;
133 int nr;
134
135 /* restore GPIO pull-up settings */
136 __raw_writel(chip->pm_save[2], base + OFFS_UP);
137
138 /* Create a change_mask of all the items that need to have
139 * their CON value changed before their DAT value, so that
140 * we minimise the work between the two settings.
141 */
142
143 for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) {
144 old = (old_gpcon & mask) >> nr;
145 new = (gps_gpcon & mask) >> nr;
146
147 /* If there is no change, then skip */
148
149 if (old == new)
150 continue;
151
152 /* If both are special function, then skip */
153
154 if (is_sfn(old) && is_sfn(new))
155 continue;
156
157 /* Change is IN => OUT, do not change now */
158
159 if (is_in(old) && is_out(new))
160 continue;
161
162 /* Change is SFN => OUT, do not change now */
163
164 if (is_sfn(old) && is_out(new))
165 continue;
166
167 /* We should now be at the case of IN=>SFN,
168 * OUT=>SFN, OUT=>IN, SFN=>IN. */
169
170 change_mask |= mask;
171 }
172
173
174 /* Write the new CON settings */
175
176 gpcon = old_gpcon & ~change_mask;
177 gpcon |= gps_gpcon & change_mask;
178
179 __raw_writel(gpcon, base + OFFS_CON);
180
181 /* Now change any items that require DAT,CON */
182
183 __raw_writel(gps_gpdat, base + OFFS_DAT);
184 __raw_writel(gps_gpcon, base + OFFS_CON);
185
186 S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
187 chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
188}
189
190struct s3c_gpio_pm s3c_gpio_pm_2bit = {
191 .save = s3c_gpio_pm_2bit_save,
192 .resume = s3c_gpio_pm_2bit_resume,
193};
194
195#ifdef CONFIG_ARCH_S3C64XX
196static void s3c_gpio_pm_4bit_save(struct s3c_gpio_chip *chip)
197{
198 chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON);
199 chip->pm_save[2] = __raw_readl(chip->base + OFFS_DAT);
200 chip->pm_save[3] = __raw_readl(chip->base + OFFS_UP);
201
202 if (chip->chip.ngpio > 8)
203 chip->pm_save[0] = __raw_readl(chip->base - 4);
204}
205
206static u32 s3c_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon)
207{
208 u32 old, new, mask;
209 u32 change_mask = 0x0;
210 int nr;
211
212 for (nr = 0, mask = 0x0f; nr < 16; nr += 4, mask <<= 4) {
213 old = (old_gpcon & mask) >> nr;
214 new = (gps_gpcon & mask) >> nr;
215
216 /* If there is no change, then skip */
217
218 if (old == new)
219 continue;
220
221 /* If both are special function, then skip */
222
223 if (is_sfn(old) && is_sfn(new))
224 continue;
225
226 /* Change is IN => OUT, do not change now */
227
228 if (is_in(old) && is_out(new))
229 continue;
230
231 /* Change is SFN => OUT, do not change now */
232
233 if (is_sfn(old) && is_out(new))
234 continue;
235
236 /* We should now be at the case of IN=>SFN,
237 * OUT=>SFN, OUT=>IN, SFN=>IN. */
238
239 change_mask |= mask;
240 }
241
242 return change_mask;
243}
244
245static void s3c_gpio_pm_4bit_con(struct s3c_gpio_chip *chip, int index)
246{
247 void __iomem *con = chip->base + (index * 4);
248 u32 old_gpcon = __raw_readl(con);
249 u32 gps_gpcon = chip->pm_save[index + 1];
250 u32 gpcon, mask;
251
252 mask = s3c_gpio_pm_4bit_mask(old_gpcon, gps_gpcon);
253
254 gpcon = old_gpcon & ~mask;
255 gpcon |= gps_gpcon & mask;
256
257 __raw_writel(gpcon, con);
258}
259
260static void s3c_gpio_pm_4bit_resume(struct s3c_gpio_chip *chip)
261{
262 void __iomem *base = chip->base;
263 u32 old_gpcon[2];
264 u32 old_gpdat = __raw_readl(base + OFFS_DAT);
265 u32 gps_gpdat = chip->pm_save[2];
266
267 /* First, modify the CON settings */
268
269 old_gpcon[0] = 0;
270 old_gpcon[1] = __raw_readl(base + OFFS_CON);
271
272 s3c_gpio_pm_4bit_con(chip, 0);
273 if (chip->chip.ngpio > 8) {
274 old_gpcon[0] = __raw_readl(base - 4);
275 s3c_gpio_pm_4bit_con(chip, -1);
276 }
277
278 /* Now change the configurations that require DAT,CON */
279
280 __raw_writel(chip->pm_save[2], base + OFFS_DAT);
281 __raw_writel(chip->pm_save[1], base + OFFS_CON);
282 if (chip->chip.ngpio > 8)
283 __raw_writel(chip->pm_save[0], base - 4);
284
285 __raw_writel(chip->pm_save[2], base + OFFS_DAT);
286 __raw_writel(chip->pm_save[3], base + OFFS_UP);
287
288 if (chip->chip.ngpio > 8) {
289 S3C_PMDBG("%s: CON4 %08x,%08x => %08x,%08x, DAT %08x => %08x\n",
290 chip->chip.label, old_gpcon[0], old_gpcon[1],
291 __raw_readl(base - 4),
292 __raw_readl(base + OFFS_CON),
293 old_gpdat, gps_gpdat);
294 } else
295 S3C_PMDBG("%s: CON4 %08x => %08x, DAT %08x => %08x\n",
296 chip->chip.label, old_gpcon[1],
297 __raw_readl(base + OFFS_CON),
298 old_gpdat, gps_gpdat);
299}
300
301struct s3c_gpio_pm s3c_gpio_pm_4bit = {
302 .save = s3c_gpio_pm_4bit_save,
303 .resume = s3c_gpio_pm_4bit_resume,
304};
305#endif /* CONFIG_ARCH_S3C64XX */
306
307/**
308 * s3c_pm_save_gpio() - save gpio chip data for suspend
309 * @ourchip: The chip for suspend.
310 */
311static void s3c_pm_save_gpio(struct s3c_gpio_chip *ourchip)
312{
313 struct s3c_gpio_pm *pm = ourchip->pm;
314
315 if (pm == NULL || pm->save == NULL)
316 S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label);
317 else
318 pm->save(ourchip);
319}
320
321/**
322 * s3c_pm_save_gpios() - Save the state of the GPIO banks.
323 *
324 * For all the GPIO banks, save the state of each one ready for going
325 * into a suspend mode.
326 */
327void s3c_pm_save_gpios(void)
328{
329 struct s3c_gpio_chip *ourchip;
330 unsigned int gpio_nr;
331
332 for (gpio_nr = 0; gpio_nr < S3C_GPIO_END; gpio_nr++) {
333 ourchip = s3c_gpiolib_getchip(gpio_nr);
334 if (!ourchip)
335 continue;
336
337 s3c_pm_save_gpio(ourchip);
338
339 S3C_PMDBG("%s: save %08x,%08x,%08x,%08x\n",
340 ourchip->chip.label,
341 ourchip->pm_save[0],
342 ourchip->pm_save[1],
343 ourchip->pm_save[2],
344 ourchip->pm_save[3]);
345
346 gpio_nr += ourchip->chip.ngpio;
347 gpio_nr += CONFIG_S3C_GPIO_SPACE;
348 }
349}
350
351/**
352 * s3c_pm_resume_gpio() - restore gpio chip data after suspend
353 * @ourchip: The suspended chip.
354 */
355static void s3c_pm_resume_gpio(struct s3c_gpio_chip *ourchip)
356{
357 struct s3c_gpio_pm *pm = ourchip->pm;
358
359 if (pm == NULL || pm->resume == NULL)
360 S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label);
361 else
362 pm->resume(ourchip);
363}
364
365void s3c_pm_restore_gpios(void)
366{
367 struct s3c_gpio_chip *ourchip;
368 unsigned int gpio_nr;
369
370 for (gpio_nr = 0; gpio_nr < S3C_GPIO_END; gpio_nr++) {
371 ourchip = s3c_gpiolib_getchip(gpio_nr);
372 if (!ourchip)
373 continue;
374
375 s3c_pm_resume_gpio(ourchip);
376
377 gpio_nr += ourchip->chip.ngpio;
378 gpio_nr += CONFIG_S3C_GPIO_SPACE;
379 }
380}
diff --git a/arch/arm/plat-s3c24xx/gpiolib.c b/arch/arm/plat-s3c24xx/gpiolib.c
index 5c0491bf738b..4bac12dc0733 100644
--- a/arch/arm/plat-s3c24xx/gpiolib.c
+++ b/arch/arm/plat-s3c24xx/gpiolib.c
@@ -22,6 +22,7 @@
22#include <mach/gpio-core.h> 22#include <mach/gpio-core.h>
23#include <mach/hardware.h> 23#include <mach/hardware.h>
24#include <asm/irq.h> 24#include <asm/irq.h>
25#include <plat/pm.h>
25 26
26#include <mach/regs-gpio.h> 27#include <mach/regs-gpio.h>
27 28
@@ -78,6 +79,7 @@ static int s3c24xx_gpiolib_bankg_toirq(struct gpio_chip *chip, unsigned offset)
78struct s3c_gpio_chip s3c24xx_gpios[] = { 79struct s3c_gpio_chip s3c24xx_gpios[] = {
79 [0] = { 80 [0] = {
80 .base = S3C24XX_GPIO_BASE(S3C2410_GPA0), 81 .base = S3C24XX_GPIO_BASE(S3C2410_GPA0),
82 .pm = __gpio_pm(&s3c_gpio_pm_1bit),
81 .chip = { 83 .chip = {
82 .base = S3C2410_GPA0, 84 .base = S3C2410_GPA0,
83 .owner = THIS_MODULE, 85 .owner = THIS_MODULE,
@@ -89,6 +91,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
89 }, 91 },
90 [1] = { 92 [1] = {
91 .base = S3C24XX_GPIO_BASE(S3C2410_GPB0), 93 .base = S3C24XX_GPIO_BASE(S3C2410_GPB0),
94 .pm = __gpio_pm(&s3c_gpio_pm_2bit),
92 .chip = { 95 .chip = {
93 .base = S3C2410_GPB0, 96 .base = S3C2410_GPB0,
94 .owner = THIS_MODULE, 97 .owner = THIS_MODULE,
@@ -98,6 +101,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
98 }, 101 },
99 [2] = { 102 [2] = {
100 .base = S3C24XX_GPIO_BASE(S3C2410_GPC0), 103 .base = S3C24XX_GPIO_BASE(S3C2410_GPC0),
104 .pm = __gpio_pm(&s3c_gpio_pm_2bit),
101 .chip = { 105 .chip = {
102 .base = S3C2410_GPC0, 106 .base = S3C2410_GPC0,
103 .owner = THIS_MODULE, 107 .owner = THIS_MODULE,
@@ -107,6 +111,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
107 }, 111 },
108 [3] = { 112 [3] = {
109 .base = S3C24XX_GPIO_BASE(S3C2410_GPD0), 113 .base = S3C24XX_GPIO_BASE(S3C2410_GPD0),
114 .pm = __gpio_pm(&s3c_gpio_pm_2bit),
110 .chip = { 115 .chip = {
111 .base = S3C2410_GPD0, 116 .base = S3C2410_GPD0,
112 .owner = THIS_MODULE, 117 .owner = THIS_MODULE,
@@ -116,6 +121,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
116 }, 121 },
117 [4] = { 122 [4] = {
118 .base = S3C24XX_GPIO_BASE(S3C2410_GPE0), 123 .base = S3C24XX_GPIO_BASE(S3C2410_GPE0),
124 .pm = __gpio_pm(&s3c_gpio_pm_2bit),
119 .chip = { 125 .chip = {
120 .base = S3C2410_GPE0, 126 .base = S3C2410_GPE0,
121 .label = "GPIOE", 127 .label = "GPIOE",
@@ -125,6 +131,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
125 }, 131 },
126 [5] = { 132 [5] = {
127 .base = S3C24XX_GPIO_BASE(S3C2410_GPF0), 133 .base = S3C24XX_GPIO_BASE(S3C2410_GPF0),
134 .pm = __gpio_pm(&s3c_gpio_pm_2bit),
128 .chip = { 135 .chip = {
129 .base = S3C2410_GPF0, 136 .base = S3C2410_GPF0,
130 .owner = THIS_MODULE, 137 .owner = THIS_MODULE,
@@ -135,6 +142,7 @@ struct s3c_gpio_chip s3c24xx_gpios[] = {
135 }, 142 },
136 [6] = { 143 [6] = {
137 .base = S3C24XX_GPIO_BASE(S3C2410_GPG0), 144 .base = S3C24XX_GPIO_BASE(S3C2410_GPG0),
145 .pm = __gpio_pm(&s3c_gpio_pm_2bit),
138 .chip = { 146 .chip = {
139 .base = S3C2410_GPG0, 147 .base = S3C2410_GPG0,
140 .owner = THIS_MODULE, 148 .owner = THIS_MODULE,
diff --git a/arch/arm/plat-s3c24xx/pm.c b/arch/arm/plat-s3c24xx/pm.c
index 062a29339a91..5135c40a1b90 100644
--- a/arch/arm/plat-s3c24xx/pm.c
+++ b/arch/arm/plat-s3c24xx/pm.c
@@ -75,43 +75,10 @@ static struct sleep_save core_save[] = {
75 SAVE_ITEM(S3C2410_CLKSLOW), 75 SAVE_ITEM(S3C2410_CLKSLOW),
76}; 76};
77 77
78static struct gpio_sleep {
79 void __iomem *base;
80 unsigned int gpcon;
81 unsigned int gpdat;
82 unsigned int gpup;
83} gpio_save[] = {
84 [0] = {
85 .base = S3C2410_GPACON,
86 },
87 [1] = {
88 .base = S3C2410_GPBCON,
89 },
90 [2] = {
91 .base = S3C2410_GPCCON,
92 },
93 [3] = {
94 .base = S3C2410_GPDCON,
95 },
96 [4] = {
97 .base = S3C2410_GPECON,
98 },
99 [5] = {
100 .base = S3C2410_GPFCON,
101 },
102 [6] = {
103 .base = S3C2410_GPGCON,
104 },
105 [7] = {
106 .base = S3C2410_GPHCON,
107 },
108};
109
110static struct sleep_save misc_save[] = { 78static struct sleep_save misc_save[] = {
111 SAVE_ITEM(S3C2410_DCLKCON), 79 SAVE_ITEM(S3C2410_DCLKCON),
112}; 80};
113 81
114
115/* s3c_pm_check_resume_pin 82/* s3c_pm_check_resume_pin
116 * 83 *
117 * check to see if the pin is configured correctly for sleep mode, and 84 * check to see if the pin is configured correctly for sleep mode, and
@@ -165,186 +132,6 @@ void s3c_pm_configure_extint(void)
165 } 132 }
166} 133}
167 134
168/* offsets for CON/DAT/UP registers */
169
170#define OFFS_CON (S3C2410_GPACON - S3C2410_GPACON)
171#define OFFS_DAT (S3C2410_GPADAT - S3C2410_GPACON)
172#define OFFS_UP (S3C2410_GPBUP - S3C2410_GPBCON)
173
174/* s3c_pm_save_gpios()
175 *
176 * Save the state of the GPIOs
177 */
178
179void s3c_pm_save_gpios(void)
180{
181 struct gpio_sleep *gps = gpio_save;
182 unsigned int gpio;
183
184 for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
185 void __iomem *base = gps->base;
186
187 gps->gpcon = __raw_readl(base + OFFS_CON);
188 gps->gpdat = __raw_readl(base + OFFS_DAT);
189
190 if (gpio > 0)
191 gps->gpup = __raw_readl(base + OFFS_UP);
192
193 }
194}
195
196/* Test whether the given masked+shifted bits of an GPIO configuration
197 * are one of the SFN (special function) modes. */
198
199static inline int is_sfn(unsigned long con)
200{
201 return (con == 2 || con == 3);
202}
203
204/* Test if the given masked+shifted GPIO configuration is an input */
205
206static inline int is_in(unsigned long con)
207{
208 return con == 0;
209}
210
211/* Test if the given masked+shifted GPIO configuration is an output */
212
213static inline int is_out(unsigned long con)
214{
215 return con == 1;
216}
217
218/**
219 * s3c2410_pm_restore_gpio() - restore the given GPIO bank
220 * @index: The number of the GPIO bank being resumed.
221 * @gps: The sleep confgiuration for the bank.
222 *
223 * Restore one of the GPIO banks that was saved during suspend. This is
224 * not as simple as once thought, due to the possibility of glitches
225 * from the order that the CON and DAT registers are set in.
226 *
227 * The three states the pin can be are {IN,OUT,SFN} which gives us 9
228 * combinations of changes to check. Three of these, if the pin stays
229 * in the same configuration can be discounted. This leaves us with
230 * the following:
231 *
232 * { IN => OUT } Change DAT first
233 * { IN => SFN } Change CON first
234 * { OUT => SFN } Change CON first, so new data will not glitch
235 * { OUT => IN } Change CON first, so new data will not glitch
236 * { SFN => IN } Change CON first
237 * { SFN => OUT } Change DAT first, so new data will not glitch [1]
238 *
239 * We do not currently deal with the UP registers as these control
240 * weak resistors, so a small delay in change should not need to bring
241 * these into the calculations.
242 *
243 * [1] this assumes that writing to a pin DAT whilst in SFN will set the
244 * state for when it is next output.
245 */
246
247static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps)
248{
249 void __iomem *base = gps->base;
250 unsigned long gps_gpcon = gps->gpcon;
251 unsigned long gps_gpdat = gps->gpdat;
252 unsigned long old_gpcon;
253 unsigned long old_gpdat;
254 unsigned long old_gpup = 0x0;
255 unsigned long gpcon;
256 int nr;
257
258 old_gpcon = __raw_readl(base + OFFS_CON);
259 old_gpdat = __raw_readl(base + OFFS_DAT);
260
261 if (base == S3C2410_GPACON) {
262 /* GPACON only has one bit per control / data and no PULLUPs.
263 * GPACON[x] = 0 => Output, 1 => SFN */
264
265 /* first set all SFN bits to SFN */
266
267 gpcon = old_gpcon | gps->gpcon;
268 __raw_writel(gpcon, base + OFFS_CON);
269
270 /* now set all the other bits */
271
272 __raw_writel(gps_gpdat, base + OFFS_DAT);
273 __raw_writel(gps_gpcon, base + OFFS_CON);
274 } else {
275 unsigned long old, new, mask;
276 unsigned long change_mask = 0x0;
277
278 old_gpup = __raw_readl(base + OFFS_UP);
279
280 /* Create a change_mask of all the items that need to have
281 * their CON value changed before their DAT value, so that
282 * we minimise the work between the two settings.
283 */
284
285 for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) {
286 old = (old_gpcon & mask) >> nr;
287 new = (gps_gpcon & mask) >> nr;
288
289 /* If there is no change, then skip */
290
291 if (old == new)
292 continue;
293
294 /* If both are special function, then skip */
295
296 if (is_sfn(old) && is_sfn(new))
297 continue;
298
299 /* Change is IN => OUT, do not change now */
300
301 if (is_in(old) && is_out(new))
302 continue;
303
304 /* Change is SFN => OUT, do not change now */
305
306 if (is_sfn(old) && is_out(new))
307 continue;
308
309 /* We should now be at the case of IN=>SFN,
310 * OUT=>SFN, OUT=>IN, SFN=>IN. */
311
312 change_mask |= mask;
313 }
314
315 /* Write the new CON settings */
316
317 gpcon = old_gpcon & ~change_mask;
318 gpcon |= gps_gpcon & change_mask;
319
320 __raw_writel(gpcon, base + OFFS_CON);
321
322 /* Now change any items that require DAT,CON */
323
324 __raw_writel(gps_gpdat, base + OFFS_DAT);
325 __raw_writel(gps_gpcon, base + OFFS_CON);
326 __raw_writel(gps->gpup, base + OFFS_UP);
327 }
328
329 S3C_PMDBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n",
330 index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
331}
332
333
334/** s3c2410_pm_restore_gpios()
335 *
336 * Restore the state of the GPIOs
337 */
338
339void s3c_pm_restore_gpios(void)
340{
341 struct gpio_sleep *gps = gpio_save;
342 int gpio;
343
344 for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
345 s3c2410_pm_restore_gpio(gpio, gps);
346 }
347}
348 135
349void s3c_pm_restore_core(void) 136void s3c_pm_restore_core(void)
350{ 137{
diff --git a/arch/arm/plat-s3c64xx/gpiolib.c b/arch/arm/plat-s3c64xx/gpiolib.c
index ee9188add8fb..ccb82e854962 100644
--- a/arch/arm/plat-s3c64xx/gpiolib.c
+++ b/arch/arm/plat-s3c64xx/gpiolib.c
@@ -385,12 +385,19 @@ static __init void s3c64xx_gpiolib_add_4bit(struct s3c_gpio_chip *chip)
385{ 385{
386 chip->chip.direction_input = s3c64xx_gpiolib_4bit_input; 386 chip->chip.direction_input = s3c64xx_gpiolib_4bit_input;
387 chip->chip.direction_output = s3c64xx_gpiolib_4bit_output; 387 chip->chip.direction_output = s3c64xx_gpiolib_4bit_output;
388 chip->pm = __gpio_pm(&s3c_gpio_pm_4bit);
388} 389}
389 390
390static __init void s3c64xx_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) 391static __init void s3c64xx_gpiolib_add_4bit2(struct s3c_gpio_chip *chip)
391{ 392{
392 chip->chip.direction_input = s3c64xx_gpiolib_4bit2_input; 393 chip->chip.direction_input = s3c64xx_gpiolib_4bit2_input;
393 chip->chip.direction_output = s3c64xx_gpiolib_4bit2_output; 394 chip->chip.direction_output = s3c64xx_gpiolib_4bit2_output;
395 chip->pm = __gpio_pm(&s3c_gpio_pm_4bit);
396}
397
398static __init void s3c64xx_gpiolib_add_2bit(struct s3c_gpio_chip *chip)
399{
400 chip->pm = __gpio_pm(&s3c_gpio_pm_2bit);
394} 401}
395 402
396static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips, 403static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips,
@@ -412,7 +419,8 @@ static __init int s3c64xx_gpiolib_init(void)
412 s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2), 419 s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2),
413 s3c64xx_gpiolib_add_4bit2); 420 s3c64xx_gpiolib_add_4bit2);
414 421
415 s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), NULL); 422 s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit),
423 s3c64xx_gpiolib_add_2bit);
416 424
417 return 0; 425 return 0;
418} 426}
diff --git a/arch/arm/plat-s3c64xx/pm.c b/arch/arm/plat-s3c64xx/pm.c
index 98190aa364ae..07a6516a4f3c 100644
--- a/arch/arm/plat-s3c64xx/pm.c
+++ b/arch/arm/plat-s3c64xx/pm.c
@@ -96,17 +96,6 @@ void s3c_pm_configure_extint(void)
96 __raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK); 96 __raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK);
97} 97}
98 98
99void s3c_pm_save_gpios(void)
100{
101 /* currently, unless the bootloader does something really stupid
102 * the gpio blocks should be maintained over their sleep.
103 */
104}
105
106void s3c_pm_restore_gpios(void)
107{
108}
109
110void s3c_pm_restore_core(void) 99void s3c_pm_restore_core(void)
111{ 100{
112 __raw_writel(0, S3C64XX_EINT_MASK); 101 __raw_writel(0, S3C64XX_EINT_MASK);