]> Pileus Git - ~andy/linux/blob - arch/arm/plat-samsung/pm.c
Merge tag 'for-linus-v3.10-rc4' of git://oss.sgi.com/xfs/xfs
[~andy/linux] / arch / arm / plat-samsung / pm.c
1 /* linux/arch/arm/plat-s3c/pm.c
2  *
3  * Copyright 2008 Openmoko, Inc.
4  * Copyright 2004-2008 Simtec Electronics
5  *      Ben Dooks <ben@simtec.co.uk>
6  *      http://armlinux.simtec.co.uk/
7  *
8  * S3C common power management (suspend to ram) support.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
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/serial_core.h>
20 #include <linux/io.h>
21
22 #include <asm/cacheflush.h>
23 #include <asm/suspend.h>
24 #include <mach/hardware.h>
25 #include <mach/map.h>
26
27 #include <plat/regs-serial.h>
28 #include <mach/regs-clock.h>
29 #include <mach/regs-irq.h>
30 #include <mach/irqs.h>
31 #include <asm/irq.h>
32
33 #include <plat/pm.h>
34 #include <mach/pm-core.h>
35
36 /* for external use */
37
38 unsigned long s3c_pm_flags;
39
40 /* Debug code:
41  *
42  * This code supports debug output to the low level UARTs for use on
43  * resume before the console layer is available.
44 */
45
46 #ifdef CONFIG_SAMSUNG_PM_DEBUG
47 extern void printascii(const char *);
48
49 void s3c_pm_dbg(const char *fmt, ...)
50 {
51         va_list va;
52         char buff[256];
53
54         va_start(va, fmt);
55         vsnprintf(buff, sizeof(buff), fmt, va);
56         va_end(va);
57
58         printascii(buff);
59 }
60
61 static inline void s3c_pm_debug_init(void)
62 {
63         /* restart uart clocks so we can use them to output */
64         s3c_pm_debug_init_uart();
65 }
66
67 #else
68 #define s3c_pm_debug_init() do { } while(0)
69
70 #endif /* CONFIG_SAMSUNG_PM_DEBUG */
71
72 /* Save the UART configurations if we are configured for debug. */
73
74 unsigned char pm_uart_udivslot;
75
76 #ifdef CONFIG_SAMSUNG_PM_DEBUG
77
78 static struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS];
79
80 static void s3c_pm_save_uart(unsigned int uart, struct pm_uart_save *save)
81 {
82         void __iomem *regs = S3C_VA_UARTx(uart);
83
84         save->ulcon = __raw_readl(regs + S3C2410_ULCON);
85         save->ucon = __raw_readl(regs + S3C2410_UCON);
86         save->ufcon = __raw_readl(regs + S3C2410_UFCON);
87         save->umcon = __raw_readl(regs + S3C2410_UMCON);
88         save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV);
89
90         if (pm_uart_udivslot)
91                 save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT);
92
93         S3C_PMDBG("UART[%d]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n",
94                   uart, save->ulcon, save->ucon, save->ufcon, save->ubrdiv);
95 }
96
97 static void s3c_pm_save_uarts(void)
98 {
99         struct pm_uart_save *save = uart_save;
100         unsigned int uart;
101
102         for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++)
103                 s3c_pm_save_uart(uart, save);
104 }
105
106 static void s3c_pm_restore_uart(unsigned int uart, struct pm_uart_save *save)
107 {
108         void __iomem *regs = S3C_VA_UARTx(uart);
109
110         s3c_pm_arch_update_uart(regs, save);
111
112         __raw_writel(save->ulcon, regs + S3C2410_ULCON);
113         __raw_writel(save->ucon,  regs + S3C2410_UCON);
114         __raw_writel(save->ufcon, regs + S3C2410_UFCON);
115         __raw_writel(save->umcon, regs + S3C2410_UMCON);
116         __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV);
117
118         if (pm_uart_udivslot)
119                 __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT);
120 }
121
122 static void s3c_pm_restore_uarts(void)
123 {
124         struct pm_uart_save *save = uart_save;
125         unsigned int uart;
126
127         for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++)
128                 s3c_pm_restore_uart(uart, save);
129 }
130 #else
131 static void s3c_pm_save_uarts(void) { }
132 static void s3c_pm_restore_uarts(void) { }
133 #endif
134
135 /* The IRQ ext-int code goes here, it is too small to currently bother
136  * with its own file. */
137
138 unsigned long s3c_irqwake_intmask       = 0xffffffffL;
139 unsigned long s3c_irqwake_eintmask      = 0xffffffffL;
140
141 int s3c_irqext_wake(struct irq_data *data, unsigned int state)
142 {
143         unsigned long bit = 1L << IRQ_EINT_BIT(data->irq);
144
145         if (!(s3c_irqwake_eintallow & bit))
146                 return -ENOENT;
147
148         printk(KERN_INFO "wake %s for irq %d\n",
149                state ? "enabled" : "disabled", data->irq);
150
151         if (!state)
152                 s3c_irqwake_eintmask |= bit;
153         else
154                 s3c_irqwake_eintmask &= ~bit;
155
156         return 0;
157 }
158
159 /* helper functions to save and restore register state */
160
161 /**
162  * s3c_pm_do_save() - save a set of registers for restoration on resume.
163  * @ptr: Pointer to an array of registers.
164  * @count: Size of the ptr array.
165  *
166  * Run through the list of registers given, saving their contents in the
167  * array for later restoration when we wakeup.
168  */
169 void s3c_pm_do_save(struct sleep_save *ptr, int count)
170 {
171         for (; count > 0; count--, ptr++) {
172                 ptr->val = __raw_readl(ptr->reg);
173                 S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
174         }
175 }
176
177 /**
178  * s3c_pm_do_restore() - restore register values from the save list.
179  * @ptr: Pointer to an array of registers.
180  * @count: Size of the ptr array.
181  *
182  * Restore the register values saved from s3c_pm_do_save().
183  *
184  * Note, we do not use S3C_PMDBG() in here, as the system may not have
185  * restore the UARTs state yet
186 */
187
188 void s3c_pm_do_restore(struct sleep_save *ptr, int count)
189 {
190         for (; count > 0; count--, ptr++) {
191                 printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n",
192                        ptr->reg, ptr->val, __raw_readl(ptr->reg));
193
194                 __raw_writel(ptr->val, ptr->reg);
195         }
196 }
197
198 /**
199  * s3c_pm_do_restore_core() - early restore register values from save list.
200  *
201  * This is similar to s3c_pm_do_restore() except we try and minimise the
202  * side effects of the function in case registers that hardware might need
203  * to work has been restored.
204  *
205  * WARNING: Do not put any debug in here that may effect memory or use
206  * peripherals, as things may be changing!
207 */
208
209 void s3c_pm_do_restore_core(struct sleep_save *ptr, int count)
210 {
211         for (; count > 0; count--, ptr++)
212                 __raw_writel(ptr->val, ptr->reg);
213 }
214
215 /* s3c2410_pm_show_resume_irqs
216  *
217  * print any IRQs asserted at resume time (ie, we woke from)
218 */
219 static void __maybe_unused s3c_pm_show_resume_irqs(int start,
220                                                    unsigned long which,
221                                                    unsigned long mask)
222 {
223         int i;
224
225         which &= ~mask;
226
227         for (i = 0; i <= 31; i++) {
228                 if (which & (1L<<i)) {
229                         S3C_PMDBG("IRQ %d asserted at resume\n", start+i);
230                 }
231         }
232 }
233
234
235 void (*pm_cpu_prep)(void);
236 int (*pm_cpu_sleep)(unsigned long);
237
238 #define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
239
240 /* s3c_pm_enter
241  *
242  * central control for sleep/resume process
243 */
244
245 static int s3c_pm_enter(suspend_state_t state)
246 {
247         int ret;
248         /* ensure the debug is initialised (if enabled) */
249
250         s3c_pm_debug_init();
251
252         S3C_PMDBG("%s(%d)\n", __func__, state);
253
254         if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
255                 printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__);
256                 return -EINVAL;
257         }
258
259         /* check if we have anything to wake-up with... bad things seem
260          * to happen if you suspend with no wakeup (system will often
261          * require a full power-cycle)
262         */
263
264         if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
265             !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
266                 printk(KERN_ERR "%s: No wake-up sources!\n", __func__);
267                 printk(KERN_ERR "%s: Aborting sleep\n", __func__);
268                 return -EINVAL;
269         }
270
271         /* save all necessary core registers not covered by the drivers */
272
273         samsung_pm_save_gpios();
274         samsung_pm_saved_gpios();
275         s3c_pm_save_uarts();
276         s3c_pm_save_core();
277
278         /* set the irq configuration for wake */
279
280         s3c_pm_configure_extint();
281
282         S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n",
283             s3c_irqwake_intmask, s3c_irqwake_eintmask);
284
285         s3c_pm_arch_prepare_irqs();
286
287         /* call cpu specific preparation */
288
289         pm_cpu_prep();
290
291         /* flush cache back to ram */
292
293         flush_cache_all();
294
295         s3c_pm_check_store();
296
297         /* send the cpu to sleep... */
298
299         s3c_pm_arch_stop_clocks();
300
301         /* this will also act as our return point from when
302          * we resume as it saves its own register state and restores it
303          * during the resume.  */
304
305         ret = cpu_suspend(0, pm_cpu_sleep);
306         if (ret)
307                 return ret;
308
309         /* restore the system state */
310
311         s3c_pm_restore_core();
312         s3c_pm_restore_uarts();
313         samsung_pm_restore_gpios();
314         s3c_pm_restored_gpios();
315
316         s3c_pm_debug_init();
317
318         /* check what irq (if any) restored the system */
319
320         s3c_pm_arch_show_resume_irqs();
321
322         S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);
323
324         /* LEDs should now be 1110 */
325         s3c_pm_debug_smdkled(1 << 1, 0);
326
327         s3c_pm_check_restore();
328
329         /* ok, let's return from sleep */
330
331         S3C_PMDBG("S3C PM Resume (post-restore)\n");
332         return 0;
333 }
334
335 static int s3c_pm_prepare(void)
336 {
337         /* prepare check area if configured */
338
339         s3c_pm_check_prepare();
340         return 0;
341 }
342
343 static void s3c_pm_finish(void)
344 {
345         s3c_pm_check_cleanup();
346 }
347
348 static const struct platform_suspend_ops s3c_pm_ops = {
349         .enter          = s3c_pm_enter,
350         .prepare        = s3c_pm_prepare,
351         .finish         = s3c_pm_finish,
352         .valid          = suspend_valid_only_mem,
353 };
354
355 /* s3c_pm_init
356  *
357  * Attach the power management functions. This should be called
358  * from the board specific initialisation if the board supports
359  * it.
360 */
361
362 int __init s3c_pm_init(void)
363 {
364         printk("S3C Power Management, Copyright 2004 Simtec Electronics\n");
365
366         suspend_set_ops(&s3c_pm_ops);
367         return 0;
368 }