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