1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include <linux/kernel.h>
17#include <linux/device.h>
18#include <linux/init.h>
19#include <linux/io.h>
20#include <linux/gpio.h>
21
22#include <mach/gpio-samsung.h>
23
24#include <plat/gpio-core.h>
25#include <plat/pm.h>
26
27
28
29#define OFFS_CON (0x00)
30#define OFFS_DAT (0x04)
31#define OFFS_UP (0x08)
32
33static void samsung_gpio_pm_1bit_save(struct samsung_gpio_chip *chip)
34{
35 chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
36 chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
37}
38
39static void samsung_gpio_pm_1bit_resume(struct samsung_gpio_chip *chip)
40{
41 void __iomem *base = chip->base;
42 u32 old_gpcon = __raw_readl(base + OFFS_CON);
43 u32 old_gpdat = __raw_readl(base + OFFS_DAT);
44 u32 gps_gpcon = chip->pm_save[0];
45 u32 gps_gpdat = chip->pm_save[1];
46 u32 gpcon;
47
48
49
50
51
52
53 gpcon = old_gpcon | gps_gpcon;
54 __raw_writel(gpcon, base + OFFS_CON);
55
56
57
58 __raw_writel(gps_gpdat, base + OFFS_DAT);
59 __raw_writel(gps_gpcon, base + OFFS_CON);
60
61 S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
62 chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
63}
64
65struct samsung_gpio_pm samsung_gpio_pm_1bit = {
66 .save = samsung_gpio_pm_1bit_save,
67 .resume = samsung_gpio_pm_1bit_resume,
68};
69
70static void samsung_gpio_pm_2bit_save(struct samsung_gpio_chip *chip)
71{
72 chip->pm_save[0] = __raw_readl(chip->base + OFFS_CON);
73 chip->pm_save[1] = __raw_readl(chip->base + OFFS_DAT);
74 chip->pm_save[2] = __raw_readl(chip->base + OFFS_UP);
75}
76
77
78
79
80static inline int is_sfn(unsigned long con)
81{
82 return con >= 2;
83}
84
85
86
87static inline int is_in(unsigned long con)
88{
89 return con == 0;
90}
91
92
93
94static inline int is_out(unsigned long con)
95{
96 return con == 1;
97}
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126static void samsung_gpio_pm_2bit_resume(struct samsung_gpio_chip *chip)
127{
128 void __iomem *base = chip->base;
129 u32 old_gpcon = __raw_readl(base + OFFS_CON);
130 u32 old_gpdat = __raw_readl(base + OFFS_DAT);
131 u32 gps_gpcon = chip->pm_save[0];
132 u32 gps_gpdat = chip->pm_save[1];
133 u32 gpcon, old, new, mask;
134 u32 change_mask = 0x0;
135 int nr;
136
137
138 __raw_writel(chip->pm_save[2], base + OFFS_UP);
139
140
141
142
143
144
145 for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) {
146 old = (old_gpcon & mask) >> nr;
147 new = (gps_gpcon & mask) >> nr;
148
149
150
151 if (old == new)
152 continue;
153
154
155
156 if (is_sfn(old) && is_sfn(new))
157 continue;
158
159
160
161 if (is_in(old) && is_out(new))
162 continue;
163
164
165
166 if (is_sfn(old) && is_out(new))
167 continue;
168
169
170
171
172 change_mask |= mask;
173 }
174
175
176
177
178 gpcon = old_gpcon & ~change_mask;
179 gpcon |= gps_gpcon & change_mask;
180
181 __raw_writel(gpcon, base + OFFS_CON);
182
183
184
185 __raw_writel(gps_gpdat, base + OFFS_DAT);
186 __raw_writel(gps_gpcon, base + OFFS_CON);
187
188 S3C_PMDBG("%s: CON %08x => %08x, DAT %08x => %08x\n",
189 chip->chip.label, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
190}
191
192struct samsung_gpio_pm samsung_gpio_pm_2bit = {
193 .save = samsung_gpio_pm_2bit_save,
194 .resume = samsung_gpio_pm_2bit_resume,
195};
196
197#if defined(CONFIG_ARCH_S3C64XX)
198static void samsung_gpio_pm_4bit_save(struct samsung_gpio_chip *chip)
199{
200 chip->pm_save[1] = __raw_readl(chip->base + OFFS_CON);
201 chip->pm_save[2] = __raw_readl(chip->base + OFFS_DAT);
202 chip->pm_save[3] = __raw_readl(chip->base + OFFS_UP);
203
204 if (chip->chip.ngpio > 8)
205 chip->pm_save[0] = __raw_readl(chip->base - 4);
206}
207
208static u32 samsung_gpio_pm_4bit_mask(u32 old_gpcon, u32 gps_gpcon)
209{
210 u32 old, new, mask;
211 u32 change_mask = 0x0;
212 int nr;
213
214 for (nr = 0, mask = 0x0f; nr < 16; nr += 4, mask <<= 4) {
215 old = (old_gpcon & mask) >> nr;
216 new = (gps_gpcon & mask) >> nr;
217
218
219
220 if (old == new)
221 continue;
222
223
224
225 if (is_sfn(old) && is_sfn(new))
226 continue;
227
228
229
230 if (is_in(old) && is_out(new))
231 continue;
232
233
234
235 if (is_sfn(old) && is_out(new))
236 continue;
237
238
239
240
241 change_mask |= mask;
242 }
243
244 return change_mask;
245}
246
247static void samsung_gpio_pm_4bit_con(struct samsung_gpio_chip *chip, int index)
248{
249 void __iomem *con = chip->base + (index * 4);
250 u32 old_gpcon = __raw_readl(con);
251 u32 gps_gpcon = chip->pm_save[index + 1];
252 u32 gpcon, mask;
253
254 mask = samsung_gpio_pm_4bit_mask(old_gpcon, gps_gpcon);
255
256 gpcon = old_gpcon & ~mask;
257 gpcon |= gps_gpcon & mask;
258
259 __raw_writel(gpcon, con);
260}
261
262static void samsung_gpio_pm_4bit_resume(struct samsung_gpio_chip *chip)
263{
264 void __iomem *base = chip->base;
265 u32 old_gpcon[2];
266 u32 old_gpdat = __raw_readl(base + OFFS_DAT);
267 u32 gps_gpdat = chip->pm_save[2];
268
269
270
271 old_gpcon[0] = 0;
272 old_gpcon[1] = __raw_readl(base + OFFS_CON);
273
274 samsung_gpio_pm_4bit_con(chip, 0);
275 if (chip->chip.ngpio > 8) {
276 old_gpcon[0] = __raw_readl(base - 4);
277 samsung_gpio_pm_4bit_con(chip, -1);
278 }
279
280
281
282 __raw_writel(chip->pm_save[2], base + OFFS_DAT);
283 __raw_writel(chip->pm_save[1], base + OFFS_CON);
284 if (chip->chip.ngpio > 8)
285 __raw_writel(chip->pm_save[0], base - 4);
286
287 __raw_writel(chip->pm_save[2], base + OFFS_DAT);
288 __raw_writel(chip->pm_save[3], base + OFFS_UP);
289
290 if (chip->chip.ngpio > 8) {
291 S3C_PMDBG("%s: CON4 %08x,%08x => %08x,%08x, DAT %08x => %08x\n",
292 chip->chip.label, old_gpcon[0], old_gpcon[1],
293 __raw_readl(base - 4),
294 __raw_readl(base + OFFS_CON),
295 old_gpdat, gps_gpdat);
296 } else
297 S3C_PMDBG("%s: CON4 %08x => %08x, DAT %08x => %08x\n",
298 chip->chip.label, old_gpcon[1],
299 __raw_readl(base + OFFS_CON),
300 old_gpdat, gps_gpdat);
301}
302
303struct samsung_gpio_pm samsung_gpio_pm_4bit = {
304 .save = samsung_gpio_pm_4bit_save,
305 .resume = samsung_gpio_pm_4bit_resume,
306};
307#endif
308
309
310
311
312
313static void samsung_pm_save_gpio(struct samsung_gpio_chip *ourchip)
314{
315 struct samsung_gpio_pm *pm = ourchip->pm;
316
317 if (pm == NULL || pm->save == NULL)
318 S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label);
319 else
320 pm->save(ourchip);
321}
322
323
324
325
326
327
328
329void samsung_pm_save_gpios(void)
330{
331 struct samsung_gpio_chip *ourchip;
332 unsigned int gpio_nr;
333
334 for (gpio_nr = 0; gpio_nr < S3C_GPIO_END;) {
335 ourchip = samsung_gpiolib_getchip(gpio_nr);
336 if (!ourchip) {
337 gpio_nr++;
338 continue;
339 }
340
341 samsung_pm_save_gpio(ourchip);
342
343 S3C_PMDBG("%s: save %08x,%08x,%08x,%08x\n",
344 ourchip->chip.label,
345 ourchip->pm_save[0],
346 ourchip->pm_save[1],
347 ourchip->pm_save[2],
348 ourchip->pm_save[3]);
349
350 gpio_nr += ourchip->chip.ngpio;
351 gpio_nr += CONFIG_S3C_GPIO_SPACE;
352 }
353}
354
355
356
357
358
359static void samsung_pm_resume_gpio(struct samsung_gpio_chip *ourchip)
360{
361 struct samsung_gpio_pm *pm = ourchip->pm;
362
363 if (pm == NULL || pm->resume == NULL)
364 S3C_PMDBG("%s: no pm for %s\n", __func__, ourchip->chip.label);
365 else
366 pm->resume(ourchip);
367}
368
369void samsung_pm_restore_gpios(void)
370{
371 struct samsung_gpio_chip *ourchip;
372 unsigned int gpio_nr;
373
374 for (gpio_nr = 0; gpio_nr < S3C_GPIO_END;) {
375 ourchip = samsung_gpiolib_getchip(gpio_nr);
376 if (!ourchip) {
377 gpio_nr++;
378 continue;
379 }
380
381 samsung_pm_resume_gpio(ourchip);
382
383 gpio_nr += ourchip->chip.ngpio;
384 gpio_nr += CONFIG_S3C_GPIO_SPACE;
385 }
386}
387