aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s3c24xx
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2008-12-11 19:24:30 -0500
committerBen Dooks <ben-linux@fluff.org>2009-05-07 06:04:56 -0400
commitd87964c46005ccb04754f6309df0fd8f67b08c6d (patch)
treed35fc1fb3660dca0cdb236ef14ba27dcd69618f2 /arch/arm/plat-s3c24xx
parent966bcc14386000e8b4dc7bbb426910bcb55a8588 (diff)
[ARM] S3C: GPIO PM core GPIOlib integration
Move the GPIO suspend/resume support inline with the gpiolib support so that it will work with both the S3C24XX and S3C64XX series. The s3c_gpio_chip is extended to have a pm callback and a save block to keep the state of the GPIO over suspend, and the code from the s3c24xx implementation is added to a new common file. The suspend process now uses the list of registered chips to go through saving and restoring each one as appropriate, using the pm callback to select the appropriate routine depending on the type of control register present. This change also means that any additional GPIO added should not require changes to the PM. Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/plat-s3c24xx')
-rw-r--r--arch/arm/plat-s3c24xx/gpiolib.c8
-rw-r--r--arch/arm/plat-s3c24xx/pm.c213
2 files changed, 8 insertions, 213 deletions
diff --git a/arch/arm/plat-s3c24xx/gpiolib.c b/arch/arm/plat-s3c24xx/gpiolib.c
index 5c0491bf738..4bac12dc073 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 062a29339a9..5135c40a1b9 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{