]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/mach-omap2/pm34xx.c
Merge current mainline tree into linux-omap tree
[linux-2.6-omap-h63xx.git] / arch / arm / mach-omap2 / pm34xx.c
1 /*
2  * linux/arch/arm/mach-omap2/pm34xx.c
3  *
4  * OMAP3 Power Management Routines
5  *
6  * Copyright (C) 2006-2008 Nokia Corporation
7  * Tony Lindgren <tony@atomide.com>
8  * Jouni Hogander
9  *
10  * Copyright (C) 2005 Texas Instruments, Inc.
11  * Richard Woodruff <r-woodruff2@ti.com>
12  *
13  * Based on pm.c for omap1
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License version 2 as
17  * published by the Free Software Foundation.
18  */
19
20 #include <linux/pm.h>
21 #include <linux/suspend.h>
22 #include <linux/interrupt.h>
23 #include <linux/module.h>
24 #include <linux/list.h>
25 #include <linux/err.h>
26
27 #include <asm/arch/gpio.h>
28 #include <asm/arch/sram.h>
29 #include <asm/arch/pm.h>
30 #include <asm/arch/clockdomain.h>
31 #include <asm/arch/powerdomain.h>
32
33 #include "cm.h"
34 #include "cm-regbits-34xx.h"
35 #include "prm-regbits-34xx.h"
36
37 #include "prm.h"
38 #include "pm.h"
39
40 struct power_state {
41         struct powerdomain *pwrdm;
42         u32 next_state;
43         u32 saved_state;
44         struct list_head node;
45 };
46
47 static LIST_HEAD(pwrst_list);
48
49 void (*_omap_sram_idle)(u32 *addr, int save_state);
50
51 static void (*saved_idle)(void);
52
53 static struct powerdomain *mpu_pwrdm;
54
55 /* PRCM Interrupt Handler for wakeups */
56 static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
57 {
58         u32 wkst, irqstatus_mpu;
59         u32 fclk, iclk;
60
61         /* WKUP */
62         wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST);
63         if (wkst) {
64                 iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN);
65                 fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN);
66                 cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_ICLKEN);
67                 cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_FCLKEN);
68                 prm_write_mod_reg(wkst, WKUP_MOD, PM_WKST);
69                 while (prm_read_mod_reg(WKUP_MOD, PM_WKST));
70                 cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN);
71                 cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN);
72         }
73
74         /* CORE */
75         wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1);
76         if (wkst) {
77                 iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN1);
78                 fclk = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
79                 cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN1);
80                 cm_set_mod_reg_bits(wkst, CORE_MOD, CM_FCLKEN1);
81                 prm_write_mod_reg(wkst, CORE_MOD, PM_WKST1);
82                 while (prm_read_mod_reg(CORE_MOD, PM_WKST1));
83                 cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN1);
84                 cm_write_mod_reg(fclk, CORE_MOD, CM_FCLKEN1);
85         }
86         wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
87         if (wkst) {
88                 iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3);
89                 fclk = cm_read_mod_reg(CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
90                 cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3);
91                 cm_set_mod_reg_bits(wkst, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
92                 prm_write_mod_reg(wkst, CORE_MOD, OMAP3430ES2_PM_WKST3);
93                 while (prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3));
94                 cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3);
95                 cm_write_mod_reg(fclk, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
96         }
97
98         /* PER */
99         wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
100         if (wkst) {
101                 iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN);
102                 fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN);
103                 cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_ICLKEN);
104                 cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_FCLKEN);
105                 prm_write_mod_reg(wkst, OMAP3430_PER_MOD, PM_WKST);
106                 while (prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST));
107                 cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN);
108                 cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN);
109         }
110
111         if (is_sil_rev_greater_than(OMAP3430_REV_ES1_0)) {
112                 /* USBHOST */
113                 wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
114                 if (wkst) {
115                         iclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
116                                                CM_ICLKEN);
117                         fclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
118                                                CM_FCLKEN);
119                         cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
120                                          CM_ICLKEN);
121                         cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD,
122                                          CM_FCLKEN);
123                         prm_write_mod_reg(wkst, OMAP3430ES2_USBHOST_MOD,
124                                           PM_WKST);
125                         while (prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
126                                                 PM_WKST));
127                         cm_write_mod_reg(iclk, OMAP3430ES2_USBHOST_MOD,
128                                          CM_ICLKEN);
129                         cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD,
130                                          CM_FCLKEN);
131                 }
132         }
133
134         irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
135                                         OMAP3430_PRM_IRQSTATUS_MPU_OFFSET);
136         prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
137                                         OMAP3430_PRM_IRQSTATUS_MPU_OFFSET);
138
139         while (prm_read_mod_reg(OCP_MOD, OMAP3430_PRM_IRQSTATUS_MPU_OFFSET));
140
141         return IRQ_HANDLED;
142 }
143
144 static void omap_sram_idle(void)
145 {
146         /* Variable to tell what needs to be saved and restored
147          * in omap_sram_idle*/
148         /* save_state = 0 => Nothing to save and restored */
149         /* save_state = 1 => Only L1 and logic lost */
150         /* save_state = 2 => Only L2 lost */
151         /* save_state = 3 => L1, L2 and logic lost */
152         int save_state = 0, mpu_next_state;
153
154         if (!_omap_sram_idle)
155                 return;
156
157         mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
158         switch (mpu_next_state) {
159         case PWRDM_POWER_RET:
160                 /* No need to save context */
161                 save_state = 0;
162                 break;
163         default:
164                 /* Invalid state */
165                 printk(KERN_ERR "Invalid mpu state in sram_idle\n");
166                 return;
167         }
168
169         omap2_gpio_prepare_for_retention();
170
171         _omap_sram_idle(NULL, save_state);
172
173         omap2_gpio_resume_after_retention();
174 }
175
176 static int omap3_can_sleep(void)
177 {
178         if (!enable_dyn_sleep)
179                 return 0;
180         if (atomic_read(&sleep_block) > 0)
181                 return 0;
182         return 1;
183 }
184
185 /* _clkdm_deny_idle - private callback function used by set_pwrdm_state() */
186 static int _clkdm_deny_idle(struct powerdomain *pwrdm,
187                             struct clockdomain *clkdm)
188 {
189         omap2_clkdm_deny_idle(clkdm);
190         return 0;
191 }
192
193 /* _clkdm_allow_idle - private callback function used by set_pwrdm_state() */
194 static int _clkdm_allow_idle(struct powerdomain *pwrdm,
195                              struct clockdomain *clkdm)
196 {
197         omap2_clkdm_allow_idle(clkdm);
198         return 0;
199 }
200
201 /* This sets pwrdm state (other than mpu & core. Currently only ON &
202  * RET are supported. Function is assuming that clkdm doesn't have
203  * hw_sup mode enabled. */
204 static int set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
205 {
206         u32 cur_state;
207         int ret = 0;
208
209         if (pwrdm == NULL || IS_ERR(pwrdm))
210                 return -EINVAL;
211
212         cur_state = pwrdm_read_next_pwrst(pwrdm);
213
214         if (cur_state == state)
215                 return ret;
216
217         pwrdm_for_each_clkdm(pwrdm, _clkdm_deny_idle);
218
219         ret = pwrdm_set_next_pwrst(pwrdm, state);
220         if (ret) {
221                 printk(KERN_ERR "Unable to set state of powerdomain: %s\n",
222                        pwrdm->name);
223                 goto err;
224         }
225
226         pwrdm_for_each_clkdm(pwrdm, _clkdm_allow_idle);
227
228 err:
229         return ret;
230 }
231
232 static void omap3_pm_idle(void)
233 {
234         local_irq_disable();
235         local_fiq_disable();
236
237         if (!omap3_can_sleep())
238                 goto out;
239
240         if (omap_irq_pending())
241                 goto out;
242
243         omap_sram_idle();
244
245 out:
246         local_fiq_enable();
247         local_irq_enable();
248 }
249
250 static int omap3_pm_prepare(void)
251 {
252         saved_idle = pm_idle;
253         pm_idle = NULL;
254         return 0;
255 }
256
257 static int omap3_pm_suspend(void)
258 {
259         struct power_state *pwrst;
260         int state, ret = 0;
261
262         /* Read current next_pwrsts */
263         list_for_each_entry(pwrst, &pwrst_list, node)
264                 pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
265         /* Set ones wanted by suspend */
266         list_for_each_entry(pwrst, &pwrst_list, node) {
267                 if (set_pwrdm_state(pwrst->pwrdm, pwrst->next_state))
268                         goto restore;
269                 if (pwrdm_clear_all_prev_pwrst(pwrst->pwrdm))
270                         goto restore;
271         }
272
273         omap_sram_idle();
274
275 restore:
276         /* Restore next_pwrsts */
277         list_for_each_entry(pwrst, &pwrst_list, node) {
278                 set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
279                 state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
280                 if (state != pwrst->next_state) {
281                         printk(KERN_INFO "Powerdomain (%s) didn't enter "
282                                "target state %d\n",
283                                pwrst->pwrdm->name, pwrst->next_state);
284                         ret = -1;
285                 }
286         }
287         if (ret)
288                 printk(KERN_ERR "Could not enter target state in pm_suspend\n");
289         else
290                 printk(KERN_INFO "Successfully put all powerdomains "
291                        "to target state\n");
292
293         return ret;
294 }
295
296 static int omap3_pm_enter(suspend_state_t state)
297 {
298         int ret = 0;
299
300         switch (state) {
301         case PM_SUSPEND_STANDBY:
302         case PM_SUSPEND_MEM:
303                 ret = omap3_pm_suspend();
304                 break;
305         default:
306                 ret = -EINVAL;
307         }
308
309         return ret;
310 }
311
312 static void omap3_pm_finish(void)
313 {
314         pm_idle = saved_idle;
315 }
316
317 static struct platform_suspend_ops omap_pm_ops = {
318         .prepare        = omap3_pm_prepare,
319         .enter          = omap3_pm_enter,
320         .finish         = omap3_pm_finish,
321         .valid          = suspend_valid_only_mem,
322 };
323
324 static void __init prcm_setup_regs(void)
325 {
326         /* setup wakup source */
327         prm_write_mod_reg(OMAP3430_EN_IO | OMAP3430_EN_GPIO1 | OMAP3430_EN_GPT1,
328                           WKUP_MOD, PM_WKEN);
329         /* No need to write EN_IO, that is always enabled */
330         prm_write_mod_reg(OMAP3430_EN_GPIO1 | OMAP3430_EN_GPT1,
331                           WKUP_MOD, OMAP3430_PM_MPUGRPSEL);
332         /* For some reason IO doesn't generate wakeup event even if
333          * it is selected to mpu wakeup goup */
334         prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN,
335                         OCP_MOD, OMAP3430_PRM_IRQENABLE_MPU_OFFSET);
336 }
337
338 static int __init pwrdms_setup(struct powerdomain *pwrdm)
339 {
340         struct power_state *pwrst;
341
342         if (!pwrdm->pwrsts)
343                 return 0;
344
345         pwrst = kmalloc(sizeof(struct power_state), GFP_KERNEL);
346         if (!pwrst)
347                 return -ENOMEM;
348         pwrst->pwrdm = pwrdm;
349         pwrst->next_state = PWRDM_POWER_RET;
350         list_add(&pwrst->node, &pwrst_list);
351         return set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
352 }
353
354 int __init omap3_pm_init(void)
355 {
356         struct power_state *pwrst;
357         int ret;
358
359         printk(KERN_ERR "Power Management for TI OMAP3.\n");
360
361         ret = request_irq(INT_34XX_PRCM_MPU_IRQ,
362                           (irq_handler_t)prcm_interrupt_handler,
363                           IRQF_DISABLED, "prcm", NULL);
364         if (ret) {
365                 printk(KERN_ERR "request_irq failed to register for 0x%x\n",
366                        INT_34XX_PRCM_MPU_IRQ);
367                 goto err1;
368         }
369
370         ret = pwrdm_for_each(pwrdms_setup);
371         if (ret) {
372                 printk(KERN_ERR "Failed to setup powerdomains\n");
373                 goto err2;
374         }
375
376         mpu_pwrdm = pwrdm_lookup("mpu_pwrdm");
377         if (mpu_pwrdm == NULL) {
378                 printk(KERN_ERR "Failed to get mpu_pwrdm\n");
379                 goto err2;
380         }
381
382         _omap_sram_idle = omap_sram_push(omap34xx_cpu_suspend,
383                                         omap34xx_cpu_suspend_sz);
384
385         suspend_set_ops(&omap_pm_ops);
386
387         prcm_setup_regs();
388
389         pm_idle = omap3_pm_idle;
390
391 err1:
392         return ret;
393 err2:
394         free_irq(INT_34XX_PRCM_MPU_IRQ, NULL);
395         list_for_each_entry(pwrst, &pwrst_list, node) {
396                 list_del(&pwrst->node);
397                 kfree(pwrst);
398         }
399         return ret;
400 }