ARM: OMAP4: PM: Initialise all the clockdomains to supported states
[linux-2.6.git] / arch / arm / mach-omap2 / pm44xx.c
1 /*
2  * OMAP4 Power Management Routines
3  *
4  * Copyright (C) 2010 Texas Instruments, Inc.
5  * Rajendra Nayak <rnayak@ti.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  */
11
12 #include <linux/pm.h>
13 #include <linux/suspend.h>
14 #include <linux/module.h>
15 #include <linux/list.h>
16 #include <linux/err.h>
17 #include <linux/slab.h>
18
19 #include "common.h"
20 #include "clockdomain.h"
21 #include "powerdomain.h"
22
23 struct power_state {
24         struct powerdomain *pwrdm;
25         u32 next_state;
26 #ifdef CONFIG_SUSPEND
27         u32 saved_state;
28 #endif
29         struct list_head node;
30 };
31
32 static LIST_HEAD(pwrst_list);
33
34 #ifdef CONFIG_SUSPEND
35 static int omap4_pm_suspend(void)
36 {
37         do_wfi();
38         return 0;
39 }
40
41 static int omap4_pm_enter(suspend_state_t suspend_state)
42 {
43         int ret = 0;
44
45         switch (suspend_state) {
46         case PM_SUSPEND_STANDBY:
47         case PM_SUSPEND_MEM:
48                 ret = omap4_pm_suspend();
49                 break;
50         default:
51                 ret = -EINVAL;
52         }
53
54         return ret;
55 }
56
57 static int omap4_pm_begin(suspend_state_t state)
58 {
59         disable_hlt();
60         return 0;
61 }
62
63 static void omap4_pm_end(void)
64 {
65         enable_hlt();
66         return;
67 }
68
69 static const struct platform_suspend_ops omap_pm_ops = {
70         .begin          = omap4_pm_begin,
71         .end            = omap4_pm_end,
72         .enter          = omap4_pm_enter,
73         .valid          = suspend_valid_only_mem,
74 };
75 #endif /* CONFIG_SUSPEND */
76
77 /*
78  * Enable hardware supervised mode for all clockdomains if it's
79  * supported. Initiate sleep transition for other clockdomains, if
80  * they are not used
81  */
82 static int __init clkdms_setup(struct clockdomain *clkdm, void *unused)
83 {
84         if (clkdm->flags & CLKDM_CAN_ENABLE_AUTO)
85                 clkdm_allow_idle(clkdm);
86         else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP &&
87                         atomic_read(&clkdm->usecount) == 0)
88                 clkdm_sleep(clkdm);
89         return 0;
90 }
91
92
93 static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)
94 {
95         struct power_state *pwrst;
96
97         if (!pwrdm->pwrsts)
98                 return 0;
99
100         pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC);
101         if (!pwrst)
102                 return -ENOMEM;
103         pwrst->pwrdm = pwrdm;
104         pwrst->next_state = PWRDM_POWER_ON;
105         list_add(&pwrst->node, &pwrst_list);
106
107         return pwrdm_set_next_pwrst(pwrst->pwrdm, pwrst->next_state);
108 }
109
110 /**
111  * omap4_pm_init - Init routine for OMAP4 PM
112  *
113  * Initializes all powerdomain and clockdomain target states
114  * and all PRCM settings.
115  */
116 static int __init omap4_pm_init(void)
117 {
118         int ret;
119         struct clockdomain *emif_clkdm, *mpuss_clkdm, *l3_1_clkdm;
120         struct clockdomain *ducati_clkdm, *l3_2_clkdm, *l4_per_clkdm;
121
122         if (!cpu_is_omap44xx())
123                 return -ENODEV;
124
125         if (omap_rev() == OMAP4430_REV_ES1_0) {
126                 WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
127                 return -ENODEV;
128         }
129
130         pr_err("Power Management for TI OMAP4.\n");
131
132         ret = pwrdm_for_each(pwrdms_setup, NULL);
133         if (ret) {
134                 pr_err("Failed to setup powerdomains\n");
135                 goto err2;
136         }
137
138         /*
139          * The dynamic dependency between MPUSS -> MEMIF and
140          * MPUSS -> L4_PER/L3_* and DUCATI -> L3_* doesn't work as
141          * expected. The hardware recommendation is to enable static
142          * dependencies for these to avoid system lock ups or random crashes.
143          */
144         mpuss_clkdm = clkdm_lookup("mpuss_clkdm");
145         emif_clkdm = clkdm_lookup("l3_emif_clkdm");
146         l3_1_clkdm = clkdm_lookup("l3_1_clkdm");
147         l3_2_clkdm = clkdm_lookup("l3_2_clkdm");
148         l4_per_clkdm = clkdm_lookup("l4_per_clkdm");
149         ducati_clkdm = clkdm_lookup("ducati_clkdm");
150         if ((!mpuss_clkdm) || (!emif_clkdm) || (!l3_1_clkdm) ||
151                 (!l3_2_clkdm) || (!ducati_clkdm) || (!l4_per_clkdm))
152                 goto err2;
153
154         ret = clkdm_add_wkdep(mpuss_clkdm, emif_clkdm);
155         ret |= clkdm_add_wkdep(mpuss_clkdm, l3_1_clkdm);
156         ret |= clkdm_add_wkdep(mpuss_clkdm, l3_2_clkdm);
157         ret |= clkdm_add_wkdep(mpuss_clkdm, l4_per_clkdm);
158         ret |= clkdm_add_wkdep(ducati_clkdm, l3_1_clkdm);
159         ret |= clkdm_add_wkdep(ducati_clkdm, l3_2_clkdm);
160         if (ret) {
161                 pr_err("Failed to add MPUSS -> L3/EMIF/L4PER, DUCATI -> L3 "
162                                 "wakeup dependency\n");
163                 goto err2;
164         }
165
166         (void) clkdm_for_each(clkdms_setup, NULL);
167
168 #ifdef CONFIG_SUSPEND
169         suspend_set_ops(&omap_pm_ops);
170 #endif /* CONFIG_SUSPEND */
171
172 err2:
173         return ret;
174 }
175 late_initcall(omap4_pm_init);