• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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 /* for external use */
38 
39 unsigned long s3c_pm_flags;
40 
41 /* Debug code:
42  *
43  * This code supports debug output to the low level UARTs for use on
44  * resume before the console layer is available.
45 */
46 
47 #ifdef CONFIG_SAMSUNG_PM_DEBUG
48 extern void printascii(const char *);
49 
s3c_pm_dbg(const char * fmt,...)50 void 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 
s3c_pm_debug_init(void)62 static inline void s3c_pm_debug_init(void)
63 {
64 	/* restart uart clocks so we can use them to output */
65 	s3c_pm_debug_init_uart();
66 }
67 
68 #else
69 #define s3c_pm_debug_init() do { } while(0)
70 
71 #endif /* CONFIG_SAMSUNG_PM_DEBUG */
72 
73 /* Save the UART configurations if we are configured for debug. */
74 
75 unsigned char pm_uart_udivslot;
76 
77 #ifdef CONFIG_SAMSUNG_PM_DEBUG
78 
79 static struct pm_uart_save uart_save[CONFIG_SERIAL_SAMSUNG_UARTS];
80 
s3c_pm_save_uart(unsigned int uart,struct pm_uart_save * save)81 static 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 
s3c_pm_save_uarts(void)98 static 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 
s3c_pm_restore_uart(unsigned int uart,struct pm_uart_save * save)107 static 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 
s3c_pm_restore_uarts(void)123 static 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
s3c_pm_save_uarts(void)132 static void s3c_pm_save_uarts(void) { }
s3c_pm_restore_uarts(void)133 static void s3c_pm_restore_uarts(void) { }
134 #endif
135 
136 /* The IRQ ext-int code goes here, it is too small to currently bother
137  * with its own file. */
138 
139 unsigned long s3c_irqwake_intmask	= 0xffffffffL;
140 unsigned long s3c_irqwake_eintmask	= 0xffffffffL;
141 
s3c_irqext_wake(struct irq_data * data,unsigned int state)142 int 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 /* helper functions to save and restore register state */
161 
162 /**
163  * s3c_pm_do_save() - save a set of registers for restoration on resume.
164  * @ptr: Pointer to an array of registers.
165  * @count: Size of the ptr array.
166  *
167  * Run through the list of registers given, saving their contents in the
168  * array for later restoration when we wakeup.
169  */
s3c_pm_do_save(struct sleep_save * ptr,int count)170 void 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  * s3c_pm_do_restore() - restore register values from the save list.
180  * @ptr: Pointer to an array of registers.
181  * @count: Size of the ptr array.
182  *
183  * Restore the register values saved from s3c_pm_do_save().
184  *
185  * Note, we do not use S3C_PMDBG() in here, as the system may not have
186  * restore the UARTs state yet
187 */
188 
s3c_pm_do_restore(struct sleep_save * ptr,int count)189 void 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  * s3c_pm_do_restore_core() - early restore register values from save list.
201  *
202  * This is similar to s3c_pm_do_restore() except we try and minimise the
203  * side effects of the function in case registers that hardware might need
204  * to work has been restored.
205  *
206  * WARNING: Do not put any debug in here that may effect memory or use
207  * peripherals, as things may be changing!
208 */
209 
s3c_pm_do_restore_core(struct sleep_save * ptr,int count)210 void 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 /* s3c2410_pm_show_resume_irqs
217  *
218  * print any IRQs asserted at resume time (ie, we woke from)
219 */
s3c_pm_show_resume_irqs(int start,unsigned long which,unsigned long mask)220 static 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 
236 void (*pm_cpu_prep)(void);
237 int (*pm_cpu_sleep)(unsigned long);
238 
239 #define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
240 
241 /* s3c_pm_enter
242  *
243  * central control for sleep/resume process
244 */
245 
s3c_pm_enter(suspend_state_t state)246 static int s3c_pm_enter(suspend_state_t state)
247 {
248 	int ret;
249 	/* ensure the debug is initialised (if enabled) */
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 	/* check if we have anything to wake-up with... bad things seem
261 	 * to happen if you suspend with no wakeup (system will often
262 	 * require a full power-cycle)
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 	/* save all necessary core registers not covered by the drivers */
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 	/* set the irq configuration for wake */
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 	/* call cpu specific preparation */
293 
294 	pm_cpu_prep();
295 
296 	/* flush cache back to ram */
297 
298 	flush_cache_all();
299 
300 	s3c_pm_check_store();
301 
302 	/* send the cpu to sleep... */
303 
304 	s3c_pm_arch_stop_clocks();
305 
306 	/* this will also act as our return point from when
307 	 * we resume as it saves its own register state and restores it
308 	 * during the resume.  */
309 
310 	ret = cpu_suspend(0, pm_cpu_sleep);
311 	if (ret)
312 		return ret;
313 
314 	/* restore the system state */
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 	/* check what irq (if any) restored the system */
327 
328 	s3c_pm_arch_show_resume_irqs();
329 
330 	S3C_PMDBG("%s: post sleep, preparing to return\n", __func__);
331 
332 	/* LEDs should now be 1110 */
333 	s3c_pm_debug_smdkled(1 << 1, 0);
334 
335 	s3c_pm_check_restore();
336 
337 	/* ok, let's return from sleep */
338 
339 	S3C_PMDBG("S3C PM Resume (post-restore)\n");
340 	return 0;
341 }
342 
s3c_pm_prepare(void)343 static int s3c_pm_prepare(void)
344 {
345 	/* prepare check area if configured */
346 
347 	s3c_pm_check_prepare();
348 	return 0;
349 }
350 
s3c_pm_finish(void)351 static void s3c_pm_finish(void)
352 {
353 	s3c_pm_check_cleanup();
354 }
355 
356 static 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 /* s3c_pm_init
364  *
365  * Attach the power management functions. This should be called
366  * from the board specific initialisation if the board supports
367  * it.
368 */
369 
s3c_pm_init(void)370 int __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