1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/init.h>
16#include <linux/suspend.h>
17#include <linux/errno.h>
18#include <linux/delay.h>
19#include <linux/of.h>
20#include <linux/serial_core.h>
21#include <linux/io.h>
22
23#include <asm/cacheflush.h>
24#include <asm/suspend.h>
25#include <mach/hardware.h>
26#include <mach/map.h>
27
28#include <plat/regs-serial.h>
29#include <mach/regs-clock.h>
30#include <mach/regs-irq.h>
31#include <mach/irqs.h>
32#include <asm/irq.h>
33
34#include <plat/pm.h>
35#include <mach/pm-core.h>
36
37
38
39unsigned long s3c_pm_flags;
40
41
42
43
44
45
46
47#ifdef CONFIG_SAMSUNG_PM_DEBUG
48extern void printascii(const char *);
49
50void s3c_pm_dbg(const char *fmt, ...)
51{
52 va_list va;
53 char buff[256];
54
55 va_start(va, fmt);
56 vsnprintf(buff, sizeof(buff), fmt, va);
57 va_end(va);
58
59 printascii(buff);
60}
61
62static inline void s3c_pm_debug_init(void)
63{
64
65 s3c_pm_debug_init_uart();
66}
67
68#else
69#define s3c_pm_debug_init() do { } while(0)
70
71#endif
72
73
74
75unsigned char pm_uart_udivslot;
76
77#ifdef CONFIG_SAMSUNG_PM_DEBUG
78
79static struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS];
80
81static void s3c_pm_save_uart(unsigned int uart, struct pm_uart_save *save)
82{
83 void __iomem *regs = S3C_VA_UARTx(uart);
84
85 save->ulcon = __raw_readl(regs + S3C2410_ULCON);
86 save->ucon = __raw_readl(regs + S3C2410_UCON);
87 save->ufcon = __raw_readl(regs + S3C2410_UFCON);
88 save->umcon = __raw_readl(regs + S3C2410_UMCON);
89 save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV);
90
91 if (pm_uart_udivslot)
92 save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT);
93
94 S3C_PMDBG("UART[%d]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n",
95 uart, save->ulcon, save->ucon, save->ufcon, save->ubrdiv);
96}
97
98static void s3c_pm_save_uarts(void)
99{
100 struct pm_uart_save *save = uart_save;
101 unsigned int uart;
102
103 for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++)
104 s3c_pm_save_uart(uart, save);
105}
106
107static void s3c_pm_restore_uart(unsigned int uart, struct pm_uart_save *save)
108{
109 void __iomem *regs = S3C_VA_UARTx(uart);
110
111 s3c_pm_arch_update_uart(regs, save);
112
113 __raw_writel(save->ulcon, regs + S3C2410_ULCON);
114 __raw_writel(save->ucon, regs + S3C2410_UCON);
115 __raw_writel(save->ufcon, regs + S3C2410_UFCON);
116 __raw_writel(save->umcon, regs + S3C2410_UMCON);
117 __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV);
118
119 if (pm_uart_udivslot)
120 __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT);
121}
122
123static void s3c_pm_restore_uarts(void)
124{
125 struct pm_uart_save *save = uart_save;
126 unsigned int uart;
127
128 for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++)
129 s3c_pm_restore_uart(uart, save);
130}
131#else
132static void s3c_pm_save_uarts(void) { }
133static void s3c_pm_restore_uarts(void) { }
134#endif
135
136
137
138
139unsigned long s3c_irqwake_intmask = 0xffffffffL;
140unsigned long s3c_irqwake_eintmask = 0xffffffffL;
141
142int s3c_irqext_wake(struct irq_data *data, unsigned int state)
143{
144 unsigned long bit = 1L << IRQ_EINT_BIT(data->irq);
145
146 if (!(s3c_irqwake_eintallow & bit))
147 return -ENOENT;
148
149 printk(KERN_INFO "wake %s for irq %d\n",
150 state ? "enabled" : "disabled", data->irq);
151
152 if (!state)
153 s3c_irqwake_eintmask |= bit;
154 else
155 s3c_irqwake_eintmask &= ~bit;
156
157 return 0;
158}
159
160
161
162
163
164
165
166
167
168
169
170void s3c_pm_do_save(struct sleep_save *ptr, int count)
171{
172 for (; count > 0; count--, ptr++) {
173 ptr->val = __raw_readl(ptr->reg);
174 S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
175 }
176}
177
178
179
180
181
182
183
184
185
186
187
188
189void s3c_pm_do_restore(struct sleep_save *ptr, int count)
190{
191 for (; count > 0; count--, ptr++) {
192 printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n",
193 ptr->reg, ptr->val, __raw_readl(ptr->reg));
194
195 __raw_writel(ptr->val, ptr->reg);
196 }
197}
198
199
200
201
202
203
204
205
206
207
208
209
210void s3c_pm_do_restore_core(struct sleep_save *ptr, int count)
211{
212 for (; count > 0; count--, ptr++)
213 __raw_writel(ptr->val, ptr->reg);
214}
215
216
217
218
219
220static void __maybe_unused s3c_pm_show_resume_irqs(int start,
221 unsigned long which,
222 unsigned long mask)
223{
224 int i;
225
226 which &= ~mask;
227
228 for (i = 0; i <= 31; i++) {
229 if (which & (1L<<i)) {
230 S3C_PMDBG("IRQ %d asserted at resume\n", start+i);
231 }
232 }
233}
234
235
236void (*pm_cpu_prep)(void);
237int (*pm_cpu_sleep)(unsigned long);
238
239#define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
240
241
242
243
244
245
246static int s3c_pm_enter(suspend_state_t state)
247{
248 int ret;
249
250
251 s3c_pm_debug_init();
252
253 S3C_PMDBG("%s(%d)\n", __func__, state);
254
255 if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
256 printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__);
257 return -EINVAL;
258 }
259
260
261
262
263
264
265 if (!of_have_populated_dt() &&
266 !any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
267 !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
268 printk(KERN_ERR "%s: No wake-up sources!\n", __func__);
269 printk(KERN_ERR "%s: Aborting sleep\n", __func__);
270 return -EINVAL;
271 }
272
273
274
275 if (!of_have_populated_dt()) {
276 samsung_pm_save_gpios();
277 samsung_pm_saved_gpios();
278 }
279
280 s3c_pm_save_uarts();
281 s3c_pm_save_core();
282
283
284
285 s3c_pm_configure_extint();
286
287 S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n",
288 s3c_irqwake_intmask, s3c_irqwake_eintmask);
289
290 s3c_pm_arch_prepare_irqs();
291
292
293
294 pm_cpu_prep();
295
296
297
298 flush_cache_all();
299
300 s3c_pm_check_store();
301
302
303
304 s3c_pm_arch_stop_clocks();
305
306
307
308
309
310 ret = cpu_suspend(0, pm_cpu_sleep);
311 if (ret)
312 return ret;
313
314
315
316 s3c_pm_restore_core();
317 s3c_pm_restore_uarts();
318
319 if (!of_have_populated_dt()) {
320 samsung_pm_restore_gpios();
321 s3c_pm_restored_gpios();
322 }
323
324 s3c_pm_debug_init();
325
326
327
328 s3c_pm_arch_show_resume_irqs();
329
330 S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);
331
332
333 s3c_pm_debug_smdkled(1 << 1, 0);
334
335 s3c_pm_check_restore();
336
337
338
339 S3C_PMDBG("S3C PM Resume (post-restore)\n");
340 return 0;
341}
342
343static int s3c_pm_prepare(void)
344{
345
346
347 s3c_pm_check_prepare();
348 return 0;
349}
350
351static void s3c_pm_finish(void)
352{
353 s3c_pm_check_cleanup();
354}
355
356static const struct platform_suspend_ops s3c_pm_ops = {
357 .enter = s3c_pm_enter,
358 .prepare = s3c_pm_prepare,
359 .finish = s3c_pm_finish,
360 .valid = suspend_valid_only_mem,
361};
362
363
364
365
366
367
368
369
370int __init s3c_pm_init(void)
371{
372 printk("S3C Power Management, Copyright 2004 Simtec Electronics\n");
373
374 suspend_set_ops(&s3c_pm_ops);
375 return 0;
376}
377