Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pm.c
Go to the documentation of this file.
1 /* linux/arch/arm/plat-s3c/pm.c
2  *
3  * Copyright 2008 Openmoko, Inc.
4  * Copyright 2004-2008 Simtec Electronics
5  * Ben Dooks <[email protected]>
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 <asm/irq.h>
31 
32 #include <plat/pm.h>
33 #include <mach/pm-core.h>
34 
35 /* for external use */
36 
37 unsigned long s3c_pm_flags;
38 
39 /* Debug code:
40  *
41  * This code supports debug output to the low level UARTs for use on
42  * resume before the console layer is available.
43 */
44 
45 #ifdef CONFIG_SAMSUNG_PM_DEBUG
46 extern void printascii(const char *);
47 
48 void s3c_pm_dbg(const char *fmt, ...)
49 {
50  va_list va;
51  char buff[256];
52 
53  va_start(va, fmt);
54  vsprintf(buff, fmt, va);
55  va_end(va);
56 
57  printascii(buff);
58 }
59 
60 static inline void s3c_pm_debug_init(void)
61 {
62  /* restart uart clocks so we can use them to output */
63  s3c_pm_debug_init_uart();
64 }
65 
66 #else
67 #define s3c_pm_debug_init() do { } while(0)
68 
69 #endif /* CONFIG_SAMSUNG_PM_DEBUG */
70 
71 /* Save the UART configurations if we are configured for debug. */
72 
73 unsigned char pm_uart_udivslot;
74 
75 #ifdef CONFIG_SAMSUNG_PM_DEBUG
76 
77 static struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS];
78 
79 static void s3c_pm_save_uart(unsigned int uart, struct pm_uart_save *save)
80 {
81  void __iomem *regs = S3C_VA_UARTx(uart);
82 
83  save->ulcon = __raw_readl(regs + S3C2410_ULCON);
84  save->ucon = __raw_readl(regs + S3C2410_UCON);
85  save->ufcon = __raw_readl(regs + S3C2410_UFCON);
86  save->umcon = __raw_readl(regs + S3C2410_UMCON);
87  save->ubrdiv = __raw_readl(regs + S3C2410_UBRDIV);
88 
89  if (pm_uart_udivslot)
90  save->udivslot = __raw_readl(regs + S3C2443_DIVSLOT);
91 
92  S3C_PMDBG("UART[%d]: ULCON=%04x, UCON=%04x, UFCON=%04x, UBRDIV=%04x\n",
93  uart, save->ulcon, save->ucon, save->ufcon, save->ubrdiv);
94 }
95 
96 static void s3c_pm_save_uarts(void)
97 {
98  struct pm_uart_save *save = uart_save;
99  unsigned int uart;
100 
101  for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++)
102  s3c_pm_save_uart(uart, save);
103 }
104 
105 static void s3c_pm_restore_uart(unsigned int uart, struct pm_uart_save *save)
106 {
107  void __iomem *regs = S3C_VA_UARTx(uart);
108 
109  s3c_pm_arch_update_uart(regs, save);
110 
111  __raw_writel(save->ulcon, regs + S3C2410_ULCON);
112  __raw_writel(save->ucon, regs + S3C2410_UCON);
113  __raw_writel(save->ufcon, regs + S3C2410_UFCON);
114  __raw_writel(save->umcon, regs + S3C2410_UMCON);
115  __raw_writel(save->ubrdiv, regs + S3C2410_UBRDIV);
116 
117  if (pm_uart_udivslot)
118  __raw_writel(save->udivslot, regs + S3C2443_DIVSLOT);
119 }
120 
121 static void s3c_pm_restore_uarts(void)
122 {
123  struct pm_uart_save *save = uart_save;
124  unsigned int uart;
125 
126  for (uart = 0; uart < CONFIG_SERIAL_SAMSUNG_UARTS; uart++, save++)
127  s3c_pm_restore_uart(uart, save);
128 }
129 #else
130 static void s3c_pm_save_uarts(void) { }
131 static void s3c_pm_restore_uarts(void) { }
132 #endif
133 
134 /* The IRQ ext-int code goes here, it is too small to currently bother
135  * with its own file. */
136 
137 unsigned long s3c_irqwake_intmask = 0xffffffffL;
138 unsigned long s3c_irqwake_eintmask = 0xffffffffL;
139 
140 int s3c_irqext_wake(struct irq_data *data, unsigned int state)
141 {
142  unsigned long bit = 1L << IRQ_EINT_BIT(data->irq);
143 
144  if (!(s3c_irqwake_eintallow & bit))
145  return -ENOENT;
146 
147  printk(KERN_INFO "wake %s for irq %d\n",
148  state ? "enabled" : "disabled", data->irq);
149 
150  if (!state)
151  s3c_irqwake_eintmask |= bit;
152  else
153  s3c_irqwake_eintmask &= ~bit;
154 
155  return 0;
156 }
157 
158 /* helper functions to save and restore register state */
159 
168 void s3c_pm_do_save(struct sleep_save *ptr, int count)
169 {
170  for (; count > 0; count--, ptr++) {
171  ptr->val = __raw_readl(ptr->reg);
172  S3C_PMDBG("saved %p value %08lx\n", ptr->reg, ptr->val);
173  }
174 }
175 
188 {
189  for (; count > 0; count--, ptr++) {
190  printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n",
191  ptr->reg, ptr->val, __raw_readl(ptr->reg));
192 
193  __raw_writel(ptr->val, ptr->reg);
194  }
195 }
196 
209 {
210  for (; count > 0; count--, ptr++)
211  __raw_writel(ptr->val, ptr->reg);
212 }
213 
214 /* s3c2410_pm_show_resume_irqs
215  *
216  * print any IRQs asserted at resume time (ie, we woke from)
217 */
218 static void __maybe_unused s3c_pm_show_resume_irqs(int start,
219  unsigned long which,
220  unsigned long mask)
221 {
222  int i;
223 
224  which &= ~mask;
225 
226  for (i = 0; i <= 31; i++) {
227  if (which & (1L<<i)) {
228  S3C_PMDBG("IRQ %d asserted at resume\n", start+i);
229  }
230  }
231 }
232 
233 
235 int (*pm_cpu_sleep)(unsigned long);
236 
237 #define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
238 
239 /* s3c_pm_enter
240  *
241  * central control for sleep/resume process
242 */
243 
244 static int s3c_pm_enter(suspend_state_t state)
245 {
246  /* ensure the debug is initialised (if enabled) */
247 
249 
250  S3C_PMDBG("%s(%d)\n", __func__, state);
251 
252  if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
253  printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__);
254  return -EINVAL;
255  }
256 
257  /* check if we have anything to wake-up with... bad things seem
258  * to happen if you suspend with no wakeup (system will often
259  * require a full power-cycle)
260  */
261 
262  if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
263  !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
264  printk(KERN_ERR "%s: No wake-up sources!\n", __func__);
265  printk(KERN_ERR "%s: Aborting sleep\n", __func__);
266  return -EINVAL;
267  }
268 
269  /* save all necessary core registers not covered by the drivers */
270 
272  samsung_pm_saved_gpios();
273  s3c_pm_save_uarts();
275 
276  /* set the irq configuration for wake */
277 
279 
280  S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n",
281  s3c_irqwake_intmask, s3c_irqwake_eintmask);
282 
283  s3c_pm_arch_prepare_irqs();
284 
285  /* call cpu specific preparation */
286 
287  pm_cpu_prep();
288 
289  /* flush cache back to ram */
290 
291  flush_cache_all();
292 
294 
295  /* send the cpu to sleep... */
296 
297  s3c_pm_arch_stop_clocks();
298 
299  /* this will also act as our return point from when
300  * we resume as it saves its own register state and restores it
301  * during the resume. */
302 
304 
305  /* restore the system state */
306 
308  s3c_pm_restore_uarts();
310  s3c_pm_restored_gpios();
311 
313 
314  /* check what irq (if any) restored the system */
315 
316  s3c_pm_arch_show_resume_irqs();
317 
318  S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);
319 
320  /* LEDs should now be 1110 */
321  s3c_pm_debug_smdkled(1 << 1, 0);
322 
324 
325  /* ok, let's return from sleep */
326 
327  S3C_PMDBG("S3C PM Resume (post-restore)\n");
328  return 0;
329 }
330 
331 static int s3c_pm_prepare(void)
332 {
333  /* prepare check area if configured */
334 
336  return 0;
337 }
338 
339 static void s3c_pm_finish(void)
340 {
342 }
343 
344 static const struct platform_suspend_ops s3c_pm_ops = {
345  .enter = s3c_pm_enter,
346  .prepare = s3c_pm_prepare,
347  .finish = s3c_pm_finish,
348  .valid = suspend_valid_only_mem,
349 };
350 
351 /* s3c_pm_init
352  *
353  * Attach the power management functions. This should be called
354  * from the board specific initialisation if the board supports
355  * it.
356 */
357 
359 {
360  printk("S3C Power Management, Copyright 2004 Simtec Electronics\n");
361 
362  suspend_set_ops(&s3c_pm_ops);
363  return 0;
364 }