blob: baf7d82b458b968b61b1127e646cb17150812c0c [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
Rafael J. Wysockie6c5eb92007-10-18 03:04:41 -070073static int omap2_pm_prepare(void)
Tony Lindgren670c1042006-04-02 17:46:25 +010074{
Tony Lindgren670c1042006-04-02 17:46:25 +010075 /* We cannot sleep in idle until we have resumed */
76 saved_idle = pm_idle;
77 pm_idle = NULL;
Rafael J. Wysockie6c5eb92007-10-18 03:04:41 -070078 return 0;
Tony Lindgren670c1042006-04-02 17:46:25 +010079}
80
Tony Lindgren22a16f32006-06-26 16:16:18 -070081#define INT0_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_GPIO_BANK1) | \
82 OMAP_IRQ_BIT(INT_24XX_GPIO_BANK2) | \
83 OMAP_IRQ_BIT(INT_24XX_GPIO_BANK3))
84
85#define INT1_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_GPIO_BANK4))
86
87#define INT2_WAKE_MASK (OMAP_IRQ_BIT(INT_24XX_UART1_IRQ) | \
88 OMAP_IRQ_BIT(INT_24XX_UART2_IRQ) | \
89 OMAP_IRQ_BIT(INT_24XX_UART3_IRQ))
90
91#define preg(reg) printk("%s\t(0x%p):\t0x%08x\n", #reg, &reg, reg);
92
93static void omap2_pm_debug(char * desc)
94{
95 printk("%s:\n", desc);
96
97 preg(CM_CLKSTCTRL_MPU);
98 preg(CM_CLKSTCTRL_CORE);
99 preg(CM_CLKSTCTRL_GFX);
100 preg(CM_CLKSTCTRL_DSP);
101 preg(CM_CLKSTCTRL_MDM);
102
103 preg(PM_PWSTCTRL_MPU);
104 preg(PM_PWSTCTRL_CORE);
105 preg(PM_PWSTCTRL_GFX);
106 preg(PM_PWSTCTRL_DSP);
107 preg(PM_PWSTCTRL_MDM);
108
109 preg(PM_PWSTST_MPU);
110 preg(PM_PWSTST_CORE);
111 preg(PM_PWSTST_GFX);
112 preg(PM_PWSTST_DSP);
113 preg(PM_PWSTST_MDM);
114
115 preg(CM_AUTOIDLE1_CORE);
116 preg(CM_AUTOIDLE2_CORE);
117 preg(CM_AUTOIDLE3_CORE);
118 preg(CM_AUTOIDLE4_CORE);
119 preg(CM_AUTOIDLE_WKUP);
120 preg(CM_AUTOIDLE_PLL);
121 preg(CM_AUTOIDLE_DSP);
122 preg(CM_AUTOIDLE_MDM);
123
124 preg(CM_ICLKEN1_CORE);
125 preg(CM_ICLKEN2_CORE);
126 preg(CM_ICLKEN3_CORE);
127 preg(CM_ICLKEN4_CORE);
128 preg(CM_ICLKEN_GFX);
129 preg(CM_ICLKEN_WKUP);
130 preg(CM_ICLKEN_DSP);
131 preg(CM_ICLKEN_MDM);
132
133 preg(CM_IDLEST1_CORE);
134 preg(CM_IDLEST2_CORE);
135 preg(CM_IDLEST3_CORE);
136 preg(CM_IDLEST4_CORE);
137 preg(CM_IDLEST_GFX);
138 preg(CM_IDLEST_WKUP);
139 preg(CM_IDLEST_CKGEN);
140 preg(CM_IDLEST_DSP);
141 preg(CM_IDLEST_MDM);
142
143 preg(RM_RSTST_MPU);
144 preg(RM_RSTST_GFX);
145 preg(RM_RSTST_WKUP);
146 preg(RM_RSTST_DSP);
147 preg(RM_RSTST_MDM);
148
149 preg(PM_WKDEP_MPU);
150 preg(PM_WKDEP_CORE);
151 preg(PM_WKDEP_GFX);
152 preg(PM_WKDEP_DSP);
153 preg(PM_WKDEP_MDM);
154
155 preg(CM_FCLKEN_WKUP);
156 preg(CM_ICLKEN_WKUP);
157 preg(CM_IDLEST_WKUP);
158 preg(CM_AUTOIDLE_WKUP);
159 preg(CM_CLKSEL_WKUP);
160
161 preg(PM_WKEN_WKUP);
162 preg(PM_WKST_WKUP);
163}
164
165static inline void omap2_pm_save_registers(void)
166{
167 /* Save interrupt registers */
168 OMAP24XX_SAVE(INTC_MIR0);
169 OMAP24XX_SAVE(INTC_MIR1);
170 OMAP24XX_SAVE(INTC_MIR2);
171
172 /* Save power control registers */
173 OMAP24XX_SAVE(CM_CLKSTCTRL_MPU);
174 OMAP24XX_SAVE(CM_CLKSTCTRL_CORE);
175 OMAP24XX_SAVE(CM_CLKSTCTRL_GFX);
176 OMAP24XX_SAVE(CM_CLKSTCTRL_DSP);
177 OMAP24XX_SAVE(CM_CLKSTCTRL_MDM);
178
179 /* Save power state registers */
180 OMAP24XX_SAVE(PM_PWSTCTRL_MPU);
181 OMAP24XX_SAVE(PM_PWSTCTRL_CORE);
182 OMAP24XX_SAVE(PM_PWSTCTRL_GFX);
183 OMAP24XX_SAVE(PM_PWSTCTRL_DSP);
184 OMAP24XX_SAVE(PM_PWSTCTRL_MDM);
185
186 /* Save autoidle registers */
187 OMAP24XX_SAVE(CM_AUTOIDLE1_CORE);
188 OMAP24XX_SAVE(CM_AUTOIDLE2_CORE);
189 OMAP24XX_SAVE(CM_AUTOIDLE3_CORE);
190 OMAP24XX_SAVE(CM_AUTOIDLE4_CORE);
191 OMAP24XX_SAVE(CM_AUTOIDLE_WKUP);
192 OMAP24XX_SAVE(CM_AUTOIDLE_PLL);
193 OMAP24XX_SAVE(CM_AUTOIDLE_DSP);
194 OMAP24XX_SAVE(CM_AUTOIDLE_MDM);
195
196 /* Save idle state registers */
197 OMAP24XX_SAVE(CM_IDLEST1_CORE);
198 OMAP24XX_SAVE(CM_IDLEST2_CORE);
199 OMAP24XX_SAVE(CM_IDLEST3_CORE);
200 OMAP24XX_SAVE(CM_IDLEST4_CORE);
201 OMAP24XX_SAVE(CM_IDLEST_GFX);
202 OMAP24XX_SAVE(CM_IDLEST_WKUP);
203 OMAP24XX_SAVE(CM_IDLEST_CKGEN);
204 OMAP24XX_SAVE(CM_IDLEST_DSP);
205 OMAP24XX_SAVE(CM_IDLEST_MDM);
206
207 /* Save clock registers */
208 OMAP24XX_SAVE(CM_FCLKEN1_CORE);
209 OMAP24XX_SAVE(CM_FCLKEN2_CORE);
210 OMAP24XX_SAVE(CM_ICLKEN1_CORE);
211 OMAP24XX_SAVE(CM_ICLKEN2_CORE);
212 OMAP24XX_SAVE(CM_ICLKEN3_CORE);
213 OMAP24XX_SAVE(CM_ICLKEN4_CORE);
214}
215
216static inline void omap2_pm_restore_registers(void)
217{
218 /* Restore clock state registers */
219 OMAP24XX_RESTORE(CM_CLKSTCTRL_MPU);
220 OMAP24XX_RESTORE(CM_CLKSTCTRL_CORE);
221 OMAP24XX_RESTORE(CM_CLKSTCTRL_GFX);
222 OMAP24XX_RESTORE(CM_CLKSTCTRL_DSP);
223 OMAP24XX_RESTORE(CM_CLKSTCTRL_MDM);
224
225 /* Restore power state registers */
226 OMAP24XX_RESTORE(PM_PWSTCTRL_MPU);
227 OMAP24XX_RESTORE(PM_PWSTCTRL_CORE);
228 OMAP24XX_RESTORE(PM_PWSTCTRL_GFX);
229 OMAP24XX_RESTORE(PM_PWSTCTRL_DSP);
230 OMAP24XX_RESTORE(PM_PWSTCTRL_MDM);
231
232 /* Restore idle state registers */
233 OMAP24XX_RESTORE(CM_IDLEST1_CORE);
234 OMAP24XX_RESTORE(CM_IDLEST2_CORE);
235 OMAP24XX_RESTORE(CM_IDLEST3_CORE);
236 OMAP24XX_RESTORE(CM_IDLEST4_CORE);
237 OMAP24XX_RESTORE(CM_IDLEST_GFX);
238 OMAP24XX_RESTORE(CM_IDLEST_WKUP);
239 OMAP24XX_RESTORE(CM_IDLEST_CKGEN);
240 OMAP24XX_RESTORE(CM_IDLEST_DSP);
241 OMAP24XX_RESTORE(CM_IDLEST_MDM);
242
243 /* Restore autoidle registers */
244 OMAP24XX_RESTORE(CM_AUTOIDLE1_CORE);
245 OMAP24XX_RESTORE(CM_AUTOIDLE2_CORE);
246 OMAP24XX_RESTORE(CM_AUTOIDLE3_CORE);
247 OMAP24XX_RESTORE(CM_AUTOIDLE4_CORE);
248 OMAP24XX_RESTORE(CM_AUTOIDLE_WKUP);
249 OMAP24XX_RESTORE(CM_AUTOIDLE_PLL);
250 OMAP24XX_RESTORE(CM_AUTOIDLE_DSP);
251 OMAP24XX_RESTORE(CM_AUTOIDLE_MDM);
252
253 /* Restore clock registers */
254 OMAP24XX_RESTORE(CM_FCLKEN1_CORE);
255 OMAP24XX_RESTORE(CM_FCLKEN2_CORE);
256 OMAP24XX_RESTORE(CM_ICLKEN1_CORE);
257 OMAP24XX_RESTORE(CM_ICLKEN2_CORE);
258 OMAP24XX_RESTORE(CM_ICLKEN3_CORE);
259 OMAP24XX_RESTORE(CM_ICLKEN4_CORE);
260
261 /* REVISIT: Clear interrupts here */
262
263 /* Restore interrupt registers */
264 OMAP24XX_RESTORE(INTC_MIR0);
265 OMAP24XX_RESTORE(INTC_MIR1);
266 OMAP24XX_RESTORE(INTC_MIR2);
267}
268
269static int omap2_pm_suspend(void)
270{
271 int processor_type = 0;
272
273 /* REVISIT: 0x21 or 0x26? */
274 if (cpu_is_omap2420())
275 processor_type = 0x21;
276
277 if (!processor_type)
278 return -ENOTSUPP;
279
280 local_irq_disable();
281 local_fiq_disable();
282
283 omap2_pm_save_registers();
284
285 /* Disable interrupts except for the wake events */
286 INTC_MIR_SET0 = 0xffffffff & ~INT0_WAKE_MASK;
287 INTC_MIR_SET1 = 0xffffffff & ~INT1_WAKE_MASK;
288 INTC_MIR_SET2 = 0xffffffff & ~INT2_WAKE_MASK;
289
290 pmdomain_set_autoidle();
291
292 /* Clear old wake-up events */
293 PM_WKST1_CORE = 0;
294 PM_WKST2_CORE = 0;
295 PM_WKST_WKUP = 0;
296
297 /* Enable wake-up events */
298 PM_WKEN1_CORE = (1 << 22) | (1 << 21); /* UART1 & 2 */
299 PM_WKEN2_CORE = (1 << 2); /* UART3 */
300 PM_WKEN_WKUP = (1 << 2) | (1 << 0); /* GPIO & GPT1 */
301
302 /* Disable clocks except for CM_ICLKEN2_CORE. It gets disabled
303 * in the SRAM suspend code */
304 CM_FCLKEN1_CORE = 0;
305 CM_FCLKEN2_CORE = 0;
306 CM_ICLKEN1_CORE = 0;
307 CM_ICLKEN3_CORE = 0;
308 CM_ICLKEN4_CORE = 0;
309
310 omap2_pm_debug("Status before suspend");
311
312 /* Must wait for serial buffers to clear */
313 mdelay(200);
314
315 /* Jump to SRAM suspend code
316 * REVISIT: When is this SDRC_DLLB_CTRL?
317 */
318 omap2_sram_suspend(SDRC_DLLA_CTRL, processor_type);
319
320 /* Back from sleep */
321 omap2_pm_restore_registers();
322
323 local_fiq_enable();
324 local_irq_enable();
325
326 return 0;
327}
328
Tony Lindgren670c1042006-04-02 17:46:25 +0100329static int omap2_pm_enter(suspend_state_t state)
330{
Tony Lindgren22a16f32006-06-26 16:16:18 -0700331 int ret = 0;
332
Tony Lindgren670c1042006-04-02 17:46:25 +0100333 switch (state)
334 {
335 case PM_SUSPEND_STANDBY:
336 case PM_SUSPEND_MEM:
Tony Lindgren22a16f32006-06-26 16:16:18 -0700337 ret = omap2_pm_suspend();
Tony Lindgren670c1042006-04-02 17:46:25 +0100338 break;
Tony Lindgren670c1042006-04-02 17:46:25 +0100339 default:
Tony Lindgren22a16f32006-06-26 16:16:18 -0700340 ret = -EINVAL;
Tony Lindgren670c1042006-04-02 17:46:25 +0100341 }
342
Tony Lindgren22a16f32006-06-26 16:16:18 -0700343 return ret;
Tony Lindgren670c1042006-04-02 17:46:25 +0100344}
345
Rafael J. Wysockie6c5eb92007-10-18 03:04:41 -0700346static void omap2_pm_finish(void)
Tony Lindgren670c1042006-04-02 17:46:25 +0100347{
348 pm_idle = saved_idle;
Tony Lindgren670c1042006-04-02 17:46:25 +0100349}
350
Rafael J. Wysocki26398a72007-10-18 03:04:40 -0700351static struct platform_suspend_ops omap_pm_ops = {
Tony Lindgren670c1042006-04-02 17:46:25 +0100352 .prepare = omap2_pm_prepare,
353 .enter = omap2_pm_enter,
354 .finish = omap2_pm_finish,
Rafael J. Wysocki26398a72007-10-18 03:04:40 -0700355 .valid = suspend_valid_only_mem,
Tony Lindgren670c1042006-04-02 17:46:25 +0100356};
357
358int __init omap2_pm_init(void)
359{
360 printk("Power Management for TI OMAP.\n");
361
362 vclk = clk_get(NULL, "virt_prcm_set");
363 if (IS_ERR(vclk)) {
364 printk(KERN_ERR "Could not get PM vclk\n");
365 return -ENODEV;
366 }
367
368 /*
369 * We copy the assembler sleep/wakeup routines to SRAM.
370 * These routines need to be in SRAM as that's the only
371 * memory the MPU can see when it wakes up.
372 */
373 omap2_sram_idle = omap_sram_push(omap24xx_idle_loop_suspend,
374 omap24xx_idle_loop_suspend_sz);
375
376 omap2_sram_suspend = omap_sram_push(omap24xx_cpu_suspend,
377 omap24xx_cpu_suspend_sz);
378
Rafael J. Wysocki26398a72007-10-18 03:04:40 -0700379 suspend_set_ops(&omap_pm_ops);
Tony Lindgren670c1042006-04-02 17:46:25 +0100380 pm_idle = omap2_pm_idle;
381
Tony Lindgren22a16f32006-06-26 16:16:18 -0700382 pmdomain_init();
383
Tony Lindgren670c1042006-04-02 17:46:25 +0100384 return 0;
385}
386
387__initcall(omap2_pm_init);