diff options
author | Paul Walmsley <paul@pwsan.com> | 2008-08-19 04:08:40 -0400 |
---|---|---|
committer | Tony Lindgren <tony@atomide.com> | 2008-08-19 04:08:40 -0400 |
commit | ad67ef6848a1608b0430003e11e7af1ce706e341 (patch) | |
tree | f55151e3cc4b4739f13a074af6e7f43e7e6be2d1 /arch/arm | |
parent | 1fca25427482387689fa27594c992a961d98768f (diff) |
ARM: OMAP2: Powerdomain: Add base OMAP2/3 powerdomain code
This patch creates an interface to the powerdomain registers in the
PRM/CM modules on OMAP2/3. This interface is intended to be used by
PM code, e.g., pm.c; not by device drivers directly.
Each powerdomain will be defined in later patches as static
structures. Also defined are dependencies between powerdomains,
used for adding and removing PM_WKDEP and CM_SLEEPDEP bits. The
powerdomain structures are linked into a list at boot by
pwrdm_register(), similar to the OMAP clock code.
The patch adds a Kconfig option, CONFIG_OMAP_DEBUG_POWERDOMAIN, which
when enabled will emit verbose debug messages via pr_debug().
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock34xx.c | 27 | ||||
-rw-r--r-- | arch/arm/mach-omap2/powerdomain.c | 909 | ||||
-rw-r--r-- | arch/arm/plat-omap/Kconfig | 12 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/mach/powerdomain.h | 139 |
5 files changed, 1078 insertions, 11 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 93ee990618ef..1001d42048e9 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | # Common support | 5 | # Common support |
6 | obj-y := irq.o id.o io.o memory.o control.o prcm.o clock.o mux.o \ | 6 | obj-y := irq.o id.o io.o memory.o control.o prcm.o clock.o mux.o \ |
7 | devices.o serial.o gpmc.o timer-gp.o | 7 | devices.o serial.o gpmc.o timer-gp.o powerdomain.o |
8 | 8 | ||
9 | obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o | 9 | obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o |
10 | 10 | ||
diff --git a/arch/arm/mach-omap2/clock34xx.c b/arch/arm/mach-omap2/clock34xx.c index 3ff74952f835..dff7a72fefc9 100644 --- a/arch/arm/mach-omap2/clock34xx.c +++ b/arch/arm/mach-omap2/clock34xx.c | |||
@@ -62,11 +62,14 @@ static void omap3_dpll_recalc(struct clk *clk) | |||
62 | static void _omap3_dpll_write_clken(struct clk *clk, u8 clken_bits) | 62 | static void _omap3_dpll_write_clken(struct clk *clk, u8 clken_bits) |
63 | { | 63 | { |
64 | const struct dpll_data *dd; | 64 | const struct dpll_data *dd; |
65 | u32 v; | ||
65 | 66 | ||
66 | dd = clk->dpll_data; | 67 | dd = clk->dpll_data; |
67 | 68 | ||
68 | cm_rmw_reg_bits(dd->enable_mask, clken_bits << __ffs(dd->enable_mask), | 69 | v = __raw_readl(dd->control_reg); |
69 | dd->control_reg); | 70 | v &= ~dd->enable_mask; |
71 | v |= clken_bits << __ffs(dd->enable_mask); | ||
72 | __raw_writel(v, dd->control_reg); | ||
70 | } | 73 | } |
71 | 74 | ||
72 | /* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */ | 75 | /* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */ |
@@ -82,7 +85,7 @@ static int _omap3_wait_dpll_status(struct clk *clk, u8 state) | |||
82 | state <<= dd->idlest_bit; | 85 | state <<= dd->idlest_bit; |
83 | idlest_mask = 1 << dd->idlest_bit; | 86 | idlest_mask = 1 << dd->idlest_bit; |
84 | 87 | ||
85 | while (((cm_read_reg(dd->idlest_reg) & idlest_mask) != state) && | 88 | while (((__raw_readl(dd->idlest_reg) & idlest_mask) != state) && |
86 | i < MAX_DPLL_WAIT_TRIES) { | 89 | i < MAX_DPLL_WAIT_TRIES) { |
87 | i++; | 90 | i++; |
88 | udelay(1); | 91 | udelay(1); |
@@ -285,7 +288,7 @@ static u32 omap3_dpll_autoidle_read(struct clk *clk) | |||
285 | 288 | ||
286 | dd = clk->dpll_data; | 289 | dd = clk->dpll_data; |
287 | 290 | ||
288 | v = cm_read_reg(dd->autoidle_reg); | 291 | v = __raw_readl(dd->autoidle_reg); |
289 | v &= dd->autoidle_mask; | 292 | v &= dd->autoidle_mask; |
290 | v >>= __ffs(dd->autoidle_mask); | 293 | v >>= __ffs(dd->autoidle_mask); |
291 | 294 | ||
@@ -304,6 +307,7 @@ static u32 omap3_dpll_autoidle_read(struct clk *clk) | |||
304 | static void omap3_dpll_allow_idle(struct clk *clk) | 307 | static void omap3_dpll_allow_idle(struct clk *clk) |
305 | { | 308 | { |
306 | const struct dpll_data *dd; | 309 | const struct dpll_data *dd; |
310 | u32 v; | ||
307 | 311 | ||
308 | if (!clk || !clk->dpll_data) | 312 | if (!clk || !clk->dpll_data) |
309 | return; | 313 | return; |
@@ -315,9 +319,10 @@ static void omap3_dpll_allow_idle(struct clk *clk) | |||
315 | * by writing 0x5 instead of 0x1. Add some mechanism to | 319 | * by writing 0x5 instead of 0x1. Add some mechanism to |
316 | * optionally enter this mode. | 320 | * optionally enter this mode. |
317 | */ | 321 | */ |
318 | cm_rmw_reg_bits(dd->autoidle_mask, | 322 | v = __raw_readl(dd->autoidle_reg); |
319 | DPLL_AUTOIDLE_LOW_POWER_STOP << __ffs(dd->autoidle_mask), | 323 | v &= ~dd->autoidle_mask; |
320 | dd->autoidle_reg); | 324 | v |= DPLL_AUTOIDLE_LOW_POWER_STOP << __ffs(dd->autoidle_mask); |
325 | __raw_writel(v, dd->autoidle_reg); | ||
321 | } | 326 | } |
322 | 327 | ||
323 | /** | 328 | /** |
@@ -329,15 +334,17 @@ static void omap3_dpll_allow_idle(struct clk *clk) | |||
329 | static void omap3_dpll_deny_idle(struct clk *clk) | 334 | static void omap3_dpll_deny_idle(struct clk *clk) |
330 | { | 335 | { |
331 | const struct dpll_data *dd; | 336 | const struct dpll_data *dd; |
337 | u32 v; | ||
332 | 338 | ||
333 | if (!clk || !clk->dpll_data) | 339 | if (!clk || !clk->dpll_data) |
334 | return; | 340 | return; |
335 | 341 | ||
336 | dd = clk->dpll_data; | 342 | dd = clk->dpll_data; |
337 | 343 | ||
338 | cm_rmw_reg_bits(dd->autoidle_mask, | 344 | v = __raw_readl(dd->autoidle_reg); |
339 | DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask), | 345 | v &= ~dd->autoidle_mask; |
340 | dd->autoidle_reg); | 346 | v |= DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask); |
347 | __raw_writel(v, dd->autoidle_reg); | ||
341 | } | 348 | } |
342 | 349 | ||
343 | /* Clock control for DPLL outputs */ | 350 | /* Clock control for DPLL outputs */ |
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c new file mode 100644 index 000000000000..1ec003832ef8 --- /dev/null +++ b/arch/arm/mach-omap2/powerdomain.c | |||
@@ -0,0 +1,909 @@ | |||
1 | /* | ||
2 | * OMAP powerdomain control | ||
3 | * | ||
4 | * Copyright (C) 2007-2008 Texas Instruments, Inc. | ||
5 | * Copyright (C) 2007-2008 Nokia Corporation | ||
6 | * | ||
7 | * Written by Paul Walmsley | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #ifdef CONFIG_OMAP_DEBUG_POWERDOMAIN | ||
14 | # define DEBUG | ||
15 | #endif | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <linux/list.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/err.h> | ||
25 | #include <linux/io.h> | ||
26 | |||
27 | #include <asm/atomic.h> | ||
28 | |||
29 | #include "cm.h" | ||
30 | #include "cm-regbits-34xx.h" | ||
31 | #include "prm.h" | ||
32 | #include "prm-regbits-34xx.h" | ||
33 | |||
34 | #include <mach/cpu.h> | ||
35 | #include <mach/powerdomain.h> | ||
36 | |||
37 | /* pwrdm_list contains all registered struct powerdomains */ | ||
38 | static LIST_HEAD(pwrdm_list); | ||
39 | |||
40 | /* | ||
41 | * pwrdm_rwlock protects pwrdm_list add and del ops - also reused to | ||
42 | * protect pwrdm_clkdms[] during clkdm add/del ops | ||
43 | */ | ||
44 | static DEFINE_RWLOCK(pwrdm_rwlock); | ||
45 | |||
46 | |||
47 | /* Private functions */ | ||
48 | |||
49 | static u32 prm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask) | ||
50 | { | ||
51 | u32 v; | ||
52 | |||
53 | v = prm_read_mod_reg(domain, idx); | ||
54 | v &= mask; | ||
55 | v >>= __ffs(mask); | ||
56 | |||
57 | return v; | ||
58 | } | ||
59 | |||
60 | static struct powerdomain *_pwrdm_lookup(const char *name) | ||
61 | { | ||
62 | struct powerdomain *pwrdm, *temp_pwrdm; | ||
63 | |||
64 | pwrdm = NULL; | ||
65 | |||
66 | list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { | ||
67 | if (!strcmp(name, temp_pwrdm->name)) { | ||
68 | pwrdm = temp_pwrdm; | ||
69 | break; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | return pwrdm; | ||
74 | } | ||
75 | |||
76 | /* _pwrdm_deps_lookup - look up the specified powerdomain in a pwrdm list */ | ||
77 | static struct powerdomain *_pwrdm_deps_lookup(struct powerdomain *pwrdm, | ||
78 | struct pwrdm_dep *deps) | ||
79 | { | ||
80 | struct pwrdm_dep *pd; | ||
81 | |||
82 | if (!pwrdm || !deps || !omap_chip_is(pwrdm->omap_chip)) | ||
83 | return ERR_PTR(-EINVAL); | ||
84 | |||
85 | for (pd = deps; pd; pd++) { | ||
86 | |||
87 | if (!omap_chip_is(pd->omap_chip)) | ||
88 | continue; | ||
89 | |||
90 | if (!pd->pwrdm && pd->pwrdm_name) | ||
91 | pd->pwrdm = pwrdm_lookup(pd->pwrdm_name); | ||
92 | |||
93 | if (pd->pwrdm == pwrdm) | ||
94 | break; | ||
95 | |||
96 | } | ||
97 | |||
98 | if (!pd) | ||
99 | return ERR_PTR(-ENOENT); | ||
100 | |||
101 | return pd->pwrdm; | ||
102 | } | ||
103 | |||
104 | |||
105 | /* Public functions */ | ||
106 | |||
107 | /** | ||
108 | * pwrdm_init - set up the powerdomain layer | ||
109 | * | ||
110 | * Loop through the list of powerdomains, registering all that are | ||
111 | * available on the current CPU. If pwrdm_list is supplied and not | ||
112 | * null, all of the referenced powerdomains will be registered. No | ||
113 | * return value. | ||
114 | */ | ||
115 | void pwrdm_init(struct powerdomain **pwrdm_list) | ||
116 | { | ||
117 | struct powerdomain **p = NULL; | ||
118 | |||
119 | if (pwrdm_list) | ||
120 | for (p = pwrdm_list; *p; p++) | ||
121 | pwrdm_register(*p); | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * pwrdm_register - register a powerdomain | ||
126 | * @pwrdm: struct powerdomain * to register | ||
127 | * | ||
128 | * Adds a powerdomain to the internal powerdomain list. Returns | ||
129 | * -EINVAL if given a null pointer, -EEXIST if a powerdomain is | ||
130 | * already registered by the provided name, or 0 upon success. | ||
131 | */ | ||
132 | int pwrdm_register(struct powerdomain *pwrdm) | ||
133 | { | ||
134 | unsigned long flags; | ||
135 | int ret = -EINVAL; | ||
136 | |||
137 | if (!pwrdm) | ||
138 | return -EINVAL; | ||
139 | |||
140 | if (!omap_chip_is(pwrdm->omap_chip)) | ||
141 | return -EINVAL; | ||
142 | |||
143 | write_lock_irqsave(&pwrdm_rwlock, flags); | ||
144 | if (_pwrdm_lookup(pwrdm->name)) { | ||
145 | ret = -EEXIST; | ||
146 | goto pr_unlock; | ||
147 | } | ||
148 | |||
149 | list_add(&pwrdm->node, &pwrdm_list); | ||
150 | |||
151 | pr_debug("powerdomain: registered %s\n", pwrdm->name); | ||
152 | ret = 0; | ||
153 | |||
154 | pr_unlock: | ||
155 | write_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
156 | |||
157 | return ret; | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * pwrdm_unregister - unregister a powerdomain | ||
162 | * @pwrdm: struct powerdomain * to unregister | ||
163 | * | ||
164 | * Removes a powerdomain from the internal powerdomain list. Returns | ||
165 | * -EINVAL if pwrdm argument is NULL. | ||
166 | */ | ||
167 | int pwrdm_unregister(struct powerdomain *pwrdm) | ||
168 | { | ||
169 | unsigned long flags; | ||
170 | |||
171 | if (!pwrdm) | ||
172 | return -EINVAL; | ||
173 | |||
174 | write_lock_irqsave(&pwrdm_rwlock, flags); | ||
175 | list_del(&pwrdm->node); | ||
176 | write_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
177 | |||
178 | pr_debug("powerdomain: unregistered %s\n", pwrdm->name); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * pwrdm_lookup - look up a powerdomain by name, return a pointer | ||
185 | * @name: name of powerdomain | ||
186 | * | ||
187 | * Find a registered powerdomain by its name. Returns a pointer to the | ||
188 | * struct powerdomain if found, or NULL otherwise. | ||
189 | */ | ||
190 | struct powerdomain *pwrdm_lookup(const char *name) | ||
191 | { | ||
192 | struct powerdomain *pwrdm; | ||
193 | unsigned long flags; | ||
194 | |||
195 | if (!name) | ||
196 | return NULL; | ||
197 | |||
198 | read_lock_irqsave(&pwrdm_rwlock, flags); | ||
199 | pwrdm = _pwrdm_lookup(name); | ||
200 | read_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
201 | |||
202 | return pwrdm; | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * pwrdm_for_each - call function on each registered clockdomain | ||
207 | * @fn: callback function * | ||
208 | * | ||
209 | * Call the supplied function for each registered powerdomain. The | ||
210 | * callback function can return anything but 0 to bail out early from | ||
211 | * the iterator. The callback function is called with the pwrdm_rwlock | ||
212 | * held for reading, so no powerdomain structure manipulation | ||
213 | * functions should be called from the callback, although hardware | ||
214 | * powerdomain control functions are fine. Returns the last return | ||
215 | * value of the callback function, which should be 0 for success or | ||
216 | * anything else to indicate failure; or -EINVAL if the function | ||
217 | * pointer is null. | ||
218 | */ | ||
219 | int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm)) | ||
220 | { | ||
221 | struct powerdomain *temp_pwrdm; | ||
222 | unsigned long flags; | ||
223 | int ret = 0; | ||
224 | |||
225 | if (!fn) | ||
226 | return -EINVAL; | ||
227 | |||
228 | read_lock_irqsave(&pwrdm_rwlock, flags); | ||
229 | list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { | ||
230 | ret = (*fn)(temp_pwrdm); | ||
231 | if (ret) | ||
232 | break; | ||
233 | } | ||
234 | read_unlock_irqrestore(&pwrdm_rwlock, flags); | ||
235 | |||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * pwrdm_add_wkdep - add a wakeup dependency from pwrdm2 to pwrdm1 | ||
241 | * @pwrdm1: wake this struct powerdomain * up (dependent) | ||
242 | * @pwrdm2: when this struct powerdomain * wakes up (source) | ||
243 | * | ||
244 | * When the powerdomain represented by pwrdm2 wakes up (due to an | ||
245 | * interrupt), wake up pwrdm1. Implemented in hardware on the OMAP, | ||
246 | * this feature is designed to reduce wakeup latency of the dependent | ||
247 | * powerdomain. Returns -EINVAL if presented with invalid powerdomain | ||
248 | * pointers, -ENOENT if pwrdm2 cannot wake up pwrdm1 in hardware, or | ||
249 | * 0 upon success. | ||
250 | */ | ||
251 | int pwrdm_add_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
252 | { | ||
253 | struct powerdomain *p; | ||
254 | |||
255 | if (!pwrdm1) | ||
256 | return -EINVAL; | ||
257 | |||
258 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs); | ||
259 | if (IS_ERR(p)) { | ||
260 | pr_debug("powerdomain: hardware cannot set/clear wake up of " | ||
261 | "%s when %s wakes up\n", pwrdm1->name, pwrdm2->name); | ||
262 | return IS_ERR(p); | ||
263 | } | ||
264 | |||
265 | pr_debug("powerdomain: hardware will wake up %s when %s wakes up\n", | ||
266 | pwrdm1->name, pwrdm2->name); | ||
267 | |||
268 | prm_set_mod_reg_bits((1 << pwrdm2->dep_bit), | ||
269 | pwrdm1->prcm_offs, PM_WKDEP); | ||
270 | |||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * pwrdm_del_wkdep - remove a wakeup dependency from pwrdm2 to pwrdm1 | ||
276 | * @pwrdm1: wake this struct powerdomain * up (dependent) | ||
277 | * @pwrdm2: when this struct powerdomain * wakes up (source) | ||
278 | * | ||
279 | * Remove a wakeup dependency that causes pwrdm1 to wake up when pwrdm2 | ||
280 | * wakes up. Returns -EINVAL if presented with invalid powerdomain | ||
281 | * pointers, -ENOENT if pwrdm2 cannot wake up pwrdm1 in hardware, or | ||
282 | * 0 upon success. | ||
283 | */ | ||
284 | int pwrdm_del_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
285 | { | ||
286 | struct powerdomain *p; | ||
287 | |||
288 | if (!pwrdm1) | ||
289 | return -EINVAL; | ||
290 | |||
291 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs); | ||
292 | if (IS_ERR(p)) { | ||
293 | pr_debug("powerdomain: hardware cannot set/clear wake up of " | ||
294 | "%s when %s wakes up\n", pwrdm1->name, pwrdm2->name); | ||
295 | return IS_ERR(p); | ||
296 | } | ||
297 | |||
298 | pr_debug("powerdomain: hardware will no longer wake up %s after %s " | ||
299 | "wakes up\n", pwrdm1->name, pwrdm2->name); | ||
300 | |||
301 | prm_clear_mod_reg_bits((1 << pwrdm2->dep_bit), | ||
302 | pwrdm1->prcm_offs, PM_WKDEP); | ||
303 | |||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | /** | ||
308 | * pwrdm_read_wkdep - read wakeup dependency state from pwrdm2 to pwrdm1 | ||
309 | * @pwrdm1: wake this struct powerdomain * up (dependent) | ||
310 | * @pwrdm2: when this struct powerdomain * wakes up (source) | ||
311 | * | ||
312 | * Return 1 if a hardware wakeup dependency exists wherein pwrdm1 will be | ||
313 | * awoken when pwrdm2 wakes up; 0 if dependency is not set; -EINVAL | ||
314 | * if either powerdomain pointer is invalid; or -ENOENT if the hardware | ||
315 | * is incapable. | ||
316 | * | ||
317 | * REVISIT: Currently this function only represents software-controllable | ||
318 | * wakeup dependencies. Wakeup dependencies fixed in hardware are not | ||
319 | * yet handled here. | ||
320 | */ | ||
321 | int pwrdm_read_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
322 | { | ||
323 | struct powerdomain *p; | ||
324 | |||
325 | if (!pwrdm1) | ||
326 | return -EINVAL; | ||
327 | |||
328 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs); | ||
329 | if (IS_ERR(p)) { | ||
330 | pr_debug("powerdomain: hardware cannot set/clear wake up of " | ||
331 | "%s when %s wakes up\n", pwrdm1->name, pwrdm2->name); | ||
332 | return IS_ERR(p); | ||
333 | } | ||
334 | |||
335 | return prm_read_mod_bits_shift(pwrdm1->prcm_offs, PM_WKDEP, | ||
336 | (1 << pwrdm2->dep_bit)); | ||
337 | } | ||
338 | |||
339 | /** | ||
340 | * pwrdm_add_sleepdep - add a sleep dependency from pwrdm2 to pwrdm1 | ||
341 | * @pwrdm1: prevent this struct powerdomain * from sleeping (dependent) | ||
342 | * @pwrdm2: when this struct powerdomain * is active (source) | ||
343 | * | ||
344 | * Prevent pwrdm1 from automatically going inactive (and then to | ||
345 | * retention or off) if pwrdm2 is still active. Returns -EINVAL if | ||
346 | * presented with invalid powerdomain pointers or called on a machine | ||
347 | * that does not support software-configurable hardware sleep dependencies, | ||
348 | * -ENOENT if the specified dependency cannot be set in hardware, or | ||
349 | * 0 upon success. | ||
350 | */ | ||
351 | int pwrdm_add_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
352 | { | ||
353 | struct powerdomain *p; | ||
354 | |||
355 | if (!pwrdm1) | ||
356 | return -EINVAL; | ||
357 | |||
358 | if (!cpu_is_omap34xx()) | ||
359 | return -EINVAL; | ||
360 | |||
361 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs); | ||
362 | if (IS_ERR(p)) { | ||
363 | pr_debug("powerdomain: hardware cannot set/clear sleep " | ||
364 | "dependency affecting %s from %s\n", pwrdm1->name, | ||
365 | pwrdm2->name); | ||
366 | return IS_ERR(p); | ||
367 | } | ||
368 | |||
369 | pr_debug("powerdomain: will prevent %s from sleeping if %s is active\n", | ||
370 | pwrdm1->name, pwrdm2->name); | ||
371 | |||
372 | cm_set_mod_reg_bits((1 << pwrdm2->dep_bit), | ||
373 | pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP); | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | /** | ||
379 | * pwrdm_del_sleepdep - remove a sleep dependency from pwrdm2 to pwrdm1 | ||
380 | * @pwrdm1: prevent this struct powerdomain * from sleeping (dependent) | ||
381 | * @pwrdm2: when this struct powerdomain * is active (source) | ||
382 | * | ||
383 | * Allow pwrdm1 to automatically go inactive (and then to retention or | ||
384 | * off), independent of the activity state of pwrdm2. Returns -EINVAL | ||
385 | * if presented with invalid powerdomain pointers or called on a machine | ||
386 | * that does not support software-configurable hardware sleep dependencies, | ||
387 | * -ENOENT if the specified dependency cannot be cleared in hardware, or | ||
388 | * 0 upon success. | ||
389 | */ | ||
390 | int pwrdm_del_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
391 | { | ||
392 | struct powerdomain *p; | ||
393 | |||
394 | if (!pwrdm1) | ||
395 | return -EINVAL; | ||
396 | |||
397 | if (!cpu_is_omap34xx()) | ||
398 | return -EINVAL; | ||
399 | |||
400 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs); | ||
401 | if (IS_ERR(p)) { | ||
402 | pr_debug("powerdomain: hardware cannot set/clear sleep " | ||
403 | "dependency affecting %s from %s\n", pwrdm1->name, | ||
404 | pwrdm2->name); | ||
405 | return IS_ERR(p); | ||
406 | } | ||
407 | |||
408 | pr_debug("powerdomain: will no longer prevent %s from sleeping if " | ||
409 | "%s is active\n", pwrdm1->name, pwrdm2->name); | ||
410 | |||
411 | cm_clear_mod_reg_bits((1 << pwrdm2->dep_bit), | ||
412 | pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP); | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | /** | ||
418 | * pwrdm_read_sleepdep - read sleep dependency state from pwrdm2 to pwrdm1 | ||
419 | * @pwrdm1: prevent this struct powerdomain * from sleeping (dependent) | ||
420 | * @pwrdm2: when this struct powerdomain * is active (source) | ||
421 | * | ||
422 | * Return 1 if a hardware sleep dependency exists wherein pwrdm1 will | ||
423 | * not be allowed to automatically go inactive if pwrdm2 is active; | ||
424 | * 0 if pwrdm1's automatic power state inactivity transition is independent | ||
425 | * of pwrdm2's; -EINVAL if either powerdomain pointer is invalid or called | ||
426 | * on a machine that does not support software-configurable hardware sleep | ||
427 | * dependencies; or -ENOENT if the hardware is incapable. | ||
428 | * | ||
429 | * REVISIT: Currently this function only represents software-controllable | ||
430 | * sleep dependencies. Sleep dependencies fixed in hardware are not | ||
431 | * yet handled here. | ||
432 | */ | ||
433 | int pwrdm_read_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) | ||
434 | { | ||
435 | struct powerdomain *p; | ||
436 | |||
437 | if (!pwrdm1) | ||
438 | return -EINVAL; | ||
439 | |||
440 | if (!cpu_is_omap34xx()) | ||
441 | return -EINVAL; | ||
442 | |||
443 | p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs); | ||
444 | if (IS_ERR(p)) { | ||
445 | pr_debug("powerdomain: hardware cannot set/clear sleep " | ||
446 | "dependency affecting %s from %s\n", pwrdm1->name, | ||
447 | pwrdm2->name); | ||
448 | return IS_ERR(p); | ||
449 | } | ||
450 | |||
451 | return prm_read_mod_bits_shift(pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP, | ||
452 | (1 << pwrdm2->dep_bit)); | ||
453 | } | ||
454 | |||
455 | /** | ||
456 | * pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain | ||
457 | * @pwrdm: struct powerdomain * | ||
458 | * | ||
459 | * Return the number of controllable memory banks in powerdomain pwrdm, | ||
460 | * starting with 1. Returns -EINVAL if the powerdomain pointer is null. | ||
461 | */ | ||
462 | int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm) | ||
463 | { | ||
464 | if (!pwrdm) | ||
465 | return -EINVAL; | ||
466 | |||
467 | return pwrdm->banks; | ||
468 | } | ||
469 | |||
470 | /** | ||
471 | * pwrdm_set_next_pwrst - set next powerdomain power state | ||
472 | * @pwrdm: struct powerdomain * to set | ||
473 | * @pwrst: one of the PWRDM_POWER_* macros | ||
474 | * | ||
475 | * Set the powerdomain pwrdm's next power state to pwrst. The powerdomain | ||
476 | * may not enter this state immediately if the preconditions for this state | ||
477 | * have not been satisfied. Returns -EINVAL if the powerdomain pointer is | ||
478 | * null or if the power state is invalid for the powerdomin, or returns 0 | ||
479 | * upon success. | ||
480 | */ | ||
481 | int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) | ||
482 | { | ||
483 | if (!pwrdm) | ||
484 | return -EINVAL; | ||
485 | |||
486 | if (!(pwrdm->pwrsts & (1 << pwrst))) | ||
487 | return -EINVAL; | ||
488 | |||
489 | pr_debug("powerdomain: setting next powerstate for %s to %0x\n", | ||
490 | pwrdm->name, pwrst); | ||
491 | |||
492 | prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK, | ||
493 | (pwrst << OMAP_POWERSTATE_SHIFT), | ||
494 | pwrdm->prcm_offs, PM_PWSTCTRL); | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | /** | ||
500 | * pwrdm_read_next_pwrst - get next powerdomain power state | ||
501 | * @pwrdm: struct powerdomain * to get power state | ||
502 | * | ||
503 | * Return the powerdomain pwrdm's next power state. Returns -EINVAL | ||
504 | * if the powerdomain pointer is null or returns the next power state | ||
505 | * upon success. | ||
506 | */ | ||
507 | int pwrdm_read_next_pwrst(struct powerdomain *pwrdm) | ||
508 | { | ||
509 | if (!pwrdm) | ||
510 | return -EINVAL; | ||
511 | |||
512 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTCTRL, | ||
513 | OMAP_POWERSTATE_MASK); | ||
514 | } | ||
515 | |||
516 | /** | ||
517 | * pwrdm_read_pwrst - get current powerdomain power state | ||
518 | * @pwrdm: struct powerdomain * to get power state | ||
519 | * | ||
520 | * Return the powerdomain pwrdm's current power state. Returns -EINVAL | ||
521 | * if the powerdomain pointer is null or returns the current power state | ||
522 | * upon success. | ||
523 | */ | ||
524 | int pwrdm_read_pwrst(struct powerdomain *pwrdm) | ||
525 | { | ||
526 | if (!pwrdm) | ||
527 | return -EINVAL; | ||
528 | |||
529 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, | ||
530 | OMAP_POWERSTATEST_MASK); | ||
531 | } | ||
532 | |||
533 | /** | ||
534 | * pwrdm_read_prev_pwrst - get previous powerdomain power state | ||
535 | * @pwrdm: struct powerdomain * to get previous power state | ||
536 | * | ||
537 | * Return the powerdomain pwrdm's previous power state. Returns -EINVAL | ||
538 | * if the powerdomain pointer is null or returns the previous power state | ||
539 | * upon success. | ||
540 | */ | ||
541 | int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) | ||
542 | { | ||
543 | if (!pwrdm) | ||
544 | return -EINVAL; | ||
545 | |||
546 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST, | ||
547 | OMAP3430_LASTPOWERSTATEENTERED_MASK); | ||
548 | } | ||
549 | |||
550 | /** | ||
551 | * pwrdm_set_logic_retst - set powerdomain logic power state upon retention | ||
552 | * @pwrdm: struct powerdomain * to set | ||
553 | * @pwrst: one of the PWRDM_POWER_* macros | ||
554 | * | ||
555 | * Set the next power state that the logic portion of the powerdomain | ||
556 | * pwrdm will enter when the powerdomain enters retention. This will | ||
557 | * be either RETENTION or OFF, if supported. Returns -EINVAL if the | ||
558 | * powerdomain pointer is null or the target power state is not not | ||
559 | * supported, or returns 0 upon success. | ||
560 | */ | ||
561 | int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) | ||
562 | { | ||
563 | if (!pwrdm) | ||
564 | return -EINVAL; | ||
565 | |||
566 | if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst))) | ||
567 | return -EINVAL; | ||
568 | |||
569 | pr_debug("powerdomain: setting next logic powerstate for %s to %0x\n", | ||
570 | pwrdm->name, pwrst); | ||
571 | |||
572 | /* | ||
573 | * The register bit names below may not correspond to the | ||
574 | * actual names of the bits in each powerdomain's register, | ||
575 | * but the type of value returned is the same for each | ||
576 | * powerdomain. | ||
577 | */ | ||
578 | prm_rmw_mod_reg_bits(OMAP3430_LOGICL1CACHERETSTATE, | ||
579 | (pwrst << __ffs(OMAP3430_LOGICL1CACHERETSTATE)), | ||
580 | pwrdm->prcm_offs, PM_PWSTCTRL); | ||
581 | |||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | /** | ||
586 | * pwrdm_set_mem_onst - set memory power state while powerdomain ON | ||
587 | * @pwrdm: struct powerdomain * to set | ||
588 | * @bank: memory bank number to set (0-3) | ||
589 | * @pwrst: one of the PWRDM_POWER_* macros | ||
590 | * | ||
591 | * Set the next power state that memory bank x of the powerdomain | ||
592 | * pwrdm will enter when the powerdomain enters the ON state. Bank | ||
593 | * will be a number from 0 to 3, and represents different types of | ||
594 | * memory, depending on the powerdomain. Returns -EINVAL if the | ||
595 | * powerdomain pointer is null or the target power state is not not | ||
596 | * supported for this memory bank, -EEXIST if the target memory bank | ||
597 | * does not exist or is not controllable, or returns 0 upon success. | ||
598 | */ | ||
599 | int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) | ||
600 | { | ||
601 | u32 m; | ||
602 | |||
603 | if (!pwrdm) | ||
604 | return -EINVAL; | ||
605 | |||
606 | if (pwrdm->banks < (bank + 1)) | ||
607 | return -EEXIST; | ||
608 | |||
609 | if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst))) | ||
610 | return -EINVAL; | ||
611 | |||
612 | pr_debug("powerdomain: setting next memory powerstate for domain %s " | ||
613 | "bank %0x while pwrdm-ON to %0x\n", pwrdm->name, bank, pwrst); | ||
614 | |||
615 | /* | ||
616 | * The register bit names below may not correspond to the | ||
617 | * actual names of the bits in each powerdomain's register, | ||
618 | * but the type of value returned is the same for each | ||
619 | * powerdomain. | ||
620 | */ | ||
621 | switch (bank) { | ||
622 | case 0: | ||
623 | m = OMAP3430_SHAREDL1CACHEFLATONSTATE_MASK; | ||
624 | break; | ||
625 | case 1: | ||
626 | m = OMAP3430_L1FLATMEMONSTATE_MASK; | ||
627 | break; | ||
628 | case 2: | ||
629 | m = OMAP3430_SHAREDL2CACHEFLATONSTATE_MASK; | ||
630 | break; | ||
631 | case 3: | ||
632 | m = OMAP3430_L2FLATMEMONSTATE_MASK; | ||
633 | break; | ||
634 | default: | ||
635 | WARN_ON(1); /* should never happen */ | ||
636 | return -EEXIST; | ||
637 | } | ||
638 | |||
639 | prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), | ||
640 | pwrdm->prcm_offs, PM_PWSTCTRL); | ||
641 | |||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | /** | ||
646 | * pwrdm_set_mem_retst - set memory power state while powerdomain in RET | ||
647 | * @pwrdm: struct powerdomain * to set | ||
648 | * @bank: memory bank number to set (0-3) | ||
649 | * @pwrst: one of the PWRDM_POWER_* macros | ||
650 | * | ||
651 | * Set the next power state that memory bank x of the powerdomain | ||
652 | * pwrdm will enter when the powerdomain enters the RETENTION state. | ||
653 | * Bank will be a number from 0 to 3, and represents different types | ||
654 | * of memory, depending on the powerdomain. pwrst will be either | ||
655 | * RETENTION or OFF, if supported. Returns -EINVAL if the powerdomain | ||
656 | * pointer is null or the target power state is not not supported for | ||
657 | * this memory bank, -EEXIST if the target memory bank does not exist | ||
658 | * or is not controllable, or returns 0 upon success. | ||
659 | */ | ||
660 | int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) | ||
661 | { | ||
662 | u32 m; | ||
663 | |||
664 | if (!pwrdm) | ||
665 | return -EINVAL; | ||
666 | |||
667 | if (pwrdm->banks < (bank + 1)) | ||
668 | return -EEXIST; | ||
669 | |||
670 | if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst))) | ||
671 | return -EINVAL; | ||
672 | |||
673 | pr_debug("powerdomain: setting next memory powerstate for domain %s " | ||
674 | "bank %0x while pwrdm-RET to %0x\n", pwrdm->name, bank, pwrst); | ||
675 | |||
676 | /* | ||
677 | * The register bit names below may not correspond to the | ||
678 | * actual names of the bits in each powerdomain's register, | ||
679 | * but the type of value returned is the same for each | ||
680 | * powerdomain. | ||
681 | */ | ||
682 | switch (bank) { | ||
683 | case 0: | ||
684 | m = OMAP3430_SHAREDL1CACHEFLATRETSTATE; | ||
685 | break; | ||
686 | case 1: | ||
687 | m = OMAP3430_L1FLATMEMRETSTATE; | ||
688 | break; | ||
689 | case 2: | ||
690 | m = OMAP3430_SHAREDL2CACHEFLATRETSTATE; | ||
691 | break; | ||
692 | case 3: | ||
693 | m = OMAP3430_L2FLATMEMRETSTATE; | ||
694 | break; | ||
695 | default: | ||
696 | WARN_ON(1); /* should never happen */ | ||
697 | return -EEXIST; | ||
698 | } | ||
699 | |||
700 | prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), pwrdm->prcm_offs, | ||
701 | PM_PWSTCTRL); | ||
702 | |||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | /** | ||
707 | * pwrdm_read_logic_pwrst - get current powerdomain logic retention power state | ||
708 | * @pwrdm: struct powerdomain * to get current logic retention power state | ||
709 | * | ||
710 | * Return the current power state that the logic portion of | ||
711 | * powerdomain pwrdm will enter | ||
712 | * Returns -EINVAL if the powerdomain pointer is null or returns the | ||
713 | * current logic retention power state upon success. | ||
714 | */ | ||
715 | int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) | ||
716 | { | ||
717 | if (!pwrdm) | ||
718 | return -EINVAL; | ||
719 | |||
720 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, | ||
721 | OMAP3430_LOGICSTATEST); | ||
722 | } | ||
723 | |||
724 | /** | ||
725 | * pwrdm_read_prev_logic_pwrst - get previous powerdomain logic power state | ||
726 | * @pwrdm: struct powerdomain * to get previous logic power state | ||
727 | * | ||
728 | * Return the powerdomain pwrdm's logic power state. Returns -EINVAL | ||
729 | * if the powerdomain pointer is null or returns the previous logic | ||
730 | * power state upon success. | ||
731 | */ | ||
732 | int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) | ||
733 | { | ||
734 | if (!pwrdm) | ||
735 | return -EINVAL; | ||
736 | |||
737 | /* | ||
738 | * The register bit names below may not correspond to the | ||
739 | * actual names of the bits in each powerdomain's register, | ||
740 | * but the type of value returned is the same for each | ||
741 | * powerdomain. | ||
742 | */ | ||
743 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST, | ||
744 | OMAP3430_LASTLOGICSTATEENTERED); | ||
745 | } | ||
746 | |||
747 | /** | ||
748 | * pwrdm_read_mem_pwrst - get current memory bank power state | ||
749 | * @pwrdm: struct powerdomain * to get current memory bank power state | ||
750 | * @bank: memory bank number (0-3) | ||
751 | * | ||
752 | * Return the powerdomain pwrdm's current memory power state for bank | ||
753 | * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if | ||
754 | * the target memory bank does not exist or is not controllable, or | ||
755 | * returns the current memory power state upon success. | ||
756 | */ | ||
757 | int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) | ||
758 | { | ||
759 | u32 m; | ||
760 | |||
761 | if (!pwrdm) | ||
762 | return -EINVAL; | ||
763 | |||
764 | if (pwrdm->banks < (bank + 1)) | ||
765 | return -EEXIST; | ||
766 | |||
767 | /* | ||
768 | * The register bit names below may not correspond to the | ||
769 | * actual names of the bits in each powerdomain's register, | ||
770 | * but the type of value returned is the same for each | ||
771 | * powerdomain. | ||
772 | */ | ||
773 | switch (bank) { | ||
774 | case 0: | ||
775 | m = OMAP3430_SHAREDL1CACHEFLATSTATEST_MASK; | ||
776 | break; | ||
777 | case 1: | ||
778 | m = OMAP3430_L1FLATMEMSTATEST_MASK; | ||
779 | break; | ||
780 | case 2: | ||
781 | m = OMAP3430_SHAREDL2CACHEFLATSTATEST_MASK; | ||
782 | break; | ||
783 | case 3: | ||
784 | m = OMAP3430_L2FLATMEMSTATEST_MASK; | ||
785 | break; | ||
786 | default: | ||
787 | WARN_ON(1); /* should never happen */ | ||
788 | return -EEXIST; | ||
789 | } | ||
790 | |||
791 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, m); | ||
792 | } | ||
793 | |||
794 | /** | ||
795 | * pwrdm_read_prev_mem_pwrst - get previous memory bank power state | ||
796 | * @pwrdm: struct powerdomain * to get previous memory bank power state | ||
797 | * @bank: memory bank number (0-3) | ||
798 | * | ||
799 | * Return the powerdomain pwrdm's previous memory power state for bank | ||
800 | * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if | ||
801 | * the target memory bank does not exist or is not controllable, or | ||
802 | * returns the previous memory power state upon success. | ||
803 | */ | ||
804 | int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) | ||
805 | { | ||
806 | u32 m; | ||
807 | |||
808 | if (!pwrdm) | ||
809 | return -EINVAL; | ||
810 | |||
811 | if (pwrdm->banks < (bank + 1)) | ||
812 | return -EEXIST; | ||
813 | |||
814 | /* | ||
815 | * The register bit names below may not correspond to the | ||
816 | * actual names of the bits in each powerdomain's register, | ||
817 | * but the type of value returned is the same for each | ||
818 | * powerdomain. | ||
819 | */ | ||
820 | switch (bank) { | ||
821 | case 0: | ||
822 | m = OMAP3430_LASTMEM1STATEENTERED_MASK; | ||
823 | break; | ||
824 | case 1: | ||
825 | m = OMAP3430_LASTMEM2STATEENTERED_MASK; | ||
826 | break; | ||
827 | case 2: | ||
828 | m = OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK; | ||
829 | break; | ||
830 | case 3: | ||
831 | m = OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK; | ||
832 | break; | ||
833 | default: | ||
834 | WARN_ON(1); /* should never happen */ | ||
835 | return -EEXIST; | ||
836 | } | ||
837 | |||
838 | return prm_read_mod_bits_shift(pwrdm->prcm_offs, | ||
839 | OMAP3430_PM_PREPWSTST, m); | ||
840 | } | ||
841 | |||
842 | /** | ||
843 | * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm | ||
844 | * @pwrdm: struct powerdomain * to clear | ||
845 | * | ||
846 | * Clear the powerdomain's previous power state register. Clears the | ||
847 | * entire register, including logic and memory bank previous power states. | ||
848 | * Returns -EINVAL if the powerdomain pointer is null, or returns 0 upon | ||
849 | * success. | ||
850 | */ | ||
851 | int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) | ||
852 | { | ||
853 | if (!pwrdm) | ||
854 | return -EINVAL; | ||
855 | |||
856 | /* | ||
857 | * XXX should get the powerdomain's current state here; | ||
858 | * warn & fail if it is not ON. | ||
859 | */ | ||
860 | |||
861 | pr_debug("powerdomain: clearing previous power state reg for %s\n", | ||
862 | pwrdm->name); | ||
863 | |||
864 | prm_write_mod_reg(0, pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST); | ||
865 | |||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | /** | ||
870 | * pwrdm_wait_transition - wait for powerdomain power transition to finish | ||
871 | * @pwrdm: struct powerdomain * to wait for | ||
872 | * | ||
873 | * If the powerdomain pwrdm is in the process of a state transition, | ||
874 | * spin until it completes the power transition, or until an iteration | ||
875 | * bailout value is reached. Returns -EINVAL if the powerdomain | ||
876 | * pointer is null, -EAGAIN if the bailout value was reached, or | ||
877 | * returns 0 upon success. | ||
878 | */ | ||
879 | int pwrdm_wait_transition(struct powerdomain *pwrdm) | ||
880 | { | ||
881 | u32 c = 0; | ||
882 | |||
883 | if (!pwrdm) | ||
884 | return -EINVAL; | ||
885 | |||
886 | /* | ||
887 | * REVISIT: pwrdm_wait_transition() may be better implemented | ||
888 | * via a callback and a periodic timer check -- how long do we expect | ||
889 | * powerdomain transitions to take? | ||
890 | */ | ||
891 | |||
892 | /* XXX Is this udelay() value meaningful? */ | ||
893 | while ((prm_read_mod_reg(pwrdm->prcm_offs, PM_PWSTST) & | ||
894 | OMAP_INTRANSITION) && | ||
895 | (c++ < PWRDM_TRANSITION_BAILOUT)) | ||
896 | udelay(1); | ||
897 | |||
898 | if (c >= PWRDM_TRANSITION_BAILOUT) { | ||
899 | printk(KERN_ERR "powerdomain: waited too long for " | ||
900 | "powerdomain %s to complete transition\n", pwrdm->name); | ||
901 | return -EAGAIN; | ||
902 | } | ||
903 | |||
904 | pr_debug("powerdomain: completed transition in %d loops\n", c); | ||
905 | |||
906 | return 0; | ||
907 | } | ||
908 | |||
909 | |||
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index b917206ee906..e815fa35f7f4 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig | |||
@@ -29,6 +29,18 @@ config OMAP_DEBUG_LEDS | |||
29 | depends on OMAP_DEBUG_DEVICES | 29 | depends on OMAP_DEBUG_DEVICES |
30 | default y if LEDS || LEDS_OMAP_DEBUG | 30 | default y if LEDS || LEDS_OMAP_DEBUG |
31 | 31 | ||
32 | config OMAP_DEBUG_POWERDOMAIN | ||
33 | bool "Emit debug messages from powerdomain layer" | ||
34 | depends on ARCH_OMAP2 || ARCH_OMAP3 | ||
35 | default n | ||
36 | help | ||
37 | Say Y here if you want to compile in powerdomain layer | ||
38 | debugging messages for OMAP2/3. These messages can | ||
39 | provide more detail as to why some powerdomain calls | ||
40 | may be failing, and will also emit a descriptive message | ||
41 | for every powerdomain register write. However, the | ||
42 | extra detail costs some memory. | ||
43 | |||
32 | config OMAP_RESET_CLOCKS | 44 | config OMAP_RESET_CLOCKS |
33 | bool "Reset unused clocks during boot" | 45 | bool "Reset unused clocks during boot" |
34 | depends on ARCH_OMAP | 46 | depends on ARCH_OMAP |
diff --git a/arch/arm/plat-omap/include/mach/powerdomain.h b/arch/arm/plat-omap/include/mach/powerdomain.h new file mode 100644 index 000000000000..c8f52c6f8b7e --- /dev/null +++ b/arch/arm/plat-omap/include/mach/powerdomain.h | |||
@@ -0,0 +1,139 @@ | |||
1 | /* | ||
2 | * OMAP2/3 powerdomain control | ||
3 | * | ||
4 | * Copyright (C) 2007-8 Texas Instruments, Inc. | ||
5 | * Copyright (C) 2007-8 Nokia Corporation | ||
6 | * | ||
7 | * Written by Paul Walmsley | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #ifndef ASM_ARM_ARCH_OMAP_POWERDOMAIN | ||
15 | #define ASM_ARM_ARCH_OMAP_POWERDOMAIN | ||
16 | |||
17 | #include <linux/types.h> | ||
18 | #include <linux/list.h> | ||
19 | |||
20 | #include <asm/atomic.h> | ||
21 | |||
22 | #include <mach/cpu.h> | ||
23 | |||
24 | |||
25 | /* Powerdomain basic power states */ | ||
26 | #define PWRDM_POWER_OFF 0x0 | ||
27 | #define PWRDM_POWER_RET 0x1 | ||
28 | #define PWRDM_POWER_INACTIVE 0x2 | ||
29 | #define PWRDM_POWER_ON 0x3 | ||
30 | |||
31 | /* Powerdomain allowable state bitfields */ | ||
32 | #define PWRSTS_OFF_ON ((1 << PWRDM_POWER_OFF) | \ | ||
33 | (1 << PWRDM_POWER_ON)) | ||
34 | |||
35 | #define PWRSTS_OFF_RET ((1 << PWRDM_POWER_OFF) | \ | ||
36 | (1 << PWRDM_POWER_RET)) | ||
37 | |||
38 | #define PWRSTS_OFF_RET_ON (PWRSTS_OFF_RET | (1 << PWRDM_POWER_ON)) | ||
39 | |||
40 | |||
41 | /* | ||
42 | * Number of memory banks that are power-controllable. On OMAP3430, the | ||
43 | * maximum is 4. | ||
44 | */ | ||
45 | #define PWRDM_MAX_MEM_BANKS 4 | ||
46 | |||
47 | /* XXX A completely arbitrary number. What is reasonable here? */ | ||
48 | #define PWRDM_TRANSITION_BAILOUT 100000 | ||
49 | |||
50 | struct powerdomain; | ||
51 | |||
52 | /* Encodes dependencies between powerdomains - statically defined */ | ||
53 | struct pwrdm_dep { | ||
54 | |||
55 | /* Powerdomain name */ | ||
56 | const char *pwrdm_name; | ||
57 | |||
58 | /* Powerdomain pointer - resolved by the powerdomain code */ | ||
59 | struct powerdomain *pwrdm; | ||
60 | |||
61 | /* Flags to mark OMAP chip restrictions, etc. */ | ||
62 | const struct omap_chip_id omap_chip; | ||
63 | |||
64 | }; | ||
65 | |||
66 | struct powerdomain { | ||
67 | |||
68 | /* Powerdomain name */ | ||
69 | const char *name; | ||
70 | |||
71 | /* the address offset from CM_BASE/PRM_BASE */ | ||
72 | const s16 prcm_offs; | ||
73 | |||
74 | /* Used to represent the OMAP chip types containing this pwrdm */ | ||
75 | const struct omap_chip_id omap_chip; | ||
76 | |||
77 | /* Bit shift of this powerdomain's PM_WKDEP/CM_SLEEPDEP bit */ | ||
78 | const u8 dep_bit; | ||
79 | |||
80 | /* Powerdomains that can be told to wake this powerdomain up */ | ||
81 | struct pwrdm_dep *wkdep_srcs; | ||
82 | |||
83 | /* Powerdomains that can be told to keep this pwrdm from inactivity */ | ||
84 | struct pwrdm_dep *sleepdep_srcs; | ||
85 | |||
86 | /* Possible powerdomain power states */ | ||
87 | const u8 pwrsts; | ||
88 | |||
89 | /* Possible logic power states when pwrdm in RETENTION */ | ||
90 | const u8 pwrsts_logic_ret; | ||
91 | |||
92 | /* Number of software-controllable memory banks in this powerdomain */ | ||
93 | const u8 banks; | ||
94 | |||
95 | /* Possible memory bank pwrstates when pwrdm in RETENTION */ | ||
96 | const u8 pwrsts_mem_ret[PWRDM_MAX_MEM_BANKS]; | ||
97 | |||
98 | /* Possible memory bank pwrstates when pwrdm is ON */ | ||
99 | const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS]; | ||
100 | |||
101 | struct list_head node; | ||
102 | |||
103 | }; | ||
104 | |||
105 | |||
106 | void pwrdm_init(struct powerdomain **pwrdm_list); | ||
107 | |||
108 | int pwrdm_register(struct powerdomain *pwrdm); | ||
109 | int pwrdm_unregister(struct powerdomain *pwrdm); | ||
110 | struct powerdomain *pwrdm_lookup(const char *name); | ||
111 | |||
112 | int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm)); | ||
113 | |||
114 | int pwrdm_add_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2); | ||
115 | int pwrdm_del_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2); | ||
116 | int pwrdm_read_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2); | ||
117 | int pwrdm_add_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2); | ||
118 | int pwrdm_del_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2); | ||
119 | int pwrdm_read_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2); | ||
120 | |||
121 | int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm); | ||
122 | |||
123 | int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst); | ||
124 | int pwrdm_read_next_pwrst(struct powerdomain *pwrdm); | ||
125 | int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm); | ||
126 | int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm); | ||
127 | |||
128 | int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst); | ||
129 | int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst); | ||
130 | int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst); | ||
131 | |||
132 | int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm); | ||
133 | int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm); | ||
134 | int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank); | ||
135 | int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank); | ||
136 | |||
137 | int pwrdm_wait_transition(struct powerdomain *pwrdm); | ||
138 | |||
139 | #endif | ||