Commit | Line | Data |
---|---|---|
670c104a TL |
1 | /* |
2 | * linux/arch/arm/mach-omap2/pm.c | |
3 | * | |
4 | * OMAP2 Power Management Routines | |
5 | * | |
6 | * Copyright (C) 2006 Nokia Corporation | |
7 | * Tony Lindgren <tony@atomide.com> | |
8 | * | |
9 | * Copyright (C) 2005 Texas Instruments, Inc. | |
10 | * Richard Woodruff <r-woodruff2@ti.com> | |
11 | * | |
12 | * Based on pm.c for omap1 | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License version 2 as | |
16 | * published by the Free Software Foundation. | |
17 | */ | |
18 | ||
19 | #include <linux/pm.h> | |
20 | #include <linux/sched.h> | |
21 | #include <linux/proc_fs.h> | |
22 | #include <linux/pm.h> | |
23 | #include <linux/interrupt.h> | |
24 | #include <linux/sysfs.h> | |
25 | #include <linux/module.h> | |
22a16f39 | 26 | #include <linux/delay.h> |
670c104a TL |
27 | |
28 | #include <asm/io.h> | |
29 | #include <asm/irq.h> | |
30 | #include <asm/atomic.h> | |
31 | #include <asm/mach/time.h> | |
32 | #include <asm/mach/irq.h> | |
33 | #include <asm/mach-types.h> | |
34 | ||
35 | #include <asm/arch/irqs.h> | |
36 | #include <asm/arch/clock.h> | |
37 | #include <asm/arch/sram.h> | |
38 | #include <asm/arch/pm.h> | |
39 | ||
22a16f39 TL |
40 | #include "prcm-regs.h" |
41 | ||
670c104a TL |
42 | static struct clk *vclk; |
43 | static void (*omap2_sram_idle)(void); | |
44 | static void (*omap2_sram_suspend)(int dllctrl, int cpu_rev); | |
45 | static void (*saved_idle)(void); | |
46 | ||
22a16f39 TL |
47 | extern void __init pmdomain_init(void); |
48 | extern void pmdomain_set_autoidle(void); | |
49 | ||
50 | static unsigned int omap24xx_sleep_save[OMAP24XX_SLEEP_SAVE_SIZE]; | |
51 | ||
670c104a TL |
52 | void omap2_pm_idle(void) |
53 | { | |
54 | local_irq_disable(); | |
55 | local_fiq_disable(); | |
56 | if (need_resched()) { | |
57 | local_fiq_enable(); | |
58 | local_irq_enable(); | |
59 | return; | |
60 | } | |
61 | ||
62 | /* | |
63 | * Since an interrupt may set up a timer, we don't want to | |
64 | * reprogram the hardware timer with interrupts enabled. | |
65 | * Re-enable interrupts only after returning from idle. | |
66 | */ | |
67 | timer_dyn_reprogram(); | |
68 | ||
69 | omap2_sram_idle(); | |
70 | local_fiq_enable(); | |
71 | local_irq_enable(); | |
72 | } | |
73 | ||
74 | static int omap2_pm_prepare(suspend_state_t state) | |
75 | { | |
76 | int error = 0; | |
77 | ||
78 | /* We cannot sleep in idle until we have resumed */ | |
79 | saved_idle = pm_idle; | |
80 | pm_idle = NULL; | |
81 | ||
82 | switch (state) | |
83 | { | |
84 | case PM_SUSPEND_STANDBY: | |
85 | case PM_SUSPEND_MEM: | |
86 | break; | |
87 | ||
88 | case PM_SUSPEND_DISK: | |
89 | return -ENOTSUPP; | |
90 | ||
91 | default: | |
92 | return -EINVAL; | |
93 | } | |
94 | ||
95 | return error; | |
96 | } | |
97 | ||
22a16f39 TL |
98 | #define INT0_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_GPIO_BANK1) | \ |
99 | OMAP_IRQ_BIT(INT_24XX_GPIO_BANK2) | \ | |
100 | OMAP_IRQ_BIT(INT_24XX_GPIO_BANK3)) | |
101 | ||
102 | #define INT1_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_GPIO_BANK4)) | |
103 | ||
104 | #define INT2_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_UART1_IRQ) | \ | |
105 | OMAP_IRQ_BIT(INT_24XX_UART2_IRQ) | \ | |
106 | OMAP_IRQ_BIT(INT_24XX_UART3_IRQ)) | |
107 | ||
108 | #define preg(reg) printk("%s\t(0x%p):\t0x%08x\n", #reg, ®, reg); | |
109 | ||
110 | static void omap2_pm_debug(char * desc) | |
111 | { | |
112 | printk("%s:\n", desc); | |
113 | ||
114 | preg(CM_CLKSTCTRL_MPU); | |
115 | preg(CM_CLKSTCTRL_CORE); | |
116 | preg(CM_CLKSTCTRL_GFX); | |
117 | preg(CM_CLKSTCTRL_DSP); | |
118 | preg(CM_CLKSTCTRL_MDM); | |
119 | ||
120 | preg(PM_PWSTCTRL_MPU); | |
121 | preg(PM_PWSTCTRL_CORE); | |
122 | preg(PM_PWSTCTRL_GFX); | |
123 | preg(PM_PWSTCTRL_DSP); | |
124 | preg(PM_PWSTCTRL_MDM); | |
125 | ||
126 | preg(PM_PWSTST_MPU); | |
127 | preg(PM_PWSTST_CORE); | |
128 | preg(PM_PWSTST_GFX); | |
129 | preg(PM_PWSTST_DSP); | |
130 | preg(PM_PWSTST_MDM); | |
131 | ||
132 | preg(CM_AUTOIDLE1_CORE); | |
133 | preg(CM_AUTOIDLE2_CORE); | |
134 | preg(CM_AUTOIDLE3_CORE); | |
135 | preg(CM_AUTOIDLE4_CORE); | |
136 | preg(CM_AUTOIDLE_WKUP); | |
137 | preg(CM_AUTOIDLE_PLL); | |
138 | preg(CM_AUTOIDLE_DSP); | |
139 | preg(CM_AUTOIDLE_MDM); | |
140 | ||
141 | preg(CM_ICLKEN1_CORE); | |
142 | preg(CM_ICLKEN2_CORE); | |
143 | preg(CM_ICLKEN3_CORE); | |
144 | preg(CM_ICLKEN4_CORE); | |
145 | preg(CM_ICLKEN_GFX); | |
146 | preg(CM_ICLKEN_WKUP); | |
147 | preg(CM_ICLKEN_DSP); | |
148 | preg(CM_ICLKEN_MDM); | |
149 | ||
150 | preg(CM_IDLEST1_CORE); | |
151 | preg(CM_IDLEST2_CORE); | |
152 | preg(CM_IDLEST3_CORE); | |
153 | preg(CM_IDLEST4_CORE); | |
154 | preg(CM_IDLEST_GFX); | |
155 | preg(CM_IDLEST_WKUP); | |
156 | preg(CM_IDLEST_CKGEN); | |
157 | preg(CM_IDLEST_DSP); | |
158 | preg(CM_IDLEST_MDM); | |
159 | ||
160 | preg(RM_RSTST_MPU); | |
161 | preg(RM_RSTST_GFX); | |
162 | preg(RM_RSTST_WKUP); | |
163 | preg(RM_RSTST_DSP); | |
164 | preg(RM_RSTST_MDM); | |
165 | ||
166 | preg(PM_WKDEP_MPU); | |
167 | preg(PM_WKDEP_CORE); | |
168 | preg(PM_WKDEP_GFX); | |
169 | preg(PM_WKDEP_DSP); | |
170 | preg(PM_WKDEP_MDM); | |
171 | ||
172 | preg(CM_FCLKEN_WKUP); | |
173 | preg(CM_ICLKEN_WKUP); | |
174 | preg(CM_IDLEST_WKUP); | |
175 | preg(CM_AUTOIDLE_WKUP); | |
176 | preg(CM_CLKSEL_WKUP); | |
177 | ||
178 | preg(PM_WKEN_WKUP); | |
179 | preg(PM_WKST_WKUP); | |
180 | } | |
181 | ||
182 | static inline void omap2_pm_save_registers(void) | |
183 | { | |
184 | /* Save interrupt registers */ | |
185 | OMAP24XX_SAVE(INTC_MIR0); | |
186 | OMAP24XX_SAVE(INTC_MIR1); | |
187 | OMAP24XX_SAVE(INTC_MIR2); | |
188 | ||
189 | /* Save power control registers */ | |
190 | OMAP24XX_SAVE(CM_CLKSTCTRL_MPU); | |
191 | OMAP24XX_SAVE(CM_CLKSTCTRL_CORE); | |
192 | OMAP24XX_SAVE(CM_CLKSTCTRL_GFX); | |
193 | OMAP24XX_SAVE(CM_CLKSTCTRL_DSP); | |
194 | OMAP24XX_SAVE(CM_CLKSTCTRL_MDM); | |
195 | ||
196 | /* Save power state registers */ | |
197 | OMAP24XX_SAVE(PM_PWSTCTRL_MPU); | |
198 | OMAP24XX_SAVE(PM_PWSTCTRL_CORE); | |
199 | OMAP24XX_SAVE(PM_PWSTCTRL_GFX); | |
200 | OMAP24XX_SAVE(PM_PWSTCTRL_DSP); | |
201 | OMAP24XX_SAVE(PM_PWSTCTRL_MDM); | |
202 | ||
203 | /* Save autoidle registers */ | |
204 | OMAP24XX_SAVE(CM_AUTOIDLE1_CORE); | |
205 | OMAP24XX_SAVE(CM_AUTOIDLE2_CORE); | |
206 | OMAP24XX_SAVE(CM_AUTOIDLE3_CORE); | |
207 | OMAP24XX_SAVE(CM_AUTOIDLE4_CORE); | |
208 | OMAP24XX_SAVE(CM_AUTOIDLE_WKUP); | |
209 | OMAP24XX_SAVE(CM_AUTOIDLE_PLL); | |
210 | OMAP24XX_SAVE(CM_AUTOIDLE_DSP); | |
211 | OMAP24XX_SAVE(CM_AUTOIDLE_MDM); | |
212 | ||
213 | /* Save idle state registers */ | |
214 | OMAP24XX_SAVE(CM_IDLEST1_CORE); | |
215 | OMAP24XX_SAVE(CM_IDLEST2_CORE); | |
216 | OMAP24XX_SAVE(CM_IDLEST3_CORE); | |
217 | OMAP24XX_SAVE(CM_IDLEST4_CORE); | |
218 | OMAP24XX_SAVE(CM_IDLEST_GFX); | |
219 | OMAP24XX_SAVE(CM_IDLEST_WKUP); | |
220 | OMAP24XX_SAVE(CM_IDLEST_CKGEN); | |
221 | OMAP24XX_SAVE(CM_IDLEST_DSP); | |
222 | OMAP24XX_SAVE(CM_IDLEST_MDM); | |
223 | ||
224 | /* Save clock registers */ | |
225 | OMAP24XX_SAVE(CM_FCLKEN1_CORE); | |
226 | OMAP24XX_SAVE(CM_FCLKEN2_CORE); | |
227 | OMAP24XX_SAVE(CM_ICLKEN1_CORE); | |
228 | OMAP24XX_SAVE(CM_ICLKEN2_CORE); | |
229 | OMAP24XX_SAVE(CM_ICLKEN3_CORE); | |
230 | OMAP24XX_SAVE(CM_ICLKEN4_CORE); | |
231 | } | |
232 | ||
233 | static inline void omap2_pm_restore_registers(void) | |
234 | { | |
235 | /* Restore clock state registers */ | |
236 | OMAP24XX_RESTORE(CM_CLKSTCTRL_MPU); | |
237 | OMAP24XX_RESTORE(CM_CLKSTCTRL_CORE); | |
238 | OMAP24XX_RESTORE(CM_CLKSTCTRL_GFX); | |
239 | OMAP24XX_RESTORE(CM_CLKSTCTRL_DSP); | |
240 | OMAP24XX_RESTORE(CM_CLKSTCTRL_MDM); | |
241 | ||
242 | /* Restore power state registers */ | |
243 | OMAP24XX_RESTORE(PM_PWSTCTRL_MPU); | |
244 | OMAP24XX_RESTORE(PM_PWSTCTRL_CORE); | |
245 | OMAP24XX_RESTORE(PM_PWSTCTRL_GFX); | |
246 | OMAP24XX_RESTORE(PM_PWSTCTRL_DSP); | |
247 | OMAP24XX_RESTORE(PM_PWSTCTRL_MDM); | |
248 | ||
249 | /* Restore idle state registers */ | |
250 | OMAP24XX_RESTORE(CM_IDLEST1_CORE); | |
251 | OMAP24XX_RESTORE(CM_IDLEST2_CORE); | |
252 | OMAP24XX_RESTORE(CM_IDLEST3_CORE); | |
253 | OMAP24XX_RESTORE(CM_IDLEST4_CORE); | |
254 | OMAP24XX_RESTORE(CM_IDLEST_GFX); | |
255 | OMAP24XX_RESTORE(CM_IDLEST_WKUP); | |
256 | OMAP24XX_RESTORE(CM_IDLEST_CKGEN); | |
257 | OMAP24XX_RESTORE(CM_IDLEST_DSP); | |
258 | OMAP24XX_RESTORE(CM_IDLEST_MDM); | |
259 | ||
260 | /* Restore autoidle registers */ | |
261 | OMAP24XX_RESTORE(CM_AUTOIDLE1_CORE); | |
262 | OMAP24XX_RESTORE(CM_AUTOIDLE2_CORE); | |
263 | OMAP24XX_RESTORE(CM_AUTOIDLE3_CORE); | |
264 | OMAP24XX_RESTORE(CM_AUTOIDLE4_CORE); | |
265 | OMAP24XX_RESTORE(CM_AUTOIDLE_WKUP); | |
266 | OMAP24XX_RESTORE(CM_AUTOIDLE_PLL); | |
267 | OMAP24XX_RESTORE(CM_AUTOIDLE_DSP); | |
268 | OMAP24XX_RESTORE(CM_AUTOIDLE_MDM); | |
269 | ||
270 | /* Restore clock registers */ | |
271 | OMAP24XX_RESTORE(CM_FCLKEN1_CORE); | |
272 | OMAP24XX_RESTORE(CM_FCLKEN2_CORE); | |
273 | OMAP24XX_RESTORE(CM_ICLKEN1_CORE); | |
274 | OMAP24XX_RESTORE(CM_ICLKEN2_CORE); | |
275 | OMAP24XX_RESTORE(CM_ICLKEN3_CORE); | |
276 | OMAP24XX_RESTORE(CM_ICLKEN4_CORE); | |
277 | ||
278 | /* REVISIT: Clear interrupts here */ | |
279 | ||
280 | /* Restore interrupt registers */ | |
281 | OMAP24XX_RESTORE(INTC_MIR0); | |
282 | OMAP24XX_RESTORE(INTC_MIR1); | |
283 | OMAP24XX_RESTORE(INTC_MIR2); | |
284 | } | |
285 | ||
286 | static int omap2_pm_suspend(void) | |
287 | { | |
288 | int processor_type = 0; | |
289 | ||
290 | /* REVISIT: 0x21 or 0x26? */ | |
291 | if (cpu_is_omap2420()) | |
292 | processor_type = 0x21; | |
293 | ||
294 | if (!processor_type) | |
295 | return -ENOTSUPP; | |
296 | ||
297 | local_irq_disable(); | |
298 | local_fiq_disable(); | |
299 | ||
300 | omap2_pm_save_registers(); | |
301 | ||
302 | /* Disable interrupts except for the wake events */ | |
303 | INTC_MIR_SET0 = 0xffffffff & ~INT0_WAKE_MASK; | |
304 | INTC_MIR_SET1 = 0xffffffff & ~INT1_WAKE_MASK; | |
305 | INTC_MIR_SET2 = 0xffffffff & ~INT2_WAKE_MASK; | |
306 | ||
307 | pmdomain_set_autoidle(); | |
308 | ||
309 | /* Clear old wake-up events */ | |
310 | PM_WKST1_CORE = 0; | |
311 | PM_WKST2_CORE = 0; | |
312 | PM_WKST_WKUP = 0; | |
313 | ||
314 | /* Enable wake-up events */ | |
315 | PM_WKEN1_CORE = (1 << 22) | (1 << 21); /* UART1 & 2 */ | |
316 | PM_WKEN2_CORE = (1 << 2); /* UART3 */ | |
317 | PM_WKEN_WKUP = (1 << 2) | (1 << 0); /* GPIO & GPT1 */ | |
318 | ||
319 | /* Disable clocks except for CM_ICLKEN2_CORE. It gets disabled | |
320 | * in the SRAM suspend code */ | |
321 | CM_FCLKEN1_CORE = 0; | |
322 | CM_FCLKEN2_CORE = 0; | |
323 | CM_ICLKEN1_CORE = 0; | |
324 | CM_ICLKEN3_CORE = 0; | |
325 | CM_ICLKEN4_CORE = 0; | |
326 | ||
327 | omap2_pm_debug("Status before suspend"); | |
328 | ||
329 | /* Must wait for serial buffers to clear */ | |
330 | mdelay(200); | |
331 | ||
332 | /* Jump to SRAM suspend code | |
333 | * REVISIT: When is this SDRC_DLLB_CTRL? | |
334 | */ | |
335 | omap2_sram_suspend(SDRC_DLLA_CTRL, processor_type); | |
336 | ||
337 | /* Back from sleep */ | |
338 | omap2_pm_restore_registers(); | |
339 | ||
340 | local_fiq_enable(); | |
341 | local_irq_enable(); | |
342 | ||
343 | return 0; | |
344 | } | |
345 | ||
670c104a TL |
346 | static int omap2_pm_enter(suspend_state_t state) |
347 | { | |
22a16f39 TL |
348 | int ret = 0; |
349 | ||
670c104a TL |
350 | switch (state) |
351 | { | |
352 | case PM_SUSPEND_STANDBY: | |
353 | case PM_SUSPEND_MEM: | |
22a16f39 | 354 | ret = omap2_pm_suspend(); |
670c104a | 355 | break; |
670c104a | 356 | case PM_SUSPEND_DISK: |
22a16f39 TL |
357 | ret = -ENOTSUPP; |
358 | break; | |
670c104a | 359 | default: |
22a16f39 | 360 | ret = -EINVAL; |
670c104a TL |
361 | } |
362 | ||
22a16f39 | 363 | return ret; |
670c104a TL |
364 | } |
365 | ||
366 | static int omap2_pm_finish(suspend_state_t state) | |
367 | { | |
368 | pm_idle = saved_idle; | |
369 | return 0; | |
370 | } | |
371 | ||
372 | static struct pm_ops omap_pm_ops = { | |
670c104a TL |
373 | .prepare = omap2_pm_prepare, |
374 | .enter = omap2_pm_enter, | |
375 | .finish = omap2_pm_finish, | |
e8c9c502 | 376 | .valid = pm_valid_only_mem, |
670c104a TL |
377 | }; |
378 | ||
379 | int __init omap2_pm_init(void) | |
380 | { | |
381 | printk("Power Management for TI OMAP.\n"); | |
382 | ||
383 | vclk = clk_get(NULL, "virt_prcm_set"); | |
384 | if (IS_ERR(vclk)) { | |
385 | printk(KERN_ERR "Could not get PM vclk\n"); | |
386 | return -ENODEV; | |
387 | } | |
388 | ||
389 | /* | |
390 | * We copy the assembler sleep/wakeup routines to SRAM. | |
391 | * These routines need to be in SRAM as that's the only | |
392 | * memory the MPU can see when it wakes up. | |
393 | */ | |
394 | omap2_sram_idle = omap_sram_push(omap24xx_idle_loop_suspend, | |
395 | omap24xx_idle_loop_suspend_sz); | |
396 | ||
397 | omap2_sram_suspend = omap_sram_push(omap24xx_cpu_suspend, | |
398 | omap24xx_cpu_suspend_sz); | |
399 | ||
400 | pm_set_ops(&omap_pm_ops); | |
401 | pm_idle = omap2_pm_idle; | |
402 | ||
22a16f39 TL |
403 | pmdomain_init(); |
404 | ||
670c104a TL |
405 | return 0; |
406 | } | |
407 | ||
408 | __initcall(omap2_pm_init); |